mirror of
https://github.com/nushell/reedline.git
synced 2024-10-05 21:38:16 +03:00
get correct cursor pos when menu indicator contains newline (#708)
This commit is contained in:
parent
a3769f9b98
commit
9ca229de32
@ -277,7 +277,8 @@ pub use validator::{DefaultValidator, ValidationResult, Validator};
|
||||
|
||||
mod menu;
|
||||
pub use menu::{
|
||||
menu_functions, ColumnarMenu, IdeMenu, ListMenu, Menu, MenuEvent, MenuTextStyle, ReedlineMenu,
|
||||
menu_functions, ColumnarMenu, DescriptionMode, IdeMenu, ListMenu, Menu, MenuEvent,
|
||||
MenuTextStyle, ReedlineMenu,
|
||||
};
|
||||
|
||||
mod terminal_extensions;
|
||||
|
@ -11,6 +11,7 @@ use nu_ansi_term::{ansi::RESET, Style};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
/// The direction of the description box
|
||||
pub enum DescriptionMode {
|
||||
/// Description is always shown on the left
|
||||
Left,
|
||||
|
@ -7,6 +7,7 @@ use crate::core_editor::Editor;
|
||||
use crate::History;
|
||||
use crate::{completion::history::HistoryCompleter, painting::Painter, Completer, Suggestion};
|
||||
pub use columnar_menu::ColumnarMenu;
|
||||
pub use ide_menu::DescriptionMode;
|
||||
pub use ide_menu::IdeMenu;
|
||||
pub use list_menu::ListMenu;
|
||||
use nu_ansi_term::{Color, Style};
|
||||
|
@ -92,16 +92,28 @@ impl<'prompt> PromptLines<'prompt> {
|
||||
/// The height is relative to the prompt
|
||||
pub(crate) fn cursor_pos(&self, terminal_columns: u16) -> (u16, u16) {
|
||||
// If we have a multiline prompt (e.g starship), we expect the cursor to be on the last line
|
||||
let prompt_str = self.prompt_str_left.lines().last().unwrap_or_default();
|
||||
let prompt_width = line_width(&format!("{}{}", prompt_str, self.prompt_indicator));
|
||||
let buffer_width = line_width(&self.before_cursor);
|
||||
let prompt_str = format!("{}{}", self.prompt_str_left, self.prompt_indicator);
|
||||
// The Cursor position will be relative to this
|
||||
let last_prompt_str = prompt_str.lines().last().unwrap_or_default();
|
||||
|
||||
let total_width = prompt_width + buffer_width;
|
||||
let is_multiline = self.before_cursor.contains('\n');
|
||||
let buffer_width = line_width(self.before_cursor.lines().last().unwrap_or_default());
|
||||
|
||||
let total_width = if is_multiline {
|
||||
// The buffer already contains the multiline prompt
|
||||
buffer_width
|
||||
} else {
|
||||
buffer_width + line_width(last_prompt_str)
|
||||
};
|
||||
|
||||
let buffer_width_prompt = format!("{}{}", last_prompt_str, self.before_cursor);
|
||||
|
||||
let cursor_y = (estimate_required_lines(&buffer_width_prompt, terminal_columns) as u16)
|
||||
.saturating_sub(1); // 0 based
|
||||
|
||||
let cursor_x = (total_width % terminal_columns as usize) as u16;
|
||||
let cursor_y = (total_width / terminal_columns as usize) as u16;
|
||||
|
||||
(cursor_x, cursor_y)
|
||||
(cursor_x, cursor_y as u16)
|
||||
}
|
||||
|
||||
/// Total lines that the prompt uses considering that it may wrap the screen
|
||||
@ -154,3 +166,90 @@ impl<'prompt> PromptLines<'prompt> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
use rstest::rstest;
|
||||
|
||||
#[rstest]
|
||||
#[case(
|
||||
"~/path/",
|
||||
"❯ ",
|
||||
"",
|
||||
100,
|
||||
(9, 0)
|
||||
)]
|
||||
#[case(
|
||||
"~/longer/path/\n",
|
||||
"❯ ",
|
||||
"test",
|
||||
100,
|
||||
(6, 0)
|
||||
)]
|
||||
#[case(
|
||||
"~/longer/path/",
|
||||
"\n❯ ",
|
||||
"test",
|
||||
100,
|
||||
(6, 0)
|
||||
)]
|
||||
#[case(
|
||||
"~/longer/path/\n",
|
||||
"\n❯ ",
|
||||
"test",
|
||||
100,
|
||||
(6, 0)
|
||||
)]
|
||||
#[case(
|
||||
"~/path/",
|
||||
"❯ ",
|
||||
"very long input that does not fit in a single line",
|
||||
40,
|
||||
(19, 1)
|
||||
)]
|
||||
#[case(
|
||||
"~/path/\n",
|
||||
"\n❯\n ",
|
||||
"very long input that does not fit in a single line",
|
||||
10,
|
||||
(1, 5)
|
||||
)]
|
||||
#[case(
|
||||
"~/path/",
|
||||
"❯ ",
|
||||
"this is a text that contains newlines\n::: and a multiline prompt",
|
||||
40,
|
||||
(26, 2)
|
||||
)]
|
||||
#[case(
|
||||
"~/path/",
|
||||
"❯ ",
|
||||
"this is a text that contains newlines\n::: and very loooooooooooooooong text that wraps",
|
||||
40,
|
||||
(8, 3)
|
||||
)]
|
||||
|
||||
fn test_cursor_pos(
|
||||
#[case] prompt_str_left: &str,
|
||||
#[case] prompt_indicator: &str,
|
||||
#[case] before_cursor: &str,
|
||||
#[case] terminal_columns: u16,
|
||||
#[case] expected: (u16, u16),
|
||||
) {
|
||||
let prompt_lines = PromptLines {
|
||||
prompt_str_left: Cow::Borrowed(prompt_str_left),
|
||||
prompt_str_right: Cow::Borrowed(""),
|
||||
prompt_indicator: Cow::Borrowed(prompt_indicator),
|
||||
before_cursor: Cow::Borrowed(before_cursor),
|
||||
after_cursor: Cow::Borrowed(""),
|
||||
hint: Cow::Borrowed(""),
|
||||
right_prompt_on_last_line: false,
|
||||
};
|
||||
|
||||
let pos = prompt_lines.cursor_pos(terminal_columns);
|
||||
|
||||
assert_eq!(pos, expected);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user