diff --git a/examples/custom_key_bindings.rs b/examples/custom_key_bindings.rs index 7d6624af7..c5a45505a 100644 --- a/examples/custom_key_bindings.rs +++ b/examples/custom_key_bindings.rs @@ -14,7 +14,7 @@ struct MyHelper(#[rustyline(Hinter)] HistoryHinter); impl Highlighter for MyHelper { fn highlight_prompt<'b, 's: 'b, 'p: 'b>( - &'s self, + &'s mut self, prompt: &'p str, default: bool, ) -> Cow<'b, str> { @@ -25,7 +25,7 @@ impl Highlighter for MyHelper { } } - fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { + fn highlight_hint<'h>(&mut self, hint: &'h str) -> Cow<'h, str> { Owned(format!("\x1b[1m{hint}\x1b[m")) } } diff --git a/examples/diy_hints.rs b/examples/diy_hints.rs index af8fadc80..dc061448f 100644 --- a/examples/diy_hints.rs +++ b/examples/diy_hints.rs @@ -6,7 +6,7 @@ use rustyline::Context; use rustyline::{Completer, Helper, Highlighter, Validator}; use rustyline::{Editor, Result}; -#[derive(Completer, Helper, Validator, Highlighter)] +#[derive(Completer, Helper, Highlighter, Validator)] struct DIYHinter { // It's simple example of rustyline, for more efficient, please use ** radix trie ** hints: HashSet, @@ -52,7 +52,7 @@ impl CommandHint { impl Hinter for DIYHinter { type Hint = CommandHint; - fn hint(&self, line: &str, pos: usize, _ctx: &Context<'_>) -> Option { + fn hint(&mut self, line: &str, pos: usize, _ctx: &Context<'_>) -> Option { if line.is_empty() || pos < line.len() { return None; } diff --git a/examples/example.rs b/examples/example.rs index cc3e2a0cc..2fa7abff0 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -8,7 +8,7 @@ use rustyline::validate::MatchingBracketValidator; use rustyline::{Cmd, CompletionType, Config, EditMode, Editor, KeyEvent}; use rustyline::{Completer, Helper, Hinter, Validator}; -#[derive(Helper, Completer, Hinter, Validator)] +#[derive(Completer, Helper, Hinter, Validator)] struct MyHelper { #[rustyline(Completer)] completer: FilenameCompleter, @@ -22,7 +22,7 @@ struct MyHelper { impl Highlighter for MyHelper { fn highlight_prompt<'b, 's: 'b, 'p: 'b>( - &'s self, + &'s mut self, prompt: &'p str, default: bool, ) -> Cow<'b, str> { @@ -33,15 +33,15 @@ impl Highlighter for MyHelper { } } - fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { + fn highlight_hint<'h>(&mut self, hint: &'h str) -> Cow<'h, str> { Owned("\x1b[1m".to_owned() + hint + "\x1b[m") } - fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> { + fn highlight<'l>(&mut self, line: &'l str, pos: usize) -> Cow<'l, str> { self.highlighter.highlight(line, pos) } - fn highlight_char(&self, line: &str, pos: usize, forced: bool) -> bool { + fn highlight_char(&mut self, line: &str, pos: usize, forced: bool) -> bool { self.highlighter.highlight_char(line, pos, forced) } } diff --git a/examples/input_validation.rs b/examples/input_validation.rs index 583d94f62..76b83ead2 100644 --- a/examples/input_validation.rs +++ b/examples/input_validation.rs @@ -6,7 +6,7 @@ use rustyline::{Editor, Result}; struct InputValidator {} impl Validator for InputValidator { - fn validate(&self, ctx: &mut ValidationContext) -> Result { + fn validate(&mut self, ctx: &mut ValidationContext) -> Result { use ValidationResult::{Incomplete, Invalid, Valid}; let input = ctx.input(); let result = if !input.starts_with("SELECT") { diff --git a/examples/read_password.rs b/examples/read_password.rs index d669d511b..73f11c414 100644 --- a/examples/read_password.rs +++ b/examples/read_password.rs @@ -11,7 +11,7 @@ struct MaskingHighlighter { } impl Highlighter for MaskingHighlighter { - fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> { + fn highlight<'l>(&mut self, line: &'l str, _pos: usize) -> Cow<'l, str> { use unicode_width::UnicodeWidthStr; if self.masking { Owned(" ".repeat(line.width())) @@ -20,7 +20,7 @@ impl Highlighter for MaskingHighlighter { } } - fn highlight_char(&self, _line: &str, _pos: usize, _forced: bool) -> bool { + fn highlight_char(&mut self, _line: &str, _pos: usize, _forced: bool) -> bool { self.masking } } diff --git a/rustyline-derive/src/lib.rs b/rustyline-derive/src/lib.rs index c25e6165b..7b82d9bc4 100644 --- a/rustyline-derive/src/lib.rs +++ b/rustyline-derive/src/lib.rs @@ -51,16 +51,16 @@ pub fn completer_macro_derive(input: TokenStream) -> TokenStream { type Candidate = <#field_type as ::rustyline::completion::Completer>::Candidate; fn complete( - &self, + &mut self, line: &str, pos: usize, ctx: &::rustyline::Context<'_>, ) -> ::rustyline::Result<(usize, ::std::vec::Vec)> { - ::rustyline::completion::Completer::complete(&self.#field_name_or_index, line, pos, ctx) + ::rustyline::completion::Completer::complete(&mut self.#field_name_or_index, line, pos, ctx) } - fn update(&self, line: &mut ::rustyline::line_buffer::LineBuffer, start: usize, elected: &str, cl: &mut ::rustyline::Changeset) { - ::rustyline::completion::Completer::update(&self.#field_name_or_index, line, start, elected, cl) + fn update(&mut self, line: &mut ::rustyline::line_buffer::LineBuffer, start: usize, elected: &str, cl: &mut ::rustyline::Changeset) { + ::rustyline::completion::Completer::update(&mut self.#field_name_or_index, line, start, elected, cl) } } } @@ -102,32 +102,32 @@ pub fn highlighter_macro_derive(input: TokenStream) -> TokenStream { quote! { #[automatically_derived] impl #impl_generics ::rustyline::highlight::Highlighter for #name #ty_generics #where_clause { - fn highlight<'l>(&self, line: &'l str, pos: usize) -> ::std::borrow::Cow<'l, str> { - ::rustyline::highlight::Highlighter::highlight(&self.#field_name_or_index, line, pos) + fn highlight<'l>(&mut self, line: &'l str, pos: usize) -> ::std::borrow::Cow<'l, str> { + ::rustyline::highlight::Highlighter::highlight(&mut self.#field_name_or_index, line, pos) } fn highlight_prompt<'b, 's: 'b, 'p: 'b>( - &'s self, + &'s mut self, prompt: &'p str, default: bool, ) -> ::std::borrow::Cow<'b, str> { - ::rustyline::highlight::Highlighter::highlight_prompt(&self.#field_name_or_index, prompt, default) + ::rustyline::highlight::Highlighter::highlight_prompt(&mut self.#field_name_or_index, prompt, default) } - fn highlight_hint<'h>(&self, hint: &'h str) -> ::std::borrow::Cow<'h, str> { - ::rustyline::highlight::Highlighter::highlight_hint(&self.#field_name_or_index, hint) + fn highlight_hint<'h>(&mut self, hint: &'h str) -> ::std::borrow::Cow<'h, str> { + ::rustyline::highlight::Highlighter::highlight_hint(&mut self.#field_name_or_index, hint) } fn highlight_candidate<'c>( - &self, + &mut self, candidate: &'c str, completion: ::rustyline::config::CompletionType, ) -> ::std::borrow::Cow<'c, str> { - ::rustyline::highlight::Highlighter::highlight_candidate(&self.#field_name_or_index, candidate, completion) + ::rustyline::highlight::Highlighter::highlight_candidate(&mut self.#field_name_or_index, candidate, completion) } - fn highlight_char(&self, line: &str, pos: usize, forced: bool) -> bool { - ::rustyline::highlight::Highlighter::highlight_char(&self.#field_name_or_index, line, pos, forced) + fn highlight_char(&mut self, line: &str, pos: usize, forced: bool) -> bool { + ::rustyline::highlight::Highlighter::highlight_char(&mut self.#field_name_or_index, line, pos, forced) } } } @@ -156,8 +156,8 @@ pub fn hinter_macro_derive(input: TokenStream) -> TokenStream { impl #impl_generics ::rustyline::hint::Hinter for #name #ty_generics #where_clause { type Hint = <#field_type as ::rustyline::hint::Hinter>::Hint; - fn hint(&self, line: &str, pos: usize, ctx: &::rustyline::Context<'_>) -> ::std::option::Option { - ::rustyline::hint::Hinter::hint(&self.#field_name_or_index, line, pos, ctx) + fn hint(&mut self, line: &str, pos: usize, ctx: &::rustyline::Context<'_>) -> ::std::option::Option { + ::rustyline::hint::Hinter::hint(&mut self.#field_name_or_index, line, pos, ctx) } } } @@ -185,14 +185,14 @@ pub fn validator_macro_derive(input: TokenStream) -> TokenStream { #[automatically_derived] impl #impl_generics ::rustyline::validate::Validator for #name #ty_generics #where_clause { fn validate( - &self, + &mut self, ctx: &mut ::rustyline::validate::ValidationContext, ) -> ::rustyline::Result<::rustyline::validate::ValidationResult> { - ::rustyline::validate::Validator::validate(&self.#field_name_or_index, ctx) + ::rustyline::validate::Validator::validate(&mut self.#field_name_or_index, ctx) } - fn validate_while_typing(&self) -> bool { - ::rustyline::validate::Validator::validate_while_typing(&self.#field_name_or_index) + fn validate_while_typing(&mut self) -> bool { + ::rustyline::validate::Validator::validate_while_typing(&mut self.#field_name_or_index) } } } diff --git a/src/completion.rs b/src/completion.rs index 2d1971f01..b0161152f 100644 --- a/src/completion.rs +++ b/src/completion.rs @@ -90,7 +90,7 @@ pub trait Completer { /// /// ("ls /usr/loc", 11) => Ok((3, vec!["/usr/local/"])) fn complete( - &self, // FIXME should be `&mut self` + &mut self, line: &str, pos: usize, ctx: &Context<'_>, @@ -99,7 +99,7 @@ pub trait Completer { Ok((0, Vec::with_capacity(0))) } /// Updates the edited `line` with the `elected` candidate. - fn update(&self, line: &mut LineBuffer, start: usize, elected: &str, cl: &mut Changeset) { + fn update(&mut self, line: &mut LineBuffer, start: usize, elected: &str, cl: &mut Changeset) { let end = line.pos(); line.replace(start..end, elected, cl); } @@ -108,16 +108,22 @@ pub trait Completer { impl Completer for () { type Candidate = String; - fn update(&self, _line: &mut LineBuffer, _start: usize, _elected: &str, _cl: &mut Changeset) { + fn update( + &mut self, + _line: &mut LineBuffer, + _start: usize, + _elected: &str, + _cl: &mut Changeset, + ) { unreachable!(); } } -impl<'c, C: ?Sized + Completer> Completer for &'c C { +impl<'c, C: ?Sized + Completer> Completer for &'c mut C { type Candidate = C::Candidate; fn complete( - &self, + &mut self, line: &str, pos: usize, ctx: &Context<'_>, @@ -125,21 +131,29 @@ impl<'c, C: ?Sized + Completer> Completer for &'c C { (**self).complete(line, pos, ctx) } - fn update(&self, line: &mut LineBuffer, start: usize, elected: &str, cl: &mut Changeset) { + fn update(&mut self, line: &mut LineBuffer, start: usize, elected: &str, cl: &mut Changeset) { (**self).update(line, start, elected, cl); } } -macro_rules! box_completer { +macro_rules! rc_completer { ($($id: ident)*) => { $( impl Completer for $id { type Candidate = C::Candidate; - fn complete(&self, line: &str, pos: usize, ctx: &Context<'_>) -> Result<(usize, Vec)> { - (**self).complete(line, pos, ctx) + fn complete(&mut self, line: &str, pos: usize, ctx: &Context<'_>) -> Result<(usize, Vec)> { + if let Some(c) = $id::get_mut(self){ + c.complete(line, pos, ctx) + } else { + unreachable!() + } } - fn update(&self, line: &mut LineBuffer, start: usize, elected: &str, cl: &mut Changeset) { - (**self).update(line, start, elected, cl) + fn update(&mut self, line: &mut LineBuffer, start: usize, elected: &str, cl: &mut Changeset) { + if let Some(c) = $id::get_mut(self){ + c.update(line, start, elected, cl) + } else { + unreachable!() + } } } )* @@ -149,7 +163,23 @@ macro_rules! box_completer { use crate::undo::Changeset; use std::rc::Rc; use std::sync::Arc; -box_completer! { Box Rc Arc } +rc_completer! { Rc Arc } + +impl Completer for Box { + type Candidate = C::Candidate; + + fn complete( + &mut self, + line: &str, + pos: usize, + ctx: &Context<'_>, + ) -> Result<(usize, Vec)> { + (**self).complete(line, pos, ctx) + } + fn update(&mut self, line: &mut LineBuffer, start: usize, elected: &str, cl: &mut Changeset) { + (**self).update(line, start, elected, cl) + } +} /// A `Completer` for file and folder names. pub struct FilenameCompleter { @@ -256,7 +286,12 @@ impl Default for FilenameCompleter { impl Completer for FilenameCompleter { type Candidate = Pair; - fn complete(&self, line: &str, pos: usize, _ctx: &Context<'_>) -> Result<(usize, Vec)> { + fn complete( + &mut self, + line: &str, + pos: usize, + _ctx: &Context<'_>, + ) -> Result<(usize, Vec)> { self.complete_path(line, pos) } } diff --git a/src/edit.rs b/src/edit.rs index c231e4a42..adf3fb33e 100644 --- a/src/edit.rs +++ b/src/edit.rs @@ -32,7 +32,7 @@ pub struct State<'out, 'prompt, H: Helper> { saved_line_for_history: LineBuffer, // Current edited line before history browsing byte_buffer: [u8; 4], pub changes: Changeset, // changes to line, for undo/redo - pub helper: Option<&'out H>, + pub helper: Option<&'out mut H>, pub ctx: Context<'out>, // Give access to history for `hinter` pub hint: Option>, // last hint displayed pub highlight_char: bool, // `true` if a char has been highlighted @@ -49,7 +49,7 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> { pub fn new( out: &'out mut ::Writer, prompt: &'prompt str, - helper: Option<&'out H>, + helper: Option<&'out mut H>, ctx: Context<'out>, ) -> Self { let prompt_size = out.calculate_position(prompt, Position::default()); @@ -70,9 +70,12 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> { } } - pub fn highlighter(&self) -> Option<&dyn Highlighter> { + pub fn highlighter(&mut self) -> Option<&mut H> { if self.out.colors_enabled() { - self.helper.map(|h| h as &dyn Highlighter) + match self.helper { + Some(ref mut helper) => Some(*helper), + None => None, + } } else { None } @@ -168,11 +171,6 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> { Info::Hint => self.hint.as_ref().map(|h| h.display()), Info::Msg(msg) => msg, }; - let highlighter = if self.out.colors_enabled() { - self.helper.map(|h| h as &dyn Highlighter) - } else { - None - }; let new_layout = self .out @@ -186,7 +184,18 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> { info, &self.layout, &new_layout, - highlighter, + // it is same as `fn highlighter`, + // however `fn highlighter` will use entriely `self` as mutable + // but actually we only need `self.helper` to be mutable. + // Maybe use macro? + if self.out.colors_enabled() { + match self.helper { + Some(ref mut helper) => Some(*helper), + None => None, + } + } else { + None + }, )?; self.layout = new_layout; @@ -194,8 +203,8 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> { } pub fn hint(&mut self) { - if let Some(hinter) = self.helper { - let hint = hinter.hint(self.line.as_str(), self.line.pos(), &self.ctx); + if let Some(ref mut hinter) = self.helper { + let hint = (*hinter).hint(self.line.as_str(), self.line.pos(), &self.ctx); self.hint = match hint { Some(val) if !val.display().is_empty() => Some(Box::new(val) as Box), _ => None, @@ -206,7 +215,16 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> { } fn highlight_char(&mut self) -> bool { - if let Some(highlighter) = self.highlighter() { + // it is almost same as `fn highlighter`, only different is `Some(helper)` vs. `Some(*helper)` + let highlighter = if self.out.colors_enabled() { + match self.helper { + Some(ref mut helper) => Some(helper), + None => None, + } + } else { + None + }; + if let Some(highlighter) = highlighter { let highlight_char = highlighter.highlight_char(&self.line, self.line.pos(), self.forced_refresh); if highlight_char { @@ -229,9 +247,10 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> { } pub fn validate(&mut self) -> Result { - if let Some(validator) = self.helper { + if let Some(ref mut validator) = self.helper { self.changes.begin(); - let result = validator.validate(&mut ValidationContext::new(self))?; + let result = + (*validator).validate(&mut ValidationContext::new(&mut self.line))?; let corrected = self.changes.end(); match result { ValidationResult::Incomplete => {} @@ -256,9 +275,9 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> { } } -impl<'out, 'prompt, H: Helper> Invoke for State<'out, 'prompt, H> { +impl Invoke for LineBuffer { fn input(&self) -> &str { - self.line.as_str() + self.as_str() } } @@ -749,7 +768,7 @@ pub fn init_state<'out, H: Helper>( out: &'out mut ::Writer, line: &str, pos: usize, - helper: Option<&'out H>, + helper: Option<&'out mut H>, history: &'out crate::history::DefaultHistory, ) -> State<'out, 'static, H> { State { @@ -782,8 +801,8 @@ mod test { history.add("line0").unwrap(); history.add("line1").unwrap(); let line = "current edited line"; - let helper: Option<()> = None; - let mut s = init_state(&mut out, line, 6, helper.as_ref(), &history); + let mut helper: Option<()> = None; + let mut s = init_state(&mut out, line, 6, helper.as_mut(), &history); s.ctx.history_index = history.len(); for _ in 0..2 { diff --git a/src/highlight.rs b/src/highlight.rs index a21459e0a..358b9c2bf 100644 --- a/src/highlight.rs +++ b/src/highlight.rs @@ -14,14 +14,14 @@ pub trait Highlighter { /// /// For example, you can implement /// [blink-matching-paren](https://www.gnu.org/software/bash/manual/html_node/Readline-Init-File-Syntax.html). - fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> { + fn highlight<'l>(&mut self, line: &'l str, pos: usize) -> Cow<'l, str> { let _ = pos; Borrowed(line) } /// Takes the `prompt` and /// returns the highlighted version (with ANSI color). fn highlight_prompt<'b, 's: 'b, 'p: 'b>( - &'s self, + &'s mut self, prompt: &'p str, default: bool, ) -> Cow<'b, str> { @@ -30,7 +30,7 @@ pub trait Highlighter { } /// Takes the `hint` and /// returns the highlighted version (with ANSI color). - fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { + fn highlight_hint<'h>(&mut self, hint: &'h str) -> Cow<'h, str> { Borrowed(hint) } /// Takes the completion `candidate` and @@ -38,7 +38,7 @@ pub trait Highlighter { /// /// Currently, used only with `CompletionType::List`. fn highlight_candidate<'c>( - &self, + &mut self, candidate: &'c str, // FIXME should be Completer::Candidate completion: CompletionType, ) -> Cow<'c, str> { @@ -52,7 +52,7 @@ pub trait Highlighter { /// /// Used to optimize refresh when a character is inserted or the cursor is /// moved. - fn highlight_char(&self, line: &str, pos: usize, forced: bool) -> bool { + fn highlight_char(&mut self, line: &str, pos: usize, forced: bool) -> bool { let _ = (line, pos, forced); false } @@ -60,32 +60,32 @@ pub trait Highlighter { impl Highlighter for () {} -impl<'r, H: ?Sized + Highlighter> Highlighter for &'r H { - fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> { +impl<'r, H: ?Sized + Highlighter> Highlighter for &'r mut H { + fn highlight<'l>(&mut self, line: &'l str, pos: usize) -> Cow<'l, str> { (**self).highlight(line, pos) } fn highlight_prompt<'b, 's: 'b, 'p: 'b>( - &'s self, + &'s mut self, prompt: &'p str, default: bool, ) -> Cow<'b, str> { (**self).highlight_prompt(prompt, default) } - fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { + fn highlight_hint<'h>(&mut self, hint: &'h str) -> Cow<'h, str> { (**self).highlight_hint(hint) } fn highlight_candidate<'c>( - &self, + &mut self, candidate: &'c str, completion: CompletionType, ) -> Cow<'c, str> { (**self).highlight_candidate(candidate, completion) } - fn highlight_char(&self, line: &str, pos: usize, forced: bool) -> bool { + fn highlight_char(&mut self, line: &str, pos: usize, forced: bool) -> bool { (**self).highlight_char(line, pos, forced) } } @@ -109,7 +109,7 @@ impl MatchingBracketHighlighter { } impl Highlighter for MatchingBracketHighlighter { - fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> { + fn highlight<'l>(&mut self, line: &'l str, _pos: usize) -> Cow<'l, str> { if line.len() <= 1 { return Borrowed(line); } @@ -124,7 +124,7 @@ impl Highlighter for MatchingBracketHighlighter { Borrowed(line) } - fn highlight_char(&self, line: &str, pos: usize, forced: bool) -> bool { + fn highlight_char(&mut self, line: &str, pos: usize, forced: bool) -> bool { if forced { self.bracket.set(None); return false; diff --git a/src/hint.rs b/src/hint.rs index efff9357c..71ee0595d 100644 --- a/src/hint.rs +++ b/src/hint.rs @@ -30,7 +30,7 @@ pub trait Hinter { /// returns the string that should be displayed or `None` /// if no hint is available for the text the user currently typed. // TODO Validate: called while editing line but not while moving cursor. - fn hint(&self, line: &str, pos: usize, ctx: &Context<'_>) -> Option { + fn hint(&mut self, line: &str, pos: usize, ctx: &Context<'_>) -> Option { let _ = (line, pos, ctx); None } @@ -40,10 +40,10 @@ impl Hinter for () { type Hint = String; } -impl<'r, H: ?Sized + Hinter> Hinter for &'r H { +impl<'r, H: ?Sized + Hinter> Hinter for &'r mut H { type Hint = H::Hint; - fn hint(&self, line: &str, pos: usize, ctx: &Context<'_>) -> Option { + fn hint(&mut self, line: &str, pos: usize, ctx: &Context<'_>) -> Option { (**self).hint(line, pos, ctx) } } @@ -63,7 +63,7 @@ impl HistoryHinter { impl Hinter for HistoryHinter { type Hint = String; - fn hint(&self, line: &str, pos: usize, ctx: &Context<'_>) -> Option { + fn hint(&mut self, line: &str, pos: usize, ctx: &Context<'_>) -> Option { if line.is_empty() || pos < line.len() { return None; } @@ -96,7 +96,7 @@ mod test { pub fn empty_history() { let history = DefaultHistory::new(); let ctx = Context::new(&history); - let hinter = HistoryHinter {}; + let mut hinter = HistoryHinter {}; let hint = hinter.hint("test", 4, &ctx); assert_eq!(None, hint); } diff --git a/src/keymap.rs b/src/keymap.rs index 8564c163a..87d48e950 100644 --- a/src/keymap.rs +++ b/src/keymap.rs @@ -363,9 +363,9 @@ pub trait Invoke { //fn invoke(&mut self, cmd: Cmd) -> Result; } -impl Invoke for &str { +impl Invoke for String { fn input(&self) -> &str { - self + self.as_str() } } diff --git a/src/lib.rs b/src/lib.rs index a40db34db..23e8ab82f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,10 +84,12 @@ fn complete_line( use skim::prelude::{ unbounded, Skim, SkimItem, SkimItemReceiver, SkimItemSender, SkimOptionsBuilder, }; - - let completer = s.helper.unwrap(); // get a list of completions - let (start, candidates) = completer.complete(&s.line, s.line.pos(), &s.ctx)?; + let (start, candidates) = s + .helper + .as_mut() + .unwrap() + .complete(&s.line, s.line.pos(), &s.ctx)?; // if no completions, we are done if candidates.is_empty() { s.out.beep()?; @@ -109,7 +111,10 @@ fn complete_line( } else { Borrowed(candidate) };*/ - completer.update(&mut s.line, start, candidate, &mut s.changes); + s.helper + .as_mut() + .unwrap() + .update(&mut s.line, start, candidate, &mut s.changes); } else { // Restore current edited line s.line.update(&backup, backup_pos, &mut s.changes); @@ -152,7 +157,10 @@ fn complete_line( if let Some(lcp) = longest_common_prefix(&candidates) { // if we can extend the item, extend it if lcp.len() > s.line.pos() - start || candidates.len() == 1 { - completer.update(&mut s.line, start, lcp, &mut s.changes); + s.helper + .as_mut() + .unwrap() + .update(&mut s.line, start, lcp, &mut s.changes); s.refresh_line()?; } } @@ -247,7 +255,7 @@ fn complete_line( .downcast_ref::() // downcast to concrete type .expect("something wrong with downcast"); if let Some(candidate) = candidates.get(item.index) { - completer.update( + s.helper.as_mut().unwrap().update( &mut s.line, start, candidate.replacement(), @@ -482,7 +490,7 @@ fn apply_backspace_direct(input: &str) -> String { fn readline_direct( mut reader: impl BufRead, mut writer: impl Write, - validator: &Option, + mut validator: Option<&mut impl Validator>, ) -> Result { let mut input = String::new(); @@ -506,11 +514,10 @@ fn readline_direct( input = apply_backspace_direct(&input); - match validator.as_ref() { + match validator { None => return Ok(input), - Some(v) => { - let mut ctx = input.as_str(); - let mut ctx = validate::ValidationContext::new(&mut ctx); + Some(ref mut v) => { + let mut ctx = validate::ValidationContext::new(&mut input); match v.validate(&mut ctx)? { validate::ValidationResult::Valid(msg) => { @@ -550,7 +557,7 @@ where impl Helper for () {} -impl<'h, H: ?Sized + Helper> Helper for &'h H {} +impl<'h, H: ?Sized + Helper> Helper for &'h mut H {} /// Completion/suggestion context pub struct Context<'h> { @@ -659,7 +666,7 @@ impl Editor { stdout.write_all(prompt.as_bytes())?; stdout.flush()?; - readline_direct(io::stdin().lock(), io::stderr(), &self.helper) + readline_direct(io::stdin().lock(), io::stderr(), self.helper.as_mut()) } else if self.term.is_input_tty() { let (original_mode, term_key_map) = self.term.enable_raw_mode()?; let guard = Guard(&original_mode); @@ -675,7 +682,7 @@ impl Editor { } else { debug!(target: "rustyline", "stdin is not a tty"); // Not a tty: read from file / pipe. - readline_direct(io::stdin().lock(), io::stderr(), &self.helper) + readline_direct(io::stdin().lock(), io::stderr(), self.helper.as_mut()) } } @@ -693,7 +700,7 @@ impl Editor { self.kill_ring.reset(); // TODO recreate a new kill ring vs reset let ctx = Context::new(&self.history); - let mut s = State::new(&mut stdout, prompt, self.helper.as_ref(), ctx); + let mut s = State::new(&mut stdout, prompt, self.helper.as_mut(), ctx); let mut input_state = InputState::new(&self.config, &self.custom_bindings); diff --git a/src/test/mod.rs b/src/test/mod.rs index 14ff9052b..4dc952f89 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -30,7 +30,7 @@ impl Completer for SimpleCompleter { type Candidate = String; fn complete( - &self, + &mut self, line: &str, _pos: usize, _ctx: &Context<'_>, @@ -50,7 +50,7 @@ impl Completer for SimpleCompleter { impl Hinter for SimpleCompleter { type Hint = String; - fn hint(&self, _line: &str, _pos: usize, _ctx: &Context<'_>) -> Option { + fn hint(&mut self, _line: &str, _pos: usize, _ctx: &Context<'_>) -> Option { None } } @@ -63,8 +63,8 @@ impl Validator for SimpleCompleter {} fn complete_line() { let mut out = Sink::default(); let history = crate::history::DefaultHistory::new(); - let helper = Some(SimpleCompleter); - let mut s = init_state(&mut out, "rus", 3, helper.as_ref(), &history); + let mut helper = Some(SimpleCompleter); + let mut s = init_state(&mut out, "rus", 3, helper.as_mut(), &history); let config = Config::default(); let bindings = Bindings::new(); let mut input_state = InputState::new(&config, &bindings); @@ -85,8 +85,8 @@ fn complete_line() { fn complete_symbol() { let mut out = Sink::default(); let history = crate::history::DefaultHistory::new(); - let helper = Some(SimpleCompleter); - let mut s = init_state(&mut out, "\\hbar", 5, helper.as_ref(), &history); + let mut helper = Some(SimpleCompleter); + let mut s = init_state(&mut out, "\\hbar", 5, helper.as_mut(), &history); let config = Config::builder() .completion_type(CompletionType::List) .build(); @@ -188,7 +188,7 @@ fn test_readline_direct() { let output = readline_direct( Cursor::new("([)\n\u{0008}\n\n\r\n])".as_bytes()), Cursor::new(&mut write_buf), - &Some(crate::validate::MatchingBracketValidator::new()), + Some(&mut crate::validate::MatchingBracketValidator::new()), ); assert_eq!( diff --git a/src/tty/mod.rs b/src/tty/mod.rs index d5a0c7cf9..368aac0a2 100644 --- a/src/tty/mod.rs +++ b/src/tty/mod.rs @@ -46,14 +46,14 @@ pub trait Renderer { fn move_cursor(&mut self, old: Position, new: Position) -> Result<()>; /// Display `prompt`, line and cursor in terminal output - fn refresh_line( + fn refresh_line( &mut self, prompt: &str, line: &LineBuffer, hint: Option<&str>, old_layout: &Layout, new_layout: &Layout, - highlighter: Option<&dyn Highlighter>, + highlighter: Option<&mut H>, ) -> Result<()>; /// Compute layout for rendering prompt + line + some info (either hint, @@ -125,14 +125,14 @@ impl<'a, R: Renderer + ?Sized> Renderer for &'a mut R { (**self).move_cursor(old, new) } - fn refresh_line( + fn refresh_line( &mut self, prompt: &str, line: &LineBuffer, hint: Option<&str>, old_layout: &Layout, new_layout: &Layout, - highlighter: Option<&dyn Highlighter>, + highlighter: Option<&mut H>, ) -> Result<()> { (**self).refresh_line(prompt, line, hint, old_layout, new_layout, highlighter) } diff --git a/src/tty/test.rs b/src/tty/test.rs index 00ef42152..9544d1dac 100644 --- a/src/tty/test.rs +++ b/src/tty/test.rs @@ -100,14 +100,14 @@ impl Renderer for Sink { Ok(()) } - fn refresh_line( + fn refresh_line( &mut self, _prompt: &str, _line: &LineBuffer, _hint: Option<&str>, _old_layout: &Layout, _new_layout: &Layout, - _highlighter: Option<&dyn Highlighter>, + _highlighter: Option<&mut H>, ) -> Result<()> { Ok(()) } diff --git a/src/tty/unix.rs b/src/tty/unix.rs index 2800af617..df9520427 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -978,14 +978,14 @@ impl Renderer for PosixRenderer { Ok(()) } - fn refresh_line( + fn refresh_line( &mut self, prompt: &str, line: &LineBuffer, hint: Option<&str>, old_layout: &Layout, new_layout: &Layout, - highlighter: Option<&dyn Highlighter>, + highlighter: Option<&mut H>, ) -> Result<()> { use std::fmt::Write; self.buffer.clear(); @@ -1003,17 +1003,17 @@ impl Renderer for PosixRenderer { // display the input line self.buffer .push_str(&highlighter.highlight(line, line.pos())); + // display hint + if let Some(hint) = hint { + self.buffer.push_str(&highlighter.highlight_hint(hint)); + } } else { // display the prompt self.buffer.push_str(prompt); // display the input line self.buffer.push_str(line); - } - // display hint - if let Some(hint) = hint { - if let Some(highlighter) = highlighter { - self.buffer.push_str(&highlighter.highlight_hint(hint)); - } else { + // display hint + if let Some(hint) = hint { self.buffer.push_str(hint); } } @@ -1693,7 +1693,7 @@ mod test { let new_layout = out.compute_layout(prompt_size, default_prompt, &line, None); assert_eq!(Position { col: 1, row: 1 }, new_layout.cursor); assert_eq!(new_layout.cursor, new_layout.end); - out.refresh_line(prompt, &line, None, &old_layout, &new_layout, None) + out.refresh_line::<()>(prompt, &line, None, &old_layout, &new_layout, None) .unwrap(); #[rustfmt::skip] assert_eq!( diff --git a/src/tty/windows.rs b/src/tty/windows.rs index 02cf69a4b..394249eed 100644 --- a/src/tty/windows.rs +++ b/src/tty/windows.rs @@ -429,14 +429,14 @@ impl Renderer for ConsoleRenderer { .map(|_| ()) } - fn refresh_line( + fn refresh_line( &mut self, prompt: &str, line: &LineBuffer, hint: Option<&str>, old_layout: &Layout, new_layout: &Layout, - highlighter: Option<&dyn Highlighter>, + highlighter: Option<&mut H>, ) -> Result<()> { let default_prompt = new_layout.default_prompt; let cursor = new_layout.cursor; diff --git a/src/validate.rs b/src/validate.rs index 12b7f3314..b25e16d97 100644 --- a/src/validate.rs +++ b/src/validate.rs @@ -67,7 +67,7 @@ pub trait Validator { /// /// For auto-correction like a missing closing quote or to reject invalid /// char while typing, the input will be mutable (TODO). - fn validate(&self, ctx: &mut ValidationContext) -> Result { + fn validate(&mut self, ctx: &mut ValidationContext) -> Result { let _ = ctx; Ok(ValidationResult::Valid(None)) } @@ -79,19 +79,19 @@ pub trait Validator { /// /// This feature is not yet implemented, so this function is currently a /// no-op - fn validate_while_typing(&self) -> bool { + fn validate_while_typing(&mut self) -> bool { false } } impl Validator for () {} -impl<'v, V: ?Sized + Validator> Validator for &'v V { - fn validate(&self, ctx: &mut ValidationContext) -> Result { +impl<'v, V: ?Sized + Validator> Validator for &'v mut V { + fn validate(&mut self, ctx: &mut ValidationContext) -> Result { (**self).validate(ctx) } - fn validate_while_typing(&self) -> bool { + fn validate_while_typing(&mut self) -> bool { (**self).validate_while_typing() } } @@ -111,7 +111,7 @@ impl MatchingBracketValidator { } impl Validator for MatchingBracketValidator { - fn validate(&self, ctx: &mut ValidationContext) -> Result { + fn validate(&mut self, ctx: &mut ValidationContext) -> Result { Ok(validate_brackets(ctx.input())) } }