From ea79982342456fdda5cf7101ff7c583139446308 Mon Sep 17 00:00:00 2001 From: Admin Date: Sat, 2 Dec 2023 03:55:22 +0100 Subject: [PATCH 1/7] Sqlite database now gets the session_timestamp from the history session id --- examples/demo.rs | 1 - src/history/base.rs | 5 ++++- src/history/file_backed.rs | 4 ++++ src/history/sqlite_backed.rs | 10 ++++++++-- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/examples/demo.rs b/examples/demo.rs index 2e7a402e..937db0dc 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -37,7 +37,6 @@ fn main() -> std::io::Result<()> { reedline::SqliteBackedHistory::with_file( "history.sqlite3".into(), history_session_id, - Some(chrono::Utc::now()), ) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?, ); diff --git a/src/history/base.rs b/src/history/base.rs index 26d4da05..9216e34f 100644 --- a/src/history/base.rs +++ b/src/history/base.rs @@ -218,6 +218,9 @@ pub trait History: Send { fn sync(&mut self) -> std::io::Result<()>; /// get the history session id fn session(&self) -> Option; + // Dev comment: This has been implemented due to the `history session --set` command which couldn't get done so this is commented + // /// updates the history session id + // fn update_session(&mut self, history_session: Option); } #[cfg(test)] @@ -395,7 +398,7 @@ mod test { #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] fn open_history() -> Box { Box::new( - crate::SqliteBackedHistory::with_file("target/test-history.db".into(), None, None) + crate::SqliteBackedHistory::with_file("target/test-history.db".into(), None) .unwrap(), ) } diff --git a/src/history/file_backed.rs b/src/history/file_backed.rs index 28b437f7..1108027d 100644 --- a/src/history/file_backed.rs +++ b/src/history/file_backed.rs @@ -278,6 +278,10 @@ impl History for FileBackedHistory { fn session(&self) -> Option { self.session } + + // fn update_session(&mut self, history_session: Option) { + // self.session = history_session + // } } impl FileBackedHistory { diff --git a/src/history/sqlite_backed.rs b/src/history/sqlite_backed.rs index 7ab56e96..350a87ba 100644 --- a/src/history/sqlite_backed.rs +++ b/src/history/sqlite_backed.rs @@ -176,6 +176,11 @@ impl History for SqliteBackedHistory { fn session(&self) -> Option { self.session } + + // fn update_session(&mut self, history_session: Option) { + // self.session = history_session; + // self.session_timestamp = history_session.map(|hs| chrono::Utc.timestamp_nanos(hs.0)) + // } } fn map_sqlite_err(err: rusqlite::Error) -> ReedlineError { // TODO: better error mapping @@ -195,7 +200,6 @@ impl SqliteBackedHistory { pub fn with_file( file: PathBuf, session: Option, - session_timestamp: Option>, ) -> Result { if let Some(base_dir) = file.parent() { std::fs::create_dir_all(base_dir).map_err(|e| { @@ -203,7 +207,7 @@ impl SqliteBackedHistory { })?; } let db = Connection::open(&file).map_err(map_sqlite_err)?; - Self::from_connection(db, session, session_timestamp) + Self::from_connection(db, session, session.map(|s| chrono::Utc.timestamp_nanos(s.0))) } /// Creates a new history in memory pub fn in_memory() -> Result { @@ -357,6 +361,7 @@ impl SqliteBackedHistory { wheres.push("exit_status != 0"); } } + if let (Some(session_id), Some(session_timestamp)) = (query.filter.session, self.session_timestamp) { @@ -374,6 +379,7 @@ impl SqliteBackedHistory { if wheres.is_empty() { wheres = "true".to_string(); } + let query = format!( "SELECT {select_expression} \ FROM history \ From a8c06820dde952e8bfbfe29f15bd56d6f128c221 Mon Sep 17 00:00:00 2001 From: Admin Date: Sat, 2 Dec 2023 20:56:05 +0100 Subject: [PATCH 2/7] cargo fmt + clippy fix (besides cargo fmt internal errors that don't reside from my changes) --- examples/basic.rs | 6 +- examples/completions.rs | 7 +- examples/custom_prompt.rs | 3 +- examples/demo.rs | 43 ++-- examples/event_listener.rs | 26 +- examples/event_listener_kitty_proto.rs | 29 ++- examples/external_printer.rs | 10 +- examples/highlighter.rs | 3 +- examples/hinter.rs | 3 +- examples/history.rs | 3 +- examples/transient_prompt.rs | 3 +- examples/validator.rs | 18 +- src/completion/base.rs | 12 +- src/completion/default.rs | 209 +++++++++++---- src/core_editor/clip_buffer.rs | 18 +- src/core_editor/edit_stack.rs | 10 +- src/core_editor/editor.rs | 20 +- src/core_editor/line_buffer.rs | 29 ++- src/edit_mode/base.rs | 3 +- src/edit_mode/cursors.rs | 3 +- src/edit_mode/emacs.rs | 9 +- src/edit_mode/keybindings.rs | 15 +- src/edit_mode/vi/command.rs | 10 +- src/edit_mode/vi/mod.rs | 4 +- src/edit_mode/vi/motion.rs | 3 +- src/edit_mode/vi/parser.rs | 12 +- src/engine.rs | 283 +++++++++++---------- src/enums.rs | 31 ++- src/external_printer.rs | 6 +- src/highlighter/example.rs | 7 +- src/highlighter/mod.rs | 12 +- src/highlighter/simple_match.rs | 4 +- src/hinter/cwd_aware.rs | 6 +- src/hinter/default.rs | 6 +- src/hinter/mod.rs | 9 +- src/history/base.rs | 29 ++- src/history/cursor.rs | 63 +++-- src/history/file_backed.rs | 38 +-- src/history/item.rs | 15 +- src/history/mod.rs | 8 +- src/history/sqlite_backed.rs | 37 +-- src/lib.rs | 95 +++---- src/menu/columnar_menu.rs | 16 +- src/menu/list_menu.rs | 34 +-- src/menu/menu_functions.rs | 1 - src/menu/mod.rs | 32 ++- src/painting/painter.rs | 99 +++---- src/painting/prompt_lines.rs | 3 +- src/painting/styled_text.rs | 16 +- src/painting/utils.rs | 4 +- src/prompt/base.rs | 18 +- src/prompt/default.rs | 23 +- src/prompt/mod.rs | 1 - src/result.rs | 4 +- src/terminal_extensions/bracketed_paste.rs | 2 + src/terminal_extensions/kitty.rs | 5 +- src/utils/query.rs | 14 +- src/utils/text_manipulation.rs | 3 +- src/validator/default.rs | 3 +- src/validator/mod.rs | 7 +- 60 files changed, 850 insertions(+), 595 deletions(-) diff --git a/examples/basic.rs b/examples/basic.rs index 2c04b75b..76cd2aa0 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -3,11 +3,13 @@ // // You can browse the local (non persistent) history using Up/Down or Ctrl n/p. -use reedline::{DefaultPrompt, Reedline, Signal}; use std::io; +use reedline::{DefaultPrompt, Reedline, Signal}; + fn main() -> io::Result<()> { - // Create a new Reedline engine with a local History that is not synchronized to a file. + // Create a new Reedline engine with a local History that is not synchronized to + // a file. let mut line_editor = Reedline::create(); let prompt = DefaultPrompt::default(); diff --git a/examples/completions.rs b/examples/completions.rs index 95f4975e..cba4308a 100644 --- a/examples/completions.rs +++ b/examples/completions.rs @@ -1,14 +1,15 @@ // Create a reedline object with tab completions support // cargo run --example completions // -// "t" [Tab] will allow you to select the completions "test" and "this is the reedline crate" -// [Enter] to select the chosen alternative +// "t" [Tab] will allow you to select the completions "test" and "this is the +// reedline crate" [Enter] to select the chosen alternative + +use std::io; use reedline::{ default_emacs_keybindings, ColumnarMenu, DefaultCompleter, DefaultPrompt, Emacs, KeyCode, KeyModifiers, Keybindings, Reedline, ReedlineEvent, ReedlineMenu, Signal, }; -use std::io; fn add_menu_keybindings(keybindings: &mut Keybindings) { keybindings.add_binding( diff --git a/examples/custom_prompt.rs b/examples/custom_prompt.rs index fc9b1034..c748a098 100644 --- a/examples/custom_prompt.rs +++ b/examples/custom_prompt.rs @@ -3,10 +3,11 @@ // // Pressing keys will increase the right prompt value +use std::{borrow::Cow, cell::Cell, io}; + use reedline::{ Prompt, PromptEditMode, PromptHistorySearch, PromptHistorySearchStatus, Reedline, Signal, }; -use std::{borrow::Cow, cell::Cell, io}; // For custom prompt, implement the Prompt trait // diff --git a/examples/demo.rs b/examples/demo.rs index 937db0dc..fe145492 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -1,30 +1,27 @@ -use std::env::temp_dir; -use std::process::Command; -use { - crossterm::{ - cursor::SetCursorStyle, - event::{KeyCode, KeyModifiers}, - }, - nu_ansi_term::{Color, Style}, - reedline::{ - default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings, - ColumnarMenu, DefaultCompleter, DefaultHinter, DefaultPrompt, DefaultValidator, - EditCommand, EditMode, Emacs, ExampleHighlighter, Keybindings, ListMenu, Reedline, - ReedlineEvent, ReedlineMenu, Signal, Vi, - }, -}; +use std::{env::temp_dir, process::Command}; -use reedline::CursorConfig; +use crossterm::{ + cursor::SetCursorStyle, + event::{KeyCode, KeyModifiers}, +}; +use nu_ansi_term::{Color, Style}; #[cfg(not(any(feature = "sqlite", feature = "sqlite-dynlib")))] use reedline::FileBackedHistory; +use reedline::{ + default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings, + ColumnarMenu, CursorConfig, DefaultCompleter, DefaultHinter, DefaultPrompt, DefaultValidator, + EditCommand, EditMode, Emacs, ExampleHighlighter, Keybindings, ListMenu, Reedline, + ReedlineEvent, ReedlineMenu, Signal, Vi, +}; fn main() -> std::io::Result<()> { println!("Ctrl-D to quit"); // quick command like parameter handling let vi_mode = matches!(std::env::args().nth(1), Some(x) if x == "--vi"); - // Setting history_per_session to true will allow the history to be isolated to the current session - // Setting history_per_session to false will allow the history to be shared across all sessions + // Setting history_per_session to true will allow the history to be isolated to + // the current session Setting history_per_session to false will allow the + // history to be shared across all sessions let history_per_session = true; let mut history_session_id = if history_per_session { Reedline::create_history_session_id() @@ -34,11 +31,8 @@ fn main() -> std::io::Result<()> { #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] let history = Box::new( - reedline::SqliteBackedHistory::with_file( - "history.sqlite3".into(), - history_session_id, - ) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?, + reedline::SqliteBackedHistory::with_file("history.sqlite3".into(), history_session_id) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?, ); #[cfg(not(any(feature = "sqlite", feature = "sqlite-dynlib")))] let history = Box::new(FileBackedHistory::with_file(50, "history.txt".into())?); @@ -179,7 +173,8 @@ fn main() -> std::io::Result<()> { line_editor.print_history_session_id()?; continue; } - // Toggle between the full history and the history pertinent to the current session + // Toggle between the full history and the history pertinent to the current + // session if buffer.trim() == "toggle history_session" { let hist_session_id = if history_session_id.is_none() { // If we never created a history session ID, create one now diff --git a/examples/event_listener.rs b/examples/event_listener.rs index 6c7e9cc7..f335d170 100644 --- a/examples/event_listener.rs +++ b/examples/event_listener.rs @@ -1,12 +1,11 @@ -use { - crossterm::{ - event::{poll, Event, KeyCode, KeyEvent}, - terminal, - }, - std::{ - io::{stdout, Write}, - time::Duration, - }, +use std::{ + io::{stdout, Write}, + time::Duration, +}; + +use crossterm::{ + event::{poll, Event, KeyCode, KeyEvent}, + terminal, }; fn main() -> std::io::Result<()> { @@ -16,7 +15,8 @@ fn main() -> std::io::Result<()> { Ok(()) } -/// **For debugging purposes only:** Track the terminal events observed by [`Reedline`] and print them. +/// **For debugging purposes only:** Track the terminal events observed by +/// [`Reedline`] and print them. pub fn print_events() -> std::io::Result<()> { stdout().flush()?; terminal::enable_raw_mode()?; @@ -48,7 +48,8 @@ fn print_events_helper() -> std::io::Result<()> { match code { KeyCode::Char(c) => { println!( - "Char: {} code: {:#08x}; Modifier {:?}; Flags {:#08b}; Kind {kind:?}; state {state:?}\r", + "Char: {} code: {:#08x}; Modifier {:?}; Flags {:#08b}; Kind {kind:?}; \ + state {state:?}\r", c, u32::from(c), modifiers, @@ -57,7 +58,8 @@ fn print_events_helper() -> std::io::Result<()> { } _ => { println!( - "Keycode: {code:?}; Modifier {modifiers:?}; Flags {modifiers:#08b}; Kind {kind:?}; state {state:?}\r" + "Keycode: {code:?}; Modifier {modifiers:?}; Flags {modifiers:#08b}; \ + Kind {kind:?}; state {state:?}\r" ); } } diff --git a/examples/event_listener_kitty_proto.rs b/examples/event_listener_kitty_proto.rs index cbace1ac..e557680e 100644 --- a/examples/event_listener_kitty_proto.rs +++ b/examples/event_listener_kitty_proto.rs @@ -1,16 +1,14 @@ -use crossterm::event::{ - KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags, +use std::{ + io::{stdout, Result, Write}, + time::Duration, }; -use crossterm::execute; -use { - crossterm::{ - event::{poll, Event, KeyCode, KeyEvent}, - terminal, - }, - std::{ - io::{stdout, Result, Write}, - time::Duration, + +use crossterm::{ + event::{ + poll, Event, KeyCode, KeyEvent, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, + PushKeyboardEnhancementFlags, }, + execute, terminal, }; fn main() -> Result<()> { @@ -20,7 +18,8 @@ fn main() -> Result<()> { Ok(()) } -/// **For debugging purposes only:** Track the terminal events observed by [`Reedline`] and print them. +/// **For debugging purposes only:** Track the terminal events observed by +/// [`Reedline`] and print them. pub fn print_events() -> Result<()> { stdout().flush()?; terminal::enable_raw_mode()?; @@ -73,7 +72,8 @@ fn print_events_helper() -> Result<()> { match code { KeyCode::Char(c) => { println!( - "Char: {} code: {:#08x}; Modifier {:?}; Flags {:#08b}; Kind {kind:?}; state {state:?}\r", + "Char: {} code: {:#08x}; Modifier {:?}; Flags {:#08b}; Kind {kind:?}; \ + state {state:?}\r", c, u32::from(c), modifiers, @@ -82,7 +82,8 @@ fn print_events_helper() -> Result<()> { } _ => { println!( - "Keycode: {code:?}; Modifier {modifiers:?}; Flags {modifiers:#08b}; Kind {kind:?}; state {state:?}\r" + "Keycode: {code:?}; Modifier {modifiers:?}; Flags {modifiers:#08b}; \ + Kind {kind:?}; state {state:?}\r" ); } } diff --git a/examples/external_printer.rs b/examples/external_printer.rs index 633a1238..2f311a35 100644 --- a/examples/external_printer.rs +++ b/examples/external_printer.rs @@ -2,13 +2,9 @@ // to run: // cargo run --example external_printer --features=external_printer -use { - reedline::ExternalPrinter, - reedline::{DefaultPrompt, Reedline, Signal}, - std::thread, - std::thread::sleep, - std::time::Duration, -}; +use std::{thread, thread::sleep, time::Duration}; + +use reedline::{DefaultPrompt, ExternalPrinter, Reedline, Signal}; fn main() { let printer = ExternalPrinter::default(); diff --git a/examples/highlighter.rs b/examples/highlighter.rs index 3edeb974..ef20bbb4 100644 --- a/examples/highlighter.rs +++ b/examples/highlighter.rs @@ -2,9 +2,10 @@ // cargo run --example highlighter // // unmatched input is marked red, matched input is marked green -use reedline::{DefaultPrompt, ExampleHighlighter, Reedline, Signal}; use std::io; +use reedline::{DefaultPrompt, ExampleHighlighter, Reedline, Signal}; + fn main() -> io::Result<()> { let commands = vec![ "test".into(), diff --git a/examples/hinter.rs b/examples/hinter.rs index 9ed4ba2d..5271582a 100644 --- a/examples/hinter.rs +++ b/examples/hinter.rs @@ -6,9 +6,10 @@ // pressing "a" hints to abc. // Up/Down or Ctrl p/n, to select next/previous match +use std::io; + use nu_ansi_term::{Color, Style}; use reedline::{DefaultHinter, DefaultPrompt, Reedline, Signal}; -use std::io; fn main() -> io::Result<()> { let mut line_editor = Reedline::create().with_hinter(Box::new( diff --git a/examples/history.rs b/examples/history.rs index c709bce9..1065b33f 100644 --- a/examples/history.rs +++ b/examples/history.rs @@ -6,9 +6,10 @@ // // Browse history by Up/Down arrows or Ctrl-n/p -use reedline::{DefaultPrompt, FileBackedHistory, Reedline, Signal}; use std::io; +use reedline::{DefaultPrompt, FileBackedHistory, Reedline, Signal}; + fn main() -> io::Result<()> { let history = Box::new( FileBackedHistory::with_file(5, "history.txt".into()) diff --git a/examples/transient_prompt.rs b/examples/transient_prompt.rs index f971955b..e3ecbc0d 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 +use std::{borrow::Cow, io}; + use nu_ansi_term::{Color, Style}; #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] use reedline::SqliteBackedHistory; @@ -12,7 +14,6 @@ use reedline::{ PromptHistorySearch, PromptHistorySearchStatus, Reedline, ReedlineEvent, ReedlineMenu, Signal, ValidationResult, Validator, }; -use std::{borrow::Cow, io}; // For custom prompt, implement the Prompt trait // diff --git a/examples/validator.rs b/examples/validator.rs index 2e7ca4f9..7b72f81b 100644 --- a/examples/validator.rs +++ b/examples/validator.rs @@ -1,12 +1,14 @@ -// Create a reedline object with a custom validator to break the line on unfinished input. -// cargo run --example validator +// Create a reedline object with a custom validator to break the line on +// unfinished input. cargo run --example validator // -// Input "complete" followed by [Enter], will accept the input line (Signal::Succeed will be called) -// Pressing [Enter] will in other cases give you a multi-line prompt. +// Input "complete" followed by [Enter], will accept the input line +// (Signal::Succeed will be called) Pressing [Enter] will in other cases give +// you a multi-line prompt. -use reedline::{DefaultPrompt, Reedline, Signal, ValidationResult, Validator}; use std::io; +use reedline::{DefaultPrompt, Reedline, Signal, ValidationResult, Validator}; + struct CustomValidator; // For custom validation, implement the Validator trait @@ -21,7 +23,11 @@ impl Validator for CustomValidator { } fn main() -> io::Result<()> { - println!("Input \"complete\" followed by [Enter], will accept the input line (Signal::Succeed will be called)\nPressing [Enter] will in other cases give you a multi-line prompt.\nAbort with Ctrl-C or Ctrl-D"); + println!( + "Input \"complete\" followed by [Enter], will accept the input line (Signal::Succeed will \ + be called)\nPressing [Enter] will in other cases give you a multi-line prompt.\nAbort \ + with Ctrl-C or Ctrl-D" + ); let mut line_editor = Reedline::create().with_validator(Box::new(CustomValidator)); let prompt = DefaultPrompt::default(); diff --git a/src/completion/base.rs b/src/completion/base.rs index 3de63c8d..9f5a3dcf 100644 --- a/src/completion/base.rs +++ b/src/completion/base.rs @@ -24,15 +24,17 @@ impl Span { } } -/// A trait that defines how to convert a line and position to a list of potential completions in that position. +/// A trait that defines how to convert a line and position to a list of +/// potential completions in that position. pub trait Completer: Send { - /// the action that will take the line and position and convert it to a vector of completions, which include the - /// span to replace and the contents of that replacement + /// the action that will take the line and position and convert it to a + /// vector of completions, which include the span to replace and the + /// contents of that replacement fn complete(&mut self, line: &str, pos: usize) -> Vec; /// action that will return a partial section of available completions - /// this command comes handy when trying to avoid to pull all the data at once - /// from the completer + /// this command comes handy when trying to avoid to pull all the data at + /// once from the completer fn partial_complete( &mut self, line: &str, diff --git a/src/completion/default.rs b/src/completion/default.rs index 11062d7b..69044aa3 100644 --- a/src/completion/default.rs +++ b/src/completion/default.rs @@ -1,10 +1,11 @@ -use crate::{Completer, Span, Suggestion}; use std::{ collections::{BTreeMap, BTreeSet}, str::Chars, sync::Arc, }; +use crate::{Completer, Span, Suggestion}; + /// A default completer that can detect keywords /// /// # Example @@ -13,10 +14,10 @@ use std::{ /// use reedline::{DefaultCompleter, Reedline}; /// /// let commands = vec![ -/// "test".into(), -/// "hello world".into(), -/// "hello world reedline".into(), -/// "this is the reedline crate".into(), +/// "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)); /// @@ -38,8 +39,8 @@ impl Default for DefaultCompleter { } } impl Completer for DefaultCompleter { - /// Returns a vector of completions and the position in which they must be replaced; - /// based on the provided input. + /// Returns a vector of completions and the position in which they must be + /// replaced; based on the provided input. /// /// # Arguments /// @@ -48,25 +49,68 @@ impl Completer for DefaultCompleter { /// /// # Example /// ``` - /// use reedline::{DefaultCompleter,Completer,Span,Suggestion}; + /// use reedline::{Completer, DefaultCompleter, Span, Suggestion}; /// /// let mut completions = DefaultCompleter::default(); - /// completions.insert(vec!["batman","robin","batmobile","batcave","robber"].iter().map(|s| s.to_string()).collect()); + /// completions.insert( + /// vec!["batman", "robin", "batmobile", "batcave", "robber"] + /// .iter() + /// .map(|s| s.to_string()) + /// .collect(), + /// ); /// assert_eq!( - /// completions.complete("bat",3), + /// completions.complete("bat", 3), /// vec![ - /// Suggestion {value: "batcave".into(), description: None, extra: None, span: Span { start: 0, end: 3 }, append_whitespace: false}, - /// Suggestion {value: "batman".into(), description: None, extra: None, span: Span { start: 0, end: 3 }, append_whitespace: false}, - /// Suggestion {value: "batmobile".into(), description: None, extra: None, span: Span { start: 0, end: 3 }, append_whitespace: false}, - /// ]); + /// Suggestion { + /// value: "batcave".into(), + /// description: None, + /// extra: None, + /// span: Span { start: 0, end: 3 }, + /// append_whitespace: false + /// }, + /// Suggestion { + /// value: "batman".into(), + /// description: None, + /// extra: None, + /// span: Span { start: 0, end: 3 }, + /// append_whitespace: false + /// }, + /// Suggestion { + /// value: "batmobile".into(), + /// description: None, + /// extra: None, + /// span: Span { start: 0, end: 3 }, + /// append_whitespace: false + /// }, + /// ] + /// ); /// /// assert_eq!( - /// completions.complete("to the bat",10), + /// completions.complete("to the bat", 10), /// vec![ - /// Suggestion {value: "batcave".into(), description: None, extra: None, span: Span { start: 7, end: 10 }, append_whitespace: false}, - /// Suggestion {value: "batman".into(), description: None, extra: None, span: Span { start: 7, end: 10 }, append_whitespace: false}, - /// Suggestion {value: "batmobile".into(), description: None, extra: None, span: Span { start: 7, end: 10 }, append_whitespace: false}, - /// ]); + /// Suggestion { + /// value: "batcave".into(), + /// description: None, + /// extra: None, + /// span: Span { start: 7, end: 10 }, + /// append_whitespace: false + /// }, + /// Suggestion { + /// value: "batman".into(), + /// description: None, + /// extra: None, + /// span: Span { start: 7, end: 10 }, + /// append_whitespace: false + /// }, + /// Suggestion { + /// value: "batmobile".into(), + /// description: None, + /// extra: None, + /// span: Span { start: 7, end: 10 }, + /// append_whitespace: false + /// }, + /// ] + /// ); /// ``` fn complete(&mut self, line: &str, pos: usize) -> Vec { let mut span_line_whitespaces = 0; @@ -116,14 +160,16 @@ impl Completer for DefaultCompleter { } } impl DefaultCompleter { - /// Construct the default completer with a list of commands/keywords to highlight + /// Construct the default completer with a list of commands/keywords to + /// highlight pub fn new(external_commands: Vec) -> Self { let mut dc = DefaultCompleter::default(); dc.insert(external_commands); dc } - /// Construct the default completer with a list of commands/keywords to highlight, given a minimum word length + /// Construct the default completer with a list of commands/keywords to + /// highlight, given a minimum word length pub fn new_with_wordlen(external_commands: Vec, min_word_len: usize) -> Self { let mut dc = DefaultCompleter::default().set_min_word_len(min_word_len); dc.insert(external_commands); @@ -138,16 +184,31 @@ impl DefaultCompleter { /// /// # Example /// ``` - /// use reedline::{DefaultCompleter,Completer}; + /// use reedline::{Completer, DefaultCompleter}; /// /// let mut completions = DefaultCompleter::default(); /// /// // Insert multiple words - /// completions.insert(vec!["a","line","with","many","words"].iter().map(|s| s.to_string()).collect()); + /// completions.insert( + /// vec!["a", "line", "with", "many", "words"] + /// .iter() + /// .map(|s| s.to_string()) + /// .collect(), + /// ); /// /// // The above line is equal to the following: - /// completions.insert(vec!["a","line","with"].iter().map(|s| s.to_string()).collect()); - /// completions.insert(vec!["many","words"].iter().map(|s| s.to_string()).collect()); + /// completions.insert( + /// vec!["a", "line", "with"] + /// .iter() + /// .map(|s| s.to_string()) + /// .collect(), + /// ); + /// completions.insert( + /// vec!["many", "words"] + /// .iter() + /// .map(|s| s.to_string()) + /// .collect(), + /// ); /// ``` pub fn insert(&mut self, words: Vec) { for word in words { @@ -157,9 +218,10 @@ impl DefaultCompleter { } } - /// Create a new `DefaultCompleter` with provided non alphabet characters whitelisted. - /// The default `DefaultCompleter` will only parse alphabet characters (a-z, A-Z). Use this to - /// introduce additional accepted special characters. + /// Create a new `DefaultCompleter` with provided non alphabet characters + /// whitelisted. The default `DefaultCompleter` will only parse alphabet + /// characters (a-z, A-Z). Use this to introduce additional accepted + /// special characters. /// /// # Arguments /// @@ -167,22 +229,52 @@ impl DefaultCompleter { /// /// # Example /// ``` - /// use reedline::{DefaultCompleter,Completer,Span,Suggestion}; + /// use reedline::{Completer, DefaultCompleter, Span, Suggestion}; /// /// let mut completions = DefaultCompleter::default(); - /// completions.insert(vec!["test-hyphen","test_underscore"].iter().map(|s| s.to_string()).collect()); + /// completions.insert( + /// vec!["test-hyphen", "test_underscore"] + /// .iter() + /// .map(|s| s.to_string()) + /// .collect(), + /// ); /// assert_eq!( - /// completions.complete("te",2), - /// vec![Suggestion {value: "test".into(), description: None, extra: None, span: Span { start: 0, end: 2 }, append_whitespace: false}]); + /// completions.complete("te", 2), + /// vec![Suggestion { + /// value: "test".into(), + /// description: None, + /// extra: None, + /// span: Span { start: 0, end: 2 }, + /// append_whitespace: false + /// }] + /// ); /// /// let mut completions = DefaultCompleter::with_inclusions(&['-', '_']); - /// completions.insert(vec!["test-hyphen","test_underscore"].iter().map(|s| s.to_string()).collect()); + /// completions.insert( + /// vec!["test-hyphen", "test_underscore"] + /// .iter() + /// .map(|s| s.to_string()) + /// .collect(), + /// ); /// assert_eq!( - /// completions.complete("te",2), + /// completions.complete("te", 2), /// vec![ - /// Suggestion {value: "test-hyphen".into(), description: None, extra: None, span: Span { start: 0, end: 2 }, append_whitespace: false}, - /// Suggestion {value: "test_underscore".into(), description: None, extra: None, span: Span { start: 0, end: 2 }, append_whitespace: false}, - /// ]); + /// Suggestion { + /// value: "test-hyphen".into(), + /// description: None, + /// extra: None, + /// span: Span { start: 0, end: 2 }, + /// append_whitespace: false + /// }, + /// Suggestion { + /// value: "test_underscore".into(), + /// description: None, + /// extra: None, + /// span: Span { start: 0, end: 2 }, + /// append_whitespace: false + /// }, + /// ] + /// ); /// ``` pub fn with_inclusions(incl: &[char]) -> Self { let mut set = BTreeSet::new(); @@ -197,10 +289,15 @@ impl DefaultCompleter { /// Clears all the data from the tree /// # Example /// ``` - /// use reedline::{DefaultCompleter,Completer}; + /// use reedline::{Completer, DefaultCompleter}; /// /// let mut completions = DefaultCompleter::default(); - /// completions.insert(vec!["batman","robin","batmobile","batcave","robber"].iter().map(|s| s.to_string()).collect()); + /// completions.insert( + /// vec!["batman", "robin", "batmobile", "batcave", "robber"] + /// .iter() + /// .map(|s| s.to_string()) + /// .collect(), + /// ); /// assert_eq!(completions.word_count(), 5); /// assert_eq!(completions.size(), 24); /// completions.clear(); @@ -214,10 +311,15 @@ impl DefaultCompleter { /// Returns a count of how many words that exist in the tree /// # Example /// ``` - /// use reedline::{DefaultCompleter,Completer}; + /// use reedline::{Completer, DefaultCompleter}; /// /// let mut completions = DefaultCompleter::default(); - /// completions.insert(vec!["batman","robin","batmobile","batcave","robber"].iter().map(|s| s.to_string()).collect()); + /// completions.insert( + /// vec!["batman", "robin", "batmobile", "batcave", "robber"] + /// .iter() + /// .map(|s| s.to_string()) + /// .collect(), + /// ); /// assert_eq!(completions.word_count(), 5); /// ``` pub fn word_count(&self) -> u32 { @@ -227,10 +329,15 @@ impl DefaultCompleter { /// Returns the size of the tree, the amount of nodes, not words /// # Example /// ``` - /// use reedline::{DefaultCompleter,Completer}; + /// use reedline::{Completer, DefaultCompleter}; /// /// let mut completions = DefaultCompleter::default(); - /// completions.insert(vec!["batman","robin","batmobile","batcave","robber"].iter().map(|s| s.to_string()).collect()); + /// completions.insert( + /// vec!["batman", "robin", "batmobile", "batcave", "robber"] + /// .iter() + /// .map(|s| s.to_string()) + /// .collect(), + /// ); /// assert_eq!(completions.size(), 24); /// ``` pub fn size(&self) -> u32 { @@ -243,14 +350,24 @@ impl DefaultCompleter { /// ignored. /// # Example /// ``` - /// use reedline::{DefaultCompleter,Completer}; + /// use reedline::{Completer, DefaultCompleter}; /// /// let mut completions = DefaultCompleter::default().set_min_word_len(4); - /// completions.insert(vec!["one","two","three","four","five"].iter().map(|s| s.to_string()).collect()); + /// completions.insert( + /// vec!["one", "two", "three", "four", "five"] + /// .iter() + /// .map(|s| s.to_string()) + /// .collect(), + /// ); /// assert_eq!(completions.word_count(), 3); /// /// let mut completions = DefaultCompleter::default().set_min_word_len(1); - /// completions.insert(vec!["one","two","three","four","five"].iter().map(|s| s.to_string()).collect()); + /// completions.insert( + /// vec!["one", "two", "three", "four", "five"] + /// .iter() + /// .map(|s| s.to_string()) + /// .collect(), + /// ); /// assert_eq!(completions.word_count(), 5); /// ``` pub fn min_word_len(&self) -> usize { diff --git a/src/core_editor/clip_buffer.rs b/src/core_editor/clip_buffer.rs index 0bd9272a..bb88d22c 100644 --- a/src/core_editor/clip_buffer.rs +++ b/src/core_editor/clip_buffer.rs @@ -1,6 +1,7 @@ /// Defines an interface to interact with a Clipboard for cut and paste. /// -/// Mutable reference requirements are stricter than always necessary, but the currently used system clipboard API demands them for exclusive access. +/// Mutable reference requirements are stricter than always necessary, but the +/// currently used system clipboard API demands them for exclusive access. pub trait Clipboard: Send { fn set(&mut self, content: &str, mode: ClipboardMode); @@ -25,7 +26,8 @@ pub enum ClipboardMode { Lines, } -/// Simple buffer that provides a clipboard only usable within the application/library. +/// Simple buffer that provides a clipboard only usable within the +/// application/library. #[derive(Default)] pub struct LocalClipboard { content: String, @@ -58,7 +60,8 @@ pub use system_clipboard::SystemClipboard; /// /// Enabled -> [`SystemClipboard`], which talks to the system /// -/// Disabled -> [`LocalClipboard`], which supports cutting and pasting limited to the [`crate::Reedline`] instance +/// Disabled -> [`LocalClipboard`], which supports cutting and pasting limited +/// to the [`crate::Reedline`] instance pub fn get_default_clipboard() -> SystemClipboard { SystemClipboard::new() } @@ -68,16 +71,18 @@ pub fn get_default_clipboard() -> SystemClipboard { /// /// Enabled -> `SystemClipboard`, which talks to the system /// -/// Disabled -> [`LocalClipboard`], which supports cutting and pasting limited to the [`crate::Reedline`] instance +/// Disabled -> [`LocalClipboard`], which supports cutting and pasting limited +/// to the [`crate::Reedline`] instance pub fn get_default_clipboard() -> LocalClipboard { LocalClipboard::new() } #[cfg(feature = "system_clipboard")] mod system_clipboard { - use super::*; use clipboard::{ClipboardContext, ClipboardProvider}; + use super::*; + /// Wrapper around [`clipboard`](https://docs.rs/clipboard) crate /// /// Requires that the feature `system_clipboard` is enabled @@ -108,7 +113,8 @@ mod system_clipboard { fn get(&mut self) -> (String, ClipboardMode) { let system_content = self.cb.get_contents().unwrap_or_default(); if system_content == self.local_copy { - // We assume the content was yanked inside the line editor and the last yank determined the mode. + // We assume the content was yanked inside the line editor and the last yank + // determined the mode. (system_content, self.mode) } else { // Content has changed, default to direct insertion. diff --git a/src/core_editor/edit_stack.rs b/src/core_editor/edit_stack.rs index 0a0f60a4..6e79d906 100644 --- a/src/core_editor/edit_stack.rs +++ b/src/core_editor/edit_stack.rs @@ -26,7 +26,8 @@ where &self.internal_list[self.index] } - /// Go forward one point in the undo stack. If present on the last edit do nothing + /// Go forward one point in the undo stack. If present on the last edit do + /// nothing pub(super) fn redo(&mut self) -> &T { self.index = if self.index == self.internal_list.len() - 1 { self.index @@ -37,8 +38,8 @@ where } /// Insert a new entry to the undo stack. - /// NOTE: (IMP): If we have hit undo a few times then discard all the other values that come - /// after the current point + /// NOTE: (IMP): If we have hit undo a few times then discard all the other + /// values that come after the current point pub(super) fn insert(&mut self, value: T) { if self.index < self.internal_list.len() - 1 { self.internal_list.resize_with(self.index + 1, || { @@ -63,10 +64,11 @@ where #[cfg(test)] mod test { - use super::*; use pretty_assertions::assert_eq; use rstest::rstest; + use super::*; + fn edit_stack(values: &[T], index: usize) -> EditStack where T: Clone, diff --git a/src/core_editor/editor.rs b/src/core_editor/editor.rs index 3cabe1ae..52fbdb5a 100644 --- a/src/core_editor/editor.rs +++ b/src/core_editor/editor.rs @@ -1,6 +1,9 @@ use super::{edit_stack::EditStack, Clipboard, ClipboardMode, LineBuffer}; -use crate::enums::{EditType, UndoBehavior}; -use crate::{core_editor::get_default_clipboard, EditCommand}; +use crate::{ + core_editor::get_default_clipboard, + enums::{EditType, UndoBehavior}, + EditCommand, +}; /// Stateful editor executing changes to the underlying [`LineBuffer`] /// @@ -32,7 +35,8 @@ impl Editor { } /// Set the current [`LineBuffer`]. - /// [`UndoBehavior`] specifies how this change should be reflected on the undo stack. + /// [`UndoBehavior`] specifies how this change should be reflected on the + /// undo stack. pub(crate) fn set_line_buffer(&mut self, line_buffer: LineBuffer, undo_behavior: UndoBehavior) { self.line_buffer = line_buffer; self.update_undo_state(undo_behavior); @@ -139,8 +143,8 @@ impl Editor { func(&mut self.line_buffer); } - /// Set the text of the current [`LineBuffer`] given the specified [`UndoBehavior`] - /// Insertion point update to the end of the buffer. + /// Set the text of the current [`LineBuffer`] given the specified + /// [`UndoBehavior`] Insertion point update to the end of the buffer. pub(crate) fn set_buffer(&mut self, buffer: String, undo_behavior: UndoBehavior) { self.line_buffer.set_buffer(buffer); self.update_undo_state(undo_behavior); @@ -466,10 +470,11 @@ impl Editor { #[cfg(test)] mod test { - use super::*; use pretty_assertions::assert_eq; use rstest::rstest; + use super::*; + fn editor_with(buffer: &str) -> Editor { let mut editor = Editor::default(); editor.set_buffer(buffer.to_string(), UndoBehavior::CreateUndoPoint); @@ -510,7 +515,8 @@ mod test { #[rstest] #[case("hello world", 0, 'l', 1, false, "lo world")] #[case("hello world", 0, 'l', 1, true, "llo world")] - #[ignore = "Deleting two consecutives is not implemented correctly and needs the multiplier explicitly."] + #[ignore = "Deleting two consecutives is not implemented correctly and needs the multiplier \ + explicitly."] #[case("hello world", 0, 'l', 2, false, "o world")] #[case("hello world", 0, 'h', 1, false, "hello world")] #[case("hello world", 0, 'l', 3, true, "ld")] diff --git a/src/core_editor/line_buffer.rs b/src/core_editor/line_buffer.rs index 01759ba7..828fb0f1 100644 --- a/src/core_editor/line_buffer.rs +++ b/src/core_editor/line_buffer.rs @@ -1,10 +1,10 @@ -use { - itertools::Itertools, - std::{convert::From, ops::Range}, - unicode_segmentation::UnicodeSegmentation, -}; +use std::{convert::From, ops::Range}; -/// In memory representation of the entered line(s) including a cursor position to facilitate cursor based editing. +use itertools::Itertools; +use unicode_segmentation::UnicodeSegmentation; + +/// In memory representation of the entered line(s) including a cursor position +/// to facilitate cursor based editing. #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct LineBuffer { lines: String, @@ -30,7 +30,8 @@ impl LineBuffer { self.lines.is_empty() } - /// Check if the line buffer is valid utf-8 and the cursor sits on a valid grapheme boundary + /// Check if the line buffer is valid utf-8 and the cursor sits on a valid + /// grapheme boundary pub fn is_valid(&self) -> bool { self.lines.is_char_boundary(self.insertion_point()) && (self @@ -77,7 +78,8 @@ impl LineBuffer { &self.lines } - /// Set to a single line of `buffer` and reset the `InsertionPoint` cursor to the end + /// Set to a single line of `buffer` and reset the `InsertionPoint` cursor + /// to the end pub fn set_buffer(&mut self, buffer: String) { self.lines = buffer; self.insertion_point = self.lines.len(); @@ -343,7 +345,7 @@ impl LineBuffer { self.insertion_point = self.big_word_right_end_index(); } - ///Insert a single character at the insertion point and move right + /// Insert a single character at the insertion point and move right pub fn insert_char(&mut self, c: char) { self.lines.insert(self.insertion_point, c); self.move_right(); @@ -398,7 +400,8 @@ impl LineBuffer { /// Clear text covered by `range` in the current line /// - /// Safety: Does not change the insertion point/offset and is thus not unicode safe! + /// Safety: Does not change the insertion point/offset and is thus not + /// unicode safe! pub(crate) fn clear_range(&mut self, range: R) where R: std::ops::RangeBounds, @@ -408,7 +411,8 @@ impl LineBuffer { /// Substitute text covered by `range` in the current line /// - /// Safety: Does not change the insertion point/offset and is thus not unicode safe! + /// Safety: Does not change the insertion point/offset and is thus not + /// unicode safe! pub fn replace_range(&mut self, range: R, replace_with: &str) where R: std::ops::RangeBounds, @@ -755,10 +759,11 @@ fn is_whitespace_str(s: &str) -> bool { #[cfg(test)] mod test { - use super::*; use pretty_assertions::assert_eq; use rstest::rstest; + use super::*; + fn buffer_with(content: &str) -> LineBuffer { let mut line_buffer = LineBuffer::new(); line_buffer.insert_str(content); diff --git a/src/edit_mode/base.rs b/src/edit_mode/base.rs index 455e2c80..1f33b884 100644 --- a/src/edit_mode/base.rs +++ b/src/edit_mode/base.rs @@ -8,7 +8,8 @@ use crate::{ /// - Emacs /// - Vi pub trait EditMode: Send { - /// Translate the given user input event into what the `LineEditor` understands + /// Translate the given user input event into what the `LineEditor` + /// understands fn parse_event(&mut self, event: ReedlineRawEvent) -> ReedlineEvent; /// What to display in the prompt indicator diff --git a/src/edit_mode/cursors.rs b/src/edit_mode/cursors.rs index 67a1765a..8d64c5e1 100644 --- a/src/edit_mode/cursors.rs +++ b/src/edit_mode/cursors.rs @@ -1,7 +1,8 @@ use crossterm::cursor::SetCursorStyle; /// 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. +/// 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 diff --git a/src/edit_mode/emacs.rs b/src/edit_mode/emacs.rs index 35aa6db7..29796009 100644 --- a/src/edit_mode/emacs.rs +++ b/src/edit_mode/emacs.rs @@ -1,3 +1,5 @@ +use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers}; + use crate::{ edit_mode::{ keybindings::{ @@ -9,7 +11,6 @@ use crate::{ enums::{EditCommand, ReedlineEvent, ReedlineRawEvent}, PromptEditMode, }; -use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers}; /// Returns the current default emacs keybindings pub fn default_emacs_keybindings() -> Keybindings { @@ -172,7 +173,8 @@ impl EditMode for Emacs { } impl Emacs { - /// Emacs style input parsing constructor if you want to use custom keybindings + /// Emacs style input parsing constructor if you want to use custom + /// keybindings pub const fn new(keybindings: Keybindings) -> Self { Emacs { keybindings } } @@ -180,9 +182,10 @@ impl Emacs { #[cfg(test)] mod test { - use super::*; use pretty_assertions::assert_eq; + use super::*; + #[test] fn ctrl_l_leads_to_clear_screen_event() { let mut emacs = Emacs::default(); diff --git a/src/edit_mode/keybindings.rs b/src/edit_mode/keybindings.rs index 772e4199..4f226625 100644 --- a/src/edit_mode/keybindings.rs +++ b/src/edit_mode/keybindings.rs @@ -1,9 +1,9 @@ -use { - crate::{enums::ReedlineEvent, EditCommand}, - crossterm::event::{KeyCode, KeyModifiers}, - serde::{Deserialize, Serialize}, - std::collections::HashMap, -}; +use std::collections::HashMap; + +use crossterm::event::{KeyCode, KeyModifiers}; +use serde::{Deserialize, Serialize}; + +use crate::{enums::ReedlineEvent, EditCommand}; #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Debug)] pub struct KeyCombination { @@ -67,7 +67,8 @@ impl Keybindings { /// Remove a keybinding /// - /// Returns `Some(ReedlineEvent)` if the keycombination was previously bound to a particular [`ReedlineEvent`] + /// Returns `Some(ReedlineEvent)` if the keycombination was previously bound + /// to a particular [`ReedlineEvent`] pub fn remove_binding( &mut self, modifier: KeyModifiers, diff --git a/src/edit_mode/vi/command.rs b/src/edit_mode/vi/command.rs index e86f9f31..15b9c6e9 100644 --- a/src/edit_mode/vi/command.rs +++ b/src/edit_mode/vi/command.rs @@ -1,7 +1,11 @@ -use super::{motion::Motion, motion::ViCharSearch, parser::ReedlineOption}; -use crate::{EditCommand, ReedlineEvent, Vi}; use std::iter::Peekable; +use super::{ + motion::{Motion, ViCharSearch}, + parser::ReedlineOption, +}; +use crate::{EditCommand, ReedlineEvent, Vi}; + pub fn parse_command<'iter, I>(input: &mut Peekable) -> Option where I: Iterator, @@ -122,7 +126,7 @@ impl Command { matches!(self, Command::Delete | Command::Change) } - pub fn to_reedline(&self, vi_state: &mut Vi) -> Vec { + pub fn to_reedline(&self, vi_state: &Vi) -> Vec { match self { Self::EnterViInsert => vec![ReedlineOption::Event(ReedlineEvent::Repaint)], Self::EnterViAppend => vec![ReedlineOption::Edit(EditCommand::MoveRight)], diff --git a/src/edit_mode/vi/mod.rs b/src/edit_mode/vi/mod.rs index 4428c645..ca6b8aca 100644 --- a/src/edit_mode/vi/mod.rs +++ b/src/edit_mode/vi/mod.rs @@ -7,7 +7,6 @@ use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers}; pub use vi_keybindings::{default_vi_insert_keybindings, default_vi_normal_keybindings}; use self::motion::ViCharSearch; - use super::EditMode; use crate::{ edit_mode::{keybindings::Keybindings, vi::parser::parse}, @@ -173,9 +172,10 @@ impl EditMode for Vi { #[cfg(test)] mod test { - use super::*; use pretty_assertions::assert_eq; + use super::*; + #[test] fn esc_leads_to_normal_mode_test() { let mut vi = Vi::default(); diff --git a/src/edit_mode/vi/motion.rs b/src/edit_mode/vi/motion.rs index 90822a52..24f404b0 100644 --- a/src/edit_mode/vi/motion.rs +++ b/src/edit_mode/vi/motion.rs @@ -1,8 +1,7 @@ use std::iter::Peekable; -use crate::{EditCommand, ReedlineEvent, Vi}; - use super::parser::{ParseResult, ReedlineOption}; +use crate::{EditCommand, ReedlineEvent, Vi}; pub fn parse_motion<'iter, I>( input: &mut Peekable, diff --git a/src/edit_mode/vi/parser.rs b/src/edit_mode/vi/parser.rs index f243e53b..cf170058 100644 --- a/src/edit_mode/vi/parser.rs +++ b/src/edit_mode/vi/parser.rs @@ -1,8 +1,11 @@ -use super::command::{parse_command, Command}; -use super::motion::{parse_motion, Motion}; -use crate::{EditCommand, ReedlineEvent, Vi}; use std::iter::Peekable; +use super::{ + command::{parse_command, Command}, + motion::{parse_motion, Motion}, +}; +use crate::{EditCommand, ReedlineEvent, Vi}; + #[derive(Debug, Clone)] pub enum ReedlineOption { Event(ReedlineEvent), @@ -180,10 +183,11 @@ where #[cfg(test)] mod tests { - use super::*; use pretty_assertions::assert_eq; use rstest::rstest; + use super::*; + fn vi_parse(input: &[char]) -> ParsedViSequence { parse(&mut input.iter().peekable()) } diff --git a/src/engine.rs b/src/engine.rs index e4ca9c7e..6a37ba47 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,48 +1,49 @@ -use std::path::PathBuf; - -use itertools::Itertools; +use std::{ + fs::File, + io, + io::{Result, Write}, + path::PathBuf, + process::Command, + time::{Duration, SystemTime}, +}; -use crate::{enums::ReedlineRawEvent, CursorConfig}; -#[cfg(feature = "bashisms")] -use crate::{ - history::SearchFilter, - menu_functions::{parse_selection_char, ParseAction}, +use crossterm::{ + cursor::{SetCursorStyle, Show}, + event, + event::{Event, KeyCode, KeyEvent, KeyModifiers}, + terminal, QueueableCommand, }; +use itertools::Itertools; #[cfg(feature = "external_printer")] use { crate::external_printer::ExternalPrinter, crossbeam::channel::TryRecvError, std::io::{Error, ErrorKind}, }; -use { - crate::{ - completion::{Completer, DefaultCompleter}, - core_editor::Editor, - edit_mode::{EditMode, Emacs}, - enums::{EventStatus, ReedlineEvent}, - highlighter::SimpleMatchHighlighter, - hinter::Hinter, - history::{ - FileBackedHistory, History, HistoryCursor, HistoryItem, HistoryItemId, - HistoryNavigationQuery, HistorySessionId, SearchDirection, SearchQuery, - }, - painting::{Painter, PromptLines}, - prompt::{PromptEditMode, PromptHistorySearchStatus}, - result::{ReedlineError, ReedlineErrorVariants}, - terminal_extensions::{bracketed_paste::BracketedPasteGuard, kitty::KittyProtocolGuard}, - utils::text_manipulation, - EditCommand, ExampleHighlighter, Highlighter, LineBuffer, Menu, MenuEvent, Prompt, - PromptHistorySearch, ReedlineMenu, Signal, UndoBehavior, ValidationResult, Validator, - }, - crossterm::{ - cursor::{SetCursorStyle, Show}, - event, - event::{Event, KeyCode, KeyEvent, KeyModifiers}, - terminal, QueueableCommand, - }, - std::{ - fs::File, io, io::Result, io::Write, process::Command, time::Duration, time::SystemTime, + +use crate::{ + completion::{Completer, DefaultCompleter}, + core_editor::Editor, + edit_mode::{EditMode, Emacs}, + enums::{EventStatus, ReedlineEvent, ReedlineRawEvent}, + highlighter::SimpleMatchHighlighter, + hinter::Hinter, + history::{ + FileBackedHistory, History, HistoryCursor, HistoryItem, HistoryItemId, + HistoryNavigationQuery, HistorySessionId, SearchDirection, SearchQuery, }, + painting::{Painter, PromptLines}, + prompt::{PromptEditMode, PromptHistorySearchStatus}, + result::{ReedlineError, ReedlineErrorVariants}, + terminal_extensions::{bracketed_paste::BracketedPasteGuard, kitty::KittyProtocolGuard}, + utils::text_manipulation, + CursorConfig, EditCommand, ExampleHighlighter, Highlighter, LineBuffer, Menu, MenuEvent, + Prompt, PromptHistorySearch, ReedlineMenu, Signal, UndoBehavior, ValidationResult, Validator, +}; +#[cfg(feature = "bashisms")] +use crate::{ + history::SearchFilter, + menu_functions::{parse_selection_char, ParseAction}, }; // The POLL_WAIT is used to specify for how long the POLL should wait for @@ -51,10 +52,10 @@ use { // arrives. This doesn't allow for the possibility of more than 1 event // happening at the same time. const POLL_WAIT: u64 = 10; -// Since a paste event is multiple Event::Key events happening at the same time, we specify -// how many events should be in the crossterm_events vector before it is considered -// a paste. 10 events in 10 milliseconds is conservative enough (unlikely somebody -// will type more than 10 characters in 10 milliseconds) +// Since a paste event is multiple Event::Key events happening at the same time, +// we specify how many events should be in the crossterm_events vector before it +// is considered a paste. 10 events in 10 milliseconds is conservative enough +// (unlikely somebody will type more than 10 characters in 10 milliseconds) const EVENTS_THRESHOLD: usize = 10; /// Determines if inputs should be used to extend the regular line buffer, @@ -69,9 +70,9 @@ enum InputMode { /// editing affects the search string, /// suggestions are provided to be inserted in the line buffer HistorySearch, - /// Hybrid mode indicating that history is walked through in the standard prompt - /// Either bash style up/down history or fish style prefix search, - /// Edits directly switch to [`InputMode::Regular`] + /// Hybrid mode indicating that history is walked through in the standard + /// prompt Either bash style up/down history or fish style prefix + /// search, Edits directly switch to [`InputMode::Regular`] HistoryTraversal, } @@ -79,19 +80,18 @@ enum InputMode { /// /// ## Example usage /// ```no_run -/// use reedline::{Reedline, Signal, DefaultPrompt}; +/// use reedline::{DefaultPrompt, Reedline, Signal}; /// let mut line_editor = Reedline::create(); /// let prompt = DefaultPrompt::default(); /// /// let out = line_editor.read_line(&prompt).unwrap(); /// match out { -/// Signal::Success(content) => { -/// // process content -/// } -/// _ => { -/// eprintln!("Entry aborted!"); -/// -/// } +/// Signal::Success(content) => { +/// // process content +/// } +/// _ => { +/// eprintln!("Entry aborted!"); +/// } /// } /// ``` pub struct Reedline { @@ -177,7 +177,8 @@ impl Drop for Reedline { impl Reedline { const FILTERED_ITEM_ID: HistoryItemId = HistoryItemId(i64::MAX); - /// Create a new [`Reedline`] engine with a local [`History`] that is not synchronized to a file. + /// Create a new [`Reedline`] engine with a local [`History`] that is not + /// synchronized to a file. #[must_use] pub fn create() -> Self { let history = Box::::default(); @@ -223,7 +224,8 @@ impl Reedline { } } - /// Get a new history session id based on the current time and the first commit datetime of reedline + /// Get a new history session id based on the current time and the first + /// commit datetime of reedline pub fn create_history_session_id() -> Option { let nanos = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { Ok(n) => n.as_nanos() as i64, @@ -235,13 +237,14 @@ impl Reedline { /// Toggle whether reedline enables bracketed paste to reed copied content /// - /// This currently alters the behavior for multiline pastes as pasting of regular text will - /// execute after every complete new line as determined by the [`Validator`]. With enabled - /// bracketed paste all lines will appear in the buffer and can then be submitted with a + /// This currently alters the behavior for multiline pastes as pasting of + /// regular text will execute after every complete new line as + /// determined by the [`Validator`]. With enabled bracketed paste all + /// lines will appear in the buffer and can then be submitted with a /// separate enter. /// - /// At this point most terminals should support it or ignore the setting of the necessary - /// flags. For full compatibility, keep it disabled. + /// At this point most terminals should support it or ignore the setting of + /// the necessary flags. For full compatibility, keep it disabled. pub fn use_bracketed_paste(mut self, enable: bool) -> Self { self.bracketed_paste.set(enable); self @@ -266,27 +269,25 @@ impl Reedline { } /// Set a new history session id - /// This should be used in situations where the user initially did not have a history_session_id - /// and then later realized they want to have one without restarting the application. + /// This should be used in situations where the user initially did not have + /// a history_session_id and then later realized they want to have one + /// without restarting the application. pub fn set_history_session_id(&mut self, session: Option) -> Result<()> { self.history_session_id = session; Ok(()) } - /// A builder to include a [`Hinter`] in your instance of the Reedline engine - /// # Example + /// A builder to include a [`Hinter`] in your instance of the Reedline + /// engine # Example /// ```rust - /// //Cargo.toml + /// // Cargo.toml /// //[dependencies] - /// //nu-ansi-term = "*" - /// use { - /// nu_ansi_term::{Color, Style}, - /// reedline::{DefaultHinter, Reedline}, - /// }; + /// // nu-ansi-term = "*" + /// use nu_ansi_term::{Color, Style}; + /// use reedline::{DefaultHinter, Reedline}; /// /// let mut line_editor = Reedline::create().with_hinter(Box::new( - /// DefaultHinter::default() - /// .with_style(Style::new().italic().fg(Color::LightGray)), + /// DefaultHinter::default().with_style(Style::new().italic().fg(Color::LightGray)), /// )); /// ``` #[must_use] @@ -310,10 +311,10 @@ impl Reedline { /// use reedline::{DefaultCompleter, Reedline}; /// /// let commands = vec![ - /// "test".into(), - /// "hello world".into(), - /// "hello world reedline".into(), - /// "this is the reedline crate".into(), + /// "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)); /// @@ -325,45 +326,45 @@ impl Reedline { self } - /// Turn on quick completions. These completions will auto-select if the completer - /// ever narrows down to a single entry. + /// Turn on quick completions. These completions will auto-select if the + /// completer ever narrows down to a single entry. #[must_use] pub fn with_quick_completions(mut self, quick_completions: bool) -> Self { self.quick_completions = quick_completions; self } - /// Turn on partial completions. These completions will fill the buffer with the - /// smallest common string from all the options + /// Turn on partial completions. These completions will fill the buffer with + /// the smallest common string from all the options #[must_use] pub fn with_partial_completions(mut self, partial_completions: bool) -> Self { self.partial_completions = partial_completions; self } - /// A builder which enables or disables the use of ansi coloring in the prompt - /// and in the command line syntax highlighting. + /// A builder which enables or disables the use of ansi coloring in the + /// prompt and in the command line syntax highlighting. #[must_use] pub fn with_ansi_colors(mut self, use_ansi_coloring: bool) -> Self { self.use_ansi_coloring = use_ansi_coloring; self } - /// A builder that configures the highlighter for your instance of the Reedline engine - /// # Example + /// A builder that configures the highlighter for your instance of the + /// Reedline engine # Example /// ```rust /// // Create a reedline object with highlighter support /// /// use reedline::{ExampleHighlighter, Reedline}; /// /// let commands = vec![ - /// "test".into(), - /// "hello world".into(), - /// "hello world reedline".into(), - /// "this is the reedline crate".into(), + /// "test".into(), + /// "hello world".into(), + /// "hello world reedline".into(), + /// "this is the reedline crate".into(), /// ]; /// let mut line_editor = - /// Reedline::create().with_highlighter(Box::new(ExampleHighlighter::new(commands))); + /// Reedline::create().with_highlighter(Box::new(ExampleHighlighter::new(commands))); /// ``` #[must_use] pub fn with_highlighter(mut self, highlighter: Box) -> Self { @@ -371,19 +372,18 @@ impl Reedline { self } - /// A builder which configures the history for your instance of the Reedline engine - /// # Example + /// A builder which configures the history for your instance of the Reedline + /// engine # Example /// ```rust,no_run /// // Create a reedline object with history support, including history size limits /// /// use reedline::{FileBackedHistory, Reedline}; /// /// let history = Box::new( - /// FileBackedHistory::with_file(5, "history.txt".into()) - /// .expect("Error configuring history with file"), + /// FileBackedHistory::with_file(5, "history.txt".into()) + /// .expect("Error configuring history with file"), /// ); - /// let mut line_editor = Reedline::create() - /// .with_history(history); + /// let mut line_editor = Reedline::create().with_history(history); /// ``` #[must_use] pub fn with_history(mut self, history: Box) -> Self { @@ -391,8 +391,8 @@ impl Reedline { self } - /// A builder which configures history exclusion for your instance of the Reedline engine - /// # Example + /// A builder which configures history exclusion for your instance of the + /// Reedline engine # Example /// ```rust,no_run /// // Create a reedline instance with history that will *not* include commands starting with a space /// @@ -412,15 +412,14 @@ impl Reedline { self } - /// A builder that configures the validator for your instance of the Reedline engine - /// # Example + /// A builder that configures the validator for your instance of the + /// Reedline engine # Example /// ```rust /// // Create a reedline object with validator support /// /// use reedline::{DefaultValidator, Reedline}; /// - /// let mut line_editor = - /// Reedline::create().with_validator(Box::new(DefaultValidator)); + /// let mut line_editor = Reedline::create().with_validator(Box::new(DefaultValidator)); /// ``` #[must_use] pub fn with_validator(mut self, validator: Box) -> Self { @@ -428,25 +427,26 @@ impl Reedline { self } - /// A builder that configures the alternate text editor used to edit the line buffer + /// A builder that configures the alternate text editor used to edit the + /// line buffer /// - /// You are responsible for providing a file path that is unique to this reedline session + /// You are responsible for providing a file path that is unique to this + /// reedline session /// /// # Example /// ```rust,no_run /// // Create a reedline object with vim as editor /// + /// use std::{env::temp_dir, process::Command}; + /// /// use reedline::Reedline; - /// use std::env::temp_dir; - /// use std::process::Command; /// /// let temp_file = std::env::temp_dir().join("my-random-unique.file"); /// let mut command = Command::new("vim"); /// // you can provide additional flags: /// command.arg("-p"); // open in a vim tab (just for demonstration) - /// // you don't have to pass the filename to the command - /// let mut line_editor = - /// Reedline::create().with_buffer_editor(command, temp_file); + /// // you don't have to pass the filename to the command + /// let mut line_editor = Reedline::create().with_buffer_editor(command, temp_file); /// ``` #[must_use] pub fn with_buffer_editor(mut self, editor: Command, temp_file: PathBuf) -> Self { @@ -475,7 +475,8 @@ impl Reedline { 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] pub fn with_edit_mode(mut self, edit_mode: Box) -> Self { self.edit_mode = edit_mode; @@ -503,9 +504,10 @@ 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. + /// 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 @@ -516,7 +518,8 @@ impl Reedline { self.edit_mode.edit_mode() } - /// Output the complete [`History`] chronologically with numbering to the terminal + /// Output the complete [`History`] chronologically with numbering to the + /// terminal pub fn print_history(&mut self) -> Result<()> { let history: Vec<_> = self .history @@ -529,7 +532,8 @@ impl Reedline { Ok(()) } - /// Output the complete [`History`] for this session, chronologically with numbering to the terminal + /// Output the complete [`History`] for this session, chronologically with + /// numbering to the terminal pub fn print_history_session(&mut self) -> Result<()> { let history: Vec<_> = self .history @@ -551,7 +555,8 @@ impl Reedline { Ok(()) } - /// Toggle between having a history that uses the history session id and one that does not + /// Toggle between having a history that uses the history session id and one + /// that does not pub fn toggle_history_session_matching( &mut self, session: Option, @@ -581,8 +586,9 @@ impl Reedline { /// Check if any commands have been run. /// - /// When no commands have been run, calling [`Self::update_last_command_context`] - /// does not make sense and is guaranteed to fail with a "No command run" error. + /// When no commands have been run, calling + /// [`Self::update_last_command_context`] does not make sense and is + /// guaranteed to fail with a "No command run" error. pub fn has_last_command_context(&self) -> bool { self.history_last_run_id.is_some() } @@ -606,8 +612,9 @@ impl Reedline { /// Wait for input and provide the user with a specified [`Prompt`]. /// - /// Returns a [`std::io::Result`] in which the `Err` type is [`std::io::Result`] - /// and the `Ok` variant wraps a [`Signal`] which handles user inputs. + /// Returns a [`std::io::Result`] in which the `Err` type is + /// [`std::io::Result`] and the `Ok` variant wraps a [`Signal`] which + /// handles user inputs. pub fn read_line(&mut self, prompt: &dyn Prompt) -> Result { terminal::enable_raw_mode()?; self.bracketed_paste.enter(); @@ -631,7 +638,8 @@ impl Reedline { self.editor.get_buffer() } - /// Writes `msg` to the terminal with a following carriage return and newline + /// Writes `msg` to the terminal with a following carriage return and + /// newline fn print_line(&mut self, msg: &str) -> Result<()> { self.painter.paint_line(msg) } @@ -651,8 +659,8 @@ impl Reedline { Ok(()) } - /// Helper implementing the logic for [`Reedline::read_line()`] to be wrapped - /// in a `raw_mode` context. + /// Helper implementing the logic for [`Reedline::read_line()`] to be + /// wrapped in a `raw_mode` context. fn read_line_helper(&mut self, prompt: &dyn Prompt) -> Result { self.painter.initialize_prompt_position()?; self.hide_hints = false; @@ -713,7 +721,8 @@ impl Reedline { // There could be multiple events queued up! // pasting text, resizes, blocking this thread (e.g. during debugging) - // We should be able to handle all of them as quickly as possible without causing unnecessary output steps. + // We should be able to handle all of them as quickly as possible without + // causing unnecessary output steps. if !event::poll(Duration::from_millis(POLL_WAIT))? { break; } @@ -752,7 +761,8 @@ impl Reedline { for event in reedline_events.drain(..) { match self.handle_event(prompt, event)? { EventStatus::Exits(signal) => { - // Move the cursor below the input area, for external commands or new read_line call + // Move the cursor below the input area, for external commands or new + // read_line call self.painter.move_cursor_to_end()?; return Ok(signal); } @@ -828,7 +838,8 @@ impl Reedline { Ok(EventStatus::Handled) } ReedlineEvent::ExecuteHostCommand(host_command) => { - // TODO: Decide if we need to do something special to have a nicer painter state on the next go + // TODO: Decide if we need to do something special to have a nicer painter state + // on the next go Ok(EventStatus::Exits(Signal::Success(host_command))) } ReedlineEvent::Edit(commands) => { @@ -1094,7 +1105,8 @@ impl Reedline { } } ReedlineEvent::ExecuteHostCommand(host_command) => { - // TODO: Decide if we need to do something special to have a nicer painter state on the next go + // TODO: Decide if we need to do something special to have a nicer painter state + // on the next go Ok(EventStatus::Exits(Signal::Success(host_command))) } ReedlineEvent::Edit(commands) => { @@ -1285,9 +1297,11 @@ impl Reedline { self.editor.move_to_end(UndoBehavior::HistoryNavigation); } - /// Enable the search and navigation through the history from the line buffer prompt + /// Enable the search and navigation through the history from the line + /// buffer prompt /// - /// Enables either prefix search with output in the line buffer or simple traversal + /// Enables either prefix search with output in the line buffer or simple + /// traversal fn get_history_navigation_based_on_line_buffer(&self) -> HistoryNavigationQuery { if self.editor.is_empty() || !self.editor.is_cursor_at_buffer_end() { // Perform bash-style basic up/down entry walking @@ -1308,7 +1322,8 @@ impl Reedline { /// Switch into reverse history search mode /// - /// This mode uses a separate prompt and handles keybindings slightly differently! + /// This mode uses a separate prompt and handles keybindings slightly + /// differently! fn enter_history_search(&mut self) { self.history_cursor = HistoryCursor::new( HistoryNavigationQuery::SubstringSearch("".to_string()), @@ -1317,7 +1332,8 @@ impl Reedline { self.input_mode = InputMode::HistorySearch; } - /// Dispatches the applicable [`EditCommand`] actions for editing the history search string. + /// Dispatches the applicable [`EditCommand`] actions for editing the + /// history search string. /// /// Only modifies internal state, does not perform regular output! fn run_history_commands(&mut self, commands: &[EditCommand]) { @@ -1363,10 +1379,12 @@ impl Reedline { } } - /// Set the buffer contents for history traversal/search in the standard prompt + /// Set the buffer contents for history traversal/search in the standard + /// prompt /// - /// When using the up/down traversal or fish/zsh style prefix search update the main line buffer accordingly. - /// Not used for the separate modal reverse search! + /// When using the up/down traversal or fish/zsh style prefix search update + /// the main line buffer accordingly. Not used for the separate modal + /// reverse search! fn update_buffer_from_history(&mut self) { match self.history_cursor.get_navigation() { _ if self.history_cursor_on_excluded => self.editor.set_buffer( @@ -1400,7 +1418,8 @@ impl Reedline { } } - /// Executes [`EditCommand`] actions by modifying the internal state appropriately. Does not output itself. + /// Executes [`EditCommand`] actions by modifying the internal state + /// appropriately. Does not output itself. pub fn run_edit_commands(&mut self, commands: &[EditCommand]) { if self.input_mode == InputMode::HistoryTraversal { if matches!( diff --git a/src/enums.rs b/src/enums.rs index 210dac8e..dc63e846 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -1,6 +1,7 @@ +use std::fmt::{Display, Formatter}; + use crossterm::event::{Event, KeyEvent, KeyEventKind}; use serde::{Deserialize, Serialize}; -use std::fmt::{Display, Formatter}; use strum_macros::EnumIter; /// Valid ways how `Reedline::read_line()` can return @@ -10,7 +11,8 @@ pub enum Signal { Success(String), /// Entry was aborted with `Ctrl+C` CtrlC, // Interrupt current editing - /// Abort with `Ctrl+D` signalling `EOF` or abort of a whole interactive session + /// Abort with `Ctrl+D` signalling `EOF` or abort of a whole interactive + /// session CtrlD, // End terminal session } @@ -100,7 +102,8 @@ pub enum EditCommand { /// Clear to the end of the current line ClearToLineEnd, - /// Insert completion: entire completion if there is only one possibility, or else up to shared prefix. + /// Insert completion: entire completion if there is only one possibility, + /// or else up to shared prefix. Complete, /// Cut the current line @@ -334,8 +337,8 @@ pub enum EditType { EditText, } -/// Every line change should come with an `UndoBehavior` tag, which can be used to -/// calculate how the change should be reflected on the undo stack +/// Every line change should come with an `UndoBehavior` tag, which can be used +/// to calculate how the change should be reflected on the undo stack #[derive(Debug)] pub enum UndoBehavior { /// Character insertion, tracking the character inserted @@ -350,8 +353,8 @@ pub enum UndoBehavior { MoveCursor, /// Navigated the history using up or down arrows HistoryNavigation, - /// Catch-all for actions that should always form a unique undo point and never be - /// grouped with later edits + /// Catch-all for actions that should always form a unique undo point and + /// never be grouped with later edits CreateUndoPoint, /// Undo/Redo actions shouldn't be reflected on the edit stack UndoRedo, @@ -452,10 +455,12 @@ pub enum ReedlineEvent { /// Navigate to the previous historic buffer PreviousHistory, - /// Move up to the previous line, if multiline, or up into the historic buffers + /// Move up to the previous line, if multiline, or up into the historic + /// buffers Up, - /// Move down to the next line, if multiline, or down through the historic buffers + /// Move down to the next line, if multiline, or down through the historic + /// buffers Down, /// Move right to the next column, completion entry, or complete hint @@ -504,7 +509,8 @@ pub enum ReedlineEvent { /// Move to the previous history page MenuPagePrevious, - /// Way to bind the execution of a whole command (directly returning from [`crate::Reedline::read_line()`]) to a keybinding + /// Way to bind the execution of a whole command (directly returning from + /// [`crate::Reedline::read_line()`]) to a keybinding ExecuteHostCommand(String), /// Open text editor @@ -564,8 +570,9 @@ pub(crate) enum EventStatus { /// A simple wrapper for [crossterm::event::Event] /// -/// Which will make sure that the given event doesn't contain [KeyEventKind::Release] -/// and convert from [KeyEventKind::Repeat] to [KeyEventKind::Press] +/// Which will make sure that the given event doesn't contain +/// [KeyEventKind::Release] and convert from [KeyEventKind::Repeat] to +/// [KeyEventKind::Press] pub struct ReedlineRawEvent { inner: Event, } diff --git a/src/external_printer.rs b/src/external_printer.rs index c2cdf707..1b602061 100644 --- a/src/external_printer.rs +++ b/src/external_printer.rs @@ -37,17 +37,19 @@ where let (sender, receiver) = bounded::(max_cap); Self { sender, receiver } } + /// Gets a Sender to use the printer externally by sending lines to it pub fn sender(&self) -> Sender { self.sender.clone() } + /// Receiver to get messages if any pub fn receiver(&self) -> &Receiver { &self.receiver } - /// Convenience method if the whole Printer is cloned, blocks if max_cap is reached. - /// + /// Convenience method if the whole Printer is cloned, blocks if max_cap is + /// reached. pub fn print(&self, line: T) -> Result<(), SendError> { self.sender.send(line) } diff --git a/src/highlighter/example.rs b/src/highlighter/example.rs index 1f1c9aee..78961939 100644 --- a/src/highlighter/example.rs +++ b/src/highlighter/example.rs @@ -1,7 +1,7 @@ -use crate::highlighter::Highlighter; -use crate::StyledText; use nu_ansi_term::{Color, Style}; +use crate::{highlighter::Highlighter, StyledText}; + pub static DEFAULT_BUFFER_MATCH_COLOR: Color = Color::Green; pub static DEFAULT_BUFFER_NEUTRAL_COLOR: Color = Color::White; pub static DEFAULT_BUFFER_NOTMATCH_COLOR: Color = Color::Red; @@ -58,7 +58,8 @@ impl Highlighter for ExampleHighlighter { } } impl ExampleHighlighter { - /// Construct the default highlighter with a given set of extern commands/keywords to detect and highlight + /// Construct the default highlighter with a given set of extern + /// commands/keywords to detect and highlight pub fn new(external_commands: Vec) -> ExampleHighlighter { ExampleHighlighter { external_commands, diff --git a/src/highlighter/mod.rs b/src/highlighter/mod.rs index e9ebafd6..d05a7151 100644 --- a/src/highlighter/mod.rs +++ b/src/highlighter/mod.rs @@ -1,14 +1,16 @@ mod example; mod simple_match; -use crate::StyledText; - pub use example::ExampleHighlighter; pub use simple_match::SimpleMatchHighlighter; -/// The syntax highlighting trait. Implementers of this trait will take in the current string and then -/// return a `StyledText` object, which represents the contents of the original line as styled strings + +use crate::StyledText; +/// The syntax highlighting trait. Implementers of this trait will take in the +/// current string and then return a `StyledText` object, which represents the +/// contents of the original line as styled strings pub trait Highlighter: Send { - /// The action that will handle the current buffer as a line and return the corresponding `StyledText` for the buffer + /// The action that will handle the current buffer as a line and return the + /// corresponding `StyledText` for the buffer /// /// Cursor position as byte offsets in the string fn highlight(&self, line: &str, cursor: usize) -> StyledText; diff --git a/src/highlighter/simple_match.rs b/src/highlighter/simple_match.rs index 981092b7..8f37f617 100644 --- a/src/highlighter/simple_match.rs +++ b/src/highlighter/simple_match.rs @@ -1,7 +1,7 @@ -use crate::highlighter::Highlighter; -use crate::StyledText; use nu_ansi_term::{Color, Style}; +use crate::{highlighter::Highlighter, StyledText}; + /// Highlight all matches for a given search string in a line /// /// Default style: diff --git a/src/hinter/cwd_aware.rs b/src/hinter/cwd_aware.rs index c5c43815..69843ebc 100644 --- a/src/hinter/cwd_aware.rs +++ b/src/hinter/cwd_aware.rs @@ -1,10 +1,11 @@ +use nu_ansi_term::{Color, Style}; + use crate::{ hinter::get_first_token, history::SearchQuery, result::{ReedlineError, ReedlineErrorVariants::HistoryFeatureUnsupported}, Hinter, History, }; -use nu_ansi_term::{Color, Style}; /// A hinter that uses the completions or the history to show a hint to the user /// @@ -100,7 +101,8 @@ impl CwdAwareHinter { self } - /// A builder that sets the number of characters that have to be present to enable history hints + /// A builder that sets the number of characters that have to be present to + /// enable history hints #[must_use] pub fn with_min_chars(mut self, min_chars: usize) -> Self { self.min_chars = min_chars; diff --git a/src/hinter/default.rs b/src/hinter/default.rs index 2606a4d6..3b23ca7f 100644 --- a/src/hinter/default.rs +++ b/src/hinter/default.rs @@ -1,6 +1,7 @@ -use crate::{hinter::get_first_token, history::SearchQuery, Hinter, History}; use nu_ansi_term::{Color, Style}; +use crate::{hinter::get_first_token, history::SearchQuery, Hinter, History}; + /// A hinter that uses the completions or the history to show a hint to the user pub struct DefaultHinter { style: Style, @@ -69,7 +70,8 @@ impl DefaultHinter { self } - /// A builder that sets the number of characters that have to be present to enable history hints + /// A builder that sets the number of characters that have to be present to + /// enable history hints #[must_use] pub fn with_min_chars(mut self, min_chars: usize) -> Self { self.min_chars = min_chars; diff --git a/src/hinter/mod.rs b/src/hinter/mod.rs index cf6f4701..7a39dba3 100644 --- a/src/hinter/mod.rs +++ b/src/hinter/mod.rs @@ -2,7 +2,6 @@ mod cwd_aware; mod default; pub use cwd_aware::CwdAwareHinter; pub use default::DefaultHinter; - use unicode_segmentation::UnicodeSegmentation; pub fn is_whitespace_str(s: &str) -> bool { @@ -28,8 +27,9 @@ pub fn get_first_token(string: &str) -> String { } use crate::History; -/// A trait that's responsible for returning the hint for the current line and position -/// Hints are often shown in-line as part of the buffer, showing the user text they can accept or ignore +/// A trait that's responsible for returning the hint for the current line and +/// position Hints are often shown in-line as part of the buffer, showing the +/// user text they can accept or ignore pub trait Hinter: Send { /// Handle the hinting duty by using the line, position, and current history /// @@ -42,7 +42,8 @@ pub trait Hinter: Send { use_ansi_coloring: bool, ) -> String; - /// Return the current hint unformatted to perform the completion of the full hint + /// Return the current hint unformatted to perform the completion of the + /// full hint fn complete_hint(&self) -> String; /// Return the first semantic token of the hint diff --git a/src/history/base.rs b/src/history/base.rs index 9216e34f..e4c5630b 100644 --- a/src/history/base.rs +++ b/src/history/base.rs @@ -1,11 +1,14 @@ +use chrono::Utc; + use super::HistoryItemId; use crate::{core_editor::LineBuffer, HistoryItem, HistorySessionId, Result}; -use chrono::Utc; /// Browsing modes for a [`History`] #[derive(Debug, Clone, PartialEq, Eq)] pub enum HistoryNavigationQuery { - /// `bash` style browsing through the history. Contained `LineBuffer` is used to store the state of manual entry before browsing through the history + /// `bash` style browsing through the history. Contained `LineBuffer` is + /// used to store the state of manual entry before browsing through the + /// history Normal(LineBuffer), /// Search for entries starting with a particular string. PrefixSearch(String), @@ -42,7 +45,8 @@ pub struct SearchFilter { /// Query for the command line content pub command_line: Option, /// Considered implementation detail for now - pub(crate) not_command_line: Option, // to skip the currently shown value in up-arrow navigation + pub(crate) not_command_line: Option, /* to skip the currently shown value in + * up-arrow navigation */ /// Filter based on the executing systems hostname pub hostname: Option, /// Exact filter for the working directory @@ -96,9 +100,11 @@ impl SearchFilter { pub struct SearchQuery { /// Direction to search in pub direction: SearchDirection, - /// if given, only get results after/before this time (depending on direction) + /// if given, only get results after/before this time (depending on + /// direction) pub start_time: Option>, - /// if given, only get results after/before this time (depending on direction) + /// if given, only get results after/before this time (depending on + /// direction) pub end_time: Option>, /// if given, only get results after/before this id (depending on direction) pub start_id: Option, @@ -146,7 +152,8 @@ impl SearchQuery { )) } - /// Get the most recent entry starting with the `prefix` and `cwd` same as the current cwd + /// Get the most recent entry starting with the `prefix` and `cwd` same as + /// the current cwd pub fn last_with_prefix_and_cwd( prefix: String, session: Option, @@ -184,7 +191,8 @@ impl SearchQuery { } /// Represents a history file or database -/// Data could be stored e.g. in a plain text file, in a `JSONL` file, in a `SQLite` database +/// Data could be stored e.g. in a plain text file, in a `JSONL` file, in a +/// `SQLite` database pub trait History: Send { /// save a history item to the database /// if given id is None, a new id is created and set in the return value @@ -218,9 +226,10 @@ pub trait History: Send { fn sync(&mut self) -> std::io::Result<()>; /// get the history session id fn session(&self) -> Option; - // Dev comment: This has been implemented due to the `history session --set` command which couldn't get done so this is commented - // /// updates the history session id - // fn update_session(&mut self, history_session: Option); + // Dev comment: This has been implemented due to the `history session --set` + // command which couldn't get done so this is commented /// updates the + // history session id fn update_session(&mut self, history_session: + // Option); } #[cfg(test)] diff --git a/src/history/cursor.rs b/src/history/cursor.rs index ab41ba4f..30915a62 100644 --- a/src/history/cursor.rs +++ b/src/history/cursor.rs @@ -1,11 +1,8 @@ -use crate::{History, HistoryNavigationQuery, HistorySessionId}; - -use super::base::CommandLineSearch; -use super::base::SearchDirection; -use super::base::SearchFilter; -use super::HistoryItem; -use super::SearchQuery; -use crate::Result; +use super::{ + base::{CommandLineSearch, SearchDirection, SearchFilter}, + HistoryItem, SearchQuery, +}; +use crate::{History, HistoryNavigationQuery, HistorySessionId, Result}; /// Interface of a stateful navigation via [`HistoryNavigationQuery`]. #[derive(Debug)] @@ -26,13 +23,15 @@ impl HistoryCursor { } } - /// This moves the cursor backwards respecting the navigation query that is set + /// This moves the cursor backwards respecting the navigation query that is + /// set /// - Results in a no-op if the cursor is at the initial point pub fn back(&mut self, history: &dyn History) -> Result<()> { self.navigate_in_direction(history, SearchDirection::Backward) } - /// This moves the cursor forwards respecting the navigation-query that is set + /// This moves the cursor forwards respecting the navigation-query that is + /// set /// - Results in a no-op if the cursor is at the latest point pub fn forward(&mut self, history: &dyn History) -> Result<()> { self.navigate_in_direction(history, SearchDirection::Forward) @@ -58,13 +57,15 @@ impl HistoryCursor { filter } } + fn navigate_in_direction( &mut self, history: &dyn History, direction: SearchDirection, ) -> Result<()> { if direction == SearchDirection::Forward && self.current.is_none() { - // if searching forward but we don't have a starting point, assume we are at the end + // if searching forward but we don't have a starting point, assume we are at the + // end return Ok(()); } let start_id = self.current.as_ref().and_then(|e| e.id); @@ -103,11 +104,9 @@ mod tests { use pretty_assertions::assert_eq; + use super::{super::*, *}; use crate::LineBuffer; - use super::super::*; - use super::*; - fn create_history() -> (Box, HistoryCursor) { #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] let hist = Box::new(SqliteBackedHistory::in_memory().unwrap()); @@ -379,7 +378,8 @@ mod tests { use tempfile::tempdir; let tmp = tempdir().unwrap(); - // check that it also works for a path where the directory has not been created yet + // check that it also works for a path where the directory has not been created + // yet let histfile = tmp.path().join("nested_path").join(".history"); let entries = vec!["test", "text", "more test text"]; @@ -388,7 +388,8 @@ mod tests { let (mut hist, _) = create_history_at(5, &histfile); add_text_entries(hist.as_mut(), &entries); - // As `hist` goes out of scope and get's dropped, its contents are flushed to disk + // As `hist` goes out of scope and get's dropped, its contents are + // flushed to disk } let (reading_hist, _) = create_history_at(5, &histfile); @@ -416,7 +417,8 @@ mod tests { { let (mut writing_hist, _) = create_history_at(5, &histfile); add_text_entries(writing_hist.as_mut(), &entries); - // As `hist` goes out of scope and get's dropped, its contents are flushed to disk + // As `hist` goes out of scope and get's dropped, its contents are + // flushed to disk } let (reading_hist, _) = create_history_at(5, &histfile); @@ -446,13 +448,15 @@ mod tests { { let (mut writing_hist, _) = create_history_at(capacity, &histfile); add_text_entries(writing_hist.as_mut(), &initial_entries); - // As `hist` goes out of scope and get's dropped, its contents are flushed to disk + // As `hist` goes out of scope and get's dropped, its contents are + // flushed to disk } { let (mut appending_hist, _) = create_history_at(capacity, &histfile); add_text_entries(appending_hist.as_mut(), &appending_entries); - // As `hist` goes out of scope and get's dropped, its contents are flushed to disk + // As `hist` goes out of scope and get's dropped, its contents are flushed to + // disk let actual: Vec<_> = get_all_entry_texts(appending_hist.as_ref()); assert_eq!(expected_appended_entries, actual); } @@ -462,7 +466,8 @@ mod tests { add_text_entries(truncating_hist.as_mut(), &truncating_entries); let actual: Vec<_> = get_all_entry_texts(truncating_hist.as_ref()); assert_eq!(expected_truncated_entries, actual); - // As `hist` goes out of scope and get's dropped, its contents are flushed to disk + // As `hist` goes out of scope and get's dropped, its contents are + // flushed to disk } let (reading_hist, _) = create_history_at(capacity, &histfile); @@ -489,7 +494,8 @@ mod tests { { let (mut writing_hist, _) = create_history_at(10, &histfile); add_text_entries(writing_hist.as_mut(), &overly_large_previous_entries); - // As `hist` goes out of scope and get's dropped, its contents are flushed to disk + // As `hist` goes out of scope and get's dropped, its contents are + // flushed to disk } { @@ -497,7 +503,8 @@ mod tests { let actual: Vec<_> = get_all_entry_texts(truncating_hist.as_ref()); assert_eq!(expected_truncated_entries, actual); - // As `hist` goes out of scope and get's dropped, its contents are flushed to disk + // As `hist` goes out of scope and get's dropped, its contents are + // flushed to disk } let (reading_hist, _) = create_history_at(5, &histfile); @@ -525,7 +532,8 @@ mod tests { { let (mut writing_hist, _) = create_history_at(capacity, &histfile); add_text_entries(writing_hist.as_mut(), &initial_entries); - // As `hist` goes out of scope and get's dropped, its contents are flushed to disk + // As `hist` goes out of scope and get's dropped, its contents are + // flushed to disk } { @@ -535,10 +543,12 @@ mod tests { let (mut hist_b, _) = create_history_at(capacity, &histfile); add_text_entries(hist_b.as_mut(), &entries_b); - // As `hist` goes out of scope and get's dropped, its contents are flushed to disk + // As `hist` goes out of scope and get's dropped, its contents + // are flushed to disk } add_text_entries(hist_a.as_mut(), &entries_a); - // As `hist` goes out of scope and get's dropped, its contents are flushed to disk + // As `hist` goes out of scope and get's dropped, its contents are + // flushed to disk } let (reading_hist, _) = create_history_at(capacity, &histfile); @@ -565,7 +575,8 @@ mod tests { { let (mut writing_hist, _) = create_history_at(capacity, &histfile); add_text_entries(writing_hist.as_mut(), &initial_entries); - // As `hist` goes out of scope and get's dropped, its contents are flushed to disk + // As `hist` goes out of scope and get's dropped, its contents are + // flushed to disk } let threads = (0..num_threads) diff --git a/src/history/file_backed.rs b/src/history/file_backed.rs index 1108027d..b6359960 100644 --- a/src/history/file_backed.rs +++ b/src/history/file_backed.rs @@ -1,11 +1,3 @@ -use super::{ - base::CommandLineSearch, History, HistoryItem, HistoryItemId, SearchDirection, SearchQuery, -}; -use crate::{ - result::{ReedlineError, ReedlineErrorVariants}, - HistorySessionId, Result, -}; - use std::{ collections::VecDeque, fs::OpenOptions, @@ -14,16 +6,26 @@ use std::{ path::PathBuf, }; -/// Default size of the [`FileBackedHistory`] used when calling [`FileBackedHistory::default()`] +use super::{ + base::CommandLineSearch, History, HistoryItem, HistoryItemId, SearchDirection, SearchQuery, +}; +use crate::{ + result::{ReedlineError, ReedlineErrorVariants}, + HistorySessionId, Result, +}; + +/// Default size of the [`FileBackedHistory`] used when calling +/// [`FileBackedHistory::default()`] pub const HISTORY_SIZE: usize = 1000; pub const NEWLINE_ESCAPE: &str = "<\\n>"; /// Stateful history that allows up/down-arrow browsing with an internal cursor. /// -/// Can optionally be associated with a newline separated history file using the [`FileBackedHistory::with_file()`] constructor. -/// Similar to bash's behavior without HISTTIMEFORMAT. -/// (See ) -/// If the history is associated to a file all new changes within a given history capacity will be written to disk when History is dropped. +/// Can optionally be associated with a newline separated history file using the +/// [`FileBackedHistory::with_file()`] constructor. Similar to bash's behavior +/// without HISTTIMEFORMAT. (See ) +/// If the history is associated to a file all new changes within a given +/// history capacity will be written to disk when History is dropped. #[derive(Debug)] pub struct FileBackedHistory { capacity: usize, @@ -34,9 +36,11 @@ pub struct FileBackedHistory { } impl Default for FileBackedHistory { - /// Creates an in-memory [`History`] with a maximal capacity of [`HISTORY_SIZE`]. + /// Creates an in-memory [`History`] with a maximal capacity of + /// [`HISTORY_SIZE`]. /// - /// To create a [`History`] that is synchronized with a file use [`FileBackedHistory::with_file()`] + /// To create a [`History`] that is synchronized with a file use + /// [`FileBackedHistory::with_file()`] fn default() -> Self { Self::new(HISTORY_SIZE) } @@ -310,7 +314,6 @@ impl FileBackedHistory { /// /// /// **Side effects:** creates all nested directories to the file - /// pub fn with_file(capacity: usize, file: PathBuf) -> std::io::Result { let mut hist = Self::new(capacity); if let Some(base_dir) = file.parent() { @@ -338,7 +341,8 @@ impl FileBackedHistory { } impl Drop for FileBackedHistory { - /// On drop the content of the [`History`] will be written to the file if specified via [`FileBackedHistory::with_file()`]. + /// On drop the content of the [`History`] will be written to the file if + /// specified via [`FileBackedHistory::with_file()`]. fn drop(&mut self) { let _res = self.sync(); } diff --git a/src/history/item.rs b/src/history/item.rs index ec477bbc..e6b47fa2 100644 --- a/src/history/item.rs +++ b/src/history/item.rs @@ -1,10 +1,12 @@ +use std::{fmt::Display, time::Duration}; + use chrono::Utc; #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] use rusqlite::ToSql; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use std::{fmt::Display, time::Duration}; -/// Unique ID for the [`HistoryItem`]. More recent items have higher ids than older ones. +/// Unique ID for the [`HistoryItem`]. More recent items have higher ids than +/// older ones. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct HistoryItemId(pub(crate) i64); impl HistoryItemId { @@ -19,7 +21,8 @@ impl Display for HistoryItemId { } } -/// Unique ID for the session in which reedline was run to disambiguate different sessions +/// Unique ID for the session in which reedline was run to disambiguate +/// different sessions #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct HistorySessionId(pub(crate) i64); impl HistorySessionId { @@ -49,7 +52,8 @@ impl From for i64 { } } -/// This trait represents additional arbitrary context to be added to a history (optional, see [`HistoryItem`]) +/// This trait represents additional arbitrary context to be added to a history +/// (optional, see [`HistoryItem`]) pub trait HistoryItemExtraInfo: Serialize + DeserializeOwned + Default + Send {} #[derive(Clone, Copy, Default, Debug, PartialEq, Eq)] @@ -101,7 +105,8 @@ pub struct HistoryItem { } impl HistoryItem { - /// create a history item purely from the command line with everything else set to None + /// create a history item purely from the command line with everything else + /// set to None pub fn from_command_line(cmd: impl Into) -> HistoryItem { HistoryItem { id: None, diff --git a/src/history/mod.rs b/src/history/mod.rs index c611a96f..84ea2ee4 100644 --- a/src/history/mod.rs +++ b/src/history/mod.rs @@ -4,13 +4,11 @@ mod file_backed; mod item; #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] mod sqlite_backed; -#[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] -pub use sqlite_backed::SqliteBackedHistory; - pub use base::{ CommandLineSearch, History, HistoryNavigationQuery, SearchDirection, SearchFilter, SearchQuery, }; pub use cursor::HistoryCursor; -pub use item::{HistoryItem, HistoryItemId, HistorySessionId}; - pub use file_backed::{FileBackedHistory, HISTORY_SIZE}; +pub use item::{HistoryItem, HistoryItemId, HistorySessionId}; +#[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] +pub use sqlite_backed::SqliteBackedHistory; diff --git a/src/history/sqlite_backed.rs b/src/history/sqlite_backed.rs index 350a87ba..0a519769 100644 --- a/src/history/sqlite_backed.rs +++ b/src/history/sqlite_backed.rs @@ -1,3 +1,8 @@ +use std::{path::PathBuf, time::Duration}; + +use chrono::{TimeZone, Utc}; +use rusqlite::{named_params, params, Connection, ToSql}; + use super::{ base::{CommandLineSearch, SearchDirection, SearchQuery}, History, HistoryItem, HistoryItemId, HistorySessionId, @@ -6,14 +11,12 @@ use crate::{ result::{ReedlineError, ReedlineErrorVariants}, Result, }; -use chrono::{TimeZone, Utc}; -use rusqlite::{named_params, params, Connection, ToSql}; -use std::{path::PathBuf, time::Duration}; const SQLITE_APPLICATION_ID: i32 = 1151497937; /// A history that stores the values to an SQLite database. -/// In addition to storing the command, the history can store an additional arbitrary HistoryEntryContext, -/// to add information such as a timestamp, running directory, result... +/// In addition to storing the command, the history can store an additional +/// arbitrary HistoryEntryContext, to add information such as a timestamp, +/// running directory, result... pub struct SqliteBackedHistory { db: rusqlite::Connection, session: Option, @@ -179,8 +182,8 @@ impl History for SqliteBackedHistory { // fn update_session(&mut self, history_session: Option) { // self.session = history_session; - // self.session_timestamp = history_session.map(|hs| chrono::Utc.timestamp_nanos(hs.0)) - // } + // self.session_timestamp = history_session.map(|hs| + // chrono::Utc.timestamp_nanos(hs.0)) } } fn map_sqlite_err(err: rusqlite::Error) -> ReedlineError { // TODO: better error mapping @@ -196,19 +199,20 @@ impl SqliteBackedHistory { /// /// /// **Side effects:** creates all nested directories to the file - /// - pub fn with_file( - file: PathBuf, - session: Option, - ) -> Result { + pub fn with_file(file: PathBuf, session: Option) -> Result { if let Some(base_dir) = file.parent() { std::fs::create_dir_all(base_dir).map_err(|e| { ReedlineError(ReedlineErrorVariants::HistoryDatabaseError(format!("{e}"))) })?; } let db = Connection::open(&file).map_err(map_sqlite_err)?; - Self::from_connection(db, session, session.map(|s| chrono::Utc.timestamp_nanos(s.0))) + Self::from_connection( + db, + session, + session.map(|s| chrono::Utc.timestamp_nanos(s.0)), + ) } + /// Creates a new history in memory pub fn in_memory() -> Result { Self::from_connection( @@ -217,6 +221,7 @@ impl SqliteBackedHistory { None, ) } + /// initialize a new database / migrate an existing one fn from_connection( db: Connection, @@ -381,11 +386,7 @@ impl SqliteBackedHistory { } let query = format!( - "SELECT {select_expression} \ - FROM history \ - WHERE ({wheres}) \ - ORDER BY id {asc} \ - {limit}" + "SELECT {select_expression} FROM history WHERE ({wheres}) ORDER BY id {asc} {limit}" ); (query, params) } diff --git a/src/lib.rs b/src/lib.rs index ec31a401..27a161ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,14 +39,12 @@ //! ```rust //! // Configure reedline with custom keybindings //! -//! //Cargo.toml +//! // Cargo.toml //! // [dependencies] //! // crossterm = "*" //! -//! use { -//! crossterm::event::{KeyCode, KeyModifiers}, -//! reedline::{default_emacs_keybindings, EditCommand, Reedline, Emacs, ReedlineEvent}, -//! }; +//! use crossterm::event::{KeyCode, KeyModifiers}; +//! use reedline::{default_emacs_keybindings, EditCommand, Emacs, Reedline, ReedlineEvent}; //! //! let mut keybindings = default_emacs_keybindings(); //! keybindings.add_binding( @@ -70,8 +68,7 @@ //! FileBackedHistory::with_file(5, "history.txt".into()) //! .expect("Error configuring history with file"), //! ); -//! let mut line_editor = Reedline::create() -//! .with_history(history); +//! let mut line_editor = Reedline::create().with_history(history); //! ``` //! //! ## Integrate with custom syntax [`Highlighter`] @@ -82,13 +79,13 @@ //! use reedline::{ExampleHighlighter, Reedline}; //! //! let commands = vec![ -//! "test".into(), -//! "hello world".into(), -//! "hello world reedline".into(), -//! "this is the reedline crate".into(), +//! "test".into(), +//! "hello world".into(), +//! "hello world reedline".into(), +//! "this is the reedline crate".into(), //! ]; //! let mut line_editor = -//! Reedline::create().with_highlighter(Box::new(ExampleHighlighter::new(commands))); +//! Reedline::create().with_highlighter(Box::new(ExampleHighlighter::new(commands))); //! ``` //! //! ## Integrate with custom tab completion @@ -96,13 +93,16 @@ //! ```rust //! // Create a reedline object with tab completions support //! -//! use reedline::{default_emacs_keybindings, ColumnarMenu, DefaultCompleter, Emacs, KeyCode, KeyModifiers, Reedline, ReedlineEvent, ReedlineMenu}; +//! use reedline::{ +//! default_emacs_keybindings, ColumnarMenu, DefaultCompleter, Emacs, KeyCode, KeyModifiers, +//! Reedline, ReedlineEvent, ReedlineMenu, +//! }; //! //! let commands = vec![ -//! "test".into(), -//! "hello world".into(), -//! "hello world reedline".into(), -//! "this is the reedline crate".into(), +//! "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 @@ -131,19 +131,15 @@ //! ```rust //! // Create a reedline object with in-line hint support //! -//! //Cargo.toml +//! // Cargo.toml //! // [dependencies] //! // nu-ansi-term = "*" //! -//! use { -//! nu_ansi_term::{Color, Style}, -//! reedline::{DefaultHinter, Reedline}, -//! }; -//! +//! use nu_ansi_term::{Color, Style}; +//! use reedline::{DefaultHinter, Reedline}; //! //! let mut line_editor = Reedline::create().with_hinter(Box::new( -//! DefaultHinter::default() -//! .with_style(Style::new().italic().fg(Color::LightGray)), +//! DefaultHinter::default().with_style(Style::new().italic().fg(Color::LightGray)), //! )); //! ``` //! @@ -177,27 +173,35 @@ //! //! ## Crate features //! -//! - `clipboard`: Enable support to use the `SystemClipboard`. Enabling this feature will return a `SystemClipboard` instead of a local clipboard when calling `get_default_clipboard()`. +//! - `clipboard`: Enable support to use the `SystemClipboard`. Enabling this +//! feature will return a `SystemClipboard` instead of a local clipboard when +//! calling `get_default_clipboard()`. //! - `bashisms`: Enable support for special text sequences that recall components from the history. e.g. `!!` and `!$`. For use in shells like `bash` or [`nushell`](https://nushell.sh). -//! - `sqlite`: Provides the `SqliteBackedHistory` to store richer information in the history. Statically links the required sqlite version. -//! - `sqlite-dynlib`: Alternative to the feature `sqlite`. Will not statically link. Requires `sqlite >= 3.38` to link dynamically! -//! - `external_printer`: **Experimental:** Thread-safe `ExternalPrinter` handle to print lines from concurrently running threads. +//! - `sqlite`: Provides the `SqliteBackedHistory` to store richer information +//! in the history. Statically links the required sqlite version. +//! - `sqlite-dynlib`: Alternative to the feature `sqlite`. Will not statically +//! link. Requires `sqlite >= 3.38` to link dynamically! +//! - `external_printer`: **Experimental:** Thread-safe `ExternalPrinter` handle +//! to print lines from concurrently running threads. //! //! ## Are we prompt yet? (Development status) //! //! Nushell has now all the basic features to become the primary line editor for [nushell](https://github.com/nushell/nushell //! ) //! -//! - General editing functionality, that should feel familiar coming from other shells (e.g. bash, fish, zsh). +//! - General editing functionality, that should feel familiar coming from other +//! shells (e.g. bash, fish, zsh). //! - Configurable keybindings (emacs-style bindings and basic vi-style). //! - Configurable prompt //! - Content-aware syntax highlighting. //! - Autocompletion (With graphical selection menu or simple cycling inline). -//! - History with interactive search options (optionally persists to file, can support multilple sessions accessing the same file) +//! - History with interactive search options (optionally persists to file, can +//! support multilple sessions accessing the same file) //! - Fish-style history autosuggestion hints //! - Undo support. //! - Clipboard integration -//! - Line completeness validation for seamless entry of multiline command sequences. +//! - Line completeness validation for seamless entry of multiline command +//! sequences. //! //! ### Areas for future improvements //! @@ -205,8 +209,10 @@ //! - [ ] Easier keybinding configuration //! - [ ] Support for more advanced vi commands //! - [ ] Visual selection -//! - [ ] Smooth experience if completion or prompt content takes long to compute -//! - [ ] Support for a concurrent output stream from background tasks to be displayed, while the input prompt is active. ("Full duplex" mode) +//! - [ ] Smooth experience if completion or prompt content takes long to +//! compute +//! - [ ] Support for a concurrent output stream from background tasks to be +//! displayed, while the input prompt is active. ("Full duplex" mode) //! //! For more ideas check out the [feature discussion](https://github.com/nushell/reedline/issues/63) or hop on the `#reedline` channel of the [nushell discord](https://discordapp.com/invite/NtAbbGn). //! @@ -221,13 +227,11 @@ //! For currently more mature Rust line editing check out: //! //! - [rustyline](https://crates.io/crates/rustyline) -//! #![warn(rustdoc::missing_crate_level_docs)] #![warn(missing_docs)] // #![deny(warnings)] mod core_editor; -pub use core_editor::Editor; -pub use core_editor::LineBuffer; +pub use core_editor::{Editor, LineBuffer}; mod enums; pub use enums::{EditCommand, ReedlineEvent, ReedlineRawEvent, Signal, UndoBehavior}; @@ -269,8 +273,7 @@ mod completion; pub use completion::{Completer, DefaultCompleter, Span, Suggestion}; mod hinter; -pub use hinter::CwdAwareHinter; -pub use hinter::{DefaultHinter, Hinter}; +pub use hinter::{CwdAwareHinter, DefaultHinter, Hinter}; mod validator; pub use validator::{DefaultValidator, ValidationResult, Validator}; @@ -286,16 +289,16 @@ pub use terminal_extensions::kitty_protocol_available; mod utils; mod external_printer; -pub use utils::{ - get_reedline_default_keybindings, get_reedline_edit_commands, - get_reedline_keybinding_modifiers, get_reedline_keycodes, get_reedline_prompt_edit_modes, - get_reedline_reedline_events, -}; - -// Reexport the key types to be independent from an explicit crossterm dependency. +// Reexport the key types to be independent from an explicit crossterm +// dependency. pub use crossterm::{ event::{KeyCode, KeyModifiers}, style::Color, }; #[cfg(feature = "external_printer")] pub use external_printer::ExternalPrinter; +pub use utils::{ + get_reedline_default_keybindings, get_reedline_edit_commands, + get_reedline_keybinding_modifiers, get_reedline_keycodes, get_reedline_prompt_edit_modes, + get_reedline_reedline_events, +}; diff --git a/src/menu/columnar_menu.rs b/src/menu/columnar_menu.rs index ec391777..66cffcb1 100644 --- a/src/menu/columnar_menu.rs +++ b/src/menu/columnar_menu.rs @@ -1,9 +1,10 @@ +use nu_ansi_term::{ansi::RESET, Style}; + use super::{menu_functions::find_common_string, Menu, MenuEvent, MenuTextStyle}; use crate::{ core_editor::Editor, menu_functions::string_difference, painting::Painter, Completer, Suggestion, UndoBehavior, }; -use nu_ansi_term::{ansi::RESET, Style}; /// Default values used as reference for the menu. These values are set during /// the initial declaration of the menu and are always kept as reference for the @@ -28,7 +29,8 @@ impl Default for DefaultColumnDetails { } /// Represents the actual column conditions of the menu. These conditions change -/// since they need to accommodate possible different line sizes for the column values +/// since they need to accommodate possible different line sizes for the column +/// values #[derive(Default)] struct ColumnDetails { /// Number of columns that the menu will have @@ -399,7 +401,8 @@ impl ColumnarMenu { ) } } else { - // If no ansi coloring is found, then the selection word is the line in uppercase + // If no ansi coloring is found, then the selection word is the line in + // uppercase let marker = if index == self.index() { ">" } else { "" }; let line = if let Some(description) = &suggestion.description { @@ -558,8 +561,8 @@ impl Menu for ColumnarMenu { // The working value for the menu are updated first before executing any of the // menu events // - // If there is at least one suggestion that contains a description, then the layout - // is changed to one column to fit the description + // If there is at least one suggestion that contains a description, then the + // layout is changed to one column to fit the description let exist_description = self .get_values() .iter() @@ -725,9 +728,8 @@ impl Menu for ColumnarMenu { #[cfg(test)] mod tests { - use crate::Span; - use super::*; + use crate::Span; macro_rules! partial_completion_tests { (name: $test_group_name:ident, completions: $completions:expr, test_cases: $($name:ident: $value:expr,)*) => { diff --git a/src/menu/list_menu.rs b/src/menu/list_menu.rs index 5b10e68d..958f8403 100644 --- a/src/menu/list_menu.rs +++ b/src/menu/list_menu.rs @@ -1,16 +1,16 @@ -use { - super::{ - menu_functions::{parse_selection_char, string_difference}, - Menu, MenuEvent, MenuTextStyle, - }, - crate::{ - core_editor::Editor, - painting::{estimate_single_line_wraps, Painter}, - Completer, Suggestion, UndoBehavior, - }, - nu_ansi_term::{ansi::RESET, Style}, - std::{fmt::Write, iter::Sum}, - unicode_width::UnicodeWidthStr, +use std::{fmt::Write, iter::Sum}; + +use nu_ansi_term::{ansi::RESET, Style}; +use unicode_width::UnicodeWidthStr; + +use super::{ + menu_functions::{parse_selection_char, string_difference}, + Menu, MenuEvent, MenuTextStyle, +}; +use crate::{ + core_editor::Editor, + painting::{estimate_single_line_wraps, Painter}, + Completer, Suggestion, UndoBehavior, }; const SELECTION_CHAR: char = '!'; @@ -592,8 +592,9 @@ impl Menu for ListMenu { } } - /// Calculates the real required lines for the menu considering how many lines - /// wrap the terminal and if an entry is larger than the remaining lines + /// Calculates the real required lines for the menu considering how many + /// lines wrap the terminal and if an entry is larger than the remaining + /// lines fn menu_required_lines(&self, terminal_columns: u16) -> u16 { let mut entry_index = 0; self.get_values().iter().fold(0, |total_lines, suggestion| { @@ -610,7 +611,8 @@ impl Menu for ListMenu { }) + 1 } - /// Creates the menu representation as a string which will be painted by the painter + /// Creates the menu representation as a string which will be painted by the + /// painter fn menu_string(&self, _available_lines: u16, use_ansi_coloring: bool) -> String { let values_before_page = self.pages.iter().take(self.page).sum::().size; match self.pages.get(self.page) { diff --git a/src/menu/menu_functions.rs b/src/menu/menu_functions.rs index 8d15d696..6ca497e2 100644 --- a/src/menu/menu_functions.rs +++ b/src/menu/menu_functions.rs @@ -49,7 +49,6 @@ pub enum ParseAction { /// action: ParseAction::ForwardSearch /// } /// ) -/// /// ``` pub fn parse_selection_char(buffer: &str, marker: char) -> ParseResult { if buffer.is_empty() { diff --git a/src/menu/mod.rs b/src/menu/mod.rs index fb559583..cb723336 100644 --- a/src/menu/mod.rs +++ b/src/menu/mod.rs @@ -2,13 +2,15 @@ mod columnar_menu; mod list_menu; pub mod menu_functions; -use crate::core_editor::Editor; -use crate::History; -use crate::{completion::history::HistoryCompleter, painting::Painter, Completer, Suggestion}; pub use columnar_menu::ColumnarMenu; pub use list_menu::ListMenu; use nu_ansi_term::{Color, Style}; +use crate::{ + completion::history::HistoryCompleter, core_editor::Editor, painting::Painter, Completer, + History, Suggestion, +}; + /// Struct to store the menu style pub struct MenuTextStyle { /// Text style for selected text in a menu @@ -32,13 +34,15 @@ impl Default for MenuTextStyle { /// Defines all possible events that could happen with a menu. #[derive(Clone)] pub enum MenuEvent { - /// Activation event for the menu. When the bool is true it means that the values - /// have already being updated. This is true when the option `quick_completions` is true + /// Activation event for the menu. When the bool is true it means that the + /// values have already being updated. This is true when the option + /// `quick_completions` is true Activate(bool), /// Deactivation event Deactivate, /// Line buffer edit event. When the bool is true it means that the values - /// have already being updated. This is true when the option `quick_completions` is true + /// have already being updated. This is true when the option + /// `quick_completions` is true Edit(bool), /// Selecting next element in the menu NextElement, @@ -87,9 +91,9 @@ pub trait Menu: Send { /// Updates the values presented in the menu /// This function needs to be defined in the trait because when the menu is - /// activated or the `quick_completion` option is true, the len of the values - /// is calculated to know if there is only one value so it can be selected - /// immediately + /// activated or the `quick_completion` option is true, the len of the + /// values is calculated to know if there is only one value so it can be + /// selected immediately fn update_values(&mut self, editor: &mut Editor, completer: &mut dyn Completer); /// The working details of a menu are values that could change based on @@ -104,14 +108,16 @@ pub trait Menu: Send { painter: &Painter, ); - /// Indicates how to replace in the line buffer the selected value from the menu + /// Indicates how to replace in the line buffer the selected value from the + /// menu fn replace_in_buffer(&self, editor: &mut Editor); - /// Calculates the real required lines for the menu considering how many lines - /// wrap the terminal or if entries have multiple lines + /// Calculates the real required lines for the menu considering how many + /// lines wrap the terminal or if entries have multiple lines fn menu_required_lines(&self, terminal_columns: u16) -> u16; - /// Creates the menu representation as a string which will be painted by the painter + /// Creates the menu representation as a string which will be painted by the + /// painter fn menu_string(&self, available_lines: u16, use_ansi_coloring: bool) -> String; /// Minimum rows that should be displayed by the menu diff --git a/src/painting/painter.rs b/src/painting/painter.rs index e6dc79a8..d9f8f7a1 100644 --- a/src/painting/painter.rs +++ b/src/painting/painter.rs @@ -1,23 +1,21 @@ -use crate::{CursorConfig, PromptEditMode, PromptViMode}; - -use { - super::utils::{coerce_crlf, line_width}, - crate::{ - menu::{Menu, ReedlineMenu}, - painting::PromptLines, - Prompt, - }, - crossterm::{ - cursor::{self, MoveTo, RestorePosition, SavePosition}, - style::{Attribute, Print, ResetColor, SetAttribute, SetForegroundColor}, - terminal::{self, Clear, ClearType}, - QueueableCommand, - }, - std::io::{Result, Write}, +use std::io::{Result, Write}; + +use crossterm::{ + cursor::{self, MoveTo, RestorePosition, SavePosition}, + style::{Attribute, Print, ResetColor, SetAttribute, SetForegroundColor}, + terminal::{self, Clear, ClearType}, + QueueableCommand, }; #[cfg(feature = "external_printer")] use {crate::LineBuffer, crossterm::cursor::MoveUp}; +use super::utils::{coerce_crlf, line_width}; +use crate::{ + menu::{Menu, ReedlineMenu}, + painting::PromptLines, + CursorConfig, Prompt, PromptEditMode, PromptViMode, +}; + // Returns a string that skips N number of lines with the next offset of lines // An offset of 0 would return only one line after skipping the required lines fn skip_buffer_lines(string: &str, skip: usize, offset: Option) -> &str { @@ -128,8 +126,8 @@ impl Painter { /// prompt should scroll up and how much space is required to print all the /// lines for the buffer /// - /// Note. The `ScrollUp` operation in `crossterm` deletes lines from the top of - /// the screen. + /// Note. The `ScrollUp` operation in `crossterm` deletes lines from the top + /// of the screen. pub(crate) fn repaint_buffer( &mut self, prompt: &dyn Prompt, @@ -151,7 +149,8 @@ impl Painter { // Marking the painter state as larger buffer to avoid animations self.large_buffer = required_lines >= screen_height; - // Moving the start position of the cursor based on the size of the required lines + // Moving the start position of the cursor based on the size of the required + // lines if self.large_buffer { self.prompt_start_row = 0; } else if required_lines >= remaining_lines { @@ -314,8 +313,9 @@ impl Painter { let remaining_lines = screen_height.saturating_sub(cursor_distance); // Calculating the total lines before the cursor - // The -1 in the total_lines_before is there because the at least one line of the prompt - // indicator is printed in the same line as the first line of the buffer + // The -1 in the total_lines_before is there because the at least one line of + // the prompt indicator is printed in the same line as the first line of + // the buffer let prompt_lines = lines.prompt_lines_with_wrap(screen_width) as usize; let prompt_indicator = match menu { @@ -327,7 +327,8 @@ impl Painter { let before_cursor_lines = lines.before_cursor.lines().count(); let total_lines_before = prompt_lines + prompt_indicator_lines + before_cursor_lines - 1; - // Extra rows represent how many rows are "above" the visible area in the terminal + // Extra rows represent how many rows are "above" the visible area in the + // terminal let extra_rows = (total_lines_before).saturating_sub(screen_height as usize); // print our prompt with color @@ -355,9 +356,9 @@ impl Painter { self.stdout.queue(ResetColor)?; } - // The minimum number of lines from the menu are removed from the buffer if there is no more - // space to print the menu. This will only happen if the cursor is at the last line and - // it is a large buffer + // The minimum number of lines from the menu are removed from the buffer if + // there is no more space to print the menu. This will only happen if + // the cursor is at the last line and it is a large buffer let offset = menu.and_then(|menu| { if cursor_distance >= screen_height.saturating_sub(1) { let rows = lines @@ -383,9 +384,10 @@ impl Painter { self.print_menu(menu, lines, use_ansi_coloring)?; } else { // Selecting lines for the hint - // The -1 subtraction is done because the remaining lines consider the line where the - // cursor is located as a remaining line. That has to be removed to get the correct offset - // for the after-cursor and hint lines + // The -1 subtraction is done because the remaining lines consider the line + // where the cursor is located as a remaining line. That has to be + // removed to get the correct offset for the after-cursor and hint + // lines let offset = remaining_lines.saturating_sub(1) as usize; // Selecting lines after the cursor let after_cursor_skipped = skip_buffer_lines(&lines.after_cursor, 0, Some(offset)); @@ -409,25 +411,27 @@ impl Painter { && height <= prev_terminal_size.1 && width == prev_terminal_size.0 { - // The terminal got smaller in height but the start of the prompt is still visible - // The width didn't change + // The terminal got smaller in height but the start of the prompt is still + // visible The width didn't change return; } // Either: // - The terminal got larger in height - // - Note: if the terminal doesn't have sufficient history, this will leave a trail + // - Note: if the terminal doesn't have sufficient history, this will leave a + // trail of previous prompts currently. + // - Note: if the the prompt contains multiple lines, this will leave a trail + // of previous prompts currently. + // - The terminal got smaller in height and the whole prompt is no longer + // visible + // - Note: if the the prompt contains multiple lines, this will leave a trail // of previous prompts currently. - // - Note: if the the prompt contains multiple lines, this will leave a trail of - // previous prompts currently. - // - The terminal got smaller in height and the whole prompt is no longer visible - // - Note: if the the prompt contains multiple lines, this will leave a trail of - // previous prompts currently. // - The width changed self.prompt_start_row = height.saturating_sub(1); } - /// Writes `line` to the terminal with a following carriage return and newline + /// Writes `line` to the terminal with a following carriage return and + /// newline pub(crate) fn paint_line(&mut self, line: &str) -> Result<()> { self.stdout.queue(Print(line))?.queue(Print("\r\n"))?; @@ -468,8 +472,8 @@ impl Painter { } // The prompt is moved to the end of the buffer after the event was handled - // If the prompt is in the middle of a multiline buffer, then the output to stdout - // could overwrite the buffer writing + // If the prompt is in the middle of a multiline buffer, then the output to + // stdout could overwrite the buffer writing pub(crate) fn move_cursor_to_end(&mut self) -> Result<()> { let final_row = self.prompt_start_row + self.last_required_lines; let scroll = final_row.saturating_sub(self.screen_height() - 1); @@ -537,16 +541,16 @@ impl Painter { /// Queue scroll of `num` lines to `self.stdout`. /// - /// On some platforms and terminals (e.g. windows terminal, alacritty on windows and linux) - /// using special escape sequence '\[eS' (provided by [`ScrollUp`]) does not put lines - /// that go offscreen in scrollback history. This method prints newlines near the edge of screen - /// (which always works) instead. See [here](https://github.com/nushell/nushell/issues/9166) + /// On some platforms and terminals (e.g. windows terminal, alacritty on + /// windows and linux) using special escape sequence '\[eS' + /// (provided by [`ScrollUp`]) does not put lines that go offscreen in + /// scrollback history. This method prints newlines near the edge of screen (which always works) instead. See [here](https://github.com/nushell/nushell/issues/9166) /// for more info on subject. /// /// ## Note - /// This method does not return cursor to the original position and leaves it at the first - /// column of last line. **Be sure to use [`MoveTo`] afterwards if this is not the desired - /// location** + /// This method does not return cursor to the original position and leaves + /// it at the first column of last line. **Be sure to use [`MoveTo`] + /// afterwards if this is not the desired location** fn queue_universal_scroll(&mut self, num: u16) -> Result<()> { // If cursor is not near end of screen printing new will not scroll terminal. // Move it to the last line to ensure that every newline results in scroll @@ -560,9 +564,10 @@ impl Painter { #[cfg(test)] mod tests { - use super::*; use pretty_assertions::assert_eq; + use super::*; + #[test] fn test_skip_lines() { let string = "sentence1\nsentence2\nsentence3\n"; diff --git a/src/painting/prompt_lines.rs b/src/painting/prompt_lines.rs index 30c1ca61..f319b2ef 100644 --- a/src/painting/prompt_lines.rs +++ b/src/painting/prompt_lines.rs @@ -1,10 +1,11 @@ +use std::borrow::Cow; + use super::utils::{coerce_crlf, estimate_required_lines, line_width}; use crate::{ menu::{Menu, ReedlineMenu}, prompt::PromptEditMode, Prompt, PromptHistorySearch, }; -use std::borrow::Cow; /// Aggregate of prompt and input string used by `Painter` pub(crate) struct PromptLines<'prompt> { diff --git a/src/painting/styled_text.rs b/src/painting/styled_text.rs index eeb88071..128ee2e5 100644 --- a/src/painting/styled_text.rs +++ b/src/painting/styled_text.rs @@ -1,10 +1,10 @@ use nu_ansi_term::Style; -use crate::Prompt; - use super::utils::strip_ansi; +use crate::Prompt; -/// A representation of a buffer with styling, used for doing syntax highlighting +/// A representation of a buffer with styling, used for doing syntax +/// highlighting pub struct StyledText { /// The component, styled parts of the text pub buffer: Vec<(Style, String)>, @@ -27,11 +27,11 @@ impl StyledText { self.buffer.push(styled_string); } - /// Render the styled string. We use the insertion point to render around so that - /// we can properly write out the styled string to the screen and find the correct - /// place to put the cursor. This assumes a logic that prints the first part of the - /// string, saves the cursor position, prints the second half, and then restores - /// the cursor position + /// Render the styled string. We use the insertion point to render around so + /// that we can properly write out the styled string to the screen and + /// find the correct place to put the cursor. This assumes a logic that + /// prints the first part of the string, saves the cursor position, + /// prints the second half, and then restores the cursor position /// /// Also inserts the multiline continuation prompt pub fn render_around_insertion_point( diff --git a/src/painting/utils.rs b/src/painting/utils.rs index b0df1228..035ae4a4 100644 --- a/src/painting/utils.rs +++ b/src/painting/utils.rs @@ -1,4 +1,5 @@ use std::borrow::Cow; + use unicode_width::UnicodeWidthStr; /// Ensures input uses CRLF line endings. @@ -72,10 +73,11 @@ pub(crate) fn line_width(line: &str) -> usize { #[cfg(test)] mod test { - use super::*; use pretty_assertions::assert_eq; use rstest::rstest; + use super::*; + #[rstest] #[case("sentence\nsentence", "sentence\r\nsentence")] #[case("sentence\r\nsentence", "sentence\r\nsentence")] diff --git a/src/prompt/base.rs b/src/prompt/base.rs index 01d91658..5d6914d9 100644 --- a/src/prompt/base.rs +++ b/src/prompt/base.rs @@ -1,13 +1,12 @@ -use { - crossterm::style::Color, - serde::{Deserialize, Serialize}, - std::{ - borrow::Cow, - fmt::{Display, Formatter}, - }, - strum_macros::EnumIter, +use std::{ + borrow::Cow, + fmt::{Display, Formatter}, }; +use crossterm::style::Color; +use serde::{Deserialize, Serialize}; +use strum_macros::EnumIter; + /// The default color for the prompt, indicator, and right prompt pub static DEFAULT_PROMPT_COLOR: Color = Color::Green; pub static DEFAULT_PROMPT_MULTILINE_COLOR: nu_ansi_term::Color = nu_ansi_term::Color::LightBlue; @@ -88,7 +87,8 @@ pub trait Prompt: Send { fn render_prompt_left(&self) -> Cow; /// Provide content of the right full prompt fn render_prompt_right(&self) -> Cow; - /// 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; /// Indicator to show before explicit new lines fn render_prompt_multiline_indicator(&self) -> Cow; diff --git a/src/prompt/default.rs b/src/prompt/default.rs index f56ae903..f21c46ca 100644 --- a/src/prompt/default.rs +++ b/src/prompt/default.rs @@ -1,9 +1,8 @@ -use crate::{Prompt, PromptEditMode, PromptHistorySearch, PromptHistorySearchStatus, PromptViMode}; +use std::{borrow::Cow, env}; + +use chrono::Local; -use { - chrono::Local, - std::{borrow::Cow, env}, -}; +use crate::{Prompt, PromptEditMode, PromptHistorySearch, PromptHistorySearchStatus, PromptViMode}; /// The default prompt indicator pub static DEFAULT_PROMPT_INDICATOR: &str = "〉"; @@ -22,7 +21,8 @@ pub struct DefaultPrompt { pub right_prompt: DefaultPromptSegment, } -/// A struct to control the appearance of the left or right prompt in a [`DefaultPrompt`] +/// A struct to control the appearance of the left or right prompt in a +/// [`DefaultPrompt`] #[derive(Clone)] pub enum DefaultPromptSegment { /// A basic user-defined prompt (i.e. just text) @@ -82,8 +82,8 @@ impl Prompt for DefaultPrompt { PromptHistorySearchStatus::Passing => "", PromptHistorySearchStatus::Failing => "failing ", }; - // NOTE: magic strings, given there is logic on how these compose I am not sure if it - // is worth extracting in to static constant + // NOTE: magic strings, given there is logic on how these compose I am not sure + // if it is worth extracting in to static constant Cow::Owned(format!( "({}reverse-search: {}) ", prefix, history_search.term @@ -101,9 +101,10 @@ impl Default for DefaultPrompt { } impl DefaultPrompt { - /// Constructor for the default prompt, which takes a configurable left and right prompt. - /// For less customization, use [`DefaultPrompt::default`]. - /// For more fine-tuned configuration, implement the [`Prompt`] trait. + /// Constructor for the default prompt, which takes a configurable left and + /// right prompt. For less customization, use + /// [`DefaultPrompt::default`]. For more fine-tuned configuration, + /// implement the [`Prompt`] trait. pub const fn new( left_prompt: DefaultPromptSegment, right_prompt: DefaultPromptSegment, diff --git a/src/prompt/mod.rs b/src/prompt/mod.rs index 83d2e3b5..6159853f 100644 --- a/src/prompt/mod.rs +++ b/src/prompt/mod.rs @@ -4,5 +4,4 @@ mod default; pub use base::{ Prompt, PromptEditMode, PromptHistorySearch, PromptHistorySearchStatus, PromptViMode, }; - pub use default::{DefaultPrompt, DefaultPromptSegment}; diff --git a/src/result.rs b/src/result.rs index 3651a5ae..5daf6f78 100644 --- a/src/result.rs +++ b/src/result.rs @@ -1,4 +1,5 @@ use std::fmt::Display; + use thiserror::Error; /// non-public (for now) @@ -30,5 +31,6 @@ impl Display for ReedlineError { } impl std::error::Error for ReedlineError {} -/// Standard [`std::result::Result`], with [`ReedlineError`] as the error variant +/// Standard [`std::result::Result`], with [`ReedlineError`] as the error +/// variant pub type Result = std::result::Result; diff --git a/src/terminal_extensions/bracketed_paste.rs b/src/terminal_extensions/bracketed_paste.rs index b6601cd8..7b3517d7 100644 --- a/src/terminal_extensions/bracketed_paste.rs +++ b/src/terminal_extensions/bracketed_paste.rs @@ -13,12 +13,14 @@ impl BracketedPasteGuard { pub fn set(&mut self, enable: bool) { self.enabled = enable; } + pub fn enter(&mut self) { if self.enabled && !self.active { let _ = execute!(std::io::stdout(), event::EnableBracketedPaste); self.active = true; } } + pub fn exit(&mut self) { if self.active { let _ = execute!(std::io::stdout(), event::DisableBracketedPaste); diff --git a/src/terminal_extensions/kitty.rs b/src/terminal_extensions/kitty.rs index e6bdd637..6354e50f 100644 --- a/src/terminal_extensions/kitty.rs +++ b/src/terminal_extensions/kitty.rs @@ -1,6 +1,7 @@ use crossterm::{event, execute}; -/// Helper managing proper setup and teardown of the kitty keyboard enhancement protocol +/// Helper managing proper setup and teardown of the kitty keyboard enhancement +/// protocol /// /// Note that, currently, only the following support this protocol: /// * [kitty terminal](https://sw.kovidgoyal.net/kitty/) @@ -22,6 +23,7 @@ impl KittyProtocolGuard { pub fn set(&mut self, enable: bool) { self.enabled = enable && super::kitty_protocol_available(); } + pub fn enter(&mut self) { if self.enabled && !self.active { let _ = execute!( @@ -34,6 +36,7 @@ impl KittyProtocolGuard { self.active = true; } } + pub fn exit(&mut self) { if self.active { let _ = execute!(std::io::stdout(), event::PopKeyboardEnhancementFlags); diff --git a/src/utils/query.rs b/src/utils/query.rs index 2b762e9a..59e98276 100644 --- a/src/utils/query.rs +++ b/src/utils/query.rs @@ -1,10 +1,12 @@ +use std::fmt::{Display, Formatter}; + +use crossterm::event::KeyCode; +use strum::IntoEnumIterator; + use crate::{ default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings, EditCommand, Keybindings, PromptEditMode, ReedlineEvent, }; -use crossterm::event::KeyCode; -use std::fmt::{Display, Formatter}; -use strum::IntoEnumIterator; struct ReedLineCrossTermKeyCode(crossterm::event::KeyCode); impl ReedLineCrossTermKeyCode { @@ -111,9 +113,9 @@ pub fn get_reedline_edit_commands() -> Vec { EditCommand::iter().map(|edit| edit.to_string()).collect() } -/// Get the default keybindings and return a `Vec<(String, String, String, String)>` -/// where String 1 is `mode`, String 2 is `key_modifiers`, String 3 is `key_code`, and -/// Sting 4 is `event` +/// Get the default keybindings and return a `Vec<(String, String, String, +/// String)>` where String 1 is `mode`, String 2 is `key_modifiers`, String 3 is +/// `key_code`, and Sting 4 is `event` pub fn get_reedline_default_keybindings() -> Vec<(String, String, String, String)> { let options = vec![ ("emacs", default_emacs_keybindings()), diff --git a/src/utils/text_manipulation.rs b/src/utils/text_manipulation.rs index 0428180b..295da23a 100644 --- a/src/utils/text_manipulation.rs +++ b/src/utils/text_manipulation.rs @@ -12,9 +12,10 @@ pub fn remove_last_grapheme(string: &str) -> &str { #[cfg(test)] mod test { - use super::*; use pretty_assertions::assert_eq; + use super::*; + #[test] fn remove_last_char_works_with_empty_string() { let string = ""; diff --git a/src/validator/default.rs b/src/validator/default.rs index f53b186e..3c28f89c 100644 --- a/src/validator/default.rs +++ b/src/validator/default.rs @@ -37,9 +37,10 @@ fn incomplete_brackets(line: &str) -> bool { #[cfg(test)] mod test { - use super::*; use rstest::rstest; + use super::*; + #[rstest] #[case("(([[]]))", false)] #[case("(([[]]", true)] diff --git a/src/validator/mod.rs b/src/validator/mod.rs index 588e3794..c2f1a696 100644 --- a/src/validator/mod.rs +++ b/src/validator/mod.rs @@ -1,10 +1,11 @@ mod default; pub use default::DefaultValidator; -/// The syntax validation trait. Implementers of this trait will check to see if the current input -/// is incomplete and spans multiple lines +/// The syntax validation trait. Implementers of this trait will check to see if +/// the current input is incomplete and spans multiple lines pub trait Validator: Send { - /// The action that will handle the current buffer as a line and return the corresponding validation + /// The action that will handle the current buffer as a line and return the + /// corresponding validation fn validate(&self, line: &str) -> ValidationResult; } From 967b15d2d5637c807f24c777f9a7cfcd989922d7 Mon Sep 17 00:00:00 2001 From: Admin Date: Sat, 2 Dec 2023 21:00:57 +0100 Subject: [PATCH 3/7] Uncommented update_session for History trait --- src/history/base.rs | 6 ++---- src/history/file_backed.rs | 6 +++--- src/history/sqlite_backed.rs | 8 ++++---- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/history/base.rs b/src/history/base.rs index e4c5630b..e8875352 100644 --- a/src/history/base.rs +++ b/src/history/base.rs @@ -226,10 +226,8 @@ pub trait History: Send { fn sync(&mut self) -> std::io::Result<()>; /// get the history session id fn session(&self) -> Option; - // Dev comment: This has been implemented due to the `history session --set` - // command which couldn't get done so this is commented /// updates the - // history session id fn update_session(&mut self, history_session: - // Option); + /// updates the history session id + fn update_session(&mut self, history_session: Option); } #[cfg(test)] diff --git a/src/history/file_backed.rs b/src/history/file_backed.rs index b6359960..a803a937 100644 --- a/src/history/file_backed.rs +++ b/src/history/file_backed.rs @@ -283,9 +283,9 @@ impl History for FileBackedHistory { self.session } - // fn update_session(&mut self, history_session: Option) { - // self.session = history_session - // } + fn update_session(&mut self, history_session: Option) { + self.session = history_session + } } impl FileBackedHistory { diff --git a/src/history/sqlite_backed.rs b/src/history/sqlite_backed.rs index 0a519769..2095a351 100644 --- a/src/history/sqlite_backed.rs +++ b/src/history/sqlite_backed.rs @@ -180,10 +180,10 @@ impl History for SqliteBackedHistory { self.session } - // fn update_session(&mut self, history_session: Option) { - // self.session = history_session; - // self.session_timestamp = history_session.map(|hs| - // chrono::Utc.timestamp_nanos(hs.0)) } + fn update_session(&mut self, history_session: Option) { + self.session = history_session; + self.session_timestamp = history_session.map(|hs| chrono::Utc.timestamp_nanos(hs.0)) + } } fn map_sqlite_err(err: rusqlite::Error) -> ReedlineError { // TODO: better error mapping From ac7e2911cb905eaee336f26138ce39e1df43487c Mon Sep 17 00:00:00 2001 From: Admin Date: Sat, 2 Dec 2023 21:02:15 +0100 Subject: [PATCH 4/7] Made HistorySessionId.0 public for nushell access --- src/history/item.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/history/item.rs b/src/history/item.rs index e6b47fa2..3eee1380 100644 --- a/src/history/item.rs +++ b/src/history/item.rs @@ -24,7 +24,7 @@ impl Display for HistoryItemId { /// Unique ID for the session in which reedline was run to disambiguate /// different sessions #[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct HistorySessionId(pub(crate) i64); +pub struct HistorySessionId(pub i64); impl HistorySessionId { pub(crate) const fn new(i: i64) -> HistorySessionId { HistorySessionId(i) From 8badeadcee3790f2283a33b177fd6ad845a632d2 Mon Sep 17 00:00:00 2001 From: Admin Date: Mon, 4 Dec 2023 22:58:07 +0100 Subject: [PATCH 5/7] Correct cargo fmt + cargo clippy --- examples/demo.rs | 7 ++----- src/edit_mode/vi/command.rs | 2 +- src/history/base.rs | 3 --- src/history/file_backed.rs | 4 ---- src/history/item.rs | 2 +- src/history/sqlite_backed.rs | 16 ++++++---------- 6 files changed, 10 insertions(+), 24 deletions(-) diff --git a/examples/demo.rs b/examples/demo.rs index 937db0dc..347fe3d1 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -34,11 +34,8 @@ fn main() -> std::io::Result<()> { #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] let history = Box::new( - reedline::SqliteBackedHistory::with_file( - "history.sqlite3".into(), - history_session_id, - ) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?, + reedline::SqliteBackedHistory::with_file("history.sqlite3".into(), history_session_id) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?, ); #[cfg(not(any(feature = "sqlite", feature = "sqlite-dynlib")))] let history = Box::new(FileBackedHistory::with_file(50, "history.txt".into())?); diff --git a/src/edit_mode/vi/command.rs b/src/edit_mode/vi/command.rs index e86f9f31..598867ba 100644 --- a/src/edit_mode/vi/command.rs +++ b/src/edit_mode/vi/command.rs @@ -122,7 +122,7 @@ impl Command { matches!(self, Command::Delete | Command::Change) } - pub fn to_reedline(&self, vi_state: &mut Vi) -> Vec { + pub fn to_reedline(&self, vi_state: &Vi) -> Vec { match self { Self::EnterViInsert => vec![ReedlineOption::Event(ReedlineEvent::Repaint)], Self::EnterViAppend => vec![ReedlineOption::Edit(EditCommand::MoveRight)], diff --git a/src/history/base.rs b/src/history/base.rs index 9216e34f..0f1b9a41 100644 --- a/src/history/base.rs +++ b/src/history/base.rs @@ -218,9 +218,6 @@ pub trait History: Send { fn sync(&mut self) -> std::io::Result<()>; /// get the history session id fn session(&self) -> Option; - // Dev comment: This has been implemented due to the `history session --set` command which couldn't get done so this is commented - // /// updates the history session id - // fn update_session(&mut self, history_session: Option); } #[cfg(test)] diff --git a/src/history/file_backed.rs b/src/history/file_backed.rs index 1108027d..28b437f7 100644 --- a/src/history/file_backed.rs +++ b/src/history/file_backed.rs @@ -278,10 +278,6 @@ impl History for FileBackedHistory { fn session(&self) -> Option { self.session } - - // fn update_session(&mut self, history_session: Option) { - // self.session = history_session - // } } impl FileBackedHistory { diff --git a/src/history/item.rs b/src/history/item.rs index ec477bbc..a0537e43 100644 --- a/src/history/item.rs +++ b/src/history/item.rs @@ -21,7 +21,7 @@ impl Display for HistoryItemId { /// Unique ID for the session in which reedline was run to disambiguate different sessions #[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct HistorySessionId(pub(crate) i64); +pub struct HistorySessionId(pub i64); impl HistorySessionId { pub(crate) const fn new(i: i64) -> HistorySessionId { HistorySessionId(i) diff --git a/src/history/sqlite_backed.rs b/src/history/sqlite_backed.rs index 350a87ba..1ecf6a71 100644 --- a/src/history/sqlite_backed.rs +++ b/src/history/sqlite_backed.rs @@ -176,11 +176,6 @@ impl History for SqliteBackedHistory { fn session(&self) -> Option { self.session } - - // fn update_session(&mut self, history_session: Option) { - // self.session = history_session; - // self.session_timestamp = history_session.map(|hs| chrono::Utc.timestamp_nanos(hs.0)) - // } } fn map_sqlite_err(err: rusqlite::Error) -> ReedlineError { // TODO: better error mapping @@ -197,17 +192,18 @@ impl SqliteBackedHistory { /// /// **Side effects:** creates all nested directories to the file /// - pub fn with_file( - file: PathBuf, - session: Option, - ) -> Result { + pub fn with_file(file: PathBuf, session: Option) -> Result { if let Some(base_dir) = file.parent() { std::fs::create_dir_all(base_dir).map_err(|e| { ReedlineError(ReedlineErrorVariants::HistoryDatabaseError(format!("{e}"))) })?; } let db = Connection::open(&file).map_err(map_sqlite_err)?; - Self::from_connection(db, session, session.map(|s| chrono::Utc.timestamp_nanos(s.0))) + Self::from_connection( + db, + session, + session.map(|s| chrono::Utc.timestamp_nanos(s.0)), + ) } /// Creates a new history in memory pub fn in_memory() -> Result { From 7443f6c8df9476dc3fe214f1d00692455ec52820 Mon Sep 17 00:00:00 2001 From: Admin Date: Mon, 4 Dec 2023 23:12:24 +0100 Subject: [PATCH 6/7] Reverted weird cargo fmt + clippy commit. Revert "cargo fmt + clippy fix (besides cargo fmt internal errors that don't reside from my changes)" This reverts commit a8c06820dde952e8bfbfe29f15bd56d6f128c221. --- examples/basic.rs | 6 +- examples/completions.rs | 7 +- examples/custom_prompt.rs | 3 +- examples/demo.rs | 43 ++-- examples/event_listener.rs | 26 +- examples/event_listener_kitty_proto.rs | 29 +-- examples/external_printer.rs | 10 +- examples/highlighter.rs | 3 +- examples/hinter.rs | 3 +- examples/history.rs | 3 +- examples/transient_prompt.rs | 3 +- examples/validator.rs | 18 +- src/completion/base.rs | 12 +- src/completion/default.rs | 209 ++++----------- src/core_editor/clip_buffer.rs | 18 +- src/core_editor/edit_stack.rs | 10 +- src/core_editor/editor.rs | 20 +- src/core_editor/line_buffer.rs | 29 +-- src/edit_mode/base.rs | 3 +- src/edit_mode/cursors.rs | 3 +- src/edit_mode/emacs.rs | 9 +- src/edit_mode/keybindings.rs | 15 +- src/edit_mode/vi/command.rs | 10 +- src/edit_mode/vi/mod.rs | 4 +- src/edit_mode/vi/motion.rs | 3 +- src/edit_mode/vi/parser.rs | 12 +- src/engine.rs | 283 ++++++++++----------- src/enums.rs | 31 +-- src/external_printer.rs | 6 +- src/highlighter/example.rs | 7 +- src/highlighter/mod.rs | 12 +- src/highlighter/simple_match.rs | 4 +- src/hinter/cwd_aware.rs | 6 +- src/hinter/default.rs | 6 +- src/hinter/mod.rs | 9 +- src/history/base.rs | 22 +- src/history/cursor.rs | 63 ++--- src/history/file_backed.rs | 38 ++- src/history/item.rs | 15 +- src/history/mod.rs | 8 +- src/history/sqlite_backed.rs | 27 +- src/lib.rs | 95 ++++--- src/menu/columnar_menu.rs | 16 +- src/menu/list_menu.rs | 34 ++- src/menu/menu_functions.rs | 1 + src/menu/mod.rs | 32 +-- src/painting/painter.rs | 99 ++++--- src/painting/prompt_lines.rs | 3 +- src/painting/styled_text.rs | 16 +- src/painting/utils.rs | 4 +- src/prompt/base.rs | 18 +- src/prompt/default.rs | 23 +- src/prompt/mod.rs | 1 + src/result.rs | 4 +- src/terminal_extensions/bracketed_paste.rs | 2 - src/terminal_extensions/kitty.rs | 5 +- src/utils/query.rs | 14 +- src/utils/text_manipulation.rs | 3 +- src/validator/default.rs | 3 +- src/validator/mod.rs | 7 +- 60 files changed, 585 insertions(+), 843 deletions(-) diff --git a/examples/basic.rs b/examples/basic.rs index 76cd2aa0..2c04b75b 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -3,13 +3,11 @@ // // You can browse the local (non persistent) history using Up/Down or Ctrl n/p. -use std::io; - use reedline::{DefaultPrompt, Reedline, Signal}; +use std::io; fn main() -> io::Result<()> { - // Create a new Reedline engine with a local History that is not synchronized to - // a file. + // Create a new Reedline engine with a local History that is not synchronized to a file. let mut line_editor = Reedline::create(); let prompt = DefaultPrompt::default(); diff --git a/examples/completions.rs b/examples/completions.rs index cba4308a..95f4975e 100644 --- a/examples/completions.rs +++ b/examples/completions.rs @@ -1,15 +1,14 @@ // Create a reedline object with tab completions support // cargo run --example completions // -// "t" [Tab] will allow you to select the completions "test" and "this is the -// reedline crate" [Enter] to select the chosen alternative - -use std::io; +// "t" [Tab] will allow you to select the completions "test" and "this is the reedline crate" +// [Enter] to select the chosen alternative use reedline::{ default_emacs_keybindings, ColumnarMenu, DefaultCompleter, DefaultPrompt, Emacs, KeyCode, KeyModifiers, Keybindings, Reedline, ReedlineEvent, ReedlineMenu, Signal, }; +use std::io; fn add_menu_keybindings(keybindings: &mut Keybindings) { keybindings.add_binding( diff --git a/examples/custom_prompt.rs b/examples/custom_prompt.rs index c748a098..fc9b1034 100644 --- a/examples/custom_prompt.rs +++ b/examples/custom_prompt.rs @@ -3,11 +3,10 @@ // // Pressing keys will increase the right prompt value -use std::{borrow::Cow, cell::Cell, io}; - use reedline::{ Prompt, PromptEditMode, PromptHistorySearch, PromptHistorySearchStatus, Reedline, Signal, }; +use std::{borrow::Cow, cell::Cell, io}; // For custom prompt, implement the Prompt trait // diff --git a/examples/demo.rs b/examples/demo.rs index fe145492..937db0dc 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -1,27 +1,30 @@ -use std::{env::temp_dir, process::Command}; - -use crossterm::{ - cursor::SetCursorStyle, - event::{KeyCode, KeyModifiers}, +use std::env::temp_dir; +use std::process::Command; +use { + crossterm::{ + cursor::SetCursorStyle, + event::{KeyCode, KeyModifiers}, + }, + nu_ansi_term::{Color, Style}, + reedline::{ + default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings, + ColumnarMenu, DefaultCompleter, DefaultHinter, DefaultPrompt, DefaultValidator, + EditCommand, EditMode, Emacs, ExampleHighlighter, Keybindings, ListMenu, Reedline, + ReedlineEvent, ReedlineMenu, Signal, Vi, + }, }; -use nu_ansi_term::{Color, Style}; + +use reedline::CursorConfig; #[cfg(not(any(feature = "sqlite", feature = "sqlite-dynlib")))] use reedline::FileBackedHistory; -use reedline::{ - default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings, - ColumnarMenu, CursorConfig, DefaultCompleter, DefaultHinter, DefaultPrompt, DefaultValidator, - EditCommand, EditMode, Emacs, ExampleHighlighter, Keybindings, ListMenu, Reedline, - ReedlineEvent, ReedlineMenu, Signal, Vi, -}; fn main() -> std::io::Result<()> { println!("Ctrl-D to quit"); // quick command like parameter handling let vi_mode = matches!(std::env::args().nth(1), Some(x) if x == "--vi"); - // Setting history_per_session to true will allow the history to be isolated to - // the current session Setting history_per_session to false will allow the - // history to be shared across all sessions + // Setting history_per_session to true will allow the history to be isolated to the current session + // Setting history_per_session to false will allow the history to be shared across all sessions let history_per_session = true; let mut history_session_id = if history_per_session { Reedline::create_history_session_id() @@ -31,8 +34,11 @@ fn main() -> std::io::Result<()> { #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] let history = Box::new( - reedline::SqliteBackedHistory::with_file("history.sqlite3".into(), history_session_id) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?, + reedline::SqliteBackedHistory::with_file( + "history.sqlite3".into(), + history_session_id, + ) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?, ); #[cfg(not(any(feature = "sqlite", feature = "sqlite-dynlib")))] let history = Box::new(FileBackedHistory::with_file(50, "history.txt".into())?); @@ -173,8 +179,7 @@ fn main() -> std::io::Result<()> { line_editor.print_history_session_id()?; continue; } - // Toggle between the full history and the history pertinent to the current - // session + // Toggle between the full history and the history pertinent to the current session if buffer.trim() == "toggle history_session" { let hist_session_id = if history_session_id.is_none() { // If we never created a history session ID, create one now diff --git a/examples/event_listener.rs b/examples/event_listener.rs index f335d170..6c7e9cc7 100644 --- a/examples/event_listener.rs +++ b/examples/event_listener.rs @@ -1,11 +1,12 @@ -use std::{ - io::{stdout, Write}, - time::Duration, -}; - -use crossterm::{ - event::{poll, Event, KeyCode, KeyEvent}, - terminal, +use { + crossterm::{ + event::{poll, Event, KeyCode, KeyEvent}, + terminal, + }, + std::{ + io::{stdout, Write}, + time::Duration, + }, }; fn main() -> std::io::Result<()> { @@ -15,8 +16,7 @@ fn main() -> std::io::Result<()> { Ok(()) } -/// **For debugging purposes only:** Track the terminal events observed by -/// [`Reedline`] and print them. +/// **For debugging purposes only:** Track the terminal events observed by [`Reedline`] and print them. pub fn print_events() -> std::io::Result<()> { stdout().flush()?; terminal::enable_raw_mode()?; @@ -48,8 +48,7 @@ fn print_events_helper() -> std::io::Result<()> { match code { KeyCode::Char(c) => { println!( - "Char: {} code: {:#08x}; Modifier {:?}; Flags {:#08b}; Kind {kind:?}; \ - state {state:?}\r", + "Char: {} code: {:#08x}; Modifier {:?}; Flags {:#08b}; Kind {kind:?}; state {state:?}\r", c, u32::from(c), modifiers, @@ -58,8 +57,7 @@ fn print_events_helper() -> std::io::Result<()> { } _ => { println!( - "Keycode: {code:?}; Modifier {modifiers:?}; Flags {modifiers:#08b}; \ - Kind {kind:?}; state {state:?}\r" + "Keycode: {code:?}; Modifier {modifiers:?}; Flags {modifiers:#08b}; Kind {kind:?}; state {state:?}\r" ); } } diff --git a/examples/event_listener_kitty_proto.rs b/examples/event_listener_kitty_proto.rs index e557680e..cbace1ac 100644 --- a/examples/event_listener_kitty_proto.rs +++ b/examples/event_listener_kitty_proto.rs @@ -1,14 +1,16 @@ -use std::{ - io::{stdout, Result, Write}, - time::Duration, +use crossterm::event::{ + KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags, }; - -use crossterm::{ - event::{ - poll, Event, KeyCode, KeyEvent, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, - PushKeyboardEnhancementFlags, +use crossterm::execute; +use { + crossterm::{ + event::{poll, Event, KeyCode, KeyEvent}, + terminal, + }, + std::{ + io::{stdout, Result, Write}, + time::Duration, }, - execute, terminal, }; fn main() -> Result<()> { @@ -18,8 +20,7 @@ fn main() -> Result<()> { Ok(()) } -/// **For debugging purposes only:** Track the terminal events observed by -/// [`Reedline`] and print them. +/// **For debugging purposes only:** Track the terminal events observed by [`Reedline`] and print them. pub fn print_events() -> Result<()> { stdout().flush()?; terminal::enable_raw_mode()?; @@ -72,8 +73,7 @@ fn print_events_helper() -> Result<()> { match code { KeyCode::Char(c) => { println!( - "Char: {} code: {:#08x}; Modifier {:?}; Flags {:#08b}; Kind {kind:?}; \ - state {state:?}\r", + "Char: {} code: {:#08x}; Modifier {:?}; Flags {:#08b}; Kind {kind:?}; state {state:?}\r", c, u32::from(c), modifiers, @@ -82,8 +82,7 @@ fn print_events_helper() -> Result<()> { } _ => { println!( - "Keycode: {code:?}; Modifier {modifiers:?}; Flags {modifiers:#08b}; \ - Kind {kind:?}; state {state:?}\r" + "Keycode: {code:?}; Modifier {modifiers:?}; Flags {modifiers:#08b}; Kind {kind:?}; state {state:?}\r" ); } } diff --git a/examples/external_printer.rs b/examples/external_printer.rs index 2f311a35..633a1238 100644 --- a/examples/external_printer.rs +++ b/examples/external_printer.rs @@ -2,9 +2,13 @@ // to run: // cargo run --example external_printer --features=external_printer -use std::{thread, thread::sleep, time::Duration}; - -use reedline::{DefaultPrompt, ExternalPrinter, Reedline, Signal}; +use { + reedline::ExternalPrinter, + reedline::{DefaultPrompt, Reedline, Signal}, + std::thread, + std::thread::sleep, + std::time::Duration, +}; fn main() { let printer = ExternalPrinter::default(); diff --git a/examples/highlighter.rs b/examples/highlighter.rs index ef20bbb4..3edeb974 100644 --- a/examples/highlighter.rs +++ b/examples/highlighter.rs @@ -2,9 +2,8 @@ // cargo run --example highlighter // // unmatched input is marked red, matched input is marked green -use std::io; - use reedline::{DefaultPrompt, ExampleHighlighter, Reedline, Signal}; +use std::io; fn main() -> io::Result<()> { let commands = vec![ diff --git a/examples/hinter.rs b/examples/hinter.rs index 5271582a..9ed4ba2d 100644 --- a/examples/hinter.rs +++ b/examples/hinter.rs @@ -6,10 +6,9 @@ // pressing "a" hints to abc. // Up/Down or Ctrl p/n, to select next/previous match -use std::io; - use nu_ansi_term::{Color, Style}; use reedline::{DefaultHinter, DefaultPrompt, Reedline, Signal}; +use std::io; fn main() -> io::Result<()> { let mut line_editor = Reedline::create().with_hinter(Box::new( diff --git a/examples/history.rs b/examples/history.rs index 1065b33f..c709bce9 100644 --- a/examples/history.rs +++ b/examples/history.rs @@ -6,9 +6,8 @@ // // Browse history by Up/Down arrows or Ctrl-n/p -use std::io; - use reedline::{DefaultPrompt, FileBackedHistory, Reedline, Signal}; +use std::io; fn main() -> io::Result<()> { let history = Box::new( diff --git a/examples/transient_prompt.rs b/examples/transient_prompt.rs index e3ecbc0d..f971955b 100644 --- a/examples/transient_prompt.rs +++ b/examples/transient_prompt.rs @@ -3,8 +3,6 @@ // // Prompts for previous lines will be replaced with a shorter prompt -use std::{borrow::Cow, io}; - use nu_ansi_term::{Color, Style}; #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] use reedline::SqliteBackedHistory; @@ -14,6 +12,7 @@ use reedline::{ PromptHistorySearch, PromptHistorySearchStatus, Reedline, ReedlineEvent, ReedlineMenu, Signal, ValidationResult, Validator, }; +use std::{borrow::Cow, io}; // For custom prompt, implement the Prompt trait // diff --git a/examples/validator.rs b/examples/validator.rs index 7b72f81b..2e7ca4f9 100644 --- a/examples/validator.rs +++ b/examples/validator.rs @@ -1,13 +1,11 @@ -// Create a reedline object with a custom validator to break the line on -// unfinished input. cargo run --example validator +// Create a reedline object with a custom validator to break the line on unfinished input. +// cargo run --example validator // -// Input "complete" followed by [Enter], will accept the input line -// (Signal::Succeed will be called) Pressing [Enter] will in other cases give -// you a multi-line prompt. - -use std::io; +// Input "complete" followed by [Enter], will accept the input line (Signal::Succeed will be called) +// Pressing [Enter] will in other cases give you a multi-line prompt. use reedline::{DefaultPrompt, Reedline, Signal, ValidationResult, Validator}; +use std::io; struct CustomValidator; @@ -23,11 +21,7 @@ impl Validator for CustomValidator { } fn main() -> io::Result<()> { - println!( - "Input \"complete\" followed by [Enter], will accept the input line (Signal::Succeed will \ - be called)\nPressing [Enter] will in other cases give you a multi-line prompt.\nAbort \ - with Ctrl-C or Ctrl-D" - ); + println!("Input \"complete\" followed by [Enter], will accept the input line (Signal::Succeed will be called)\nPressing [Enter] will in other cases give you a multi-line prompt.\nAbort with Ctrl-C or Ctrl-D"); let mut line_editor = Reedline::create().with_validator(Box::new(CustomValidator)); let prompt = DefaultPrompt::default(); diff --git a/src/completion/base.rs b/src/completion/base.rs index 9f5a3dcf..3de63c8d 100644 --- a/src/completion/base.rs +++ b/src/completion/base.rs @@ -24,17 +24,15 @@ impl Span { } } -/// A trait that defines how to convert a line and position to a list of -/// potential completions in that position. +/// A trait that defines how to convert a line and position to a list of potential completions in that position. pub trait Completer: Send { - /// the action that will take the line and position and convert it to a - /// vector of completions, which include the span to replace and the - /// contents of that replacement + /// the action that will take the line and position and convert it to a vector of completions, which include the + /// span to replace and the contents of that replacement fn complete(&mut self, line: &str, pos: usize) -> Vec; /// action that will return a partial section of available completions - /// this command comes handy when trying to avoid to pull all the data at - /// once from the completer + /// this command comes handy when trying to avoid to pull all the data at once + /// from the completer fn partial_complete( &mut self, line: &str, diff --git a/src/completion/default.rs b/src/completion/default.rs index 69044aa3..11062d7b 100644 --- a/src/completion/default.rs +++ b/src/completion/default.rs @@ -1,11 +1,10 @@ +use crate::{Completer, Span, Suggestion}; use std::{ collections::{BTreeMap, BTreeSet}, str::Chars, sync::Arc, }; -use crate::{Completer, Span, Suggestion}; - /// A default completer that can detect keywords /// /// # Example @@ -14,10 +13,10 @@ use crate::{Completer, Span, Suggestion}; /// use reedline::{DefaultCompleter, Reedline}; /// /// let commands = vec![ -/// "test".into(), -/// "hello world".into(), -/// "hello world reedline".into(), -/// "this is the reedline crate".into(), +/// "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)); /// @@ -39,8 +38,8 @@ impl Default for DefaultCompleter { } } impl Completer for DefaultCompleter { - /// Returns a vector of completions and the position in which they must be - /// replaced; based on the provided input. + /// Returns a vector of completions and the position in which they must be replaced; + /// based on the provided input. /// /// # Arguments /// @@ -49,68 +48,25 @@ impl Completer for DefaultCompleter { /// /// # Example /// ``` - /// use reedline::{Completer, DefaultCompleter, Span, Suggestion}; + /// use reedline::{DefaultCompleter,Completer,Span,Suggestion}; /// /// let mut completions = DefaultCompleter::default(); - /// completions.insert( - /// vec!["batman", "robin", "batmobile", "batcave", "robber"] - /// .iter() - /// .map(|s| s.to_string()) - /// .collect(), - /// ); + /// completions.insert(vec!["batman","robin","batmobile","batcave","robber"].iter().map(|s| s.to_string()).collect()); /// assert_eq!( - /// completions.complete("bat", 3), + /// completions.complete("bat",3), /// vec![ - /// Suggestion { - /// value: "batcave".into(), - /// description: None, - /// extra: None, - /// span: Span { start: 0, end: 3 }, - /// append_whitespace: false - /// }, - /// Suggestion { - /// value: "batman".into(), - /// description: None, - /// extra: None, - /// span: Span { start: 0, end: 3 }, - /// append_whitespace: false - /// }, - /// Suggestion { - /// value: "batmobile".into(), - /// description: None, - /// extra: None, - /// span: Span { start: 0, end: 3 }, - /// append_whitespace: false - /// }, - /// ] - /// ); + /// Suggestion {value: "batcave".into(), description: None, extra: None, span: Span { start: 0, end: 3 }, append_whitespace: false}, + /// Suggestion {value: "batman".into(), description: None, extra: None, span: Span { start: 0, end: 3 }, append_whitespace: false}, + /// Suggestion {value: "batmobile".into(), description: None, extra: None, span: Span { start: 0, end: 3 }, append_whitespace: false}, + /// ]); /// /// assert_eq!( - /// completions.complete("to the bat", 10), + /// completions.complete("to the bat",10), /// vec![ - /// Suggestion { - /// value: "batcave".into(), - /// description: None, - /// extra: None, - /// span: Span { start: 7, end: 10 }, - /// append_whitespace: false - /// }, - /// Suggestion { - /// value: "batman".into(), - /// description: None, - /// extra: None, - /// span: Span { start: 7, end: 10 }, - /// append_whitespace: false - /// }, - /// Suggestion { - /// value: "batmobile".into(), - /// description: None, - /// extra: None, - /// span: Span { start: 7, end: 10 }, - /// append_whitespace: false - /// }, - /// ] - /// ); + /// Suggestion {value: "batcave".into(), description: None, extra: None, span: Span { start: 7, end: 10 }, append_whitespace: false}, + /// Suggestion {value: "batman".into(), description: None, extra: None, span: Span { start: 7, end: 10 }, append_whitespace: false}, + /// Suggestion {value: "batmobile".into(), description: None, extra: None, span: Span { start: 7, end: 10 }, append_whitespace: false}, + /// ]); /// ``` fn complete(&mut self, line: &str, pos: usize) -> Vec { let mut span_line_whitespaces = 0; @@ -160,16 +116,14 @@ impl Completer for DefaultCompleter { } } impl DefaultCompleter { - /// Construct the default completer with a list of commands/keywords to - /// highlight + /// Construct the default completer with a list of commands/keywords to highlight pub fn new(external_commands: Vec) -> Self { let mut dc = DefaultCompleter::default(); dc.insert(external_commands); dc } - /// Construct the default completer with a list of commands/keywords to - /// highlight, given a minimum word length + /// Construct the default completer with a list of commands/keywords to highlight, given a minimum word length pub fn new_with_wordlen(external_commands: Vec, min_word_len: usize) -> Self { let mut dc = DefaultCompleter::default().set_min_word_len(min_word_len); dc.insert(external_commands); @@ -184,31 +138,16 @@ impl DefaultCompleter { /// /// # Example /// ``` - /// use reedline::{Completer, DefaultCompleter}; + /// use reedline::{DefaultCompleter,Completer}; /// /// let mut completions = DefaultCompleter::default(); /// /// // Insert multiple words - /// completions.insert( - /// vec!["a", "line", "with", "many", "words"] - /// .iter() - /// .map(|s| s.to_string()) - /// .collect(), - /// ); + /// completions.insert(vec!["a","line","with","many","words"].iter().map(|s| s.to_string()).collect()); /// /// // The above line is equal to the following: - /// completions.insert( - /// vec!["a", "line", "with"] - /// .iter() - /// .map(|s| s.to_string()) - /// .collect(), - /// ); - /// completions.insert( - /// vec!["many", "words"] - /// .iter() - /// .map(|s| s.to_string()) - /// .collect(), - /// ); + /// completions.insert(vec!["a","line","with"].iter().map(|s| s.to_string()).collect()); + /// completions.insert(vec!["many","words"].iter().map(|s| s.to_string()).collect()); /// ``` pub fn insert(&mut self, words: Vec) { for word in words { @@ -218,10 +157,9 @@ impl DefaultCompleter { } } - /// Create a new `DefaultCompleter` with provided non alphabet characters - /// whitelisted. The default `DefaultCompleter` will only parse alphabet - /// characters (a-z, A-Z). Use this to introduce additional accepted - /// special characters. + /// Create a new `DefaultCompleter` with provided non alphabet characters whitelisted. + /// The default `DefaultCompleter` will only parse alphabet characters (a-z, A-Z). Use this to + /// introduce additional accepted special characters. /// /// # Arguments /// @@ -229,52 +167,22 @@ impl DefaultCompleter { /// /// # Example /// ``` - /// use reedline::{Completer, DefaultCompleter, Span, Suggestion}; + /// use reedline::{DefaultCompleter,Completer,Span,Suggestion}; /// /// let mut completions = DefaultCompleter::default(); - /// completions.insert( - /// vec!["test-hyphen", "test_underscore"] - /// .iter() - /// .map(|s| s.to_string()) - /// .collect(), - /// ); + /// completions.insert(vec!["test-hyphen","test_underscore"].iter().map(|s| s.to_string()).collect()); /// assert_eq!( - /// completions.complete("te", 2), - /// vec![Suggestion { - /// value: "test".into(), - /// description: None, - /// extra: None, - /// span: Span { start: 0, end: 2 }, - /// append_whitespace: false - /// }] - /// ); + /// completions.complete("te",2), + /// vec![Suggestion {value: "test".into(), description: None, extra: None, span: Span { start: 0, end: 2 }, append_whitespace: false}]); /// /// let mut completions = DefaultCompleter::with_inclusions(&['-', '_']); - /// completions.insert( - /// vec!["test-hyphen", "test_underscore"] - /// .iter() - /// .map(|s| s.to_string()) - /// .collect(), - /// ); + /// completions.insert(vec!["test-hyphen","test_underscore"].iter().map(|s| s.to_string()).collect()); /// assert_eq!( - /// completions.complete("te", 2), + /// completions.complete("te",2), /// vec![ - /// Suggestion { - /// value: "test-hyphen".into(), - /// description: None, - /// extra: None, - /// span: Span { start: 0, end: 2 }, - /// append_whitespace: false - /// }, - /// Suggestion { - /// value: "test_underscore".into(), - /// description: None, - /// extra: None, - /// span: Span { start: 0, end: 2 }, - /// append_whitespace: false - /// }, - /// ] - /// ); + /// Suggestion {value: "test-hyphen".into(), description: None, extra: None, span: Span { start: 0, end: 2 }, append_whitespace: false}, + /// Suggestion {value: "test_underscore".into(), description: None, extra: None, span: Span { start: 0, end: 2 }, append_whitespace: false}, + /// ]); /// ``` pub fn with_inclusions(incl: &[char]) -> Self { let mut set = BTreeSet::new(); @@ -289,15 +197,10 @@ impl DefaultCompleter { /// Clears all the data from the tree /// # Example /// ``` - /// use reedline::{Completer, DefaultCompleter}; + /// use reedline::{DefaultCompleter,Completer}; /// /// let mut completions = DefaultCompleter::default(); - /// completions.insert( - /// vec!["batman", "robin", "batmobile", "batcave", "robber"] - /// .iter() - /// .map(|s| s.to_string()) - /// .collect(), - /// ); + /// completions.insert(vec!["batman","robin","batmobile","batcave","robber"].iter().map(|s| s.to_string()).collect()); /// assert_eq!(completions.word_count(), 5); /// assert_eq!(completions.size(), 24); /// completions.clear(); @@ -311,15 +214,10 @@ impl DefaultCompleter { /// Returns a count of how many words that exist in the tree /// # Example /// ``` - /// use reedline::{Completer, DefaultCompleter}; + /// use reedline::{DefaultCompleter,Completer}; /// /// let mut completions = DefaultCompleter::default(); - /// completions.insert( - /// vec!["batman", "robin", "batmobile", "batcave", "robber"] - /// .iter() - /// .map(|s| s.to_string()) - /// .collect(), - /// ); + /// completions.insert(vec!["batman","robin","batmobile","batcave","robber"].iter().map(|s| s.to_string()).collect()); /// assert_eq!(completions.word_count(), 5); /// ``` pub fn word_count(&self) -> u32 { @@ -329,15 +227,10 @@ impl DefaultCompleter { /// Returns the size of the tree, the amount of nodes, not words /// # Example /// ``` - /// use reedline::{Completer, DefaultCompleter}; + /// use reedline::{DefaultCompleter,Completer}; /// /// let mut completions = DefaultCompleter::default(); - /// completions.insert( - /// vec!["batman", "robin", "batmobile", "batcave", "robber"] - /// .iter() - /// .map(|s| s.to_string()) - /// .collect(), - /// ); + /// completions.insert(vec!["batman","robin","batmobile","batcave","robber"].iter().map(|s| s.to_string()).collect()); /// assert_eq!(completions.size(), 24); /// ``` pub fn size(&self) -> u32 { @@ -350,24 +243,14 @@ impl DefaultCompleter { /// ignored. /// # Example /// ``` - /// use reedline::{Completer, DefaultCompleter}; + /// use reedline::{DefaultCompleter,Completer}; /// /// let mut completions = DefaultCompleter::default().set_min_word_len(4); - /// completions.insert( - /// vec!["one", "two", "three", "four", "five"] - /// .iter() - /// .map(|s| s.to_string()) - /// .collect(), - /// ); + /// completions.insert(vec!["one","two","three","four","five"].iter().map(|s| s.to_string()).collect()); /// assert_eq!(completions.word_count(), 3); /// /// let mut completions = DefaultCompleter::default().set_min_word_len(1); - /// completions.insert( - /// vec!["one", "two", "three", "four", "five"] - /// .iter() - /// .map(|s| s.to_string()) - /// .collect(), - /// ); + /// completions.insert(vec!["one","two","three","four","five"].iter().map(|s| s.to_string()).collect()); /// assert_eq!(completions.word_count(), 5); /// ``` pub fn min_word_len(&self) -> usize { diff --git a/src/core_editor/clip_buffer.rs b/src/core_editor/clip_buffer.rs index bb88d22c..0bd9272a 100644 --- a/src/core_editor/clip_buffer.rs +++ b/src/core_editor/clip_buffer.rs @@ -1,7 +1,6 @@ /// Defines an interface to interact with a Clipboard for cut and paste. /// -/// Mutable reference requirements are stricter than always necessary, but the -/// currently used system clipboard API demands them for exclusive access. +/// Mutable reference requirements are stricter than always necessary, but the currently used system clipboard API demands them for exclusive access. pub trait Clipboard: Send { fn set(&mut self, content: &str, mode: ClipboardMode); @@ -26,8 +25,7 @@ pub enum ClipboardMode { Lines, } -/// Simple buffer that provides a clipboard only usable within the -/// application/library. +/// Simple buffer that provides a clipboard only usable within the application/library. #[derive(Default)] pub struct LocalClipboard { content: String, @@ -60,8 +58,7 @@ pub use system_clipboard::SystemClipboard; /// /// Enabled -> [`SystemClipboard`], which talks to the system /// -/// Disabled -> [`LocalClipboard`], which supports cutting and pasting limited -/// to the [`crate::Reedline`] instance +/// Disabled -> [`LocalClipboard`], which supports cutting and pasting limited to the [`crate::Reedline`] instance pub fn get_default_clipboard() -> SystemClipboard { SystemClipboard::new() } @@ -71,17 +68,15 @@ pub fn get_default_clipboard() -> SystemClipboard { /// /// Enabled -> `SystemClipboard`, which talks to the system /// -/// Disabled -> [`LocalClipboard`], which supports cutting and pasting limited -/// to the [`crate::Reedline`] instance +/// Disabled -> [`LocalClipboard`], which supports cutting and pasting limited to the [`crate::Reedline`] instance pub fn get_default_clipboard() -> LocalClipboard { LocalClipboard::new() } #[cfg(feature = "system_clipboard")] mod system_clipboard { - use clipboard::{ClipboardContext, ClipboardProvider}; - use super::*; + use clipboard::{ClipboardContext, ClipboardProvider}; /// Wrapper around [`clipboard`](https://docs.rs/clipboard) crate /// @@ -113,8 +108,7 @@ mod system_clipboard { fn get(&mut self) -> (String, ClipboardMode) { let system_content = self.cb.get_contents().unwrap_or_default(); if system_content == self.local_copy { - // We assume the content was yanked inside the line editor and the last yank - // determined the mode. + // We assume the content was yanked inside the line editor and the last yank determined the mode. (system_content, self.mode) } else { // Content has changed, default to direct insertion. diff --git a/src/core_editor/edit_stack.rs b/src/core_editor/edit_stack.rs index 6e79d906..0a0f60a4 100644 --- a/src/core_editor/edit_stack.rs +++ b/src/core_editor/edit_stack.rs @@ -26,8 +26,7 @@ where &self.internal_list[self.index] } - /// Go forward one point in the undo stack. If present on the last edit do - /// nothing + /// Go forward one point in the undo stack. If present on the last edit do nothing pub(super) fn redo(&mut self) -> &T { self.index = if self.index == self.internal_list.len() - 1 { self.index @@ -38,8 +37,8 @@ where } /// Insert a new entry to the undo stack. - /// NOTE: (IMP): If we have hit undo a few times then discard all the other - /// values that come after the current point + /// NOTE: (IMP): If we have hit undo a few times then discard all the other values that come + /// after the current point pub(super) fn insert(&mut self, value: T) { if self.index < self.internal_list.len() - 1 { self.internal_list.resize_with(self.index + 1, || { @@ -64,11 +63,10 @@ where #[cfg(test)] mod test { + use super::*; use pretty_assertions::assert_eq; use rstest::rstest; - use super::*; - fn edit_stack(values: &[T], index: usize) -> EditStack where T: Clone, diff --git a/src/core_editor/editor.rs b/src/core_editor/editor.rs index 52fbdb5a..3cabe1ae 100644 --- a/src/core_editor/editor.rs +++ b/src/core_editor/editor.rs @@ -1,9 +1,6 @@ use super::{edit_stack::EditStack, Clipboard, ClipboardMode, LineBuffer}; -use crate::{ - core_editor::get_default_clipboard, - enums::{EditType, UndoBehavior}, - EditCommand, -}; +use crate::enums::{EditType, UndoBehavior}; +use crate::{core_editor::get_default_clipboard, EditCommand}; /// Stateful editor executing changes to the underlying [`LineBuffer`] /// @@ -35,8 +32,7 @@ impl Editor { } /// Set the current [`LineBuffer`]. - /// [`UndoBehavior`] specifies how this change should be reflected on the - /// undo stack. + /// [`UndoBehavior`] specifies how this change should be reflected on the undo stack. pub(crate) fn set_line_buffer(&mut self, line_buffer: LineBuffer, undo_behavior: UndoBehavior) { self.line_buffer = line_buffer; self.update_undo_state(undo_behavior); @@ -143,8 +139,8 @@ impl Editor { func(&mut self.line_buffer); } - /// Set the text of the current [`LineBuffer`] given the specified - /// [`UndoBehavior`] Insertion point update to the end of the buffer. + /// Set the text of the current [`LineBuffer`] given the specified [`UndoBehavior`] + /// Insertion point update to the end of the buffer. pub(crate) fn set_buffer(&mut self, buffer: String, undo_behavior: UndoBehavior) { self.line_buffer.set_buffer(buffer); self.update_undo_state(undo_behavior); @@ -470,11 +466,10 @@ impl Editor { #[cfg(test)] mod test { + use super::*; use pretty_assertions::assert_eq; use rstest::rstest; - use super::*; - fn editor_with(buffer: &str) -> Editor { let mut editor = Editor::default(); editor.set_buffer(buffer.to_string(), UndoBehavior::CreateUndoPoint); @@ -515,8 +510,7 @@ mod test { #[rstest] #[case("hello world", 0, 'l', 1, false, "lo world")] #[case("hello world", 0, 'l', 1, true, "llo world")] - #[ignore = "Deleting two consecutives is not implemented correctly and needs the multiplier \ - explicitly."] + #[ignore = "Deleting two consecutives is not implemented correctly and needs the multiplier explicitly."] #[case("hello world", 0, 'l', 2, false, "o world")] #[case("hello world", 0, 'h', 1, false, "hello world")] #[case("hello world", 0, 'l', 3, true, "ld")] diff --git a/src/core_editor/line_buffer.rs b/src/core_editor/line_buffer.rs index 828fb0f1..01759ba7 100644 --- a/src/core_editor/line_buffer.rs +++ b/src/core_editor/line_buffer.rs @@ -1,10 +1,10 @@ -use std::{convert::From, ops::Range}; +use { + itertools::Itertools, + std::{convert::From, ops::Range}, + unicode_segmentation::UnicodeSegmentation, +}; -use itertools::Itertools; -use unicode_segmentation::UnicodeSegmentation; - -/// In memory representation of the entered line(s) including a cursor position -/// to facilitate cursor based editing. +/// In memory representation of the entered line(s) including a cursor position to facilitate cursor based editing. #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct LineBuffer { lines: String, @@ -30,8 +30,7 @@ impl LineBuffer { self.lines.is_empty() } - /// Check if the line buffer is valid utf-8 and the cursor sits on a valid - /// grapheme boundary + /// Check if the line buffer is valid utf-8 and the cursor sits on a valid grapheme boundary pub fn is_valid(&self) -> bool { self.lines.is_char_boundary(self.insertion_point()) && (self @@ -78,8 +77,7 @@ impl LineBuffer { &self.lines } - /// Set to a single line of `buffer` and reset the `InsertionPoint` cursor - /// to the end + /// Set to a single line of `buffer` and reset the `InsertionPoint` cursor to the end pub fn set_buffer(&mut self, buffer: String) { self.lines = buffer; self.insertion_point = self.lines.len(); @@ -345,7 +343,7 @@ impl LineBuffer { self.insertion_point = self.big_word_right_end_index(); } - /// Insert a single character at the insertion point and move right + ///Insert a single character at the insertion point and move right pub fn insert_char(&mut self, c: char) { self.lines.insert(self.insertion_point, c); self.move_right(); @@ -400,8 +398,7 @@ impl LineBuffer { /// Clear text covered by `range` in the current line /// - /// Safety: Does not change the insertion point/offset and is thus not - /// unicode safe! + /// Safety: Does not change the insertion point/offset and is thus not unicode safe! pub(crate) fn clear_range(&mut self, range: R) where R: std::ops::RangeBounds, @@ -411,8 +408,7 @@ impl LineBuffer { /// Substitute text covered by `range` in the current line /// - /// Safety: Does not change the insertion point/offset and is thus not - /// unicode safe! + /// Safety: Does not change the insertion point/offset and is thus not unicode safe! pub fn replace_range(&mut self, range: R, replace_with: &str) where R: std::ops::RangeBounds, @@ -759,11 +755,10 @@ fn is_whitespace_str(s: &str) -> bool { #[cfg(test)] mod test { + use super::*; use pretty_assertions::assert_eq; use rstest::rstest; - use super::*; - fn buffer_with(content: &str) -> LineBuffer { let mut line_buffer = LineBuffer::new(); line_buffer.insert_str(content); diff --git a/src/edit_mode/base.rs b/src/edit_mode/base.rs index 1f33b884..455e2c80 100644 --- a/src/edit_mode/base.rs +++ b/src/edit_mode/base.rs @@ -8,8 +8,7 @@ use crate::{ /// - Emacs /// - Vi pub trait EditMode: Send { - /// Translate the given user input event into what the `LineEditor` - /// understands + /// Translate the given user input event into what the `LineEditor` understands fn parse_event(&mut self, event: ReedlineRawEvent) -> ReedlineEvent; /// What to display in the prompt indicator diff --git a/src/edit_mode/cursors.rs b/src/edit_mode/cursors.rs index 8d64c5e1..67a1765a 100644 --- a/src/edit_mode/cursors.rs +++ b/src/edit_mode/cursors.rs @@ -1,8 +1,7 @@ use crossterm::cursor::SetCursorStyle; /// 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. +/// 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 diff --git a/src/edit_mode/emacs.rs b/src/edit_mode/emacs.rs index 29796009..35aa6db7 100644 --- a/src/edit_mode/emacs.rs +++ b/src/edit_mode/emacs.rs @@ -1,5 +1,3 @@ -use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers}; - use crate::{ edit_mode::{ keybindings::{ @@ -11,6 +9,7 @@ use crate::{ enums::{EditCommand, ReedlineEvent, ReedlineRawEvent}, PromptEditMode, }; +use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers}; /// Returns the current default emacs keybindings pub fn default_emacs_keybindings() -> Keybindings { @@ -173,8 +172,7 @@ impl EditMode for Emacs { } impl Emacs { - /// Emacs style input parsing constructor if you want to use custom - /// keybindings + /// Emacs style input parsing constructor if you want to use custom keybindings pub const fn new(keybindings: Keybindings) -> Self { Emacs { keybindings } } @@ -182,9 +180,8 @@ impl Emacs { #[cfg(test)] mod test { - use pretty_assertions::assert_eq; - use super::*; + use pretty_assertions::assert_eq; #[test] fn ctrl_l_leads_to_clear_screen_event() { diff --git a/src/edit_mode/keybindings.rs b/src/edit_mode/keybindings.rs index 4f226625..772e4199 100644 --- a/src/edit_mode/keybindings.rs +++ b/src/edit_mode/keybindings.rs @@ -1,9 +1,9 @@ -use std::collections::HashMap; - -use crossterm::event::{KeyCode, KeyModifiers}; -use serde::{Deserialize, Serialize}; - -use crate::{enums::ReedlineEvent, EditCommand}; +use { + crate::{enums::ReedlineEvent, EditCommand}, + crossterm::event::{KeyCode, KeyModifiers}, + serde::{Deserialize, Serialize}, + std::collections::HashMap, +}; #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Debug)] pub struct KeyCombination { @@ -67,8 +67,7 @@ impl Keybindings { /// Remove a keybinding /// - /// Returns `Some(ReedlineEvent)` if the keycombination was previously bound - /// to a particular [`ReedlineEvent`] + /// Returns `Some(ReedlineEvent)` if the keycombination was previously bound to a particular [`ReedlineEvent`] pub fn remove_binding( &mut self, modifier: KeyModifiers, diff --git a/src/edit_mode/vi/command.rs b/src/edit_mode/vi/command.rs index 15b9c6e9..e86f9f31 100644 --- a/src/edit_mode/vi/command.rs +++ b/src/edit_mode/vi/command.rs @@ -1,10 +1,6 @@ -use std::iter::Peekable; - -use super::{ - motion::{Motion, ViCharSearch}, - parser::ReedlineOption, -}; +use super::{motion::Motion, motion::ViCharSearch, parser::ReedlineOption}; use crate::{EditCommand, ReedlineEvent, Vi}; +use std::iter::Peekable; pub fn parse_command<'iter, I>(input: &mut Peekable) -> Option where @@ -126,7 +122,7 @@ impl Command { matches!(self, Command::Delete | Command::Change) } - pub fn to_reedline(&self, vi_state: &Vi) -> Vec { + pub fn to_reedline(&self, vi_state: &mut Vi) -> Vec { match self { Self::EnterViInsert => vec![ReedlineOption::Event(ReedlineEvent::Repaint)], Self::EnterViAppend => vec![ReedlineOption::Edit(EditCommand::MoveRight)], diff --git a/src/edit_mode/vi/mod.rs b/src/edit_mode/vi/mod.rs index ca6b8aca..4428c645 100644 --- a/src/edit_mode/vi/mod.rs +++ b/src/edit_mode/vi/mod.rs @@ -7,6 +7,7 @@ use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers}; pub use vi_keybindings::{default_vi_insert_keybindings, default_vi_normal_keybindings}; use self::motion::ViCharSearch; + use super::EditMode; use crate::{ edit_mode::{keybindings::Keybindings, vi::parser::parse}, @@ -172,9 +173,8 @@ impl EditMode for Vi { #[cfg(test)] mod test { - use pretty_assertions::assert_eq; - use super::*; + use pretty_assertions::assert_eq; #[test] fn esc_leads_to_normal_mode_test() { diff --git a/src/edit_mode/vi/motion.rs b/src/edit_mode/vi/motion.rs index 24f404b0..90822a52 100644 --- a/src/edit_mode/vi/motion.rs +++ b/src/edit_mode/vi/motion.rs @@ -1,8 +1,9 @@ use std::iter::Peekable; -use super::parser::{ParseResult, ReedlineOption}; use crate::{EditCommand, ReedlineEvent, Vi}; +use super::parser::{ParseResult, ReedlineOption}; + pub fn parse_motion<'iter, I>( input: &mut Peekable, command_char: Option, diff --git a/src/edit_mode/vi/parser.rs b/src/edit_mode/vi/parser.rs index cf170058..f243e53b 100644 --- a/src/edit_mode/vi/parser.rs +++ b/src/edit_mode/vi/parser.rs @@ -1,10 +1,7 @@ -use std::iter::Peekable; - -use super::{ - command::{parse_command, Command}, - motion::{parse_motion, Motion}, -}; +use super::command::{parse_command, Command}; +use super::motion::{parse_motion, Motion}; use crate::{EditCommand, ReedlineEvent, Vi}; +use std::iter::Peekable; #[derive(Debug, Clone)] pub enum ReedlineOption { @@ -183,11 +180,10 @@ where #[cfg(test)] mod tests { + use super::*; use pretty_assertions::assert_eq; use rstest::rstest; - use super::*; - fn vi_parse(input: &[char]) -> ParsedViSequence { parse(&mut input.iter().peekable()) } diff --git a/src/engine.rs b/src/engine.rs index 6a37ba47..e4ca9c7e 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,49 +1,48 @@ -use std::{ - fs::File, - io, - io::{Result, Write}, - path::PathBuf, - process::Command, - time::{Duration, SystemTime}, -}; +use std::path::PathBuf; -use crossterm::{ - cursor::{SetCursorStyle, Show}, - event, - event::{Event, KeyCode, KeyEvent, KeyModifiers}, - terminal, QueueableCommand, -}; use itertools::Itertools; + +use crate::{enums::ReedlineRawEvent, CursorConfig}; +#[cfg(feature = "bashisms")] +use crate::{ + history::SearchFilter, + menu_functions::{parse_selection_char, ParseAction}, +}; #[cfg(feature = "external_printer")] use { crate::external_printer::ExternalPrinter, crossbeam::channel::TryRecvError, std::io::{Error, ErrorKind}, }; - -use crate::{ - completion::{Completer, DefaultCompleter}, - core_editor::Editor, - edit_mode::{EditMode, Emacs}, - enums::{EventStatus, ReedlineEvent, ReedlineRawEvent}, - highlighter::SimpleMatchHighlighter, - hinter::Hinter, - history::{ - FileBackedHistory, History, HistoryCursor, HistoryItem, HistoryItemId, - HistoryNavigationQuery, HistorySessionId, SearchDirection, SearchQuery, +use { + crate::{ + completion::{Completer, DefaultCompleter}, + core_editor::Editor, + edit_mode::{EditMode, Emacs}, + enums::{EventStatus, ReedlineEvent}, + highlighter::SimpleMatchHighlighter, + hinter::Hinter, + history::{ + FileBackedHistory, History, HistoryCursor, HistoryItem, HistoryItemId, + HistoryNavigationQuery, HistorySessionId, SearchDirection, SearchQuery, + }, + painting::{Painter, PromptLines}, + prompt::{PromptEditMode, PromptHistorySearchStatus}, + result::{ReedlineError, ReedlineErrorVariants}, + terminal_extensions::{bracketed_paste::BracketedPasteGuard, kitty::KittyProtocolGuard}, + utils::text_manipulation, + EditCommand, ExampleHighlighter, Highlighter, LineBuffer, Menu, MenuEvent, Prompt, + PromptHistorySearch, ReedlineMenu, Signal, UndoBehavior, ValidationResult, Validator, + }, + crossterm::{ + cursor::{SetCursorStyle, Show}, + event, + event::{Event, KeyCode, KeyEvent, KeyModifiers}, + terminal, QueueableCommand, + }, + std::{ + fs::File, io, io::Result, io::Write, process::Command, time::Duration, time::SystemTime, }, - painting::{Painter, PromptLines}, - prompt::{PromptEditMode, PromptHistorySearchStatus}, - result::{ReedlineError, ReedlineErrorVariants}, - terminal_extensions::{bracketed_paste::BracketedPasteGuard, kitty::KittyProtocolGuard}, - utils::text_manipulation, - CursorConfig, EditCommand, ExampleHighlighter, Highlighter, LineBuffer, Menu, MenuEvent, - Prompt, PromptHistorySearch, ReedlineMenu, Signal, UndoBehavior, ValidationResult, Validator, -}; -#[cfg(feature = "bashisms")] -use crate::{ - history::SearchFilter, - menu_functions::{parse_selection_char, ParseAction}, }; // The POLL_WAIT is used to specify for how long the POLL should wait for @@ -52,10 +51,10 @@ use crate::{ // arrives. This doesn't allow for the possibility of more than 1 event // happening at the same time. const POLL_WAIT: u64 = 10; -// Since a paste event is multiple Event::Key events happening at the same time, -// we specify how many events should be in the crossterm_events vector before it -// is considered a paste. 10 events in 10 milliseconds is conservative enough -// (unlikely somebody will type more than 10 characters in 10 milliseconds) +// Since a paste event is multiple Event::Key events happening at the same time, we specify +// how many events should be in the crossterm_events vector before it is considered +// a paste. 10 events in 10 milliseconds is conservative enough (unlikely somebody +// will type more than 10 characters in 10 milliseconds) const EVENTS_THRESHOLD: usize = 10; /// Determines if inputs should be used to extend the regular line buffer, @@ -70,9 +69,9 @@ enum InputMode { /// editing affects the search string, /// suggestions are provided to be inserted in the line buffer HistorySearch, - /// Hybrid mode indicating that history is walked through in the standard - /// prompt Either bash style up/down history or fish style prefix - /// search, Edits directly switch to [`InputMode::Regular`] + /// Hybrid mode indicating that history is walked through in the standard prompt + /// Either bash style up/down history or fish style prefix search, + /// Edits directly switch to [`InputMode::Regular`] HistoryTraversal, } @@ -80,18 +79,19 @@ enum InputMode { /// /// ## Example usage /// ```no_run -/// use reedline::{DefaultPrompt, Reedline, Signal}; +/// use reedline::{Reedline, Signal, DefaultPrompt}; /// let mut line_editor = Reedline::create(); /// let prompt = DefaultPrompt::default(); /// /// let out = line_editor.read_line(&prompt).unwrap(); /// match out { -/// Signal::Success(content) => { -/// // process content -/// } -/// _ => { -/// eprintln!("Entry aborted!"); -/// } +/// Signal::Success(content) => { +/// // process content +/// } +/// _ => { +/// eprintln!("Entry aborted!"); +/// +/// } /// } /// ``` pub struct Reedline { @@ -177,8 +177,7 @@ impl Drop for Reedline { impl Reedline { const FILTERED_ITEM_ID: HistoryItemId = HistoryItemId(i64::MAX); - /// Create a new [`Reedline`] engine with a local [`History`] that is not - /// synchronized to a file. + /// Create a new [`Reedline`] engine with a local [`History`] that is not synchronized to a file. #[must_use] pub fn create() -> Self { let history = Box::::default(); @@ -224,8 +223,7 @@ impl Reedline { } } - /// Get a new history session id based on the current time and the first - /// commit datetime of reedline + /// Get a new history session id based on the current time and the first commit datetime of reedline pub fn create_history_session_id() -> Option { let nanos = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { Ok(n) => n.as_nanos() as i64, @@ -237,14 +235,13 @@ impl Reedline { /// Toggle whether reedline enables bracketed paste to reed copied content /// - /// This currently alters the behavior for multiline pastes as pasting of - /// regular text will execute after every complete new line as - /// determined by the [`Validator`]. With enabled bracketed paste all - /// lines will appear in the buffer and can then be submitted with a + /// This currently alters the behavior for multiline pastes as pasting of regular text will + /// execute after every complete new line as determined by the [`Validator`]. With enabled + /// bracketed paste all lines will appear in the buffer and can then be submitted with a /// separate enter. /// - /// At this point most terminals should support it or ignore the setting of - /// the necessary flags. For full compatibility, keep it disabled. + /// At this point most terminals should support it or ignore the setting of the necessary + /// flags. For full compatibility, keep it disabled. pub fn use_bracketed_paste(mut self, enable: bool) -> Self { self.bracketed_paste.set(enable); self @@ -269,25 +266,27 @@ impl Reedline { } /// Set a new history session id - /// This should be used in situations where the user initially did not have - /// a history_session_id and then later realized they want to have one - /// without restarting the application. + /// This should be used in situations where the user initially did not have a history_session_id + /// and then later realized they want to have one without restarting the application. pub fn set_history_session_id(&mut self, session: Option) -> Result<()> { self.history_session_id = session; Ok(()) } - /// A builder to include a [`Hinter`] in your instance of the Reedline - /// engine # Example + /// A builder to include a [`Hinter`] in your instance of the Reedline engine + /// # Example /// ```rust - /// // Cargo.toml + /// //Cargo.toml /// //[dependencies] - /// // nu-ansi-term = "*" - /// use nu_ansi_term::{Color, Style}; - /// use reedline::{DefaultHinter, Reedline}; + /// //nu-ansi-term = "*" + /// use { + /// nu_ansi_term::{Color, Style}, + /// reedline::{DefaultHinter, Reedline}, + /// }; /// /// let mut line_editor = Reedline::create().with_hinter(Box::new( - /// DefaultHinter::default().with_style(Style::new().italic().fg(Color::LightGray)), + /// DefaultHinter::default() + /// .with_style(Style::new().italic().fg(Color::LightGray)), /// )); /// ``` #[must_use] @@ -311,10 +310,10 @@ impl Reedline { /// use reedline::{DefaultCompleter, Reedline}; /// /// let commands = vec![ - /// "test".into(), - /// "hello world".into(), - /// "hello world reedline".into(), - /// "this is the reedline crate".into(), + /// "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)); /// @@ -326,45 +325,45 @@ impl Reedline { self } - /// Turn on quick completions. These completions will auto-select if the - /// completer ever narrows down to a single entry. + /// Turn on quick completions. These completions will auto-select if the completer + /// ever narrows down to a single entry. #[must_use] pub fn with_quick_completions(mut self, quick_completions: bool) -> Self { self.quick_completions = quick_completions; self } - /// Turn on partial completions. These completions will fill the buffer with - /// the smallest common string from all the options + /// Turn on partial completions. These completions will fill the buffer with the + /// smallest common string from all the options #[must_use] pub fn with_partial_completions(mut self, partial_completions: bool) -> Self { self.partial_completions = partial_completions; self } - /// A builder which enables or disables the use of ansi coloring in the - /// prompt and in the command line syntax highlighting. + /// A builder which enables or disables the use of ansi coloring in the prompt + /// and in the command line syntax highlighting. #[must_use] pub fn with_ansi_colors(mut self, use_ansi_coloring: bool) -> Self { self.use_ansi_coloring = use_ansi_coloring; self } - /// A builder that configures the highlighter for your instance of the - /// Reedline engine # Example + /// A builder that configures the highlighter for your instance of the Reedline engine + /// # Example /// ```rust /// // Create a reedline object with highlighter support /// /// use reedline::{ExampleHighlighter, Reedline}; /// /// let commands = vec![ - /// "test".into(), - /// "hello world".into(), - /// "hello world reedline".into(), - /// "this is the reedline crate".into(), + /// "test".into(), + /// "hello world".into(), + /// "hello world reedline".into(), + /// "this is the reedline crate".into(), /// ]; /// let mut line_editor = - /// Reedline::create().with_highlighter(Box::new(ExampleHighlighter::new(commands))); + /// Reedline::create().with_highlighter(Box::new(ExampleHighlighter::new(commands))); /// ``` #[must_use] pub fn with_highlighter(mut self, highlighter: Box) -> Self { @@ -372,18 +371,19 @@ impl Reedline { self } - /// A builder which configures the history for your instance of the Reedline - /// engine # Example + /// A builder which configures the history for your instance of the Reedline engine + /// # Example /// ```rust,no_run /// // Create a reedline object with history support, including history size limits /// /// use reedline::{FileBackedHistory, Reedline}; /// /// let history = Box::new( - /// FileBackedHistory::with_file(5, "history.txt".into()) - /// .expect("Error configuring history with file"), + /// FileBackedHistory::with_file(5, "history.txt".into()) + /// .expect("Error configuring history with file"), /// ); - /// let mut line_editor = Reedline::create().with_history(history); + /// let mut line_editor = Reedline::create() + /// .with_history(history); /// ``` #[must_use] pub fn with_history(mut self, history: Box) -> Self { @@ -391,8 +391,8 @@ impl Reedline { self } - /// A builder which configures history exclusion for your instance of the - /// Reedline engine # Example + /// A builder which configures history exclusion for your instance of the Reedline engine + /// # Example /// ```rust,no_run /// // Create a reedline instance with history that will *not* include commands starting with a space /// @@ -412,14 +412,15 @@ impl Reedline { self } - /// A builder that configures the validator for your instance of the - /// Reedline engine # Example + /// A builder that configures the validator for your instance of the Reedline engine + /// # Example /// ```rust /// // Create a reedline object with validator support /// /// use reedline::{DefaultValidator, Reedline}; /// - /// let mut line_editor = Reedline::create().with_validator(Box::new(DefaultValidator)); + /// let mut line_editor = + /// Reedline::create().with_validator(Box::new(DefaultValidator)); /// ``` #[must_use] pub fn with_validator(mut self, validator: Box) -> Self { @@ -427,26 +428,25 @@ impl Reedline { self } - /// A builder that configures the alternate text editor used to edit the - /// line buffer + /// A builder that configures the alternate text editor used to edit the line buffer /// - /// You are responsible for providing a file path that is unique to this - /// reedline session + /// You are responsible for providing a file path that is unique to this reedline session /// /// # Example /// ```rust,no_run /// // Create a reedline object with vim as editor /// - /// use std::{env::temp_dir, process::Command}; - /// /// use reedline::Reedline; + /// use std::env::temp_dir; + /// use std::process::Command; /// /// let temp_file = std::env::temp_dir().join("my-random-unique.file"); /// let mut command = Command::new("vim"); /// // you can provide additional flags: /// command.arg("-p"); // open in a vim tab (just for demonstration) - /// // you don't have to pass the filename to the command - /// let mut line_editor = Reedline::create().with_buffer_editor(command, temp_file); + /// // you don't have to pass the filename to the command + /// let mut line_editor = + /// Reedline::create().with_buffer_editor(command, temp_file); /// ``` #[must_use] pub fn with_buffer_editor(mut self, editor: Command, temp_file: PathBuf) -> Self { @@ -475,8 +475,7 @@ impl Reedline { 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] pub fn with_edit_mode(mut self, edit_mode: Box) -> Self { self.edit_mode = edit_mode; @@ -504,10 +503,9 @@ 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. + /// 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 @@ -518,8 +516,7 @@ impl Reedline { self.edit_mode.edit_mode() } - /// Output the complete [`History`] chronologically with numbering to the - /// terminal + /// Output the complete [`History`] chronologically with numbering to the terminal pub fn print_history(&mut self) -> Result<()> { let history: Vec<_> = self .history @@ -532,8 +529,7 @@ impl Reedline { Ok(()) } - /// Output the complete [`History`] for this session, chronologically with - /// numbering to the terminal + /// Output the complete [`History`] for this session, chronologically with numbering to the terminal pub fn print_history_session(&mut self) -> Result<()> { let history: Vec<_> = self .history @@ -555,8 +551,7 @@ impl Reedline { Ok(()) } - /// Toggle between having a history that uses the history session id and one - /// that does not + /// Toggle between having a history that uses the history session id and one that does not pub fn toggle_history_session_matching( &mut self, session: Option, @@ -586,9 +581,8 @@ impl Reedline { /// Check if any commands have been run. /// - /// When no commands have been run, calling - /// [`Self::update_last_command_context`] does not make sense and is - /// guaranteed to fail with a "No command run" error. + /// When no commands have been run, calling [`Self::update_last_command_context`] + /// does not make sense and is guaranteed to fail with a "No command run" error. pub fn has_last_command_context(&self) -> bool { self.history_last_run_id.is_some() } @@ -612,9 +606,8 @@ impl Reedline { /// Wait for input and provide the user with a specified [`Prompt`]. /// - /// Returns a [`std::io::Result`] in which the `Err` type is - /// [`std::io::Result`] and the `Ok` variant wraps a [`Signal`] which - /// handles user inputs. + /// Returns a [`std::io::Result`] in which the `Err` type is [`std::io::Result`] + /// and the `Ok` variant wraps a [`Signal`] which handles user inputs. pub fn read_line(&mut self, prompt: &dyn Prompt) -> Result { terminal::enable_raw_mode()?; self.bracketed_paste.enter(); @@ -638,8 +631,7 @@ impl Reedline { self.editor.get_buffer() } - /// Writes `msg` to the terminal with a following carriage return and - /// newline + /// Writes `msg` to the terminal with a following carriage return and newline fn print_line(&mut self, msg: &str) -> Result<()> { self.painter.paint_line(msg) } @@ -659,8 +651,8 @@ impl Reedline { Ok(()) } - /// Helper implementing the logic for [`Reedline::read_line()`] to be - /// wrapped in a `raw_mode` context. + /// Helper implementing the logic for [`Reedline::read_line()`] to be wrapped + /// in a `raw_mode` context. fn read_line_helper(&mut self, prompt: &dyn Prompt) -> Result { self.painter.initialize_prompt_position()?; self.hide_hints = false; @@ -721,8 +713,7 @@ impl Reedline { // There could be multiple events queued up! // pasting text, resizes, blocking this thread (e.g. during debugging) - // We should be able to handle all of them as quickly as possible without - // causing unnecessary output steps. + // We should be able to handle all of them as quickly as possible without causing unnecessary output steps. if !event::poll(Duration::from_millis(POLL_WAIT))? { break; } @@ -761,8 +752,7 @@ impl Reedline { for event in reedline_events.drain(..) { match self.handle_event(prompt, event)? { EventStatus::Exits(signal) => { - // Move the cursor below the input area, for external commands or new - // read_line call + // Move the cursor below the input area, for external commands or new read_line call self.painter.move_cursor_to_end()?; return Ok(signal); } @@ -838,8 +828,7 @@ impl Reedline { Ok(EventStatus::Handled) } ReedlineEvent::ExecuteHostCommand(host_command) => { - // TODO: Decide if we need to do something special to have a nicer painter state - // on the next go + // TODO: Decide if we need to do something special to have a nicer painter state on the next go Ok(EventStatus::Exits(Signal::Success(host_command))) } ReedlineEvent::Edit(commands) => { @@ -1105,8 +1094,7 @@ impl Reedline { } } ReedlineEvent::ExecuteHostCommand(host_command) => { - // TODO: Decide if we need to do something special to have a nicer painter state - // on the next go + // TODO: Decide if we need to do something special to have a nicer painter state on the next go Ok(EventStatus::Exits(Signal::Success(host_command))) } ReedlineEvent::Edit(commands) => { @@ -1297,11 +1285,9 @@ impl Reedline { self.editor.move_to_end(UndoBehavior::HistoryNavigation); } - /// Enable the search and navigation through the history from the line - /// buffer prompt + /// Enable the search and navigation through the history from the line buffer prompt /// - /// Enables either prefix search with output in the line buffer or simple - /// traversal + /// Enables either prefix search with output in the line buffer or simple traversal fn get_history_navigation_based_on_line_buffer(&self) -> HistoryNavigationQuery { if self.editor.is_empty() || !self.editor.is_cursor_at_buffer_end() { // Perform bash-style basic up/down entry walking @@ -1322,8 +1308,7 @@ impl Reedline { /// Switch into reverse history search mode /// - /// This mode uses a separate prompt and handles keybindings slightly - /// differently! + /// This mode uses a separate prompt and handles keybindings slightly differently! fn enter_history_search(&mut self) { self.history_cursor = HistoryCursor::new( HistoryNavigationQuery::SubstringSearch("".to_string()), @@ -1332,8 +1317,7 @@ impl Reedline { self.input_mode = InputMode::HistorySearch; } - /// Dispatches the applicable [`EditCommand`] actions for editing the - /// history search string. + /// Dispatches the applicable [`EditCommand`] actions for editing the history search string. /// /// Only modifies internal state, does not perform regular output! fn run_history_commands(&mut self, commands: &[EditCommand]) { @@ -1379,12 +1363,10 @@ impl Reedline { } } - /// Set the buffer contents for history traversal/search in the standard - /// prompt + /// Set the buffer contents for history traversal/search in the standard prompt /// - /// When using the up/down traversal or fish/zsh style prefix search update - /// the main line buffer accordingly. Not used for the separate modal - /// reverse search! + /// When using the up/down traversal or fish/zsh style prefix search update the main line buffer accordingly. + /// Not used for the separate modal reverse search! fn update_buffer_from_history(&mut self) { match self.history_cursor.get_navigation() { _ if self.history_cursor_on_excluded => self.editor.set_buffer( @@ -1418,8 +1400,7 @@ impl Reedline { } } - /// Executes [`EditCommand`] actions by modifying the internal state - /// appropriately. Does not output itself. + /// Executes [`EditCommand`] actions by modifying the internal state appropriately. Does not output itself. pub fn run_edit_commands(&mut self, commands: &[EditCommand]) { if self.input_mode == InputMode::HistoryTraversal { if matches!( diff --git a/src/enums.rs b/src/enums.rs index dc63e846..210dac8e 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -1,7 +1,6 @@ -use std::fmt::{Display, Formatter}; - use crossterm::event::{Event, KeyEvent, KeyEventKind}; use serde::{Deserialize, Serialize}; +use std::fmt::{Display, Formatter}; use strum_macros::EnumIter; /// Valid ways how `Reedline::read_line()` can return @@ -11,8 +10,7 @@ pub enum Signal { Success(String), /// Entry was aborted with `Ctrl+C` CtrlC, // Interrupt current editing - /// Abort with `Ctrl+D` signalling `EOF` or abort of a whole interactive - /// session + /// Abort with `Ctrl+D` signalling `EOF` or abort of a whole interactive session CtrlD, // End terminal session } @@ -102,8 +100,7 @@ pub enum EditCommand { /// Clear to the end of the current line ClearToLineEnd, - /// Insert completion: entire completion if there is only one possibility, - /// or else up to shared prefix. + /// Insert completion: entire completion if there is only one possibility, or else up to shared prefix. Complete, /// Cut the current line @@ -337,8 +334,8 @@ pub enum EditType { EditText, } -/// Every line change should come with an `UndoBehavior` tag, which can be used -/// to calculate how the change should be reflected on the undo stack +/// Every line change should come with an `UndoBehavior` tag, which can be used to +/// calculate how the change should be reflected on the undo stack #[derive(Debug)] pub enum UndoBehavior { /// Character insertion, tracking the character inserted @@ -353,8 +350,8 @@ pub enum UndoBehavior { MoveCursor, /// Navigated the history using up or down arrows HistoryNavigation, - /// Catch-all for actions that should always form a unique undo point and - /// never be grouped with later edits + /// Catch-all for actions that should always form a unique undo point and never be + /// grouped with later edits CreateUndoPoint, /// Undo/Redo actions shouldn't be reflected on the edit stack UndoRedo, @@ -455,12 +452,10 @@ pub enum ReedlineEvent { /// Navigate to the previous historic buffer PreviousHistory, - /// Move up to the previous line, if multiline, or up into the historic - /// buffers + /// Move up to the previous line, if multiline, or up into the historic buffers Up, - /// Move down to the next line, if multiline, or down through the historic - /// buffers + /// Move down to the next line, if multiline, or down through the historic buffers Down, /// Move right to the next column, completion entry, or complete hint @@ -509,8 +504,7 @@ pub enum ReedlineEvent { /// Move to the previous history page MenuPagePrevious, - /// Way to bind the execution of a whole command (directly returning from - /// [`crate::Reedline::read_line()`]) to a keybinding + /// Way to bind the execution of a whole command (directly returning from [`crate::Reedline::read_line()`]) to a keybinding ExecuteHostCommand(String), /// Open text editor @@ -570,9 +564,8 @@ pub(crate) enum EventStatus { /// A simple wrapper for [crossterm::event::Event] /// -/// Which will make sure that the given event doesn't contain -/// [KeyEventKind::Release] and convert from [KeyEventKind::Repeat] to -/// [KeyEventKind::Press] +/// Which will make sure that the given event doesn't contain [KeyEventKind::Release] +/// and convert from [KeyEventKind::Repeat] to [KeyEventKind::Press] pub struct ReedlineRawEvent { inner: Event, } diff --git a/src/external_printer.rs b/src/external_printer.rs index 1b602061..c2cdf707 100644 --- a/src/external_printer.rs +++ b/src/external_printer.rs @@ -37,19 +37,17 @@ where let (sender, receiver) = bounded::(max_cap); Self { sender, receiver } } - /// Gets a Sender to use the printer externally by sending lines to it pub fn sender(&self) -> Sender { self.sender.clone() } - /// Receiver to get messages if any pub fn receiver(&self) -> &Receiver { &self.receiver } - /// Convenience method if the whole Printer is cloned, blocks if max_cap is - /// reached. + /// Convenience method if the whole Printer is cloned, blocks if max_cap is reached. + /// pub fn print(&self, line: T) -> Result<(), SendError> { self.sender.send(line) } diff --git a/src/highlighter/example.rs b/src/highlighter/example.rs index 78961939..1f1c9aee 100644 --- a/src/highlighter/example.rs +++ b/src/highlighter/example.rs @@ -1,7 +1,7 @@ +use crate::highlighter::Highlighter; +use crate::StyledText; use nu_ansi_term::{Color, Style}; -use crate::{highlighter::Highlighter, StyledText}; - pub static DEFAULT_BUFFER_MATCH_COLOR: Color = Color::Green; pub static DEFAULT_BUFFER_NEUTRAL_COLOR: Color = Color::White; pub static DEFAULT_BUFFER_NOTMATCH_COLOR: Color = Color::Red; @@ -58,8 +58,7 @@ impl Highlighter for ExampleHighlighter { } } impl ExampleHighlighter { - /// Construct the default highlighter with a given set of extern - /// commands/keywords to detect and highlight + /// Construct the default highlighter with a given set of extern commands/keywords to detect and highlight pub fn new(external_commands: Vec) -> ExampleHighlighter { ExampleHighlighter { external_commands, diff --git a/src/highlighter/mod.rs b/src/highlighter/mod.rs index d05a7151..e9ebafd6 100644 --- a/src/highlighter/mod.rs +++ b/src/highlighter/mod.rs @@ -1,16 +1,14 @@ mod example; mod simple_match; +use crate::StyledText; + pub use example::ExampleHighlighter; pub use simple_match::SimpleMatchHighlighter; - -use crate::StyledText; -/// The syntax highlighting trait. Implementers of this trait will take in the -/// current string and then return a `StyledText` object, which represents the -/// contents of the original line as styled strings +/// The syntax highlighting trait. Implementers of this trait will take in the current string and then +/// return a `StyledText` object, which represents the contents of the original line as styled strings pub trait Highlighter: Send { - /// The action that will handle the current buffer as a line and return the - /// corresponding `StyledText` for the buffer + /// The action that will handle the current buffer as a line and return the corresponding `StyledText` for the buffer /// /// Cursor position as byte offsets in the string fn highlight(&self, line: &str, cursor: usize) -> StyledText; diff --git a/src/highlighter/simple_match.rs b/src/highlighter/simple_match.rs index 8f37f617..981092b7 100644 --- a/src/highlighter/simple_match.rs +++ b/src/highlighter/simple_match.rs @@ -1,7 +1,7 @@ +use crate::highlighter::Highlighter; +use crate::StyledText; use nu_ansi_term::{Color, Style}; -use crate::{highlighter::Highlighter, StyledText}; - /// Highlight all matches for a given search string in a line /// /// Default style: diff --git a/src/hinter/cwd_aware.rs b/src/hinter/cwd_aware.rs index 69843ebc..c5c43815 100644 --- a/src/hinter/cwd_aware.rs +++ b/src/hinter/cwd_aware.rs @@ -1,11 +1,10 @@ -use nu_ansi_term::{Color, Style}; - use crate::{ hinter::get_first_token, history::SearchQuery, result::{ReedlineError, ReedlineErrorVariants::HistoryFeatureUnsupported}, Hinter, History, }; +use nu_ansi_term::{Color, Style}; /// A hinter that uses the completions or the history to show a hint to the user /// @@ -101,8 +100,7 @@ impl CwdAwareHinter { self } - /// A builder that sets the number of characters that have to be present to - /// enable history hints + /// A builder that sets the number of characters that have to be present to enable history hints #[must_use] pub fn with_min_chars(mut self, min_chars: usize) -> Self { self.min_chars = min_chars; diff --git a/src/hinter/default.rs b/src/hinter/default.rs index 3b23ca7f..2606a4d6 100644 --- a/src/hinter/default.rs +++ b/src/hinter/default.rs @@ -1,6 +1,5 @@ -use nu_ansi_term::{Color, Style}; - use crate::{hinter::get_first_token, history::SearchQuery, Hinter, History}; +use nu_ansi_term::{Color, Style}; /// A hinter that uses the completions or the history to show a hint to the user pub struct DefaultHinter { @@ -70,8 +69,7 @@ impl DefaultHinter { self } - /// A builder that sets the number of characters that have to be present to - /// enable history hints + /// A builder that sets the number of characters that have to be present to enable history hints #[must_use] pub fn with_min_chars(mut self, min_chars: usize) -> Self { self.min_chars = min_chars; diff --git a/src/hinter/mod.rs b/src/hinter/mod.rs index 7a39dba3..cf6f4701 100644 --- a/src/hinter/mod.rs +++ b/src/hinter/mod.rs @@ -2,6 +2,7 @@ mod cwd_aware; mod default; pub use cwd_aware::CwdAwareHinter; pub use default::DefaultHinter; + use unicode_segmentation::UnicodeSegmentation; pub fn is_whitespace_str(s: &str) -> bool { @@ -27,9 +28,8 @@ pub fn get_first_token(string: &str) -> String { } use crate::History; -/// A trait that's responsible for returning the hint for the current line and -/// position Hints are often shown in-line as part of the buffer, showing the -/// user text they can accept or ignore +/// A trait that's responsible for returning the hint for the current line and position +/// Hints are often shown in-line as part of the buffer, showing the user text they can accept or ignore pub trait Hinter: Send { /// Handle the hinting duty by using the line, position, and current history /// @@ -42,8 +42,7 @@ pub trait Hinter: Send { use_ansi_coloring: bool, ) -> String; - /// Return the current hint unformatted to perform the completion of the - /// full hint + /// Return the current hint unformatted to perform the completion of the full hint fn complete_hint(&self) -> String; /// Return the first semantic token of the hint diff --git a/src/history/base.rs b/src/history/base.rs index 360447aa..0f1b9a41 100644 --- a/src/history/base.rs +++ b/src/history/base.rs @@ -1,14 +1,11 @@ -use chrono::Utc; - use super::HistoryItemId; use crate::{core_editor::LineBuffer, HistoryItem, HistorySessionId, Result}; +use chrono::Utc; /// Browsing modes for a [`History`] #[derive(Debug, Clone, PartialEq, Eq)] pub enum HistoryNavigationQuery { - /// `bash` style browsing through the history. Contained `LineBuffer` is - /// used to store the state of manual entry before browsing through the - /// history + /// `bash` style browsing through the history. Contained `LineBuffer` is used to store the state of manual entry before browsing through the history Normal(LineBuffer), /// Search for entries starting with a particular string. PrefixSearch(String), @@ -45,8 +42,7 @@ pub struct SearchFilter { /// Query for the command line content pub command_line: Option, /// Considered implementation detail for now - pub(crate) not_command_line: Option, /* to skip the currently shown value in - * up-arrow navigation */ + pub(crate) not_command_line: Option, // to skip the currently shown value in up-arrow navigation /// Filter based on the executing systems hostname pub hostname: Option, /// Exact filter for the working directory @@ -100,11 +96,9 @@ impl SearchFilter { pub struct SearchQuery { /// Direction to search in pub direction: SearchDirection, - /// if given, only get results after/before this time (depending on - /// direction) + /// if given, only get results after/before this time (depending on direction) pub start_time: Option>, - /// if given, only get results after/before this time (depending on - /// direction) + /// if given, only get results after/before this time (depending on direction) pub end_time: Option>, /// if given, only get results after/before this id (depending on direction) pub start_id: Option, @@ -152,8 +146,7 @@ impl SearchQuery { )) } - /// Get the most recent entry starting with the `prefix` and `cwd` same as - /// the current cwd + /// Get the most recent entry starting with the `prefix` and `cwd` same as the current cwd pub fn last_with_prefix_and_cwd( prefix: String, session: Option, @@ -191,8 +184,7 @@ impl SearchQuery { } /// Represents a history file or database -/// Data could be stored e.g. in a plain text file, in a `JSONL` file, in a -/// `SQLite` database +/// Data could be stored e.g. in a plain text file, in a `JSONL` file, in a `SQLite` database pub trait History: Send { /// save a history item to the database /// if given id is None, a new id is created and set in the return value diff --git a/src/history/cursor.rs b/src/history/cursor.rs index 30915a62..ab41ba4f 100644 --- a/src/history/cursor.rs +++ b/src/history/cursor.rs @@ -1,8 +1,11 @@ -use super::{ - base::{CommandLineSearch, SearchDirection, SearchFilter}, - HistoryItem, SearchQuery, -}; -use crate::{History, HistoryNavigationQuery, HistorySessionId, Result}; +use crate::{History, HistoryNavigationQuery, HistorySessionId}; + +use super::base::CommandLineSearch; +use super::base::SearchDirection; +use super::base::SearchFilter; +use super::HistoryItem; +use super::SearchQuery; +use crate::Result; /// Interface of a stateful navigation via [`HistoryNavigationQuery`]. #[derive(Debug)] @@ -23,15 +26,13 @@ impl HistoryCursor { } } - /// This moves the cursor backwards respecting the navigation query that is - /// set + /// This moves the cursor backwards respecting the navigation query that is set /// - Results in a no-op if the cursor is at the initial point pub fn back(&mut self, history: &dyn History) -> Result<()> { self.navigate_in_direction(history, SearchDirection::Backward) } - /// This moves the cursor forwards respecting the navigation-query that is - /// set + /// This moves the cursor forwards respecting the navigation-query that is set /// - Results in a no-op if the cursor is at the latest point pub fn forward(&mut self, history: &dyn History) -> Result<()> { self.navigate_in_direction(history, SearchDirection::Forward) @@ -57,15 +58,13 @@ impl HistoryCursor { filter } } - fn navigate_in_direction( &mut self, history: &dyn History, direction: SearchDirection, ) -> Result<()> { if direction == SearchDirection::Forward && self.current.is_none() { - // if searching forward but we don't have a starting point, assume we are at the - // end + // if searching forward but we don't have a starting point, assume we are at the end return Ok(()); } let start_id = self.current.as_ref().and_then(|e| e.id); @@ -104,9 +103,11 @@ mod tests { use pretty_assertions::assert_eq; - use super::{super::*, *}; use crate::LineBuffer; + use super::super::*; + use super::*; + fn create_history() -> (Box, HistoryCursor) { #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] let hist = Box::new(SqliteBackedHistory::in_memory().unwrap()); @@ -378,8 +379,7 @@ mod tests { use tempfile::tempdir; let tmp = tempdir().unwrap(); - // check that it also works for a path where the directory has not been created - // yet + // check that it also works for a path where the directory has not been created yet let histfile = tmp.path().join("nested_path").join(".history"); let entries = vec!["test", "text", "more test text"]; @@ -388,8 +388,7 @@ mod tests { let (mut hist, _) = create_history_at(5, &histfile); add_text_entries(hist.as_mut(), &entries); - // As `hist` goes out of scope and get's dropped, its contents are - // flushed to disk + // As `hist` goes out of scope and get's dropped, its contents are flushed to disk } let (reading_hist, _) = create_history_at(5, &histfile); @@ -417,8 +416,7 @@ mod tests { { let (mut writing_hist, _) = create_history_at(5, &histfile); add_text_entries(writing_hist.as_mut(), &entries); - // As `hist` goes out of scope and get's dropped, its contents are - // flushed to disk + // As `hist` goes out of scope and get's dropped, its contents are flushed to disk } let (reading_hist, _) = create_history_at(5, &histfile); @@ -448,15 +446,13 @@ mod tests { { let (mut writing_hist, _) = create_history_at(capacity, &histfile); add_text_entries(writing_hist.as_mut(), &initial_entries); - // As `hist` goes out of scope and get's dropped, its contents are - // flushed to disk + // As `hist` goes out of scope and get's dropped, its contents are flushed to disk } { let (mut appending_hist, _) = create_history_at(capacity, &histfile); add_text_entries(appending_hist.as_mut(), &appending_entries); - // As `hist` goes out of scope and get's dropped, its contents are flushed to - // disk + // As `hist` goes out of scope and get's dropped, its contents are flushed to disk let actual: Vec<_> = get_all_entry_texts(appending_hist.as_ref()); assert_eq!(expected_appended_entries, actual); } @@ -466,8 +462,7 @@ mod tests { add_text_entries(truncating_hist.as_mut(), &truncating_entries); let actual: Vec<_> = get_all_entry_texts(truncating_hist.as_ref()); assert_eq!(expected_truncated_entries, actual); - // As `hist` goes out of scope and get's dropped, its contents are - // flushed to disk + // As `hist` goes out of scope and get's dropped, its contents are flushed to disk } let (reading_hist, _) = create_history_at(capacity, &histfile); @@ -494,8 +489,7 @@ mod tests { { let (mut writing_hist, _) = create_history_at(10, &histfile); add_text_entries(writing_hist.as_mut(), &overly_large_previous_entries); - // As `hist` goes out of scope and get's dropped, its contents are - // flushed to disk + // As `hist` goes out of scope and get's dropped, its contents are flushed to disk } { @@ -503,8 +497,7 @@ mod tests { let actual: Vec<_> = get_all_entry_texts(truncating_hist.as_ref()); assert_eq!(expected_truncated_entries, actual); - // As `hist` goes out of scope and get's dropped, its contents are - // flushed to disk + // As `hist` goes out of scope and get's dropped, its contents are flushed to disk } let (reading_hist, _) = create_history_at(5, &histfile); @@ -532,8 +525,7 @@ mod tests { { let (mut writing_hist, _) = create_history_at(capacity, &histfile); add_text_entries(writing_hist.as_mut(), &initial_entries); - // As `hist` goes out of scope and get's dropped, its contents are - // flushed to disk + // As `hist` goes out of scope and get's dropped, its contents are flushed to disk } { @@ -543,12 +535,10 @@ mod tests { let (mut hist_b, _) = create_history_at(capacity, &histfile); add_text_entries(hist_b.as_mut(), &entries_b); - // As `hist` goes out of scope and get's dropped, its contents - // are flushed to disk + // As `hist` goes out of scope and get's dropped, its contents are flushed to disk } add_text_entries(hist_a.as_mut(), &entries_a); - // As `hist` goes out of scope and get's dropped, its contents are - // flushed to disk + // As `hist` goes out of scope and get's dropped, its contents are flushed to disk } let (reading_hist, _) = create_history_at(capacity, &histfile); @@ -575,8 +565,7 @@ mod tests { { let (mut writing_hist, _) = create_history_at(capacity, &histfile); add_text_entries(writing_hist.as_mut(), &initial_entries); - // As `hist` goes out of scope and get's dropped, its contents are - // flushed to disk + // As `hist` goes out of scope and get's dropped, its contents are flushed to disk } let threads = (0..num_threads) diff --git a/src/history/file_backed.rs b/src/history/file_backed.rs index 437d4129..28b437f7 100644 --- a/src/history/file_backed.rs +++ b/src/history/file_backed.rs @@ -1,11 +1,3 @@ -use std::{ - collections::VecDeque, - fs::OpenOptions, - io::{BufRead, BufReader, BufWriter, Seek, SeekFrom, Write}, - ops::{Deref, DerefMut}, - path::PathBuf, -}; - use super::{ base::CommandLineSearch, History, HistoryItem, HistoryItemId, SearchDirection, SearchQuery, }; @@ -14,18 +6,24 @@ use crate::{ HistorySessionId, Result, }; -/// Default size of the [`FileBackedHistory`] used when calling -/// [`FileBackedHistory::default()`] +use std::{ + collections::VecDeque, + fs::OpenOptions, + io::{BufRead, BufReader, BufWriter, Seek, SeekFrom, Write}, + ops::{Deref, DerefMut}, + path::PathBuf, +}; + +/// Default size of the [`FileBackedHistory`] used when calling [`FileBackedHistory::default()`] pub const HISTORY_SIZE: usize = 1000; pub const NEWLINE_ESCAPE: &str = "<\\n>"; /// Stateful history that allows up/down-arrow browsing with an internal cursor. /// -/// Can optionally be associated with a newline separated history file using the -/// [`FileBackedHistory::with_file()`] constructor. Similar to bash's behavior -/// without HISTTIMEFORMAT. (See ) -/// If the history is associated to a file all new changes within a given -/// history capacity will be written to disk when History is dropped. +/// Can optionally be associated with a newline separated history file using the [`FileBackedHistory::with_file()`] constructor. +/// Similar to bash's behavior without HISTTIMEFORMAT. +/// (See ) +/// If the history is associated to a file all new changes within a given history capacity will be written to disk when History is dropped. #[derive(Debug)] pub struct FileBackedHistory { capacity: usize, @@ -36,11 +34,9 @@ pub struct FileBackedHistory { } impl Default for FileBackedHistory { - /// Creates an in-memory [`History`] with a maximal capacity of - /// [`HISTORY_SIZE`]. + /// Creates an in-memory [`History`] with a maximal capacity of [`HISTORY_SIZE`]. /// - /// To create a [`History`] that is synchronized with a file use - /// [`FileBackedHistory::with_file()`] + /// To create a [`History`] that is synchronized with a file use [`FileBackedHistory::with_file()`] fn default() -> Self { Self::new(HISTORY_SIZE) } @@ -310,6 +306,7 @@ impl FileBackedHistory { /// /// /// **Side effects:** creates all nested directories to the file + /// pub fn with_file(capacity: usize, file: PathBuf) -> std::io::Result { let mut hist = Self::new(capacity); if let Some(base_dir) = file.parent() { @@ -337,8 +334,7 @@ impl FileBackedHistory { } impl Drop for FileBackedHistory { - /// On drop the content of the [`History`] will be written to the file if - /// specified via [`FileBackedHistory::with_file()`]. + /// On drop the content of the [`History`] will be written to the file if specified via [`FileBackedHistory::with_file()`]. fn drop(&mut self) { let _res = self.sync(); } diff --git a/src/history/item.rs b/src/history/item.rs index 3eee1380..a0537e43 100644 --- a/src/history/item.rs +++ b/src/history/item.rs @@ -1,12 +1,10 @@ -use std::{fmt::Display, time::Duration}; - use chrono::Utc; #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] use rusqlite::ToSql; use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use std::{fmt::Display, time::Duration}; -/// Unique ID for the [`HistoryItem`]. More recent items have higher ids than -/// older ones. +/// Unique ID for the [`HistoryItem`]. More recent items have higher ids than older ones. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct HistoryItemId(pub(crate) i64); impl HistoryItemId { @@ -21,8 +19,7 @@ impl Display for HistoryItemId { } } -/// Unique ID for the session in which reedline was run to disambiguate -/// different sessions +/// Unique ID for the session in which reedline was run to disambiguate different sessions #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct HistorySessionId(pub i64); impl HistorySessionId { @@ -52,8 +49,7 @@ impl From for i64 { } } -/// This trait represents additional arbitrary context to be added to a history -/// (optional, see [`HistoryItem`]) +/// This trait represents additional arbitrary context to be added to a history (optional, see [`HistoryItem`]) pub trait HistoryItemExtraInfo: Serialize + DeserializeOwned + Default + Send {} #[derive(Clone, Copy, Default, Debug, PartialEq, Eq)] @@ -105,8 +101,7 @@ pub struct HistoryItem { } impl HistoryItem { - /// create a history item purely from the command line with everything else - /// set to None + /// create a history item purely from the command line with everything else set to None pub fn from_command_line(cmd: impl Into) -> HistoryItem { HistoryItem { id: None, diff --git a/src/history/mod.rs b/src/history/mod.rs index 84ea2ee4..c611a96f 100644 --- a/src/history/mod.rs +++ b/src/history/mod.rs @@ -4,11 +4,13 @@ mod file_backed; mod item; #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] mod sqlite_backed; +#[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] +pub use sqlite_backed::SqliteBackedHistory; + pub use base::{ CommandLineSearch, History, HistoryNavigationQuery, SearchDirection, SearchFilter, SearchQuery, }; pub use cursor::HistoryCursor; -pub use file_backed::{FileBackedHistory, HISTORY_SIZE}; pub use item::{HistoryItem, HistoryItemId, HistorySessionId}; -#[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] -pub use sqlite_backed::SqliteBackedHistory; + +pub use file_backed::{FileBackedHistory, HISTORY_SIZE}; diff --git a/src/history/sqlite_backed.rs b/src/history/sqlite_backed.rs index 2dde9fdd..3d2d3114 100644 --- a/src/history/sqlite_backed.rs +++ b/src/history/sqlite_backed.rs @@ -1,8 +1,3 @@ -use std::{path::PathBuf, time::Duration}; - -use chrono::{TimeZone, Utc}; -use rusqlite::{named_params, params, Connection, ToSql}; - use super::{ base::{CommandLineSearch, SearchDirection, SearchQuery}, History, HistoryItem, HistoryItemId, HistorySessionId, @@ -11,12 +6,14 @@ use crate::{ result::{ReedlineError, ReedlineErrorVariants}, Result, }; +use chrono::{TimeZone, Utc}; +use rusqlite::{named_params, params, Connection, ToSql}; +use std::{path::PathBuf, time::Duration}; const SQLITE_APPLICATION_ID: i32 = 1151497937; /// A history that stores the values to an SQLite database. -/// In addition to storing the command, the history can store an additional -/// arbitrary HistoryEntryContext, to add information such as a timestamp, -/// running directory, result... +/// In addition to storing the command, the history can store an additional arbitrary HistoryEntryContext, +/// to add information such as a timestamp, running directory, result... pub struct SqliteBackedHistory { db: rusqlite::Connection, session: Option, @@ -202,13 +199,8 @@ impl SqliteBackedHistory { })?; } let db = Connection::open(&file).map_err(map_sqlite_err)?; - Self::from_connection( - db, - session, - session.map(|s| chrono::Utc.timestamp_nanos(s.0)), - ) + Self::from_connection(db, session, session.map(|s| chrono::Utc.timestamp_nanos(s.0))) } - /// Creates a new history in memory pub fn in_memory() -> Result { Self::from_connection( @@ -217,7 +209,6 @@ impl SqliteBackedHistory { None, ) } - /// initialize a new database / migrate an existing one fn from_connection( db: Connection, @@ -382,7 +373,11 @@ impl SqliteBackedHistory { } let query = format!( - "SELECT {select_expression} FROM history WHERE ({wheres}) ORDER BY id {asc} {limit}" + "SELECT {select_expression} \ + FROM history \ + WHERE ({wheres}) \ + ORDER BY id {asc} \ + {limit}" ); (query, params) } diff --git a/src/lib.rs b/src/lib.rs index 27a161ab..ec31a401 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,12 +39,14 @@ //! ```rust //! // Configure reedline with custom keybindings //! -//! // Cargo.toml +//! //Cargo.toml //! // [dependencies] //! // crossterm = "*" //! -//! use crossterm::event::{KeyCode, KeyModifiers}; -//! use reedline::{default_emacs_keybindings, EditCommand, Emacs, Reedline, ReedlineEvent}; +//! use { +//! crossterm::event::{KeyCode, KeyModifiers}, +//! reedline::{default_emacs_keybindings, EditCommand, Reedline, Emacs, ReedlineEvent}, +//! }; //! //! let mut keybindings = default_emacs_keybindings(); //! keybindings.add_binding( @@ -68,7 +70,8 @@ //! FileBackedHistory::with_file(5, "history.txt".into()) //! .expect("Error configuring history with file"), //! ); -//! let mut line_editor = Reedline::create().with_history(history); +//! let mut line_editor = Reedline::create() +//! .with_history(history); //! ``` //! //! ## Integrate with custom syntax [`Highlighter`] @@ -79,13 +82,13 @@ //! use reedline::{ExampleHighlighter, Reedline}; //! //! let commands = vec![ -//! "test".into(), -//! "hello world".into(), -//! "hello world reedline".into(), -//! "this is the reedline crate".into(), +//! "test".into(), +//! "hello world".into(), +//! "hello world reedline".into(), +//! "this is the reedline crate".into(), //! ]; //! let mut line_editor = -//! Reedline::create().with_highlighter(Box::new(ExampleHighlighter::new(commands))); +//! Reedline::create().with_highlighter(Box::new(ExampleHighlighter::new(commands))); //! ``` //! //! ## Integrate with custom tab completion @@ -93,16 +96,13 @@ //! ```rust //! // Create a reedline object with tab completions support //! -//! use reedline::{ -//! default_emacs_keybindings, ColumnarMenu, DefaultCompleter, Emacs, KeyCode, KeyModifiers, -//! Reedline, ReedlineEvent, ReedlineMenu, -//! }; +//! use reedline::{default_emacs_keybindings, ColumnarMenu, DefaultCompleter, Emacs, KeyCode, KeyModifiers, Reedline, ReedlineEvent, ReedlineMenu}; //! //! let commands = vec![ -//! "test".into(), -//! "hello world".into(), -//! "hello world reedline".into(), -//! "this is the reedline crate".into(), +//! "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 @@ -131,15 +131,19 @@ //! ```rust //! // Create a reedline object with in-line hint support //! -//! // Cargo.toml +//! //Cargo.toml //! // [dependencies] //! // nu-ansi-term = "*" //! -//! use nu_ansi_term::{Color, Style}; -//! use reedline::{DefaultHinter, Reedline}; +//! use { +//! nu_ansi_term::{Color, Style}, +//! reedline::{DefaultHinter, Reedline}, +//! }; +//! //! //! let mut line_editor = Reedline::create().with_hinter(Box::new( -//! DefaultHinter::default().with_style(Style::new().italic().fg(Color::LightGray)), +//! DefaultHinter::default() +//! .with_style(Style::new().italic().fg(Color::LightGray)), //! )); //! ``` //! @@ -173,35 +177,27 @@ //! //! ## Crate features //! -//! - `clipboard`: Enable support to use the `SystemClipboard`. Enabling this -//! feature will return a `SystemClipboard` instead of a local clipboard when -//! calling `get_default_clipboard()`. +//! - `clipboard`: Enable support to use the `SystemClipboard`. Enabling this feature will return a `SystemClipboard` instead of a local clipboard when calling `get_default_clipboard()`. //! - `bashisms`: Enable support for special text sequences that recall components from the history. e.g. `!!` and `!$`. For use in shells like `bash` or [`nushell`](https://nushell.sh). -//! - `sqlite`: Provides the `SqliteBackedHistory` to store richer information -//! in the history. Statically links the required sqlite version. -//! - `sqlite-dynlib`: Alternative to the feature `sqlite`. Will not statically -//! link. Requires `sqlite >= 3.38` to link dynamically! -//! - `external_printer`: **Experimental:** Thread-safe `ExternalPrinter` handle -//! to print lines from concurrently running threads. +//! - `sqlite`: Provides the `SqliteBackedHistory` to store richer information in the history. Statically links the required sqlite version. +//! - `sqlite-dynlib`: Alternative to the feature `sqlite`. Will not statically link. Requires `sqlite >= 3.38` to link dynamically! +//! - `external_printer`: **Experimental:** Thread-safe `ExternalPrinter` handle to print lines from concurrently running threads. //! //! ## Are we prompt yet? (Development status) //! //! Nushell has now all the basic features to become the primary line editor for [nushell](https://github.com/nushell/nushell //! ) //! -//! - General editing functionality, that should feel familiar coming from other -//! shells (e.g. bash, fish, zsh). +//! - General editing functionality, that should feel familiar coming from other shells (e.g. bash, fish, zsh). //! - Configurable keybindings (emacs-style bindings and basic vi-style). //! - Configurable prompt //! - Content-aware syntax highlighting. //! - Autocompletion (With graphical selection menu or simple cycling inline). -//! - History with interactive search options (optionally persists to file, can -//! support multilple sessions accessing the same file) +//! - History with interactive search options (optionally persists to file, can support multilple sessions accessing the same file) //! - Fish-style history autosuggestion hints //! - Undo support. //! - Clipboard integration -//! - Line completeness validation for seamless entry of multiline command -//! sequences. +//! - Line completeness validation for seamless entry of multiline command sequences. //! //! ### Areas for future improvements //! @@ -209,10 +205,8 @@ //! - [ ] Easier keybinding configuration //! - [ ] Support for more advanced vi commands //! - [ ] Visual selection -//! - [ ] Smooth experience if completion or prompt content takes long to -//! compute -//! - [ ] Support for a concurrent output stream from background tasks to be -//! displayed, while the input prompt is active. ("Full duplex" mode) +//! - [ ] Smooth experience if completion or prompt content takes long to compute +//! - [ ] Support for a concurrent output stream from background tasks to be displayed, while the input prompt is active. ("Full duplex" mode) //! //! For more ideas check out the [feature discussion](https://github.com/nushell/reedline/issues/63) or hop on the `#reedline` channel of the [nushell discord](https://discordapp.com/invite/NtAbbGn). //! @@ -227,11 +221,13 @@ //! For currently more mature Rust line editing check out: //! //! - [rustyline](https://crates.io/crates/rustyline) +//! #![warn(rustdoc::missing_crate_level_docs)] #![warn(missing_docs)] // #![deny(warnings)] mod core_editor; -pub use core_editor::{Editor, LineBuffer}; +pub use core_editor::Editor; +pub use core_editor::LineBuffer; mod enums; pub use enums::{EditCommand, ReedlineEvent, ReedlineRawEvent, Signal, UndoBehavior}; @@ -273,7 +269,8 @@ mod completion; pub use completion::{Completer, DefaultCompleter, Span, Suggestion}; mod hinter; -pub use hinter::{CwdAwareHinter, DefaultHinter, Hinter}; +pub use hinter::CwdAwareHinter; +pub use hinter::{DefaultHinter, Hinter}; mod validator; pub use validator::{DefaultValidator, ValidationResult, Validator}; @@ -289,16 +286,16 @@ pub use terminal_extensions::kitty_protocol_available; mod utils; mod external_printer; -// Reexport the key types to be independent from an explicit crossterm -// dependency. +pub use utils::{ + get_reedline_default_keybindings, get_reedline_edit_commands, + get_reedline_keybinding_modifiers, get_reedline_keycodes, get_reedline_prompt_edit_modes, + get_reedline_reedline_events, +}; + +// Reexport the key types to be independent from an explicit crossterm dependency. pub use crossterm::{ event::{KeyCode, KeyModifiers}, style::Color, }; #[cfg(feature = "external_printer")] pub use external_printer::ExternalPrinter; -pub use utils::{ - get_reedline_default_keybindings, get_reedline_edit_commands, - get_reedline_keybinding_modifiers, get_reedline_keycodes, get_reedline_prompt_edit_modes, - get_reedline_reedline_events, -}; diff --git a/src/menu/columnar_menu.rs b/src/menu/columnar_menu.rs index 66cffcb1..ec391777 100644 --- a/src/menu/columnar_menu.rs +++ b/src/menu/columnar_menu.rs @@ -1,10 +1,9 @@ -use nu_ansi_term::{ansi::RESET, Style}; - use super::{menu_functions::find_common_string, Menu, MenuEvent, MenuTextStyle}; use crate::{ core_editor::Editor, menu_functions::string_difference, painting::Painter, Completer, Suggestion, UndoBehavior, }; +use nu_ansi_term::{ansi::RESET, Style}; /// Default values used as reference for the menu. These values are set during /// the initial declaration of the menu and are always kept as reference for the @@ -29,8 +28,7 @@ impl Default for DefaultColumnDetails { } /// Represents the actual column conditions of the menu. These conditions change -/// since they need to accommodate possible different line sizes for the column -/// values +/// since they need to accommodate possible different line sizes for the column values #[derive(Default)] struct ColumnDetails { /// Number of columns that the menu will have @@ -401,8 +399,7 @@ impl ColumnarMenu { ) } } else { - // If no ansi coloring is found, then the selection word is the line in - // uppercase + // If no ansi coloring is found, then the selection word is the line in uppercase let marker = if index == self.index() { ">" } else { "" }; let line = if let Some(description) = &suggestion.description { @@ -561,8 +558,8 @@ impl Menu for ColumnarMenu { // The working value for the menu are updated first before executing any of the // menu events // - // If there is at least one suggestion that contains a description, then the - // layout is changed to one column to fit the description + // If there is at least one suggestion that contains a description, then the layout + // is changed to one column to fit the description let exist_description = self .get_values() .iter() @@ -728,9 +725,10 @@ impl Menu for ColumnarMenu { #[cfg(test)] mod tests { - use super::*; use crate::Span; + use super::*; + macro_rules! partial_completion_tests { (name: $test_group_name:ident, completions: $completions:expr, test_cases: $($name:ident: $value:expr,)*) => { mod $test_group_name { diff --git a/src/menu/list_menu.rs b/src/menu/list_menu.rs index 958f8403..5b10e68d 100644 --- a/src/menu/list_menu.rs +++ b/src/menu/list_menu.rs @@ -1,16 +1,16 @@ -use std::{fmt::Write, iter::Sum}; - -use nu_ansi_term::{ansi::RESET, Style}; -use unicode_width::UnicodeWidthStr; - -use super::{ - menu_functions::{parse_selection_char, string_difference}, - Menu, MenuEvent, MenuTextStyle, -}; -use crate::{ - core_editor::Editor, - painting::{estimate_single_line_wraps, Painter}, - Completer, Suggestion, UndoBehavior, +use { + super::{ + menu_functions::{parse_selection_char, string_difference}, + Menu, MenuEvent, MenuTextStyle, + }, + crate::{ + core_editor::Editor, + painting::{estimate_single_line_wraps, Painter}, + Completer, Suggestion, UndoBehavior, + }, + nu_ansi_term::{ansi::RESET, Style}, + std::{fmt::Write, iter::Sum}, + unicode_width::UnicodeWidthStr, }; const SELECTION_CHAR: char = '!'; @@ -592,9 +592,8 @@ impl Menu for ListMenu { } } - /// Calculates the real required lines for the menu considering how many - /// lines wrap the terminal and if an entry is larger than the remaining - /// lines + /// Calculates the real required lines for the menu considering how many lines + /// wrap the terminal and if an entry is larger than the remaining lines fn menu_required_lines(&self, terminal_columns: u16) -> u16 { let mut entry_index = 0; self.get_values().iter().fold(0, |total_lines, suggestion| { @@ -611,8 +610,7 @@ impl Menu for ListMenu { }) + 1 } - /// Creates the menu representation as a string which will be painted by the - /// painter + /// Creates the menu representation as a string which will be painted by the painter fn menu_string(&self, _available_lines: u16, use_ansi_coloring: bool) -> String { let values_before_page = self.pages.iter().take(self.page).sum::().size; match self.pages.get(self.page) { diff --git a/src/menu/menu_functions.rs b/src/menu/menu_functions.rs index 6ca497e2..8d15d696 100644 --- a/src/menu/menu_functions.rs +++ b/src/menu/menu_functions.rs @@ -49,6 +49,7 @@ pub enum ParseAction { /// action: ParseAction::ForwardSearch /// } /// ) +/// /// ``` pub fn parse_selection_char(buffer: &str, marker: char) -> ParseResult { if buffer.is_empty() { diff --git a/src/menu/mod.rs b/src/menu/mod.rs index cb723336..fb559583 100644 --- a/src/menu/mod.rs +++ b/src/menu/mod.rs @@ -2,15 +2,13 @@ mod columnar_menu; mod list_menu; pub mod menu_functions; +use crate::core_editor::Editor; +use crate::History; +use crate::{completion::history::HistoryCompleter, painting::Painter, Completer, Suggestion}; pub use columnar_menu::ColumnarMenu; pub use list_menu::ListMenu; use nu_ansi_term::{Color, Style}; -use crate::{ - completion::history::HistoryCompleter, core_editor::Editor, painting::Painter, Completer, - History, Suggestion, -}; - /// Struct to store the menu style pub struct MenuTextStyle { /// Text style for selected text in a menu @@ -34,15 +32,13 @@ impl Default for MenuTextStyle { /// Defines all possible events that could happen with a menu. #[derive(Clone)] pub enum MenuEvent { - /// Activation event for the menu. When the bool is true it means that the - /// values have already being updated. This is true when the option - /// `quick_completions` is true + /// Activation event for the menu. When the bool is true it means that the values + /// have already being updated. This is true when the option `quick_completions` is true Activate(bool), /// Deactivation event Deactivate, /// Line buffer edit event. When the bool is true it means that the values - /// have already being updated. This is true when the option - /// `quick_completions` is true + /// have already being updated. This is true when the option `quick_completions` is true Edit(bool), /// Selecting next element in the menu NextElement, @@ -91,9 +87,9 @@ pub trait Menu: Send { /// Updates the values presented in the menu /// This function needs to be defined in the trait because when the menu is - /// activated or the `quick_completion` option is true, the len of the - /// values is calculated to know if there is only one value so it can be - /// selected immediately + /// activated or the `quick_completion` option is true, the len of the values + /// is calculated to know if there is only one value so it can be selected + /// immediately fn update_values(&mut self, editor: &mut Editor, completer: &mut dyn Completer); /// The working details of a menu are values that could change based on @@ -108,16 +104,14 @@ pub trait Menu: Send { painter: &Painter, ); - /// Indicates how to replace in the line buffer the selected value from the - /// menu + /// Indicates how to replace in the line buffer the selected value from the menu fn replace_in_buffer(&self, editor: &mut Editor); - /// Calculates the real required lines for the menu considering how many - /// lines wrap the terminal or if entries have multiple lines + /// Calculates the real required lines for the menu considering how many lines + /// wrap the terminal or if entries have multiple lines fn menu_required_lines(&self, terminal_columns: u16) -> u16; - /// Creates the menu representation as a string which will be painted by the - /// painter + /// Creates the menu representation as a string which will be painted by the painter fn menu_string(&self, available_lines: u16, use_ansi_coloring: bool) -> String; /// Minimum rows that should be displayed by the menu diff --git a/src/painting/painter.rs b/src/painting/painter.rs index d9f8f7a1..e6dc79a8 100644 --- a/src/painting/painter.rs +++ b/src/painting/painter.rs @@ -1,21 +1,23 @@ -use std::io::{Result, Write}; - -use crossterm::{ - cursor::{self, MoveTo, RestorePosition, SavePosition}, - style::{Attribute, Print, ResetColor, SetAttribute, SetForegroundColor}, - terminal::{self, Clear, ClearType}, - QueueableCommand, +use crate::{CursorConfig, PromptEditMode, PromptViMode}; + +use { + super::utils::{coerce_crlf, line_width}, + crate::{ + menu::{Menu, ReedlineMenu}, + painting::PromptLines, + Prompt, + }, + crossterm::{ + cursor::{self, MoveTo, RestorePosition, SavePosition}, + style::{Attribute, Print, ResetColor, SetAttribute, SetForegroundColor}, + terminal::{self, Clear, ClearType}, + QueueableCommand, + }, + std::io::{Result, Write}, }; #[cfg(feature = "external_printer")] use {crate::LineBuffer, crossterm::cursor::MoveUp}; -use super::utils::{coerce_crlf, line_width}; -use crate::{ - menu::{Menu, ReedlineMenu}, - painting::PromptLines, - CursorConfig, Prompt, PromptEditMode, PromptViMode, -}; - // Returns a string that skips N number of lines with the next offset of lines // An offset of 0 would return only one line after skipping the required lines fn skip_buffer_lines(string: &str, skip: usize, offset: Option) -> &str { @@ -126,8 +128,8 @@ impl Painter { /// prompt should scroll up and how much space is required to print all the /// lines for the buffer /// - /// Note. The `ScrollUp` operation in `crossterm` deletes lines from the top - /// of the screen. + /// Note. The `ScrollUp` operation in `crossterm` deletes lines from the top of + /// the screen. pub(crate) fn repaint_buffer( &mut self, prompt: &dyn Prompt, @@ -149,8 +151,7 @@ impl Painter { // Marking the painter state as larger buffer to avoid animations self.large_buffer = required_lines >= screen_height; - // Moving the start position of the cursor based on the size of the required - // lines + // Moving the start position of the cursor based on the size of the required lines if self.large_buffer { self.prompt_start_row = 0; } else if required_lines >= remaining_lines { @@ -313,9 +314,8 @@ impl Painter { let remaining_lines = screen_height.saturating_sub(cursor_distance); // Calculating the total lines before the cursor - // The -1 in the total_lines_before is there because the at least one line of - // the prompt indicator is printed in the same line as the first line of - // the buffer + // The -1 in the total_lines_before is there because the at least one line of the prompt + // indicator is printed in the same line as the first line of the buffer let prompt_lines = lines.prompt_lines_with_wrap(screen_width) as usize; let prompt_indicator = match menu { @@ -327,8 +327,7 @@ impl Painter { let before_cursor_lines = lines.before_cursor.lines().count(); let total_lines_before = prompt_lines + prompt_indicator_lines + before_cursor_lines - 1; - // Extra rows represent how many rows are "above" the visible area in the - // terminal + // Extra rows represent how many rows are "above" the visible area in the terminal let extra_rows = (total_lines_before).saturating_sub(screen_height as usize); // print our prompt with color @@ -356,9 +355,9 @@ impl Painter { self.stdout.queue(ResetColor)?; } - // The minimum number of lines from the menu are removed from the buffer if - // there is no more space to print the menu. This will only happen if - // the cursor is at the last line and it is a large buffer + // The minimum number of lines from the menu are removed from the buffer if there is no more + // space to print the menu. This will only happen if the cursor is at the last line and + // it is a large buffer let offset = menu.and_then(|menu| { if cursor_distance >= screen_height.saturating_sub(1) { let rows = lines @@ -384,10 +383,9 @@ impl Painter { self.print_menu(menu, lines, use_ansi_coloring)?; } else { // Selecting lines for the hint - // The -1 subtraction is done because the remaining lines consider the line - // where the cursor is located as a remaining line. That has to be - // removed to get the correct offset for the after-cursor and hint - // lines + // The -1 subtraction is done because the remaining lines consider the line where the + // cursor is located as a remaining line. That has to be removed to get the correct offset + // for the after-cursor and hint lines let offset = remaining_lines.saturating_sub(1) as usize; // Selecting lines after the cursor let after_cursor_skipped = skip_buffer_lines(&lines.after_cursor, 0, Some(offset)); @@ -411,27 +409,25 @@ impl Painter { && height <= prev_terminal_size.1 && width == prev_terminal_size.0 { - // The terminal got smaller in height but the start of the prompt is still - // visible The width didn't change + // The terminal got smaller in height but the start of the prompt is still visible + // The width didn't change return; } // Either: // - The terminal got larger in height - // - Note: if the terminal doesn't have sufficient history, this will leave a - // trail of previous prompts currently. - // - Note: if the the prompt contains multiple lines, this will leave a trail - // of previous prompts currently. - // - The terminal got smaller in height and the whole prompt is no longer - // visible - // - Note: if the the prompt contains multiple lines, this will leave a trail + // - Note: if the terminal doesn't have sufficient history, this will leave a trail // of previous prompts currently. + // - Note: if the the prompt contains multiple lines, this will leave a trail of + // previous prompts currently. + // - The terminal got smaller in height and the whole prompt is no longer visible + // - Note: if the the prompt contains multiple lines, this will leave a trail of + // previous prompts currently. // - The width changed self.prompt_start_row = height.saturating_sub(1); } - /// Writes `line` to the terminal with a following carriage return and - /// newline + /// Writes `line` to the terminal with a following carriage return and newline pub(crate) fn paint_line(&mut self, line: &str) -> Result<()> { self.stdout.queue(Print(line))?.queue(Print("\r\n"))?; @@ -472,8 +468,8 @@ impl Painter { } // The prompt is moved to the end of the buffer after the event was handled - // If the prompt is in the middle of a multiline buffer, then the output to - // stdout could overwrite the buffer writing + // If the prompt is in the middle of a multiline buffer, then the output to stdout + // could overwrite the buffer writing pub(crate) fn move_cursor_to_end(&mut self) -> Result<()> { let final_row = self.prompt_start_row + self.last_required_lines; let scroll = final_row.saturating_sub(self.screen_height() - 1); @@ -541,16 +537,16 @@ impl Painter { /// Queue scroll of `num` lines to `self.stdout`. /// - /// On some platforms and terminals (e.g. windows terminal, alacritty on - /// windows and linux) using special escape sequence '\[eS' - /// (provided by [`ScrollUp`]) does not put lines that go offscreen in - /// scrollback history. This method prints newlines near the edge of screen (which always works) instead. See [here](https://github.com/nushell/nushell/issues/9166) + /// On some platforms and terminals (e.g. windows terminal, alacritty on windows and linux) + /// using special escape sequence '\[eS' (provided by [`ScrollUp`]) does not put lines + /// that go offscreen in scrollback history. This method prints newlines near the edge of screen + /// (which always works) instead. See [here](https://github.com/nushell/nushell/issues/9166) /// for more info on subject. /// /// ## Note - /// This method does not return cursor to the original position and leaves - /// it at the first column of last line. **Be sure to use [`MoveTo`] - /// afterwards if this is not the desired location** + /// This method does not return cursor to the original position and leaves it at the first + /// column of last line. **Be sure to use [`MoveTo`] afterwards if this is not the desired + /// location** fn queue_universal_scroll(&mut self, num: u16) -> Result<()> { // If cursor is not near end of screen printing new will not scroll terminal. // Move it to the last line to ensure that every newline results in scroll @@ -564,9 +560,8 @@ impl Painter { #[cfg(test)] mod tests { - use pretty_assertions::assert_eq; - use super::*; + use pretty_assertions::assert_eq; #[test] fn test_skip_lines() { diff --git a/src/painting/prompt_lines.rs b/src/painting/prompt_lines.rs index f319b2ef..30c1ca61 100644 --- a/src/painting/prompt_lines.rs +++ b/src/painting/prompt_lines.rs @@ -1,11 +1,10 @@ -use std::borrow::Cow; - use super::utils::{coerce_crlf, estimate_required_lines, line_width}; use crate::{ menu::{Menu, ReedlineMenu}, prompt::PromptEditMode, Prompt, PromptHistorySearch, }; +use std::borrow::Cow; /// Aggregate of prompt and input string used by `Painter` pub(crate) struct PromptLines<'prompt> { diff --git a/src/painting/styled_text.rs b/src/painting/styled_text.rs index 128ee2e5..eeb88071 100644 --- a/src/painting/styled_text.rs +++ b/src/painting/styled_text.rs @@ -1,10 +1,10 @@ use nu_ansi_term::Style; -use super::utils::strip_ansi; use crate::Prompt; -/// A representation of a buffer with styling, used for doing syntax -/// highlighting +use super::utils::strip_ansi; + +/// A representation of a buffer with styling, used for doing syntax highlighting pub struct StyledText { /// The component, styled parts of the text pub buffer: Vec<(Style, String)>, @@ -27,11 +27,11 @@ impl StyledText { self.buffer.push(styled_string); } - /// Render the styled string. We use the insertion point to render around so - /// that we can properly write out the styled string to the screen and - /// find the correct place to put the cursor. This assumes a logic that - /// prints the first part of the string, saves the cursor position, - /// prints the second half, and then restores the cursor position + /// Render the styled string. We use the insertion point to render around so that + /// we can properly write out the styled string to the screen and find the correct + /// place to put the cursor. This assumes a logic that prints the first part of the + /// string, saves the cursor position, prints the second half, and then restores + /// the cursor position /// /// Also inserts the multiline continuation prompt pub fn render_around_insertion_point( diff --git a/src/painting/utils.rs b/src/painting/utils.rs index 035ae4a4..b0df1228 100644 --- a/src/painting/utils.rs +++ b/src/painting/utils.rs @@ -1,5 +1,4 @@ use std::borrow::Cow; - use unicode_width::UnicodeWidthStr; /// Ensures input uses CRLF line endings. @@ -73,11 +72,10 @@ pub(crate) fn line_width(line: &str) -> usize { #[cfg(test)] mod test { + use super::*; use pretty_assertions::assert_eq; use rstest::rstest; - use super::*; - #[rstest] #[case("sentence\nsentence", "sentence\r\nsentence")] #[case("sentence\r\nsentence", "sentence\r\nsentence")] diff --git a/src/prompt/base.rs b/src/prompt/base.rs index 5d6914d9..01d91658 100644 --- a/src/prompt/base.rs +++ b/src/prompt/base.rs @@ -1,12 +1,13 @@ -use std::{ - borrow::Cow, - fmt::{Display, Formatter}, +use { + crossterm::style::Color, + serde::{Deserialize, Serialize}, + std::{ + borrow::Cow, + fmt::{Display, Formatter}, + }, + strum_macros::EnumIter, }; -use crossterm::style::Color; -use serde::{Deserialize, Serialize}; -use strum_macros::EnumIter; - /// The default color for the prompt, indicator, and right prompt pub static DEFAULT_PROMPT_COLOR: Color = Color::Green; pub static DEFAULT_PROMPT_MULTILINE_COLOR: nu_ansi_term::Color = nu_ansi_term::Color::LightBlue; @@ -87,8 +88,7 @@ pub trait Prompt: Send { fn render_prompt_left(&self) -> Cow; /// Provide content of the right full prompt fn render_prompt_right(&self) -> Cow; - /// 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; /// Indicator to show before explicit new lines fn render_prompt_multiline_indicator(&self) -> Cow; diff --git a/src/prompt/default.rs b/src/prompt/default.rs index f21c46ca..f56ae903 100644 --- a/src/prompt/default.rs +++ b/src/prompt/default.rs @@ -1,9 +1,10 @@ -use std::{borrow::Cow, env}; - -use chrono::Local; - use crate::{Prompt, PromptEditMode, PromptHistorySearch, PromptHistorySearchStatus, PromptViMode}; +use { + chrono::Local, + std::{borrow::Cow, env}, +}; + /// The default prompt indicator pub static DEFAULT_PROMPT_INDICATOR: &str = "〉"; pub static DEFAULT_VI_INSERT_PROMPT_INDICATOR: &str = ": "; @@ -21,8 +22,7 @@ pub struct DefaultPrompt { pub right_prompt: DefaultPromptSegment, } -/// A struct to control the appearance of the left or right prompt in a -/// [`DefaultPrompt`] +/// A struct to control the appearance of the left or right prompt in a [`DefaultPrompt`] #[derive(Clone)] pub enum DefaultPromptSegment { /// A basic user-defined prompt (i.e. just text) @@ -82,8 +82,8 @@ impl Prompt for DefaultPrompt { PromptHistorySearchStatus::Passing => "", PromptHistorySearchStatus::Failing => "failing ", }; - // NOTE: magic strings, given there is logic on how these compose I am not sure - // if it is worth extracting in to static constant + // NOTE: magic strings, given there is logic on how these compose I am not sure if it + // is worth extracting in to static constant Cow::Owned(format!( "({}reverse-search: {}) ", prefix, history_search.term @@ -101,10 +101,9 @@ impl Default for DefaultPrompt { } impl DefaultPrompt { - /// Constructor for the default prompt, which takes a configurable left and - /// right prompt. For less customization, use - /// [`DefaultPrompt::default`]. For more fine-tuned configuration, - /// implement the [`Prompt`] trait. + /// Constructor for the default prompt, which takes a configurable left and right prompt. + /// For less customization, use [`DefaultPrompt::default`]. + /// For more fine-tuned configuration, implement the [`Prompt`] trait. pub const fn new( left_prompt: DefaultPromptSegment, right_prompt: DefaultPromptSegment, diff --git a/src/prompt/mod.rs b/src/prompt/mod.rs index 6159853f..83d2e3b5 100644 --- a/src/prompt/mod.rs +++ b/src/prompt/mod.rs @@ -4,4 +4,5 @@ mod default; pub use base::{ Prompt, PromptEditMode, PromptHistorySearch, PromptHistorySearchStatus, PromptViMode, }; + pub use default::{DefaultPrompt, DefaultPromptSegment}; diff --git a/src/result.rs b/src/result.rs index 5daf6f78..3651a5ae 100644 --- a/src/result.rs +++ b/src/result.rs @@ -1,5 +1,4 @@ use std::fmt::Display; - use thiserror::Error; /// non-public (for now) @@ -31,6 +30,5 @@ impl Display for ReedlineError { } impl std::error::Error for ReedlineError {} -/// Standard [`std::result::Result`], with [`ReedlineError`] as the error -/// variant +/// Standard [`std::result::Result`], with [`ReedlineError`] as the error variant pub type Result = std::result::Result; diff --git a/src/terminal_extensions/bracketed_paste.rs b/src/terminal_extensions/bracketed_paste.rs index 7b3517d7..b6601cd8 100644 --- a/src/terminal_extensions/bracketed_paste.rs +++ b/src/terminal_extensions/bracketed_paste.rs @@ -13,14 +13,12 @@ impl BracketedPasteGuard { pub fn set(&mut self, enable: bool) { self.enabled = enable; } - pub fn enter(&mut self) { if self.enabled && !self.active { let _ = execute!(std::io::stdout(), event::EnableBracketedPaste); self.active = true; } } - pub fn exit(&mut self) { if self.active { let _ = execute!(std::io::stdout(), event::DisableBracketedPaste); diff --git a/src/terminal_extensions/kitty.rs b/src/terminal_extensions/kitty.rs index 6354e50f..e6bdd637 100644 --- a/src/terminal_extensions/kitty.rs +++ b/src/terminal_extensions/kitty.rs @@ -1,7 +1,6 @@ use crossterm::{event, execute}; -/// Helper managing proper setup and teardown of the kitty keyboard enhancement -/// protocol +/// Helper managing proper setup and teardown of the kitty keyboard enhancement protocol /// /// Note that, currently, only the following support this protocol: /// * [kitty terminal](https://sw.kovidgoyal.net/kitty/) @@ -23,7 +22,6 @@ impl KittyProtocolGuard { pub fn set(&mut self, enable: bool) { self.enabled = enable && super::kitty_protocol_available(); } - pub fn enter(&mut self) { if self.enabled && !self.active { let _ = execute!( @@ -36,7 +34,6 @@ impl KittyProtocolGuard { self.active = true; } } - pub fn exit(&mut self) { if self.active { let _ = execute!(std::io::stdout(), event::PopKeyboardEnhancementFlags); diff --git a/src/utils/query.rs b/src/utils/query.rs index 59e98276..2b762e9a 100644 --- a/src/utils/query.rs +++ b/src/utils/query.rs @@ -1,12 +1,10 @@ -use std::fmt::{Display, Formatter}; - -use crossterm::event::KeyCode; -use strum::IntoEnumIterator; - use crate::{ default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings, EditCommand, Keybindings, PromptEditMode, ReedlineEvent, }; +use crossterm::event::KeyCode; +use std::fmt::{Display, Formatter}; +use strum::IntoEnumIterator; struct ReedLineCrossTermKeyCode(crossterm::event::KeyCode); impl ReedLineCrossTermKeyCode { @@ -113,9 +111,9 @@ pub fn get_reedline_edit_commands() -> Vec { EditCommand::iter().map(|edit| edit.to_string()).collect() } -/// Get the default keybindings and return a `Vec<(String, String, String, -/// String)>` where String 1 is `mode`, String 2 is `key_modifiers`, String 3 is -/// `key_code`, and Sting 4 is `event` +/// Get the default keybindings and return a `Vec<(String, String, String, String)>` +/// where String 1 is `mode`, String 2 is `key_modifiers`, String 3 is `key_code`, and +/// Sting 4 is `event` pub fn get_reedline_default_keybindings() -> Vec<(String, String, String, String)> { let options = vec![ ("emacs", default_emacs_keybindings()), diff --git a/src/utils/text_manipulation.rs b/src/utils/text_manipulation.rs index 295da23a..0428180b 100644 --- a/src/utils/text_manipulation.rs +++ b/src/utils/text_manipulation.rs @@ -12,9 +12,8 @@ pub fn remove_last_grapheme(string: &str) -> &str { #[cfg(test)] mod test { - use pretty_assertions::assert_eq; - use super::*; + use pretty_assertions::assert_eq; #[test] fn remove_last_char_works_with_empty_string() { diff --git a/src/validator/default.rs b/src/validator/default.rs index 3c28f89c..f53b186e 100644 --- a/src/validator/default.rs +++ b/src/validator/default.rs @@ -37,9 +37,8 @@ fn incomplete_brackets(line: &str) -> bool { #[cfg(test)] mod test { - use rstest::rstest; - use super::*; + use rstest::rstest; #[rstest] #[case("(([[]]))", false)] diff --git a/src/validator/mod.rs b/src/validator/mod.rs index c2f1a696..588e3794 100644 --- a/src/validator/mod.rs +++ b/src/validator/mod.rs @@ -1,11 +1,10 @@ mod default; pub use default::DefaultValidator; -/// The syntax validation trait. Implementers of this trait will check to see if -/// the current input is incomplete and spans multiple lines +/// The syntax validation trait. Implementers of this trait will check to see if the current input +/// is incomplete and spans multiple lines pub trait Validator: Send { - /// The action that will handle the current buffer as a line and return the - /// corresponding validation + /// The action that will handle the current buffer as a line and return the corresponding validation fn validate(&self, line: &str) -> ValidationResult; } From c0890772dd5f4c5507e36f53ca9c1134b5a76c3c Mon Sep 17 00:00:00 2001 From: Admin Date: Mon, 4 Dec 2023 23:22:00 +0100 Subject: [PATCH 7/7] Another cargo fmt + clippy fix. --- examples/demo.rs | 7 ++----- src/edit_mode/vi/command.rs | 2 +- src/history/sqlite_backed.rs | 6 +++++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/examples/demo.rs b/examples/demo.rs index 937db0dc..347fe3d1 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -34,11 +34,8 @@ fn main() -> std::io::Result<()> { #[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))] let history = Box::new( - reedline::SqliteBackedHistory::with_file( - "history.sqlite3".into(), - history_session_id, - ) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?, + reedline::SqliteBackedHistory::with_file("history.sqlite3".into(), history_session_id) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?, ); #[cfg(not(any(feature = "sqlite", feature = "sqlite-dynlib")))] let history = Box::new(FileBackedHistory::with_file(50, "history.txt".into())?); diff --git a/src/edit_mode/vi/command.rs b/src/edit_mode/vi/command.rs index e86f9f31..598867ba 100644 --- a/src/edit_mode/vi/command.rs +++ b/src/edit_mode/vi/command.rs @@ -122,7 +122,7 @@ impl Command { matches!(self, Command::Delete | Command::Change) } - pub fn to_reedline(&self, vi_state: &mut Vi) -> Vec { + pub fn to_reedline(&self, vi_state: &Vi) -> Vec { match self { Self::EnterViInsert => vec![ReedlineOption::Event(ReedlineEvent::Repaint)], Self::EnterViAppend => vec![ReedlineOption::Edit(EditCommand::MoveRight)], diff --git a/src/history/sqlite_backed.rs b/src/history/sqlite_backed.rs index 3d2d3114..1ecf6a71 100644 --- a/src/history/sqlite_backed.rs +++ b/src/history/sqlite_backed.rs @@ -199,7 +199,11 @@ impl SqliteBackedHistory { })?; } let db = Connection::open(&file).map_err(map_sqlite_err)?; - Self::from_connection(db, session, session.map(|s| chrono::Utc.timestamp_nanos(s.0))) + Self::from_connection( + db, + session, + session.map(|s| chrono::Utc.timestamp_nanos(s.0)), + ) } /// Creates a new history in memory pub fn in_memory() -> Result {