# # # patch "ui.cc" # from [365fa59977896ae3163362b99a5e8d7028eab25e] # to [568e30b266476dd1e77441b48db2e666b8e2faf2] # ============================================================ --- ui.cc 365fa59977896ae3163362b99a5e8d7028eab25e +++ ui.cc 568e30b266476dd1e77441b48db2e666b8e2faf2 @@ -864,43 +864,56 @@ public: }; // See description for format_text below for more details. -static string -format_paragraph(string const & text, size_t const col, size_t curcol) +static vector +wrap_paragraph(string const & text, size_t const line_length, + size_t const first_line_length) { I(text.find('\n') == string::npos); - string formatted; - if (curcol < col) - { - formatted = string(col - curcol, ' '); - curcol = col; - } + vector wrapped; + size_t line_len = 0; + string this_line; - 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, origin::no_fault)) + 1 > maxcol) + size_t word_len = display_width(utf8(word, origin::no_fault)); + size_t wanted_len = (wrapped.empty() ? first_line_length : line_length); + if (iter != words.begin() && line_len + word_len >= wanted_len) { - formatted += '\n' + string(col, ' '); - curcol = col; + wrapped.push_back(this_line); + line_len = 0; + this_line.clear(); } - else if (iter != words.begin()) + if (!this_line.empty()) { - formatted += ' '; - curcol++; + this_line += " "; + ++line_len; } + line_len += word_len; + this_line += word; + } + if (!this_line.empty()) + wrapped.push_back(this_line); - formatted += word; - curcol += display_width(utf8(word, origin::no_fault)); + return wrapped; +} + +static string +format_paragraph(string const & text, size_t const col, size_t curcol) +{ + string ret; + size_t const maxcol = guess_terminal_width(); + vector wrapped = wrap_paragraph(text, maxcol - col, maxcol - curcol); + for (vector::iterator w = wrapped.begin(); w != wrapped.end(); ++w) + { + if (w != wrapped.begin()) + ret += "\n" + string(col, ' '); + ret += *w; } - - return formatted; + return ret; } // Reformats the given text so that it fits in the current screen with no @@ -942,11 +955,70 @@ format_text(i18n_format const & text, si return format_text(text.str(), col, curcol); } +namespace { + class option_text + { + string names; + string desc; + vector formatted_names; + vector formatted_desc; + public: + option_text(string const & n, string const & d) + : names(n), desc(d) { } + size_t format_names(size_t const width) + { + size_t const full_len = display_width(utf8(names, origin::no_fault)); + size_t const slash = names.find('/'); + if (slash == string::npos || full_len <= width) + { + formatted_names.push_back(names); + return full_len; + } + + formatted_names.push_back(names.substr(0, slash-1)); + formatted_names.push_back(" " + names.substr(slash-1)); + + size_t ret = 0; + for (vector::const_iterator i = formatted_names.begin(); + i != formatted_names.end(); ++i) + { + ret = max(ret, display_width(utf8(*i, origin::no_fault))); + } + return ret; + } + void format_desc(size_t const width) + { + formatted_desc = wrap_paragraph(desc, width, width); + } + string formatted(size_t pre_indent, size_t space, size_t namelen) const + { + string ret; + string empty; + size_t const lines = max(formatted_names.size(), formatted_desc.size()); + for (size_t i = 0; i < lines; ++i) + { + string const * left = ∅ + if (i < formatted_names.size()) + left = &formatted_names.at(i); + string const * right = ∅ + if (i < formatted_desc.size()) + right = &formatted_desc.at(i); + + ret += string(pre_indent, ' ') + + *left + string(namelen - left->size(), ' ') + + string(space, ' ') + + *right + + "\n"; + } + return ret; + } + }; +} + // Format a block of options and their descriptions. static string format_usage_strings(vector const & names, - vector const & descriptions, - unsigned int namelen) + vector const & descriptions) { // " --long [ -s ] description goes here" // ^ ^^ ^^ ^^ ^ @@ -954,33 +1026,38 @@ format_usage_strings(vector cons // ^^^^ ^^^^ // pre_indent space string result; - int pre_indent = 2; // empty space on the left - int space = 2; // space after the longest option, before the description - int termwidth = guess_terminal_width(); - int descindent = pre_indent + namelen + space; - int descwidth = termwidth - descindent; - I(names.size() == descriptions.size()); + size_t const pre_indent = 2; // empty space on the left + size_t const space = 2; // space after the longest option, before the description + size_t const termwidth = guess_terminal_width(); + size_t const desired_namewidth = (termwidth - pre_indent - space) / 2; + size_t namelen = 0; - vector::const_iterator name; - vector::const_iterator desc; - for (name = names.begin(), desc = descriptions.begin(); name != names.end(); - ++name, ++desc) + vector texts; + I(names.size() == descriptions.size()); + for (vector::const_iterator n = names.begin(), d = descriptions.begin(); + n != names.end(); ++n, ++d) { - if (name->empty()) + if (n->empty()) continue; + texts.push_back(option_text(*n, *d)); - result += string(pre_indent, ' ') - + *name + string(namelen - name->size(), ' '); + size_t my_name_len = texts.back().format_names(desired_namewidth); + if (my_name_len > namelen) + namelen = my_name_len; + } - if (!desc->empty()) - { - result += string(space, ' '); - result += format_text(*desc, descindent, descindent); - } + size_t const descindent = pre_indent + namelen + space; + size_t const descwidth = termwidth - descindent; - result += '\n'; + for (vector::iterator i = texts.begin(); + i != texts.end(); ++i) + { + i->format_desc(descwidth); + + result += i->formatted(pre_indent, space, namelen); } + result += '\n'; return result; } @@ -994,7 +1071,7 @@ get_usage_str(options::options_type cons optset.instantiate(&opts).get_usage_strings(names, descriptions, maxnamelen, opts.show_hidden_commands); - return format_usage_strings(names, descriptions, maxnamelen); + return format_usage_strings(names, descriptions); } void