From bb485c45c531b3849f15df8e3c854a3087f23958 Mon Sep 17 00:00:00 2001 From: Allen Chow Date: Fri, 8 Jul 2022 05:58:13 +0000 Subject: [PATCH 1/4] add diy_completor example --- examples/diy_completor.rs | 144 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 examples/diy_completor.rs diff --git a/examples/diy_completor.rs b/examples/diy_completor.rs new file mode 100644 index 000000000..1ff10339e --- /dev/null +++ b/examples/diy_completor.rs @@ -0,0 +1,144 @@ +use rustyline::completion::{extract_word, Completer, FilenameCompleter, Pair}; +use rustyline::error::ReadlineError; +use rustyline::hint::Hinter; +use rustyline::{CompletionType, Config, Context, EditMode, Editor}; +use rustyline_derive::{Helper, Highlighter, Validator}; +use std::collections::HashSet; + +const DEFAULT_BREAK_CHARS: [u8; 3] = [b' ', b'\t', b'\n']; + +#[derive(Hash, Debug, PartialEq, Eq)] +struct Command { + cmd: String, + pre_cmd: String, +} + +impl Command { + fn new(cmd: &str, pre_cmd: &str) -> Self { + Self { cmd: cmd.into(), pre_cmd: pre_cmd.into() } + } +} +struct CommandCompleter { + cmds: HashSet, +} + +impl CommandCompleter { + pub fn find_matches(&self, line: &str, pos: usize) -> rustyline::Result<(usize, Vec)> { + let (start, word) = extract_word(line, pos, None, &DEFAULT_BREAK_CHARS); + let pre_cmd = line[..start].trim(); + + let matches = self.cmds + .iter() + .filter_map(|hint| { + if hint.cmd.starts_with(word) + && pre_cmd == &hint.pre_cmd { + let mut replacement = hint.cmd.clone(); + replacement += " "; + Some(Pair { + display: hint.cmd.to_string(), + replacement: replacement.to_string(), + }) + } else { + None + } + }) + .collect(); + Ok((start, matches)) + } +} + +impl Completer for CommandCompleter { + type Candidate = Pair; + + fn complete( + &self, + line: &str, + pos: usize, + _ctx: &Context<'_>, + ) -> rustyline::Result<(usize, Vec)> { + self.find_matches(line, pos) + } +} + +#[derive(Helper, Validator, Highlighter)] +struct MyHelper { + #[rustyline(Completer)] + file_completer: FilenameCompleter, + cmd_completer: CommandCompleter, +} + +impl Completer for MyHelper { + type Candidate = Pair; + + fn complete( + &self, + line: &str, + pos: usize, + ctx: &Context<'_>, + ) -> Result<(usize, Vec), ReadlineError> { + match self.cmd_completer.complete(line, pos, ctx) { + Ok((start, matches)) => { + if matches.is_empty() { + self.file_completer.complete(line, pos, ctx) + } else { + Ok((start, matches)) + } + }, + Err(e) => Err(e), + } + } +} + +impl Hinter for MyHelper { + type Hint = String; + + fn hint(&self, _line: &str, _pos: usize, _ctx: &Context<'_>) -> Option { + None + } +} + +fn cmd_sets() -> HashSet { + let mut set = HashSet::new(); + set.insert(Command::new("helper", "about")); + set.insert(Command::new("hinter", "about")); + set.insert(Command::new("highlighter", "about")); + set.insert(Command::new("validator", "about")); + set.insert(Command::new("completer", "about")); + + set.insert(Command::new("release", "dev")); + set.insert(Command::new("deploy", "dev")); + set.insert(Command::new("compile", "dev")); + set.insert(Command::new("test", "dev")); + + set.insert(Command::new("history", "")); + set.insert(Command::new("about", "")); + set.insert(Command::new("help", "")); + set.insert(Command::new("dev", "")); + set +} + +// To debug rustyline: +// RUST_LOG=rustyline=debug cargo run --example example 2> debug.log +fn main() -> rustyline::Result<()> { + env_logger::init(); + let config = Config::builder() + .history_ignore_space(true) + .completion_type(CompletionType::List) + .edit_mode(EditMode::Emacs) + .build(); + let h = MyHelper { + file_completer: FilenameCompleter::new(), + cmd_completer: CommandCompleter { cmds: cmd_sets() }, + }; + let mut rl: Editor = Editor::with_config(config)?; + rl.set_helper(Some(h)); + + let mut count = 1; + loop { + let p = format!("{}> ", count); + let readline = rl.readline(&p)?; + println!("Line: {}", readline); + count += 1; + } +} + From 402f339efd26c7f1ae29cbb2940fafd165670e1a Mon Sep 17 00:00:00 2001 From: Allen Chow Date: Thu, 14 Jul 2022 20:31:43 +0800 Subject: [PATCH 2/4] fix --- examples/diy_completor.rs | 44 ++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/examples/diy_completor.rs b/examples/diy_completor.rs index 1ff10339e..8118f6fc2 100644 --- a/examples/diy_completor.rs +++ b/examples/diy_completor.rs @@ -1,8 +1,7 @@ use rustyline::completion::{extract_word, Completer, FilenameCompleter, Pair}; use rustyline::error::ReadlineError; -use rustyline::hint::Hinter; use rustyline::{CompletionType, Config, Context, EditMode, Editor}; -use rustyline_derive::{Helper, Highlighter, Validator}; +use rustyline_derive::{Helper, Highlighter, Hinter, Validator}; use std::collections::HashSet; const DEFAULT_BREAK_CHARS: [u8; 3] = [b' ', b'\t', b'\n']; @@ -15,7 +14,10 @@ struct Command { impl Command { fn new(cmd: &str, pre_cmd: &str) -> Self { - Self { cmd: cmd.into(), pre_cmd: pre_cmd.into() } + Self { + cmd: cmd.into(), + pre_cmd: pre_cmd.into(), + } } } struct CommandCompleter { @@ -27,17 +29,17 @@ impl CommandCompleter { let (start, word) = extract_word(line, pos, None, &DEFAULT_BREAK_CHARS); let pre_cmd = line[..start].trim(); - let matches = self.cmds + let matches = self + .cmds .iter() .filter_map(|hint| { - if hint.cmd.starts_with(word) - && pre_cmd == &hint.pre_cmd { - let mut replacement = hint.cmd.clone(); - replacement += " "; - Some(Pair { - display: hint.cmd.to_string(), - replacement: replacement.to_string(), - }) + if hint.cmd.starts_with(word) && pre_cmd == &hint.pre_cmd { + let mut replacement = hint.cmd.clone(); + replacement += " "; + Some(Pair { + display: hint.cmd.to_string(), + replacement: replacement.to_string(), + }) } else { None } @@ -60,9 +62,8 @@ impl Completer for CommandCompleter { } } -#[derive(Helper, Validator, Highlighter)] +#[derive(Helper, Hinter, Validator, Highlighter)] struct MyHelper { - #[rustyline(Completer)] file_completer: FilenameCompleter, cmd_completer: CommandCompleter, } @@ -76,27 +77,19 @@ impl Completer for MyHelper { pos: usize, ctx: &Context<'_>, ) -> Result<(usize, Vec), ReadlineError> { - match self.cmd_completer.complete(line, pos, ctx) { + match self.cmd_completer.find_matches(line, pos) { Ok((start, matches)) => { if matches.is_empty() { self.file_completer.complete(line, pos, ctx) } else { Ok((start, matches)) } - }, + } Err(e) => Err(e), } } } -impl Hinter for MyHelper { - type Hint = String; - - fn hint(&self, _line: &str, _pos: usize, _ctx: &Context<'_>) -> Option { - None - } -} - fn cmd_sets() -> HashSet { let mut set = HashSet::new(); set.insert(Command::new("helper", "about")); @@ -137,8 +130,7 @@ fn main() -> rustyline::Result<()> { loop { let p = format!("{}> ", count); let readline = rl.readline(&p)?; - println!("Line: {}", readline); + println!("Line: {}", readline); count += 1; } } - From 6f36083f30f1bc19d47c77d49bd15af0aaea9bc8 Mon Sep 17 00:00:00 2001 From: Allen Chow Date: Thu, 14 Jul 2022 20:32:55 +0800 Subject: [PATCH 3/4] rename --- examples/{diy_completor.rs => diy_completer.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{diy_completor.rs => diy_completer.rs} (100%) diff --git a/examples/diy_completor.rs b/examples/diy_completer.rs similarity index 100% rename from examples/diy_completor.rs rename to examples/diy_completer.rs From 2c957bae882d606b61e064de50aa4b12d2ae2f14 Mon Sep 17 00:00:00 2001 From: Allen Chow Date: Thu, 14 Jul 2022 20:37:42 +0800 Subject: [PATCH 4/4] fix --- examples/diy_completer.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/examples/diy_completer.rs b/examples/diy_completer.rs index 8118f6fc2..47a1ce15b 100644 --- a/examples/diy_completer.rs +++ b/examples/diy_completer.rs @@ -49,19 +49,6 @@ impl CommandCompleter { } } -impl Completer for CommandCompleter { - type Candidate = Pair; - - fn complete( - &self, - line: &str, - pos: usize, - _ctx: &Context<'_>, - ) -> rustyline::Result<(usize, Vec)> { - self.find_matches(line, pos) - } -} - #[derive(Helper, Hinter, Validator, Highlighter)] struct MyHelper { file_completer: FilenameCompleter,