mirror of
https://github.com/nushell/reedline.git
synced 2024-10-27 01:45:51 +03:00
Add support for a different prompt after submission (#627)
adds a separate `transient_prompt` to be displayed after submitting.
This commit is contained in:
parent
b2b8f3f486
commit
33828e5821
132
examples/transient_prompt.rs
Normal file
132
examples/transient_prompt.rs
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
// 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 nu_ansi_term::{Color, Style};
|
||||||
|
#[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))]
|
||||||
|
use reedline::SqliteBackedHistory;
|
||||||
|
use reedline::{
|
||||||
|
default_emacs_keybindings, ColumnarMenu, DefaultCompleter, DefaultHinter, DefaultPrompt, Emacs,
|
||||||
|
ExampleHighlighter, KeyCode, KeyModifiers, Keybindings, Prompt, PromptEditMode,
|
||||||
|
PromptHistorySearch, PromptHistorySearchStatus, Reedline, ReedlineEvent, ReedlineMenu, Signal,
|
||||||
|
ValidationResult, Validator,
|
||||||
|
};
|
||||||
|
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.
|
||||||
|
pub struct TransientPrompt;
|
||||||
|
|
||||||
|
pub static TRANSIENT_PROMPT: &str = "! ";
|
||||||
|
pub static TRANSIENT_MULTILINE_INDICATOR: &str = ": ";
|
||||||
|
|
||||||
|
impl Prompt for TransientPrompt {
|
||||||
|
fn render_prompt_left(&self) -> Cow<str> {
|
||||||
|
Cow::Owned(String::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_prompt_right(&self) -> Cow<str> {
|
||||||
|
Cow::Owned(String::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_prompt_indicator(&self, _prompt_mode: PromptEditMode) -> Cow<str> {
|
||||||
|
Cow::Borrowed(TRANSIENT_PROMPT)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_prompt_multiline_indicator(&self) -> Cow<str> {
|
||||||
|
Cow::Borrowed(TRANSIENT_MULTILINE_INDICATOR)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_prompt_history_search_indicator(
|
||||||
|
&self,
|
||||||
|
history_search: PromptHistorySearch,
|
||||||
|
) -> Cow<str> {
|
||||||
|
let prefix = match history_search.status {
|
||||||
|
PromptHistorySearchStatus::Passing => "",
|
||||||
|
PromptHistorySearchStatus::Failing => "failing ",
|
||||||
|
};
|
||||||
|
|
||||||
|
Cow::Owned(format!(
|
||||||
|
"({}reverse-search: {}) ",
|
||||||
|
prefix, history_search.term
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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::Incomplete
|
||||||
|
} else {
|
||||||
|
ValidationResult::Complete
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 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)))
|
||||||
|
.with_validator(Box::new(CustomValidator {}))
|
||||||
|
.with_ansi_colors(true)
|
||||||
|
.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 = DefaultPrompt::default();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let sig = line_editor.read_line(&prompt)?;
|
||||||
|
match sig {
|
||||||
|
Signal::Success(buffer) => {
|
||||||
|
println!("We processed: {buffer}");
|
||||||
|
}
|
||||||
|
Signal::CtrlD | Signal::CtrlC => {
|
||||||
|
println!("\nAborted!");
|
||||||
|
break Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -112,6 +112,8 @@ pub struct Reedline {
|
|||||||
// Stdout
|
// Stdout
|
||||||
painter: Painter,
|
painter: Painter,
|
||||||
|
|
||||||
|
transient_prompt: Option<Box<dyn Prompt>>,
|
||||||
|
|
||||||
// Edit Mode: Vi, Emacs
|
// Edit Mode: Vi, Emacs
|
||||||
edit_mode: Box<dyn EditMode>,
|
edit_mode: Box<dyn EditMode>,
|
||||||
|
|
||||||
@ -205,6 +207,7 @@ impl Reedline {
|
|||||||
history_cursor_on_excluded: false,
|
history_cursor_on_excluded: false,
|
||||||
input_mode: InputMode::Regular,
|
input_mode: InputMode::Regular,
|
||||||
painter,
|
painter,
|
||||||
|
transient_prompt: None,
|
||||||
edit_mode,
|
edit_mode,
|
||||||
completer,
|
completer,
|
||||||
quick_completions: false,
|
quick_completions: false,
|
||||||
@ -462,6 +465,13 @@ impl Reedline {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set a different prompt to be used after submitting each line
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_transient_prompt(mut self, transient_prompt: Box<dyn Prompt>) -> Self {
|
||||||
|
self.transient_prompt = Some(transient_prompt);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// A builder which configures the edit mode for your instance of the Reedline engine
|
/// A builder which configures the edit mode for your instance of the Reedline engine
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_edit_mode(mut self, edit_mode: Box<dyn EditMode>) -> Self {
|
pub fn with_edit_mode(mut self, edit_mode: Box<dyn EditMode>) -> Self {
|
||||||
@ -1757,7 +1767,12 @@ impl Reedline {
|
|||||||
let buffer = self.editor.get_buffer().to_string();
|
let buffer = self.editor.get_buffer().to_string();
|
||||||
self.hide_hints = true;
|
self.hide_hints = true;
|
||||||
// Additional repaint to show the content without hints etc.
|
// Additional repaint to show the content without hints etc.
|
||||||
self.repaint(prompt)?;
|
if let Some(transient_prompt) = self.transient_prompt.take() {
|
||||||
|
self.repaint(transient_prompt.as_ref())?;
|
||||||
|
self.transient_prompt = Some(transient_prompt);
|
||||||
|
} else {
|
||||||
|
self.repaint(prompt)?;
|
||||||
|
}
|
||||||
if !buffer.is_empty() {
|
if !buffer.is_empty() {
|
||||||
let mut entry = HistoryItem::from_command_line(&buffer);
|
let mut entry = HistoryItem::from_command_line(&buffer);
|
||||||
entry.session_id = self.get_history_session_id();
|
entry.session_id = self.get_history_session_id();
|
||||||
|
@ -84,9 +84,9 @@ impl Display for PromptEditMode {
|
|||||||
/// Implementors have to provide [`str`]-based content which will be
|
/// Implementors have to provide [`str`]-based content which will be
|
||||||
/// displayed before the `LineBuffer` is drawn.
|
/// displayed before the `LineBuffer` is drawn.
|
||||||
pub trait Prompt: Send {
|
pub trait Prompt: Send {
|
||||||
/// Provide content off the right full prompt
|
/// Provide content of the left full prompt
|
||||||
fn render_prompt_left(&self) -> Cow<str>;
|
fn render_prompt_left(&self) -> Cow<str>;
|
||||||
/// Provide content off the left full prompt
|
/// Provide content of the right full prompt
|
||||||
fn render_prompt_right(&self) -> Cow<str>;
|
fn render_prompt_right(&self) -> Cow<str>;
|
||||||
/// Render the prompt indicator (Last part of the prompt that changes based on the editor mode)
|
/// 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<str>;
|
fn render_prompt_indicator(&self, prompt_mode: PromptEditMode) -> Cow<str>;
|
||||||
|
Loading…
Reference in New Issue
Block a user