gdb: fix line wrapping in new boxed hints when styling is enabled

I noticed that the startup hint text, the stuff that's placed into a
box, is not line wrapping correctly.  For example:

  $ gdb -nw -nh -eiex 'set width 60'
  ... snip ...

  ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
  ┃ Find the GDB manual online at:                           ┃
  ┃ http://www.gnu.org/software/gdb/documentation/.          ┃
  ┃ For help, type "help".                                   ┃
  ┃ Type "apropos <word>" to search                          ┃
  ┃  for commands related to <word>                          ┃
  ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

  (gdb)

Notice the final two lines within the box, there's no need to wrap
after the word 'search', plenty more would fit on that line.  And
indeed, if we switch off styling:

  $ gdb -nw -nh -eiex 'set width 60' -eiex 'set style enabled off'
  ... snip ...

  +----------------------------------------------------------+
  | Find the GDB manual online at:                           |
  | http://www.gnu.org/software/gdb/documentation/.          |
  | For help, type "help".                                   |
  | Type "apropos <word>" to search for commands related to  |
  |  <word>                                                  |
  +----------------------------------------------------------+

  (gdb)

That's mostly fixed it, except I still think there's a stray white
space character before '<word>' on the final line.

The fact that the output is wrapped differently with styling on and
off tells me that this is not an intentional choice.

The problems are, I think all in box_one_message (from top.c).  There
are a number of issues.

 1. Each time around the loop we count all escape characters in
    MESSAGE, not just the escape characters up to the point where we
    might wrap the message.  This has the potential to over count the
    escape characters.

 2. When splitting MESSAGE we search for a space character.  This
    search starts based on the terminal width, but neglects to take
    into account any escape characters prior to the split point.

 3. If we split a line after an alternative style has been activated,
    but before the style has been reset, then the border of the box on
    that line is going to be rendered in the alternative style.

 4. When computing what content needs to be moved onto the second
    line, we don't move past the space character (as found in point
    2).

Now it just so happens that issues (1) and (3) can be ignored for now.
The surrounding box is only added (and line wrapping performed) if the
terminal is at least wide enough to fit the documentation URL (plus
box borders).  This means a minimum width of 51 characters. On all
the other lines, any styled output is always to the left of the line,
occurring before character 51.  This means that counting all escape
characters is the same as counting just the escape characters that
will appear before the line break.  And we will never need to line
break part way through some styled text.

Obviously we _could_ fix this code properly, but it's not simple, and
it would all be completely theoretical.  So in this commit I plan to
add an assert that all escape sequences occur within the first 51
characters, then if the above text ever changes we will immediately
know that box_one_message will need to be rewritten.

Fixing issue (2) is pretty easy, this line:

  line = message.substr (0, message.rfind (" ", width));

just needs to be updated to take N_ESCAPE_CHARS into account.  We
currently look for a space after WIDTH characters in MESSAGE, but
MESSAGE also contains escape sequences, so we really need to search in
for a space starting from 'WIDTH + N_ESCAPE_CHARS'.

And fixing (4) is also easy, this line:

  message = message.substr (line.length ());

finds the remainder of MESSAGE based on LINE.  But LINE was all
content up to (but not including) the space character we found.  What
we actually need to do is:

  message = message.substr (line.length () + 1);

To add the assert that I discussed above, I've moved the escape
characters counting code out of the line printing loop.  We now count
the escape characters just once, and assert that these all fit within
the WIDTH, this means they will all appear before any line break.

While making these changes I've also made use of std::move to avoid
copying a string in one place.

Finally, the gdb.base/startup-hints.exp test has been expanded to
cover both styled and non-styled output, as well as a greater range of
terminal widths.
2 files changed