浏览代码

Remove SelectPrompt's dependency on a reference T.

Kestrel 1 天之前
父节点
当前提交
e369d622ae
共有 2 个文件被更改,包括 24 次插入22 次删除
  1. 1 2
      examples/select_prompt.rs
  2. 23 20
      src/lib.rs

+ 1 - 2
examples/select_prompt.rs

@@ -3,8 +3,7 @@ fn main() {
         .with_items(["Grapefruit", "Guava", "Lychee"])
         .run().unwrap();
 
-    let numbers = (0..100).collect::<Vec<_>>();
     cliask::SelectPrompt::new("Choose a number:")
-        .with_items(numbers.iter())
+        .with_items(0..100)
         .run_cancellable().unwrap();
 }

+ 23 - 20
src/lib.rs

@@ -127,16 +127,16 @@ impl<AE: ActionEnum> ActionPrompt<AE> {
     }
 }
 
-pub struct SelectPrompt<'l, T: ?Sized + std::fmt::Display> {
+pub struct SelectPrompt<'l, T: std::fmt::Display> {
     prompt: &'l str,
     height: usize,
-    items: Vec<(String, &'l T)>,
-    filtered_items: Vec<(String, &'l T)>,
+    items: Vec<(String, T)>,
+    filtered_items: Vec<usize>,
     alphabetize: bool,
     input: String,
 }
 
-impl<'l, T: ?Sized + std::fmt::Display> SelectPrompt<'l, T> {
+impl<'l, T: std::fmt::Display> SelectPrompt<'l, T> {
     pub fn new(prompt: &'l str) -> Self {
         Self {
             prompt,
@@ -148,7 +148,7 @@ impl<'l, T: ?Sized + std::fmt::Display> SelectPrompt<'l, T> {
         }
     }
 
-    pub fn with_items(mut self, items: impl IntoIterator<Item = &'l T>) -> Self {
+    pub fn with_items(mut self, items: impl IntoIterator<Item = T>) -> Self {
         for item in items {
             self.items.push((item.to_string(), item));
         }
@@ -186,9 +186,9 @@ impl<'l, T: ?Sized + std::fmt::Display> SelectPrompt<'l, T> {
 
     fn refilter(&mut self) {
         self.filtered_items.clear();
-        for item in &self.items {
+        for (idx,item) in self.items.iter().enumerate() {
             if item.0.starts_with(&self.input) {
-                self.filtered_items.push(item.clone());
+                self.filtered_items.push(idx);
             }
         }
     }
@@ -238,8 +238,8 @@ impl<'l, T: ?Sized + std::fmt::Display> SelectPrompt<'l, T> {
         let mut rwidth = width as usize;
 
         'gencols: {
-            for item in self.filtered_items.iter() {
-                col.push(item.0.as_str());
+            for idx in self.filtered_items.iter() {
+                col.push(self.items[*idx].0.as_str());
                 if col.len() == rows.len() {
                     let Some(rval) = Self::append_column(rwidth, &mut rows, &mut col) else { break 'gencols };
                     rwidth = rval;
@@ -261,7 +261,7 @@ impl<'l, T: ?Sized + std::fmt::Display> SelectPrompt<'l, T> {
         Ok(())
     }
 
-    fn input_loop(&mut self, stdout: &mut std::io::Stdout, cancellable: bool) -> Result<Option<&'l T>, AskError> {
+    fn input_loop(&mut self, stdout: &mut std::io::Stdout, cancellable: bool) -> Result<Option<T>, AskError> {
         let mut keys = std::io::stdin().keys();
         let mut raw = stdout.into_raw_mode()?;
         self.refilter();
@@ -284,7 +284,7 @@ impl<'l, T: ?Sized + std::fmt::Display> SelectPrompt<'l, T> {
                 termion::event::Key::Char('\r') | termion::event::Key::Char('\n') => {
                     self.refilter();
                     if self.filtered_items.len() == 1 {
-                        return Ok(Some(self.filtered_items[0].1))
+                        return Ok(Some(self.items.swap_remove(self.filtered_items[0]).1))
                     }
                 },
                 termion::event::Key::Ctrl('u') => {
@@ -293,13 +293,16 @@ impl<'l, T: ?Sized + std::fmt::Display> SelectPrompt<'l, T> {
                 },
                 termion::event::Key::Char('\t') => {
                     self.refilter();
-                    // see if we can extend the input unambiguously
-                    let min_len = self.filtered_items.iter().map(|v| v.0.len()).min().unwrap_or(self.input.len());
-                    for prefix_len in (self.input.len()+1)..=min_len {
-                        if self.filtered_items.iter().all(|v| v.0.starts_with(&self.filtered_items[0].0[0..prefix_len])) {
-                            self.input.clear();
-                            self.input.push_str(&self.filtered_items[0].0[0..prefix_len]);
-                        } else { break }
+                    if !self.filtered_items.is_empty() {
+                        // see if we can extend the input unambiguously
+                        let min_len = self.filtered_items.iter().map(|v| self.items[*v].0.len()).min().unwrap_or(self.input.len());
+                        for prefix_len in (self.input.len()+1)..=min_len {
+                            let tomatch = &self.items[self.filtered_items[0]].0[0..prefix_len];
+                            if self.filtered_items.iter().all(|v| self.items[*v].0.starts_with(tomatch)) {
+                                self.input.clear();
+                                self.input.push_str(tomatch);
+                            } else { break }
+                        }
                     }
                 },
                 termion::event::Key::Char(ch) => {
@@ -313,7 +316,7 @@ impl<'l, T: ?Sized + std::fmt::Display> SelectPrompt<'l, T> {
         Ok(None)
     }
 
-    pub fn run(mut self) -> Result<&'l T, AskError> {
+    pub fn run(mut self) -> Result<T, AskError> {
         let mut stdout = std::io::stdout();
         self.setup(&mut stdout)?;
 
@@ -322,7 +325,7 @@ impl<'l, T: ?Sized + std::fmt::Display> SelectPrompt<'l, T> {
         r
     }
 
-    pub fn run_cancellable(mut self) -> Result<Option<&'l T>, AskError> {
+    pub fn run_cancellable(mut self) -> Result<Option<T>, AskError> {
         let mut stdout = std::io::stdout();
         self.setup(&mut stdout)?;