Make reedline handling cursor shapes more configurable (#515)

Adds a struct to configure the cursor shape

Co-authored-by: sholderbach <sholderbach@users.noreply.github.com>
resolve https://github.com/nushell/reedline/issues/514
This commit is contained in:
Carl Schierig 2022-12-21 12:15:46 +01:00 committed by GitHub
parent de8fc988df
commit 475495d785
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 56 additions and 9 deletions

View File

@ -12,6 +12,8 @@ use {
},
};
use crossterm::cursor::CursorShape;
use reedline::CursorConfig;
#[cfg(not(any(feature = "sqlite", feature = "sqlite-dynlib")))]
use reedline::FileBackedHistory;
@ -58,11 +60,18 @@ fn main() -> Result<()> {
let completer = Box::new(DefaultCompleter::new_with_wordlen(commands.clone(), 2));
let cursor_config = CursorConfig {
vi_insert: Some(CursorShape::Line),
vi_normal: Some(CursorShape::Block),
emacs: None,
};
let mut line_editor = Reedline::create()
.with_history(history)
.with_completer(completer)
.with_quick_completions(true)
.with_partial_completions(true)
.with_cursor_config(cursor_config)
.with_highlighter(Box::new(ExampleHighlighter::new(commands)))
.with_hinter(Box::new(
DefaultHinter::default().with_style(Style::new().fg(Color::DarkGray)),

13
src/edit_mode/cursors.rs Normal file
View File

@ -0,0 +1,13 @@
use crossterm::cursor::CursorShape;
/// Maps cursor shapes to each edit mode (emacs, vi normal & vi insert).
/// If any of the fields is `None`, the cursor won't get changed by Reedline for that mode.
#[derive(Default)]
pub struct CursorConfig {
/// The cursor to be used when in vi insert mode
pub vi_insert: Option<CursorShape>,
/// The cursor to be used when in vi normal mode
pub vi_normal: Option<CursorShape>,
/// The cursor to be used when in emacs mode
pub emacs: Option<CursorShape>,
}

View File

@ -1,9 +1,11 @@
mod base;
mod cursors;
mod emacs;
mod keybindings;
mod vi;
pub use base::EditMode;
pub use cursors::CursorConfig;
pub use emacs::{default_emacs_keybindings, Emacs};
pub use keybindings::Keybindings;
pub use vi::{default_vi_insert_keybindings, default_vi_normal_keybindings, Vi};

View File

@ -1,3 +1,4 @@
use crate::CursorConfig;
#[cfg(feature = "bashisms")]
use crate::{
history::SearchFilter,
@ -128,6 +129,9 @@ pub struct Reedline {
// Text editor used to open the line buffer for editing
buffer_editor: Option<BufferEditor>,
// Use different cursors depending on the current edit mode
cursor_shapes: Option<CursorConfig>,
#[cfg(feature = "external_printer")]
external_printer: Option<ExternalPrinter<String>>,
}
@ -179,6 +183,7 @@ impl Reedline {
use_ansi_coloring: true,
menus: Vec::new(),
buffer_editor: None,
cursor_shapes: None,
#[cfg(feature = "external_printer")]
external_printer: None,
}
@ -377,6 +382,14 @@ impl Reedline {
self
}
/// A builder that enables reedline changing the cursor shape based on the current edit mode.
/// The current implementation sets the cursor shape when drawing the prompt.
/// Do not use this if the cursor shape is set elsewhere, e.g. in the terminal settings or by ansi escape sequences.
pub fn with_cursor_config(mut self, cursor_shapes: CursorConfig) -> Self {
self.cursor_shapes = Some(cursor_shapes);
self
}
/// Returns the corresponding expected prompt style for the given edit mode
pub fn prompt_edit_mode(&self) -> PromptEditMode {
self.edit_mode.edit_mode()
@ -1393,6 +1406,7 @@ impl Reedline {
self.prompt_edit_mode(),
None,
self.use_ansi_coloring,
&self.cursor_shapes,
)?;
}
@ -1460,6 +1474,7 @@ impl Reedline {
self.prompt_edit_mode(),
menu,
self.use_ansi_coloring,
&self.cursor_shapes,
)
}

View File

@ -260,7 +260,7 @@ pub use prompt::{
mod edit_mode;
pub use edit_mode::{
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
EditMode, Emacs, Keybindings, Vi,
CursorConfig, EditMode, Emacs, Keybindings, Vi,
};
mod highlighter;

View File

@ -1,4 +1,4 @@
use crate::PromptEditMode;
use crate::{CursorConfig, PromptEditMode, PromptViMode};
use {
super::utils::{coerce_crlf, line_width},
@ -137,6 +137,7 @@ impl Painter {
prompt_mode: PromptEditMode,
menu: Option<&ReedlineMenu>,
use_ansi_coloring: bool,
cursor_config: &Option<CursorConfig>,
) -> Result<()> {
self.stdout.queue(cursor::Hide)?;
@ -175,13 +176,20 @@ impl Painter {
// can print without overwriting the things written during the painting
self.last_required_lines = required_lines;
self.stdout
.queue(RestorePosition)?
.queue(cursor::SetCursorShape(match prompt_mode {
PromptEditMode::Vi(crate::PromptViMode::Insert) => cursor::CursorShape::Line,
_ => cursor::CursorShape::Block,
}))?
.queue(cursor::Show)?;
self.stdout.queue(RestorePosition)?;
if let Some(shapes) = cursor_config {
let shape = match &prompt_mode {
PromptEditMode::Emacs => shapes.emacs,
PromptEditMode::Vi(PromptViMode::Insert) => shapes.vi_insert,
PromptEditMode::Vi(PromptViMode::Normal) => shapes.vi_normal,
_ => None,
};
if let Some(shape) = shape {
self.stdout.queue(cursor::SetCursorShape(shape))?;
}
}
self.stdout.queue(cursor::Show)?;
self.stdout.flush()
}