From 1225cb96a958a42a444d4ef7ac3ca8719a5ef60a Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 22 Aug 2023 22:03:59 -0400 Subject: [PATCH 01/23] Let prompts choose to repaint on enter --- examples/custom_prompt.rs | 4 ++++ src/engine.rs | 3 +++ src/prompt/base.rs | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/examples/custom_prompt.rs b/examples/custom_prompt.rs index fc9b1034..29177816 100644 --- a/examples/custom_prompt.rs +++ b/examples/custom_prompt.rs @@ -52,6 +52,10 @@ impl Prompt for CustomPrompt { prefix, history_search.term )) } + + fn repaint_on_enter(&self) -> bool { + true + } } fn main() -> io::Result<()> { diff --git a/src/engine.rs b/src/engine.rs index ac69df9a..f6733c88 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -713,6 +713,9 @@ impl Reedline { for event in reedline_events.drain(..) { match self.handle_event(prompt, event)? { EventStatus::Exits(signal) => { + if prompt.repaint_on_enter() { + self.repaint(prompt)?; + } // Move the cursor below the input area, for external commands or new read_line call self.painter.move_cursor_to_end()?; return Ok(signal); diff --git a/src/prompt/base.rs b/src/prompt/base.rs index 0ba570c7..1f3e4feb 100644 --- a/src/prompt/base.rs +++ b/src/prompt/base.rs @@ -97,6 +97,10 @@ pub trait Prompt: Send { &self, history_search: PromptHistorySearch, ) -> Cow; + /// Whether to repaint the prompt after the user hits enter + fn repaint_on_enter(&self) -> bool { + false + } /// Get the default prompt color fn get_prompt_color(&self) -> Color { DEFAULT_PROMPT_COLOR From ce664c397cf499298ae05ceae4e70f3e2412048b Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 22 Aug 2023 22:31:58 -0400 Subject: [PATCH 02/23] Add transient prompt to custom_prompt example --- examples/custom_prompt.rs | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/examples/custom_prompt.rs b/examples/custom_prompt.rs index 29177816..dcc773a5 100644 --- a/examples/custom_prompt.rs +++ b/examples/custom_prompt.rs @@ -13,25 +13,42 @@ use std::{borrow::Cow, cell::Cell, io}; // This example displays the number of keystrokes // or rather increments each time the prompt is rendered. #[derive(Clone)] -pub struct CustomPrompt(Cell, &'static str); +pub struct CustomPrompt { + count: Cell, + left_prompt: &'static str, + show_transient: Cell, +} pub static DEFAULT_MULTILINE_INDICATOR: &str = "::: "; +pub static TRANSIENT_PROMPT: &str = "!"; impl Prompt for CustomPrompt { fn render_prompt_left(&self) -> Cow { { - Cow::Owned(self.1.to_string()) + if self.show_transient.get() { + Cow::Owned(String::new()) + } else { + Cow::Owned(self.left_prompt.to_string()) + } } } fn render_prompt_right(&self) -> Cow { { - let old = self.0.get(); - self.0.set(old + 1); - Cow::Owned(format!("[{old}]")) + if self.show_transient.get() { + Cow::Owned(String::new()) + } else { + let old = self.count.get(); + self.count.set(old + 1); + Cow::Owned(format!("[{old}]")) + } } } fn render_prompt_indicator(&self, _edit_mode: PromptEditMode) -> Cow { - Cow::Owned(">".to_string()) + if self.show_transient.get() { + Cow::Owned(TRANSIENT_PROMPT.to_string()) + } else { + Cow::Owned(">".to_string()) + } } fn render_prompt_multiline_indicator(&self) -> Cow { @@ -54,6 +71,7 @@ impl Prompt for CustomPrompt { } fn repaint_on_enter(&self) -> bool { + self.show_transient.set(true); true } } @@ -62,9 +80,14 @@ fn main() -> io::Result<()> { println!("Custom prompt demo:\nAbort with Ctrl-C or Ctrl-D"); let mut line_editor = Reedline::create(); - let prompt = CustomPrompt(Cell::new(0), "Custom Prompt"); + let prompt = CustomPrompt { + count: Cell::new(0), + left_prompt: "Custom Prompt", + show_transient: Cell::new(false), + }; loop { + prompt.show_transient.set(false); let sig = line_editor.read_line(&prompt)?; match sig { Signal::Success(buffer) => { From 43ed497f4d1670c16853615ef2b8d255e52e1b8e Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 22 Aug 2023 22:58:26 -0400 Subject: [PATCH 03/23] Add last line before rendering transient prompt --- src/engine.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/engine.rs b/src/engine.rs index f6733c88..7bf6d3e4 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -714,7 +714,14 @@ impl Reedline { match self.handle_event(prompt, event)? { EventStatus::Exits(signal) => { if prompt.repaint_on_enter() { - self.repaint(prompt)?; + println!("{:?}", self.editor.line_buffer()); + if let Some(id) = self.history_last_run_id { + if let Ok(last) = self.history.load(id) { + self.editor.edit_buffer(|buf| buf.insert_str(&last.command_line), UndoBehavior::UndoRedo); + self.repaint(prompt)?; + self.editor.edit_buffer(|buf| buf.clear(), UndoBehavior::UndoRedo); + } + } } // Move the cursor below the input area, for external commands or new read_line call self.painter.move_cursor_to_end()?; From 41b8be6ef31cbbc49ed73b9977aac0b6ff0c02ab Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 22 Aug 2023 22:59:03 -0400 Subject: [PATCH 04/23] Remove debug println --- src/engine.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/engine.rs b/src/engine.rs index 7bf6d3e4..391a92e7 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -714,7 +714,6 @@ impl Reedline { match self.handle_event(prompt, event)? { EventStatus::Exits(signal) => { if prompt.repaint_on_enter() { - println!("{:?}", self.editor.line_buffer()); if let Some(id) = self.history_last_run_id { if let Ok(last) = self.history.load(id) { self.editor.edit_buffer(|buf| buf.insert_str(&last.command_line), UndoBehavior::UndoRedo); From 43463bdbadca5c39f0db1b4c8dae7004d5aee1a6 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 22 Aug 2023 23:03:33 -0400 Subject: [PATCH 05/23] Format with cargo fmt --- src/engine.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 391a92e7..eabe8d91 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -716,9 +716,13 @@ impl Reedline { if prompt.repaint_on_enter() { if let Some(id) = self.history_last_run_id { if let Ok(last) = self.history.load(id) { - self.editor.edit_buffer(|buf| buf.insert_str(&last.command_line), UndoBehavior::UndoRedo); + self.editor.edit_buffer( + |buf| buf.insert_str(&last.command_line), + UndoBehavior::UndoRedo, + ); self.repaint(prompt)?; - self.editor.edit_buffer(|buf| buf.clear(), UndoBehavior::UndoRedo); + self.editor + .edit_buffer(|buf| buf.clear(), UndoBehavior::UndoRedo); } } } From 1d7fc1639ff959ebde5d89d41de615bdec7c8dbe Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 22 Aug 2023 23:09:39 -0400 Subject: [PATCH 06/23] Add some comments --- examples/custom_prompt.rs | 6 ++++++ src/engine.rs | 3 +++ src/prompt/base.rs | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/custom_prompt.rs b/examples/custom_prompt.rs index dcc773a5..826940c1 100644 --- a/examples/custom_prompt.rs +++ b/examples/custom_prompt.rs @@ -12,10 +12,13 @@ use std::{borrow::Cow, cell::Cell, io}; // // This example displays the number of keystrokes // or rather increments each time the prompt is rendered. +// It also replaces the prompt for old lines with "!" as an +// example of a transient prompt. #[derive(Clone)] pub struct CustomPrompt { count: Cell, left_prompt: &'static str, + /// Whether to show the transient prompt indicator instead of the normal one show_transient: Cell, } pub static DEFAULT_MULTILINE_INDICATOR: &str = "::: "; @@ -71,6 +74,8 @@ impl Prompt for CustomPrompt { } fn repaint_on_enter(&self) -> bool { + // This method is called whenever the user hits enter to go to the next + // line, so we want it to repaint and display the transient prompt self.show_transient.set(true); true } @@ -87,6 +92,7 @@ fn main() -> io::Result<()> { }; loop { + // We're on a new line, so make sure we're showing the normal prompt prompt.show_transient.set(false); let sig = line_editor.read_line(&prompt)?; match sig { diff --git a/src/engine.rs b/src/engine.rs index eabe8d91..1a8a5339 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -716,11 +716,14 @@ impl Reedline { if prompt.repaint_on_enter() { if let Some(id) = self.history_last_run_id { if let Ok(last) = self.history.load(id) { + // Set the line buffer to the last entered line so that we can + // re-render it with the new prompt self.editor.edit_buffer( |buf| buf.insert_str(&last.command_line), UndoBehavior::UndoRedo, ); self.repaint(prompt)?; + // Clear the buffer again so that the next line can be typed self.editor .edit_buffer(|buf| buf.clear(), UndoBehavior::UndoRedo); } diff --git a/src/prompt/base.rs b/src/prompt/base.rs index 1f3e4feb..a396734c 100644 --- a/src/prompt/base.rs +++ b/src/prompt/base.rs @@ -97,7 +97,7 @@ pub trait Prompt: Send { &self, history_search: PromptHistorySearch, ) -> Cow; - /// Whether to repaint the prompt after the user hits enter + /// Whether to repaint the prompt after the user hits enter (for transient prompts) fn repaint_on_enter(&self) -> bool { false } From 2fe9f3559d7fbf0126155087dbf03c84c98237a0 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Wed, 23 Aug 2023 10:20:03 -0400 Subject: [PATCH 07/23] Make separate transient prompt example, restore custom prompt example --- examples/custom_prompt.rs | 47 +++--------------- examples/transient_prompt.rs | 95 ++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 40 deletions(-) create mode 100644 examples/transient_prompt.rs diff --git a/examples/custom_prompt.rs b/examples/custom_prompt.rs index 826940c1..fc9b1034 100644 --- a/examples/custom_prompt.rs +++ b/examples/custom_prompt.rs @@ -12,46 +12,26 @@ use std::{borrow::Cow, cell::Cell, io}; // // This example displays the number of keystrokes // or rather increments each time the prompt is rendered. -// It also replaces the prompt for old lines with "!" as an -// example of a transient prompt. #[derive(Clone)] -pub struct CustomPrompt { - count: Cell, - left_prompt: &'static str, - /// Whether to show the transient prompt indicator instead of the normal one - show_transient: Cell, -} +pub struct CustomPrompt(Cell, &'static str); pub static DEFAULT_MULTILINE_INDICATOR: &str = "::: "; -pub static TRANSIENT_PROMPT: &str = "!"; impl Prompt for CustomPrompt { fn render_prompt_left(&self) -> Cow { { - if self.show_transient.get() { - Cow::Owned(String::new()) - } else { - Cow::Owned(self.left_prompt.to_string()) - } + Cow::Owned(self.1.to_string()) } } fn render_prompt_right(&self) -> Cow { { - if self.show_transient.get() { - Cow::Owned(String::new()) - } else { - let old = self.count.get(); - self.count.set(old + 1); - Cow::Owned(format!("[{old}]")) - } + let old = self.0.get(); + self.0.set(old + 1); + Cow::Owned(format!("[{old}]")) } } fn render_prompt_indicator(&self, _edit_mode: PromptEditMode) -> Cow { - if self.show_transient.get() { - Cow::Owned(TRANSIENT_PROMPT.to_string()) - } else { - Cow::Owned(">".to_string()) - } + Cow::Owned(">".to_string()) } fn render_prompt_multiline_indicator(&self) -> Cow { @@ -72,28 +52,15 @@ impl Prompt for CustomPrompt { prefix, history_search.term )) } - - fn repaint_on_enter(&self) -> bool { - // This method is called whenever the user hits enter to go to the next - // line, so we want it to repaint and display the transient prompt - self.show_transient.set(true); - true - } } fn main() -> io::Result<()> { println!("Custom prompt demo:\nAbort with Ctrl-C or Ctrl-D"); let mut line_editor = Reedline::create(); - let prompt = CustomPrompt { - count: Cell::new(0), - left_prompt: "Custom Prompt", - show_transient: Cell::new(false), - }; + let prompt = CustomPrompt(Cell::new(0), "Custom Prompt"); loop { - // We're on a new line, so make sure we're showing the normal prompt - prompt.show_transient.set(false); let sig = line_editor.read_line(&prompt)?; match sig { Signal::Success(buffer) => { diff --git a/examples/transient_prompt.rs b/examples/transient_prompt.rs new file mode 100644 index 00000000..62149c1e --- /dev/null +++ b/examples/transient_prompt.rs @@ -0,0 +1,95 @@ +// Create a reedline object with a transient prompt. +// cargo run --example transient_prompt +// +// Prompts for previous lines will be replaced with a shorter prompt + +use reedline::{ + Prompt, PromptEditMode, PromptHistorySearch, PromptHistorySearchStatus, Reedline, Signal, +}; +use std::{borrow::Cow, cell::Cell, io}; + +// For custom prompt, implement the Prompt trait +// +// This example replaces the prompt for old lines with "!" as an +// example of a transient prompt. +#[derive(Clone)] +pub struct TransientPrompt { + /// Whether to show the transient prompt indicator instead of the normal one + show_transient: Cell, +} +pub static DEFAULT_MULTILINE_INDICATOR: &str = "::: "; +pub static NORMAL_PROMPT: &str = "(transient_prompt example)"; +pub static TRANSIENT_PROMPT: &str = "!"; +impl Prompt for TransientPrompt { + fn render_prompt_left(&self) -> Cow { + { + if self.show_transient.get() { + Cow::Owned(String::new()) + } else { + Cow::Borrowed(NORMAL_PROMPT) + } + } + } + + fn render_prompt_right(&self) -> Cow { + Cow::Owned(String::new()) + } + + fn render_prompt_indicator(&self, _edit_mode: PromptEditMode) -> Cow { + if self.show_transient.get() { + Cow::Borrowed(TRANSIENT_PROMPT) + } else { + Cow::Owned(">".to_string()) + } + } + + fn render_prompt_multiline_indicator(&self) -> Cow { + Cow::Borrowed(DEFAULT_MULTILINE_INDICATOR) + } + + fn render_prompt_history_search_indicator( + &self, + history_search: PromptHistorySearch, + ) -> Cow { + let prefix = match history_search.status { + PromptHistorySearchStatus::Passing => "", + PromptHistorySearchStatus::Failing => "failing ", + }; + + Cow::Owned(format!( + "({}reverse-search: {}) ", + prefix, history_search.term + )) + } + + fn repaint_on_enter(&self) -> bool { + // This method is called whenever the user hits enter to go to the next + // line, so we want it to repaint and display the transient prompt + self.show_transient.set(true); + true + } +} + +fn main() -> io::Result<()> { + println!("Transient prompt demo:\nAbort with Ctrl-C or Ctrl-D"); + let mut line_editor = Reedline::create(); + + let prompt = TransientPrompt { + show_transient: Cell::new(false), + }; + + loop { + // We're on a new line, so make sure we're showing the normal prompt + prompt.show_transient.set(false); + let sig = line_editor.read_line(&prompt)?; + match sig { + Signal::Success(buffer) => { + println!("We processed: {buffer}"); + } + Signal::CtrlD | Signal::CtrlC => { + println!("\nAborted!"); + break Ok(()); + } + } + } +} From 11819490c6eda79fbb2fc1907fbc064acea897c0 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Wed, 23 Aug 2023 10:31:55 -0400 Subject: [PATCH 08/23] Use sqlite-backed history in transient_prompt example --- examples/transient_prompt.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/transient_prompt.rs b/examples/transient_prompt.rs index 62149c1e..9f72c081 100644 --- a/examples/transient_prompt.rs +++ b/examples/transient_prompt.rs @@ -3,6 +3,8 @@ // // Prompts for previous lines will be replaced with a shorter prompt +#[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] +use reedline::SqliteBackedHistory; use reedline::{ Prompt, PromptEditMode, PromptHistorySearch, PromptHistorySearchStatus, Reedline, Signal, }; @@ -73,6 +75,10 @@ impl Prompt for TransientPrompt { fn main() -> io::Result<()> { println!("Transient prompt demo:\nAbort with Ctrl-C or Ctrl-D"); let mut line_editor = Reedline::create(); + #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] + { + line_editor = line_editor.with_history(Box::new(SqliteBackedHistory::in_memory().unwrap())); + } let prompt = TransientPrompt { show_transient: Cell::new(false), From fd2be4006e0349d6138fb31187c7274c5e4e08f7 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Wed, 23 Aug 2023 12:15:03 -0400 Subject: [PATCH 09/23] Add bells and whistles to transient prompt example --- examples/transient_prompt.rs | 40 ++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/examples/transient_prompt.rs b/examples/transient_prompt.rs index 9f72c081..9caa9267 100644 --- a/examples/transient_prompt.rs +++ b/examples/transient_prompt.rs @@ -3,10 +3,12 @@ // // Prompts for previous lines will be replaced with a shorter prompt +use nu_ansi_term::{Color, Style}; #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] use reedline::SqliteBackedHistory; use reedline::{ - Prompt, PromptEditMode, PromptHistorySearch, PromptHistorySearchStatus, Reedline, Signal, + ColumnarMenu, DefaultCompleter, DefaultHinter, Prompt, PromptEditMode, PromptHistorySearch, + PromptHistorySearchStatus, Reedline, ReedlineMenu, Signal, ExampleHighlighter, default_emacs_keybindings, Keybindings, KeyModifiers, KeyCode, ReedlineEvent, Emacs, }; use std::{borrow::Cow, cell::Cell, io}; @@ -72,9 +74,43 @@ impl Prompt for TransientPrompt { } } +// This is copied from the completions example +fn add_menu_keybindings(keybindings: &mut Keybindings) { + keybindings.add_binding( + KeyModifiers::NONE, + KeyCode::Tab, + ReedlineEvent::UntilFound(vec![ + ReedlineEvent::Menu("completion_menu".to_string()), + ReedlineEvent::MenuNext, + ]), + ); +} + fn main() -> io::Result<()> { println!("Transient prompt demo:\nAbort with Ctrl-C or Ctrl-D"); - let mut line_editor = Reedline::create(); + let commands = vec![ + "test".into(), + "hello world".into(), + "hello world reedline".into(), + "this is the reedline crate".into(), + ]; + let completer = Box::new(DefaultCompleter::new_with_wordlen(commands.clone(), 2)); + // Use the interactive menu to select options from the completer + let completion_menu = Box::new(ColumnarMenu::default().with_name("completion_menu")); + + let mut keybindings = default_emacs_keybindings(); + add_menu_keybindings(&mut keybindings); + + let edit_mode = Box::new(Emacs::new(keybindings)); + + let mut line_editor = Reedline::create() + .with_hinter(Box::new( + DefaultHinter::default().with_style(Style::new().fg(Color::LightGray)), + )) + .with_completer(completer) + .with_menu(ReedlineMenu::EngineCompleter(completion_menu)) + .with_edit_mode(edit_mode) + .with_highlighter(Box::new(ExampleHighlighter::new(commands))); #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] { line_editor = line_editor.with_history(Box::new(SqliteBackedHistory::in_memory().unwrap())); From 856216afadf3b993c2e4950872d675e2672b5bf3 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Wed, 23 Aug 2023 12:40:10 -0400 Subject: [PATCH 10/23] Add custom validator to transient prompt example --- examples/transient_prompt.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/examples/transient_prompt.rs b/examples/transient_prompt.rs index 9caa9267..af8c9d9c 100644 --- a/examples/transient_prompt.rs +++ b/examples/transient_prompt.rs @@ -8,7 +8,7 @@ use nu_ansi_term::{Color, Style}; use reedline::SqliteBackedHistory; use reedline::{ ColumnarMenu, DefaultCompleter, DefaultHinter, Prompt, PromptEditMode, PromptHistorySearch, - PromptHistorySearchStatus, Reedline, ReedlineMenu, Signal, ExampleHighlighter, default_emacs_keybindings, Keybindings, KeyModifiers, KeyCode, ReedlineEvent, Emacs, + PromptHistorySearchStatus, Reedline, ReedlineMenu, Signal, ExampleHighlighter, default_emacs_keybindings, Keybindings, KeyModifiers, KeyCode, ReedlineEvent, Emacs, Validator, ValidationResult, }; use std::{borrow::Cow, cell::Cell, io}; @@ -74,6 +74,19 @@ impl Prompt for TransientPrompt { } } +// To test multiline input. Only goes to the next line if the line ends with a ? +struct CustomValidator; + +impl Validator for CustomValidator { + fn validate(&self, line: &str) -> ValidationResult { + if line.ends_with("?") { + ValidationResult::Complete + } else { + ValidationResult::Incomplete + } + } +} + // This is copied from the completions example fn add_menu_keybindings(keybindings: &mut Keybindings) { keybindings.add_binding( @@ -110,7 +123,8 @@ fn main() -> io::Result<()> { .with_completer(completer) .with_menu(ReedlineMenu::EngineCompleter(completion_menu)) .with_edit_mode(edit_mode) - .with_highlighter(Box::new(ExampleHighlighter::new(commands))); + .with_highlighter(Box::new(ExampleHighlighter::new(commands))) + .with_validator(Box::new(CustomValidator {})); #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] { line_editor = line_editor.with_history(Box::new(SqliteBackedHistory::in_memory().unwrap())); From 69dd574998d9afafd5b17b7399ebba00d06f2d74 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Wed, 23 Aug 2023 12:48:23 -0400 Subject: [PATCH 11/23] Format with cargo fmt --- examples/transient_prompt.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/transient_prompt.rs b/examples/transient_prompt.rs index af8c9d9c..95032e81 100644 --- a/examples/transient_prompt.rs +++ b/examples/transient_prompt.rs @@ -7,8 +7,10 @@ use nu_ansi_term::{Color, Style}; #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] use reedline::SqliteBackedHistory; use reedline::{ - ColumnarMenu, DefaultCompleter, DefaultHinter, Prompt, PromptEditMode, PromptHistorySearch, - PromptHistorySearchStatus, Reedline, ReedlineMenu, Signal, ExampleHighlighter, default_emacs_keybindings, Keybindings, KeyModifiers, KeyCode, ReedlineEvent, Emacs, Validator, ValidationResult, + default_emacs_keybindings, ColumnarMenu, DefaultCompleter, DefaultHinter, Emacs, + ExampleHighlighter, KeyCode, KeyModifiers, Keybindings, Prompt, PromptEditMode, + PromptHistorySearch, PromptHistorySearchStatus, Reedline, ReedlineEvent, ReedlineMenu, Signal, + ValidationResult, Validator, }; use std::{borrow::Cow, cell::Cell, io}; From 2c3c74a485864dfa785bd1970261ace69efa6b80 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Sun, 27 Aug 2023 15:20:08 -0400 Subject: [PATCH 12/23] Use \ for multiline input instead of making it the default --- examples/transient_prompt.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/examples/transient_prompt.rs b/examples/transient_prompt.rs index 95032e81..47a1fadd 100644 --- a/examples/transient_prompt.rs +++ b/examples/transient_prompt.rs @@ -25,7 +25,8 @@ pub struct TransientPrompt { } pub static DEFAULT_MULTILINE_INDICATOR: &str = "::: "; pub static NORMAL_PROMPT: &str = "(transient_prompt example)"; -pub static TRANSIENT_PROMPT: &str = "!"; +pub static TRANSIENT_PROMPT: &str = "! "; +pub static TRANSIENT_MULTILINE_INDICATOR: &str = ": "; impl Prompt for TransientPrompt { fn render_prompt_left(&self) -> Cow { { @@ -50,7 +51,11 @@ impl Prompt for TransientPrompt { } fn render_prompt_multiline_indicator(&self) -> Cow { - Cow::Borrowed(DEFAULT_MULTILINE_INDICATOR) + if self.show_transient.get() { + Cow::Borrowed(TRANSIENT_MULTILINE_INDICATOR) + } else { + Cow::Borrowed(DEFAULT_MULTILINE_INDICATOR) + } } fn render_prompt_history_search_indicator( @@ -76,15 +81,15 @@ impl Prompt for TransientPrompt { } } -// To test multiline input. Only goes to the next line if the line ends with a ? +// To test multiline input. Treats as multiline if input ends with a '\' struct CustomValidator; impl Validator for CustomValidator { fn validate(&self, line: &str) -> ValidationResult { - if line.ends_with("?") { - ValidationResult::Complete - } else { + if line.ends_with("\\") { ValidationResult::Incomplete + } else { + ValidationResult::Complete } } } From 657c9839f452fa427c2eaefc3a0bb556e76fb9a9 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Sun, 27 Aug 2023 15:21:31 -0400 Subject: [PATCH 13/23] Use ansi colors, exclude ' '-prefixed lines from history --- examples/transient_prompt.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/transient_prompt.rs b/examples/transient_prompt.rs index 47a1fadd..43c3ba78 100644 --- a/examples/transient_prompt.rs +++ b/examples/transient_prompt.rs @@ -131,7 +131,9 @@ fn main() -> io::Result<()> { .with_menu(ReedlineMenu::EngineCompleter(completion_menu)) .with_edit_mode(edit_mode) .with_highlighter(Box::new(ExampleHighlighter::new(commands))) - .with_validator(Box::new(CustomValidator {})); + .with_validator(Box::new(CustomValidator {})) + .with_ansi_colors(true) + .with_history_exclusion_prefix(Some(String::from(" "))); #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] { line_editor = line_editor.with_history(Box::new(SqliteBackedHistory::in_memory().unwrap())); From 0cfe40a2abdd9c11ef7eac9bf6215bba639d26f6 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Sun, 27 Aug 2023 18:31:30 -0400 Subject: [PATCH 14/23] Add post_submit versions of render methods to Prompt --- examples/transient_prompt.rs | 55 +++++++++++++----------------------- src/engine.rs | 16 ----------- src/prompt/base.rs | 24 ++++++++++++---- 3 files changed, 38 insertions(+), 57 deletions(-) diff --git a/examples/transient_prompt.rs b/examples/transient_prompt.rs index 43c3ba78..c3ed519f 100644 --- a/examples/transient_prompt.rs +++ b/examples/transient_prompt.rs @@ -12,50 +12,46 @@ use reedline::{ PromptHistorySearch, PromptHistorySearchStatus, Reedline, ReedlineEvent, ReedlineMenu, Signal, ValidationResult, Validator, }; -use std::{borrow::Cow, cell::Cell, io}; +use std::{borrow::Cow, io}; // For custom prompt, implement the Prompt trait // // This example replaces the prompt for old lines with "!" as an // example of a transient prompt. #[derive(Clone)] -pub struct TransientPrompt { - /// Whether to show the transient prompt indicator instead of the normal one - show_transient: Cell, -} +pub struct TransientPrompt; + pub static DEFAULT_MULTILINE_INDICATOR: &str = "::: "; pub static NORMAL_PROMPT: &str = "(transient_prompt example)"; pub static TRANSIENT_PROMPT: &str = "! "; pub static TRANSIENT_MULTILINE_INDICATOR: &str = ": "; impl Prompt for TransientPrompt { fn render_prompt_left(&self) -> Cow { - { - if self.show_transient.get() { - Cow::Owned(String::new()) - } else { - Cow::Borrowed(NORMAL_PROMPT) - } - } + Cow::Borrowed(NORMAL_PROMPT) + } + + fn render_prompt_left_post_submit(&self) -> Cow { + Cow::Owned(String::new()) } fn render_prompt_right(&self) -> Cow { Cow::Owned(String::new()) } - fn render_prompt_indicator(&self, _edit_mode: PromptEditMode) -> Cow { - if self.show_transient.get() { - Cow::Borrowed(TRANSIENT_PROMPT) - } else { - Cow::Owned(">".to_string()) - } + fn render_prompt_indicator(&self, _prompt_mode: PromptEditMode) -> Cow { + Cow::Owned(">".to_string()) + } + + fn render_prompt_indicator_post_submit(&self, _prompt_mode: PromptEditMode) -> Cow { + Cow::Borrowed(TRANSIENT_PROMPT) } fn render_prompt_multiline_indicator(&self) -> Cow { - if self.show_transient.get() { - Cow::Borrowed(TRANSIENT_MULTILINE_INDICATOR) - } else { - Cow::Borrowed(DEFAULT_MULTILINE_INDICATOR) - } + Cow::Borrowed(DEFAULT_MULTILINE_INDICATOR) + } + + fn render_prompt_multiline_indicator_post_submit(&self) -> Cow { + Cow::Borrowed(TRANSIENT_MULTILINE_INDICATOR) } fn render_prompt_history_search_indicator( @@ -72,13 +68,6 @@ impl Prompt for TransientPrompt { prefix, history_search.term )) } - - fn repaint_on_enter(&self) -> bool { - // This method is called whenever the user hits enter to go to the next - // line, so we want it to repaint and display the transient prompt - self.show_transient.set(true); - true - } } // To test multiline input. Treats as multiline if input ends with a '\' @@ -139,13 +128,9 @@ fn main() -> io::Result<()> { line_editor = line_editor.with_history(Box::new(SqliteBackedHistory::in_memory().unwrap())); } - let prompt = TransientPrompt { - show_transient: Cell::new(false), - }; + let prompt = TransientPrompt {}; loop { - // We're on a new line, so make sure we're showing the normal prompt - prompt.show_transient.set(false); let sig = line_editor.read_line(&prompt)?; match sig { Signal::Success(buffer) => { diff --git a/src/engine.rs b/src/engine.rs index 1a8a5339..ac69df9a 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -713,22 +713,6 @@ impl Reedline { for event in reedline_events.drain(..) { match self.handle_event(prompt, event)? { EventStatus::Exits(signal) => { - if prompt.repaint_on_enter() { - if let Some(id) = self.history_last_run_id { - if let Ok(last) = self.history.load(id) { - // Set the line buffer to the last entered line so that we can - // re-render it with the new prompt - self.editor.edit_buffer( - |buf| buf.insert_str(&last.command_line), - UndoBehavior::UndoRedo, - ); - self.repaint(prompt)?; - // Clear the buffer again so that the next line can be typed - self.editor - .edit_buffer(|buf| buf.clear(), UndoBehavior::UndoRedo); - } - } - } // Move the cursor below the input area, for external commands or new read_line call self.painter.move_cursor_to_end()?; return Ok(signal); diff --git a/src/prompt/base.rs b/src/prompt/base.rs index a396734c..623833fa 100644 --- a/src/prompt/base.rs +++ b/src/prompt/base.rs @@ -84,23 +84,35 @@ impl Display for PromptEditMode { /// Implementors have to provide [`str`]-based content which will be /// displayed before the `LineBuffer` is drawn. pub trait Prompt: Send { - /// Provide content off the right full prompt + /// Provide content of the left full prompt fn render_prompt_left(&self) -> Cow; - /// Provide content off the left full prompt + /// Provide content of the left full prompt (after submitting) + fn render_prompt_left_post_submit(&self) -> Cow { + self.render_prompt_left() + } + /// Provide content of the right full prompt fn render_prompt_right(&self) -> Cow; + /// Provide content of the right full prompt (after submitting) + fn render_prompt_right_post_submit(&self) -> Cow { + self.render_prompt_right() + } /// Render the prompt indicator (Last part of the prompt that changes based on the editor mode) fn render_prompt_indicator(&self, prompt_mode: PromptEditMode) -> Cow; + /// Render the prompt indicator (after submitting) + fn render_prompt_indicator_post_submit(&self, prompt_mode: PromptEditMode) -> Cow { + self.render_prompt_indicator(prompt_mode) + } /// Indicator to show before explicit new lines fn render_prompt_multiline_indicator(&self) -> Cow; + /// Indicator to show before explicit new lines (after submitting) + fn render_prompt_multiline_indicator_post_submit(&self) -> Cow { + self.render_prompt_multiline_indicator() + } /// Render the prompt indicator for `Ctrl-R` history search fn render_prompt_history_search_indicator( &self, history_search: PromptHistorySearch, ) -> Cow; - /// Whether to repaint the prompt after the user hits enter (for transient prompts) - fn repaint_on_enter(&self) -> bool { - false - } /// Get the default prompt color fn get_prompt_color(&self) -> Color { DEFAULT_PROMPT_COLOR From 0070e27ab27a054cc1aab8461f870296fcaaee26 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Sun, 27 Aug 2023 18:46:56 -0400 Subject: [PATCH 15/23] Add a get_transient_prompt method to Prompt --- examples/transient_prompt.rs | 54 +++++++++++++++++++++++++++--------- src/engine.rs | 5 +++- src/prompt/base.rs | 21 ++++---------- 3 files changed, 50 insertions(+), 30 deletions(-) diff --git a/examples/transient_prompt.rs b/examples/transient_prompt.rs index c3ed519f..0a1f7444 100644 --- a/examples/transient_prompt.rs +++ b/examples/transient_prompt.rs @@ -16,24 +16,23 @@ use std::{borrow::Cow, io}; // For custom prompt, implement the Prompt trait // -// This example replaces the prompt for old lines with "!" as an -// example of a transient prompt. +// This example replaces the prompt for old lines with "!" as an example of a +// transient prompt. #[derive(Clone)] +pub struct CustomPrompt; + pub struct TransientPrompt; pub static DEFAULT_MULTILINE_INDICATOR: &str = "::: "; pub static NORMAL_PROMPT: &str = "(transient_prompt example)"; pub static TRANSIENT_PROMPT: &str = "! "; pub static TRANSIENT_MULTILINE_INDICATOR: &str = ": "; -impl Prompt for TransientPrompt { + +impl Prompt for CustomPrompt { fn render_prompt_left(&self) -> Cow { Cow::Borrowed(NORMAL_PROMPT) } - fn render_prompt_left_post_submit(&self) -> Cow { - Cow::Owned(String::new()) - } - fn render_prompt_right(&self) -> Cow { Cow::Owned(String::new()) } @@ -42,15 +41,44 @@ impl Prompt for TransientPrompt { Cow::Owned(">".to_string()) } - fn render_prompt_indicator_post_submit(&self, _prompt_mode: PromptEditMode) -> Cow { - Cow::Borrowed(TRANSIENT_PROMPT) - } - fn render_prompt_multiline_indicator(&self) -> Cow { Cow::Borrowed(DEFAULT_MULTILINE_INDICATOR) } - fn render_prompt_multiline_indicator_post_submit(&self) -> Cow { + fn render_prompt_history_search_indicator( + &self, + history_search: PromptHistorySearch, + ) -> Cow { + let prefix = match history_search.status { + PromptHistorySearchStatus::Passing => "", + PromptHistorySearchStatus::Failing => "failing ", + }; + + Cow::Owned(format!( + "({}reverse-search: {}) ", + prefix, history_search.term + )) + } + + fn get_transient_prompt(&self) -> Option> { + Some(Box::new(TransientPrompt {})) + } +} + +impl Prompt for TransientPrompt { + fn render_prompt_left(&self) -> Cow { + Cow::Owned(String::new()) + } + + fn render_prompt_right(&self) -> Cow { + Cow::Owned(String::new()) + } + + fn render_prompt_indicator(&self, _prompt_mode: PromptEditMode) -> Cow { + Cow::Borrowed(TRANSIENT_PROMPT) + } + + fn render_prompt_multiline_indicator(&self) -> Cow { Cow::Borrowed(TRANSIENT_MULTILINE_INDICATOR) } @@ -128,7 +156,7 @@ fn main() -> io::Result<()> { line_editor = line_editor.with_history(Box::new(SqliteBackedHistory::in_memory().unwrap())); } - let prompt = TransientPrompt {}; + let prompt = CustomPrompt {}; loop { let sig = line_editor.read_line(&prompt)?; diff --git a/src/engine.rs b/src/engine.rs index ac69df9a..4cd036a6 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1700,7 +1700,10 @@ impl Reedline { let buffer = self.editor.get_buffer().to_string(); self.hide_hints = true; // Additional repaint to show the content without hints etc. - self.repaint(prompt)?; + match prompt.get_transient_prompt() { + Some(transient_prompt) => self.repaint(transient_prompt.as_ref())?, + None => self.repaint(prompt)? + } if !buffer.is_empty() { let mut entry = HistoryItem::from_command_line(&buffer); entry.session_id = self.get_history_session_id(); diff --git a/src/prompt/base.rs b/src/prompt/base.rs index 623833fa..ee783186 100644 --- a/src/prompt/base.rs +++ b/src/prompt/base.rs @@ -86,28 +86,12 @@ impl Display for PromptEditMode { pub trait Prompt: Send { /// Provide content of the left full prompt fn render_prompt_left(&self) -> Cow; - /// Provide content of the left full prompt (after submitting) - fn render_prompt_left_post_submit(&self) -> Cow { - self.render_prompt_left() - } /// Provide content of the right full prompt fn render_prompt_right(&self) -> Cow; - /// Provide content of the right full prompt (after submitting) - fn render_prompt_right_post_submit(&self) -> Cow { - self.render_prompt_right() - } /// Render the prompt indicator (Last part of the prompt that changes based on the editor mode) fn render_prompt_indicator(&self, prompt_mode: PromptEditMode) -> Cow; - /// Render the prompt indicator (after submitting) - fn render_prompt_indicator_post_submit(&self, prompt_mode: PromptEditMode) -> Cow { - self.render_prompt_indicator(prompt_mode) - } /// Indicator to show before explicit new lines fn render_prompt_multiline_indicator(&self) -> Cow; - /// Indicator to show before explicit new lines (after submitting) - fn render_prompt_multiline_indicator_post_submit(&self) -> Cow { - self.render_prompt_multiline_indicator() - } /// Render the prompt indicator for `Ctrl-R` history search fn render_prompt_history_search_indicator( &self, @@ -134,4 +118,9 @@ pub trait Prompt: Send { fn right_prompt_on_last_line(&self) -> bool { false } + + /// Prompt to display after submitting a line + fn get_transient_prompt(&self) -> Option> { + None + } } From 792efff11a4bc1d16ddd9205a655cdd7f32e99fe Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Sun, 27 Aug 2023 18:47:40 -0400 Subject: [PATCH 16/23] Run cargo fmt --- src/engine.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine.rs b/src/engine.rs index 4cd036a6..9f99b93e 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1702,7 +1702,7 @@ impl Reedline { // Additional repaint to show the content without hints etc. match prompt.get_transient_prompt() { Some(transient_prompt) => self.repaint(transient_prompt.as_ref())?, - None => self.repaint(prompt)? + None => self.repaint(prompt)?, } if !buffer.is_empty() { let mut entry = HistoryItem::from_command_line(&buffer); From dc5270fb2d593c8123468cf4a0ccb02c1e27066d Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Mon, 28 Aug 2023 15:57:29 -0400 Subject: [PATCH 17/23] Add transient_prompt field to Reedline itself --- examples/transient_prompt.rs | 49 +++--------------------------------- src/engine.rs | 19 ++++++++++++-- src/prompt/base.rs | 5 ---- 3 files changed, 21 insertions(+), 52 deletions(-) diff --git a/examples/transient_prompt.rs b/examples/transient_prompt.rs index 0a1f7444..7d270a7c 100644 --- a/examples/transient_prompt.rs +++ b/examples/transient_prompt.rs @@ -7,7 +7,7 @@ use nu_ansi_term::{Color, Style}; #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] use reedline::SqliteBackedHistory; use reedline::{ - default_emacs_keybindings, ColumnarMenu, DefaultCompleter, DefaultHinter, Emacs, + default_emacs_keybindings, ColumnarMenu, DefaultCompleter, DefaultHinter, DefaultPrompt, Emacs, ExampleHighlighter, KeyCode, KeyModifiers, Keybindings, Prompt, PromptEditMode, PromptHistorySearch, PromptHistorySearchStatus, Reedline, ReedlineEvent, ReedlineMenu, Signal, ValidationResult, Validator, @@ -18,53 +18,11 @@ use std::{borrow::Cow, io}; // // This example replaces the prompt for old lines with "!" as an example of a // transient prompt. -#[derive(Clone)] -pub struct CustomPrompt; - pub struct TransientPrompt; -pub static DEFAULT_MULTILINE_INDICATOR: &str = "::: "; -pub static NORMAL_PROMPT: &str = "(transient_prompt example)"; pub static TRANSIENT_PROMPT: &str = "! "; pub static TRANSIENT_MULTILINE_INDICATOR: &str = ": "; -impl Prompt for CustomPrompt { - fn render_prompt_left(&self) -> Cow { - Cow::Borrowed(NORMAL_PROMPT) - } - - fn render_prompt_right(&self) -> Cow { - Cow::Owned(String::new()) - } - - fn render_prompt_indicator(&self, _prompt_mode: PromptEditMode) -> Cow { - Cow::Owned(">".to_string()) - } - - fn render_prompt_multiline_indicator(&self) -> Cow { - Cow::Borrowed(DEFAULT_MULTILINE_INDICATOR) - } - - fn render_prompt_history_search_indicator( - &self, - history_search: PromptHistorySearch, - ) -> Cow { - let prefix = match history_search.status { - PromptHistorySearchStatus::Passing => "", - PromptHistorySearchStatus::Failing => "failing ", - }; - - Cow::Owned(format!( - "({}reverse-search: {}) ", - prefix, history_search.term - )) - } - - fn get_transient_prompt(&self) -> Option> { - Some(Box::new(TransientPrompt {})) - } -} - impl Prompt for TransientPrompt { fn render_prompt_left(&self) -> Cow { Cow::Owned(String::new()) @@ -150,13 +108,14 @@ fn main() -> io::Result<()> { .with_highlighter(Box::new(ExampleHighlighter::new(commands))) .with_validator(Box::new(CustomValidator {})) .with_ansi_colors(true) - .with_history_exclusion_prefix(Some(String::from(" "))); + .with_history_exclusion_prefix(Some(String::from(" "))) + .with_transient_prompt(Box::new(TransientPrompt {})); #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] { line_editor = line_editor.with_history(Box::new(SqliteBackedHistory::in_memory().unwrap())); } - let prompt = CustomPrompt {}; + let prompt = DefaultPrompt::default(); loop { let sig = line_editor.read_line(&prompt)?; diff --git a/src/engine.rs b/src/engine.rs index 9f99b93e..d6150137 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use crossterm::event::{DisableBracketedPaste, EnableBracketedPaste}; use crossterm::execute; @@ -110,6 +112,8 @@ pub struct Reedline { // Stdout painter: Painter, + transient_prompt: Option>, + // Edit Mode: Vi, Emacs edit_mode: Box, @@ -197,6 +201,7 @@ impl Reedline { history_cursor_on_excluded: false, input_mode: InputMode::Regular, painter, + transient_prompt: None, edit_mode, completer, quick_completions: false, @@ -434,6 +439,13 @@ impl Reedline { self } + /// Set a different prompt to be used after submitting each line + #[must_use] + pub fn with_transient_prompt(mut self, transient_prompt: Box) -> Self { + self.transient_prompt = Some(Rc::from(transient_prompt)); + self + } + /// A builder which configures the edit mode for your instance of the Reedline engine #[must_use] pub fn with_edit_mode(mut self, edit_mode: Box) -> Self { @@ -1700,8 +1712,11 @@ impl Reedline { let buffer = self.editor.get_buffer().to_string(); self.hide_hints = true; // Additional repaint to show the content without hints etc. - match prompt.get_transient_prompt() { - Some(transient_prompt) => self.repaint(transient_prompt.as_ref())?, + match &mut self.transient_prompt { + Some(transient_prompt) => { + let prompt = transient_prompt.clone(); + self.repaint(prompt.as_ref())? + } None => self.repaint(prompt)?, } if !buffer.is_empty() { diff --git a/src/prompt/base.rs b/src/prompt/base.rs index ee783186..01d91658 100644 --- a/src/prompt/base.rs +++ b/src/prompt/base.rs @@ -118,9 +118,4 @@ pub trait Prompt: Send { fn right_prompt_on_last_line(&self) -> bool { false } - - /// Prompt to display after submitting a line - fn get_transient_prompt(&self) -> Option> { - None - } } From 9bb3efaffef5ae03171262774943b18c2d396c3a Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Mon, 28 Aug 2023 16:01:14 -0400 Subject: [PATCH 18/23] Minor stylistic change --- src/engine.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index d6150137..02119eaf 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1712,12 +1712,10 @@ impl Reedline { let buffer = self.editor.get_buffer().to_string(); self.hide_hints = true; // Additional repaint to show the content without hints etc. - match &mut self.transient_prompt { - Some(transient_prompt) => { - let prompt = transient_prompt.clone(); - self.repaint(prompt.as_ref())? - } - None => self.repaint(prompt)?, + if let Some(transient_prompt) = &self.transient_prompt { + self.repaint(transient_prompt.clone().as_ref())? + } else { + self.repaint(prompt)?; } if !buffer.is_empty() { let mut entry = HistoryItem::from_command_line(&buffer); From d70d216386fb70a891e939eb6d13343c42f79717 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 29 Aug 2023 14:48:34 -0400 Subject: [PATCH 19/23] Make transient_prompt an Arc so it's Send --- src/engine.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 02119eaf..50741983 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,4 +1,4 @@ -use std::rc::Rc; +use std::sync::{Arc, Mutex}; use crossterm::event::{DisableBracketedPaste, EnableBracketedPaste}; use crossterm::execute; @@ -112,7 +112,7 @@ pub struct Reedline { // Stdout painter: Painter, - transient_prompt: Option>, + transient_prompt: Option>>>, // Edit Mode: Vi, Emacs edit_mode: Box, @@ -442,7 +442,7 @@ impl Reedline { /// Set a different prompt to be used after submitting each line #[must_use] pub fn with_transient_prompt(mut self, transient_prompt: Box) -> Self { - self.transient_prompt = Some(Rc::from(transient_prompt)); + self.transient_prompt = Some(Arc::from(Mutex::from(transient_prompt))); self } @@ -1713,7 +1713,7 @@ impl Reedline { self.hide_hints = true; // Additional repaint to show the content without hints etc. if let Some(transient_prompt) = &self.transient_prompt { - self.repaint(transient_prompt.clone().as_ref())? + self.repaint(transient_prompt.clone().lock().unwrap().as_ref())? } else { self.repaint(prompt)?; } From 22609d0fc57c71acfbcde22532ffdb0dc3578353 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 29 Aug 2023 16:07:50 -0400 Subject: [PATCH 20/23] Get rid of unwrap() --- src/engine.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/engine.rs b/src/engine.rs index 50741983..2fa7705a 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1713,7 +1713,15 @@ impl Reedline { self.hide_hints = true; // Additional repaint to show the content without hints etc. if let Some(transient_prompt) = &self.transient_prompt { - self.repaint(transient_prompt.clone().lock().unwrap().as_ref())? + match transient_prompt.clone().lock() { + Ok(transient_prompt) => { + self.repaint(transient_prompt.as_ref())?; + } + Err(_) => { + // TODO log error + self.repaint(prompt)?; + } + } } else { self.repaint(prompt)?; } From 46090a94eaee53a37a51460c36baaa13ec76105f Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Thu, 31 Aug 2023 13:28:13 -0400 Subject: [PATCH 21/23] Use a Cell>> --- src/engine.rs | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 2fa7705a..bdcdfda8 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,4 +1,4 @@ -use std::sync::{Arc, Mutex}; +use std::cell::Cell; use crossterm::event::{DisableBracketedPaste, EnableBracketedPaste}; use crossterm::execute; @@ -112,7 +112,7 @@ pub struct Reedline { // Stdout painter: Painter, - transient_prompt: Option>>>, + transient_prompt: Cell>>, // Edit Mode: Vi, Emacs edit_mode: Box, @@ -201,7 +201,7 @@ impl Reedline { history_cursor_on_excluded: false, input_mode: InputMode::Regular, painter, - transient_prompt: None, + transient_prompt: Cell::new(None), edit_mode, completer, quick_completions: false, @@ -441,8 +441,8 @@ impl Reedline { /// Set a different prompt to be used after submitting each line #[must_use] - pub fn with_transient_prompt(mut self, transient_prompt: Box) -> Self { - self.transient_prompt = Some(Arc::from(Mutex::from(transient_prompt))); + pub fn with_transient_prompt(self, transient_prompt: Box) -> Self { + self.transient_prompt.set(Some(transient_prompt)); self } @@ -1712,16 +1712,9 @@ impl Reedline { let buffer = self.editor.get_buffer().to_string(); self.hide_hints = true; // Additional repaint to show the content without hints etc. - if let Some(transient_prompt) = &self.transient_prompt { - match transient_prompt.clone().lock() { - Ok(transient_prompt) => { - self.repaint(transient_prompt.as_ref())?; - } - Err(_) => { - // TODO log error - self.repaint(prompt)?; - } - } + if let Some(transient_prompt) = self.transient_prompt.take() { + self.repaint(transient_prompt.as_ref())?; + self.transient_prompt.set(Some(transient_prompt)); } else { self.repaint(prompt)?; } From c567bcedd3549d9333f1242c5d3a388e076ae410 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Thu, 31 Aug 2023 19:21:28 -0400 Subject: [PATCH 22/23] Don't use Cell --- src/engine.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index bdcdfda8..ea9c2448 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,5 +1,3 @@ -use std::cell::Cell; - use crossterm::event::{DisableBracketedPaste, EnableBracketedPaste}; use crossterm::execute; @@ -112,7 +110,7 @@ pub struct Reedline { // Stdout painter: Painter, - transient_prompt: Cell>>, + transient_prompt: Option>, // Edit Mode: Vi, Emacs edit_mode: Box, @@ -201,7 +199,7 @@ impl Reedline { history_cursor_on_excluded: false, input_mode: InputMode::Regular, painter, - transient_prompt: Cell::new(None), + transient_prompt: None, edit_mode, completer, quick_completions: false, @@ -441,8 +439,8 @@ impl Reedline { /// Set a different prompt to be used after submitting each line #[must_use] - pub fn with_transient_prompt(self, transient_prompt: Box) -> Self { - self.transient_prompt.set(Some(transient_prompt)); + pub fn with_transient_prompt(mut self, transient_prompt: Box) -> Self { + self.transient_prompt = Some(transient_prompt); self } @@ -1712,9 +1710,9 @@ impl Reedline { let buffer = self.editor.get_buffer().to_string(); self.hide_hints = true; // Additional repaint to show the content without hints etc. - if let Some(transient_prompt) = self.transient_prompt.take() { + if let Some(transient_prompt) = std::mem::replace(&mut self.transient_prompt, None) { self.repaint(transient_prompt.as_ref())?; - self.transient_prompt.set(Some(transient_prompt)); + self.transient_prompt = Some(transient_prompt); } else { self.repaint(prompt)?; } From a086b4c10020ddbf724d726da0fd290058097822 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 12 Sep 2023 15:59:22 -0400 Subject: [PATCH 23/23] Use Option.take instead of mem::replace --- src/engine.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine.rs b/src/engine.rs index ea9c2448..94d4d0c9 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1710,7 +1710,7 @@ impl Reedline { let buffer = self.editor.get_buffer().to_string(); self.hide_hints = true; // Additional repaint to show the content without hints etc. - if let Some(transient_prompt) = std::mem::replace(&mut self.transient_prompt, None) { + if let Some(transient_prompt) = self.transient_prompt.take() { self.repaint(transient_prompt.as_ref())?; self.transient_prompt = Some(transient_prompt); } else {