diff --git a/Cargo.toml b/Cargo.toml index 5ceac88..d6fa099 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,8 @@ atty = "0.2" ui = { package = "discourse-ui", path = "./discourse-ui" } macros = { package = "discourse-macros", path = "./discourse-macros" } +smallvec = { version = "1.6", optional = true } + [dev-dependencies] trybuild = { version = "1.0.42", features = ["diff"] } # remove color printing since it messes with the snapshot's colours @@ -29,6 +31,6 @@ regex = "1.5" # examples/prompt_module.rs fuzzy-matcher = "0.3" # examples/file.rs [features] -default = ["crossterm"] +default = ["crossterm", "smallvec"] crossterm = ["ui/crossterm"] termion = ["ui/termion"] diff --git a/examples/file.rs b/examples/file.rs index 76e89f5..0903cf3 100644 --- a/examples/file.rs +++ b/examples/file.rs @@ -2,7 +2,9 @@ use std::path::Path; use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher}; -fn auto_complete(p: String) -> Vec { +use discourse::question::Completions; + +fn auto_complete(p: String) -> Completions { let current: &Path = p.as_ref(); let (mut dir, last) = if p.ends_with('/') { (current, "") @@ -19,7 +21,7 @@ fn auto_complete(p: String) -> Vec { dir = ".".as_ref(); } - let mut files: Vec<_> = match dir.read_dir() { + let mut files: Completions<_> = match dir.read_dir() { Ok(files) => files .flatten() .flat_map(|file| { @@ -32,11 +34,13 @@ fn auto_complete(p: String) -> Vec { } }) .collect(), - Err(_) => return vec![p], + Err(_) => { + return Completions::from([p]); + } }; if files.is_empty() { - return vec![p]; + Completions::from([p]) } else { let fuzzer = SkimMatcherV2::default(); files.sort_by_cached_key(|file| fuzzer.fuzzy_match(file, last).unwrap_or(i64::MAX)); diff --git a/src/question/input.rs b/src/question/input.rs index fb910f8..b583bcb 100644 --- a/src/question/input.rs +++ b/src/question/input.rs @@ -6,7 +6,7 @@ use ui::{ widgets, Prompt, Validation, Widget, }; -use super::{AutoComplete, ChoiceList, Filter, Options, Transform, Validate}; +use super::{AutoComplete, ChoiceList, Completions, Filter, Options, Transform, Validate}; use crate::{Answer, Answers}; #[derive(Debug, Default)] @@ -269,7 +269,7 @@ mod tests { ( Input { auto_complete: AutoComplete::Sync(Box::new(|s, _| { - let mut completions: Vec<_> = ('a'..='d') + let mut completions: Completions<_> = ('a'..='d') .map(|c| { let mut s = s.clone(); s.push(c); diff --git a/src/question/mod.rs b/src/question/mod.rs index 206f413..f96b0d8 100644 --- a/src/question/mod.rs +++ b/src/question/mod.rs @@ -210,8 +210,13 @@ macro_rules! handler { }; } +#[cfg(feature = "smallvec")] +pub type Completions = smallvec::SmallVec<[T; 1]>; +#[cfg(not(feature = "smallvec"))] +pub type Completions = Vec; + handler!(Filter, FnOnce(T, &Answers) -> T); -handler!(AutoComplete, FnMut(T, &Answers) -> Vec); +handler!(AutoComplete, FnMut(T, &Answers) -> Completions); handler!(Validate, ?Sized FnMut(&T, &Answers) -> Result<(), String>); handler!(ValidateByVal, FnMut(T, &Answers) -> Result<(), String>); handler!(Transform, ?Sized FnOnce(&T, &Answers, &mut dyn Backend) -> error::Result<()>); @@ -240,7 +245,7 @@ macro_rules! impl_auto_complete_builder { ($t:ty; $inner:ident) => { pub fn auto_complete(mut self, auto_complete: F) -> Self where - F: FnMut($t, &crate::Answers) -> Vec<$t> + 'a, + F: FnMut($t, &crate::Answers) -> Completions<$t> + 'a, { self.$inner.auto_complete = crate::question::AutoComplete::Sync(Box::new(auto_complete)); diff --git a/tests/input.rs b/tests/input.rs index fe9bab5..47f09ad 100644 --- a/tests/input.rs +++ b/tests/input.rs @@ -1,4 +1,4 @@ -use discourse::{Answer, Question}; +use discourse::{question::Completions, Answer, Question}; use ui::{ events::{KeyCode, TestEvents}, style::Color, @@ -107,7 +107,7 @@ fn test_auto_complete() { let prompt = Question::input("name") .message("message") .auto_complete(|s, _| { - let mut completions: Vec<_> = ('g'..='j') + let mut completions: Completions<_> = ('g'..='j') .map(|c| { let mut s = s.clone(); s.push(c);