Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 30 additions & 15 deletions lldb/source/Interpreter/CommandInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3182,26 +3182,41 @@ void CommandInterpreter::OutputHelpText(Stream &strm, llvm::StringRef word_text,

uint32_t chars_left = max_columns;

auto nextWordLength = [](llvm::StringRef S) {
size_t pos = S.find(' ');
return pos == llvm::StringRef::npos ? S.size() : pos;
auto start_new_line = [&] {
strm.EOL();
strm.Indent();
chars_left = max_columns - indent_size;
};

while (!text.empty()) {
if (text.front() == '\n' ||
(text.front() == ' ' && nextWordLength(text.ltrim(' ')) > chars_left)) {
strm.EOL();
strm.Indent();
chars_left = max_columns - indent_size;
if (text.front() == '\n')
text = text.drop_front();
else
text = text.ltrim(' ');
} else {
strm.PutChar(text.front());
--chars_left;
if (text.starts_with('\n')) {
text = text.drop_front();
start_new_line();
continue;
}

// Calculate the size of the next fragment. A fragment is defined as zero
// or more spaces followed by a word (which is sequence of non-whitespace
// characters). It is assumed that the only possible whitespaces in the
// input text are ' ' and '\n'.
size_t word_start_pos = text.find_first_not_of(' ');
size_t word_end_pos = text.find_first_of(" \n", /*from=*/word_start_pos);
size_t fragment_size =
word_end_pos == llvm::StringRef::npos ? text.size() : word_end_pos;

if (fragment_size > chars_left && text.starts_with(' ')) {
// The fragment does not fit on the current line, but begins with a space.
// Break the line at the beginning of the word contained in the fragment.
text = text.drop_front(word_start_pos);
start_new_line();
continue;
}

// Print out the fragment. It fits on the current line or does not contain
// spaces where we could break the line.
strm.PutCString(text.take_front(fragment_size));
text = text.drop_front(fragment_size);
chars_left = fragment_size > chars_left ? 0 : chars_left - fragment_size;
}

strm.EOL();
Expand Down
16 changes: 13 additions & 3 deletions lldb/test/API/commands/help/TestHelp.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ def test_help_unknown_flag(self):
def test_help_format_output(self):
"""Test that help output reaches TerminalWidth and wraps to the next
line if needed."""
self.runCmd("settings set term-width 118")
self.runCmd("settings set term-width 105")
self.expect(
"help format",
matching=True,
Expand All @@ -249,9 +249,9 @@ def test_help_format_output(self):
],
)

# The length of the first line will not be exactly 108 because we split
# The length of the first line will not be exactly 105 because we split
# at the last whitespace point before the limit.
self.runCmd("settings set term-width 108")
self.runCmd("settings set term-width 104")
self.expect(
"help format",
matching=True,
Expand All @@ -271,6 +271,16 @@ def test_help_format_output(self):
],
)

# Check that line splitting works with newline characters too. The raw
# input after the word "pointer" does not contain spaces among more than
# a hundred subsequent characters, the words are separated by newlines.
self.runCmd("settings set term-width 80")
self.expect(
"help format",
matching=True,
substrs=["'p' or \"pointer\""],
)

@no_debug_info_test
def test_help_option_group_format_options_usage(self):
"""Test that help on commands that use OptionGroupFormat options provide relevant help specific to that command."""
Expand Down
Loading