Skip to content

Commit 6fd9cc6

Browse files
authored
Merge pull request #656 from python-cmd2/multiline_prompt
Multiline prompt
2 parents 9f5906a + 6c13126 commit 6fd9cc6

File tree

3 files changed

+35
-20
lines changed

3 files changed

+35
-20
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## 0.9.12 (March TBD, 2019)
22
* Bug Fixes
33
* Fixed a bug in how redirection and piping worked inside ``py`` or ``pyscript`` commands
4+
* Fixed bug in `async_alert` where it didn't account for prompts that contained newline characters
45
* Enhancements
56
* Added ability to include command name placeholders in the message printed when trying to run a disabled command.
67
* See docstring for ``disable_command()`` or ``disable_category()`` for more details.

cmd2/cmd2.py

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2111,7 +2111,7 @@ def pseudo_raw_input(self, prompt: str) -> str:
21112111
else:
21122112
line = input()
21132113
if self.echo:
2114-
sys.stdout.write('{}{}\n'.format(self.prompt, line))
2114+
sys.stdout.write('{}{}\n'.format(prompt, line))
21152115
except EOFError:
21162116
line = 'eof'
21172117
finally:
@@ -2121,7 +2121,7 @@ def pseudo_raw_input(self, prompt: str) -> str:
21212121
else:
21222122
if self.stdin.isatty():
21232123
# on a tty, print the prompt first, then read the line
2124-
self.poutput(self.prompt, end='')
2124+
self.poutput(prompt, end='')
21252125
self.stdout.flush()
21262126
line = self.stdin.readline()
21272127
if len(line) == 0:
@@ -2134,7 +2134,7 @@ def pseudo_raw_input(self, prompt: str) -> str:
21342134
if len(line):
21352135
# we read something, output the prompt and the something
21362136
if self.echo:
2137-
self.poutput('{}{}'.format(self.prompt, line))
2137+
self.poutput('{}{}'.format(prompt, line))
21382138
else:
21392139
line = 'eof'
21402140

@@ -3596,24 +3596,35 @@ def async_alert(self, alert_msg: str, new_prompt: Optional[str] = None) -> None:
35963596
if new_prompt is not None and new_prompt != self.prompt:
35973597
self.prompt = new_prompt
35983598

3599-
# If we aren't at a continuation prompt, then redraw the prompt now
3599+
# If we aren't at a continuation prompt, then it's OK to update it
36003600
if not self.at_continuation_prompt:
36013601
rl_set_prompt(self.prompt)
36023602
update_terminal = True
36033603

36043604
if update_terminal:
3605-
# Get the display width of the prompt
3606-
prompt_width = utils.ansi_safe_wcswidth(current_prompt)
3607-
36083605
# Get the size of the terminal
36093606
terminal_size = shutil.get_terminal_size()
36103607

3611-
# Figure out how many lines the prompt and user input take up
3612-
total_str_size = prompt_width + utils.ansi_safe_wcswidth(readline.get_line_buffer())
3613-
num_input_lines = int(total_str_size / terminal_size.columns) + 1
3608+
# Split the prompt lines since it can contain newline characters.
3609+
prompt_lines = current_prompt.splitlines()
3610+
3611+
# Calculate how many terminal lines are taken up by all prompt lines except for the last one.
3612+
# That will be included in the input lines calculations since that is where the cursor is.
3613+
num_prompt_terminal_lines = 0
3614+
for line in prompt_lines[:-1]:
3615+
line_width = utils.ansi_safe_wcswidth(line)
3616+
num_prompt_terminal_lines += int(line_width / terminal_size.columns) + 1
3617+
3618+
# Now calculate how many terminal lines are take up by the input
3619+
last_prompt_line = prompt_lines[-1]
3620+
last_prompt_line_width = utils.ansi_safe_wcswidth(last_prompt_line)
3621+
3622+
input_width = last_prompt_line_width + utils.ansi_safe_wcswidth(readline.get_line_buffer())
3623+
3624+
num_input_terminal_lines = int(input_width / terminal_size.columns) + 1
36143625

36153626
# Get the cursor's offset from the beginning of the first input line
3616-
cursor_input_offset = prompt_width + rl_get_point()
3627+
cursor_input_offset = last_prompt_line_width + rl_get_point()
36173628

36183629
# Calculate what input line the cursor is on
36193630
cursor_input_line = int(cursor_input_offset / terminal_size.columns) + 1
@@ -3622,14 +3633,17 @@ def async_alert(self, alert_msg: str, new_prompt: Optional[str] = None) -> None:
36223633
terminal_str = ''
36233634

36243635
# Move the cursor down to the last input line
3625-
if cursor_input_line != num_input_lines:
3626-
terminal_str += Cursor.DOWN(num_input_lines - cursor_input_line)
3636+
if cursor_input_line != num_input_terminal_lines:
3637+
terminal_str += Cursor.DOWN(num_input_terminal_lines - cursor_input_line)
3638+
3639+
# Clear each line from the bottom up so that the cursor ends up on the first prompt line
3640+
total_lines = num_prompt_terminal_lines + num_input_terminal_lines
3641+
terminal_str += (ansi.clear_line() + Cursor.UP(1)) * (total_lines - 1)
36273642

3628-
# Clear each input line from the bottom up so that the cursor ends up on the original first input line
3629-
terminal_str += (ansi.clear_line() + Cursor.UP(1)) * (num_input_lines - 1)
3643+
# Clear the first prompt line
36303644
terminal_str += ansi.clear_line()
36313645

3632-
# Move the cursor to the beginning of the first input line and print the alert
3646+
# Move the cursor to the beginning of the first prompt line and print the alert
36333647
terminal_str += '\r' + alert_msg
36343648

36353649
if rl_type == RlType.GNU:

examples/async_printing.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,17 +126,17 @@ def _generate_alert_str(self) -> str:
126126
alerts = self._get_alerts()
127127

128128
longest_alert = max(ALERTS, key=len)
129-
num_astericks = len(longest_alert) + 8
129+
num_asterisks = len(longest_alert) + 8
130130

131131
for i, cur_alert in enumerate(alerts):
132132
# Use padding to center the alert
133-
padding = ' ' * int((num_astericks - len(cur_alert)) / 2)
133+
padding = ' ' * int((num_asterisks - len(cur_alert)) / 2)
134134

135135
if i > 0:
136136
alert_str += '\n'
137-
alert_str += '*' * num_astericks + '\n'
137+
alert_str += '*' * num_asterisks + '\n'
138138
alert_str += padding + cur_alert + padding + '\n'
139-
alert_str += '*' * num_astericks + '\n'
139+
alert_str += '*' * num_asterisks + '\n'
140140

141141
return alert_str
142142

0 commit comments

Comments
 (0)