Skip to content

Commit f215e5e

Browse files
committed
Fixed issue when printing alerts caused by a prompt with a new line character
1 parent 9f5906a commit f215e5e

File tree

2 files changed

+32
-17
lines changed

2 files changed

+32
-17
lines changed

cmd2/cmd2.py

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3596,24 +3596,36 @@ 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 lines
3619+
last_prompt_line = prompt_lines[-1]
3620+
last_prompt_line_width = utils.ansi_safe_wcswidth(last_prompt_line)
3621+
3622+
input_width = (utils.ansi_safe_wcswidth(last_prompt_line) +
3623+
utils.ansi_safe_wcswidth(readline.get_line_buffer()))
3624+
3625+
num_input_terminal_lines = int(input_width / terminal_size.columns) + 1
36143626

36153627
# Get the cursor's offset from the beginning of the first input line
3616-
cursor_input_offset = prompt_width + rl_get_point()
3628+
cursor_input_offset = last_prompt_line_width + rl_get_point()
36173629

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

36243636
# 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)
3637+
if cursor_input_line != num_input_terminal_lines:
3638+
terminal_str += Cursor.DOWN(num_input_terminal_lines - cursor_input_line)
3639+
3640+
# Clear each line from the bottom up so that the cursor ends up on the first prompt line
3641+
total_lines = num_prompt_terminal_lines + num_input_terminal_lines
3642+
terminal_str += (ansi.clear_line() + Cursor.UP(1)) * (total_lines - 1)
36273643

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)
3644+
# Clear the first prompt line
36303645
terminal_str += ansi.clear_line()
36313646

3632-
# Move the cursor to the beginning of the first input line and print the alert
3647+
# Move the cursor to the beginning of the first prompt line and print the alert
36333648
terminal_str += '\r' + alert_msg
36343649

36353650
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)