# # # patch "commands.cc" # from [dcc5cd4f304b864e0b947a3c3e2a6087ace6fc9b] # to [526ef342799e488b6002c2c9ea8913828bf3e39a] # # patch "ui.cc" # from [21bba61b39d2178a31aa97437d0b60688003ff42] # to [5a81f282c648b1a3ed51475e214ec5475652b07e] # # patch "ui.hh" # from [ea2c11aaa5de4bc00851ee8bb7a9730f509c93fc] # to [9e12d78e6a03647b90d790c0abb0fbfa86da7c6e] # ============================================================ --- commands.cc dcc5cd4f304b864e0b947a3c3e2a6087ace6fc9b +++ commands.cc 526ef342799e488b6002c2c9ea8913828bf3e39a @@ -519,67 +519,15 @@ namespace commands static void describe(const string & tag, const string & abstract, size_t colabstract, ostream & out) { - // The algorithm below avoids printing an space on entry (note that - // there are two before the tag but just one after it) and considers - // that the colabstract is always one unit less than that given on - // entry because it always prints a single space before each word. I(colabstract > 0); size_t col = 0; out << " " << tag << " "; col += display_width(utf8(tag + " ")); - while (col++ < colabstract - 1) - out << ' '; - col = colabstract - 1; - - vector< utf8 > words = split_into_words(utf8(abstract)); - - const size_t maxcol = terminal_width(); - vector< utf8 >::const_iterator i = words.begin(); - while (i != words.end()) - { - string const & word = (*i)(); - - if (col + word.length() + 1 >= maxcol) - { - out << '\n'; - col = 0; - - // Skip empty words at the beginning of the line so that they do - // not mess with indentation. These "words" appear because we - // put two spaces between sentences, and one of these is - // transformed into a word. - // - // Another approach could be to simply omit these words (by - // modifying split_into_words) and then add this formatting (two - // spaces between sentences) from this algorithm. But then, - // other kinds of formatting could not be allowed in the original - // strings... (i.e., they'd disappear after we mangled them). - if (word == "") - { - do - i++; - while (i != words.end() && (*i)().empty()); - if (i == words.end()) - break; - else - { - while (col++ < colabstract - 1) - out << ' '; - continue; - } - } - - while (col++ < colabstract - 1) - out << ' '; - } - - out << ' ' << word; - col += word.length() + 1; - i++; - } - out << '\n'; + out << string(colabstract - col, ' '); + col = colabstract; + out << format_text(abstract, colabstract, col) << '\n'; } static void explain_children(command::children_set const & children, @@ -625,16 +573,19 @@ namespace commands ident.end()))(); if (visibleid.empty()) - out << F(safe_gettext("Commands in group '%s':")) % - join_words(ident)() << "\n\n"; + out << format_text(F(safe_gettext("Commands in group '%s':")) % + join_words(ident)()) + << "\n\n"; else { if (cmd->children().size() > 0) - out << F(safe_gettext("Subcommands of '%s %s':")) % - ui.prog_name % visibleid << "\n\n"; + out << format_text(F(safe_gettext("Subcommands of '%s %s':")) % + ui.prog_name % visibleid) + << "\n\n"; else - out << F(safe_gettext("Syntax specific to '%s %s':")) % - ui.prog_name % visibleid << "\n\n"; + out << format_text(F(safe_gettext("Syntax specific to '%s %s':")) % + ui.prog_name % visibleid) + << "\n\n"; } // Print command parameters. @@ -657,27 +608,23 @@ namespace commands // Print command description. if (visibleid.empty()) - out << F(safe_gettext("Purpose of group '%s':")) % - join_words(ident)() << "\n\n"; + out << format_text(F(safe_gettext("Purpose of group '%s':")) % + join_words(ident)()) + << "\n\n"; else - out << F(safe_gettext("Description for '%s %s':")) % - ui.prog_name % visibleid << "\n\n"; - split_into_lines(cmd->desc(), lines); - for (vector::const_iterator j = lines.begin(); - j != lines.end(); ++j) - { - describe("", *j, 4, out); - out << '\n'; - } + out << format_text(F(safe_gettext("Description for '%s %s':")) % + ui.prog_name % visibleid) + << "\n\n"; + out << format_text(cmd->desc(), 2) << "\n\n"; // Print all available aliases. if (cmd->names().size() > 1) { command::names_set othernames = cmd->names(); othernames.erase(ident[ident.size() - 1]); - describe("", "Aliases: " + join_words(othernames, ", ")() + - ".", 4, out); - out << '\n'; + out << format_text(F("Aliases: %s.") % + join_words(othernames, ", ")(), 2) + << '\n'; } } @@ -692,17 +639,19 @@ namespace commands if (ident.empty()) { - // TODO Wrap long lines in these messages. - out << "Command groups:\n\n"; + out << format_text("Command groups:") << "\n\n"; explain_children(CMD_REF(__root__)->children(), out); - out << '\n'; - out << "For information on a specific command, type " - "'mtn help [subcommand_name ...]'.\n"; - out << "To see the commands available within a group, type " - "'mtn help '.\n"; - out << "Note that you can always abbreviate a command name as " - "long as it does not conflict with other names.\n"; - out << '\n'; + out << '\n' + << format_text("For information on a specific command, type " + "'mtn help [subcommand_name ...]'.") + << '\n' + << format_text("To see the commands available within a group, " + "type 'mtn help '.") + << '\n' + << format_text("Note that you can always abbreviate a command " + "name as long as it does not conflict with other " + "names.") + << "\n\n"; } else explain_cmd_usage(ident, out); ============================================================ --- ui.cc 21bba61b39d2178a31aa97437d0b60688003ff42 +++ ui.cc 5a81f282c648b1a3ed51475e214ec5475652b07e @@ -600,6 +600,96 @@ guess_terminal_width() return w; } +// A very simple class that adds an operator() to a string that returns +// the string itself. This is to make it compatible with, for example, +// the utf8 class, allowing it to be usable in other contexts without +// encoding conversions. +class string_adaptor : public string +{ +public: + string_adaptor(string const & str) : string(str) {} + string const & operator()(void) const { return *this; } +}; + +// See description for format_text below for more details. +static string +format_paragraph(string const & text, size_t const col, size_t curcol) +{ + I(text.find('\n') == string::npos); + + string formatted; + if (curcol < col) + { + formatted = string(col - curcol, ' '); + curcol = col; + } + + const size_t maxcol = guess_terminal_width(); + + vector< string_adaptor > words = split_into_words(string_adaptor(text)); + for (vector< string_adaptor >::const_iterator iter = words.begin(); + iter != words.end(); iter++) + { + string const & word = (*iter)(); + + if (iter != words.begin() && + curcol + display_width(utf8(word)) + 1 > maxcol) + { + formatted += '\n' + string(col, ' '); + curcol = col; + } + else if (iter != words.begin()) + { + formatted += ' '; + curcol++; + } + + formatted += word; + curcol += display_width(utf8(word)); + } + + return formatted; +} + +// Reformats the given text so that it fits in the current screen with no +// wrapping. +// +// The input text is a series of words and sentences. Paragraphs may be +// separated with a '\n' character, which is taken into account to do the +// proper formatting. The text should not finish in '\n'. +// +// 'col' specifies the column where the text will start and 'curcol' +// specifies the current position of the cursor. +string +format_text(string const & text, size_t const col, size_t curcol) +{ + I(curcol <= col); + + string formatted; + + vector< string > lines; + split_into_lines(text, lines); + for (vector< string >::const_iterator iter = lines.begin(); + iter != lines.end(); iter++) + { + string const & line = *iter; + + formatted += format_paragraph(line, col, curcol); + if (iter + 1 != lines.end()) + formatted += "\n\n"; + curcol = 0; + } + + return formatted; +} + +// See description for the other format_text below for more details. +string +format_text(i18n_format const & text, size_t const col, size_t curcol) +{ + return format_text(text.str(), col, curcol); +} + // Local Variables: // mode: C++ // fill-column: 76 ============================================================ --- ui.hh ea2c11aaa5de4bc00851ee8bb7a9730f509c93fc +++ ui.hh 9e12d78e6a03647b90d790c0abb0fbfa86da7c6e @@ -129,6 +129,11 @@ unsigned int guess_terminal_width(); // (even if there is no terminal) unsigned int guess_terminal_width(); +std::string format_text(std::string const & text, + size_t const col = 0, size_t curcol = 0); +std::string format_text(i18n_format const & text, + size_t const col = 0, size_t curcol = 0); + // Local Variables: // mode: C++ // fill-column: 76