@@ -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 :
0 commit comments