1 use {
2     super::CommandParts,
3     crate::{
4         app::{
5             AppContext,
6             SelInfo,
7         },
8         path::{self, PathAnchor},
9         verb::PrefixSearchResult,
10     },
11     lazy_regex::regex_captures,
12     std::{
13         io,
14         path::Path,
15     },
16 };
17 
18 /// find the longest common start of a and b
common_start<'l>(a: &'l str, b: &str) -> &'l str19 fn common_start<'l>(a: &'l str, b: &str) -> &'l str {
20     for i in 0..a.len().min(b.len()) {
21         if a.as_bytes()[i] != b.as_bytes()[i] {
22             return &a[..i];
23         }
24     }
25     a
26 }
27 
28 /// how an input can be completed
29 #[derive(Debug)]
30 pub enum Completions {
31 
32     /// no completion found
33     None,
34 
35     /// all possible completions have this common root
36     Common(String),
37 
38     /// a list of possible completions
39     List(Vec<String>),
40 }
41 
42 impl Completions {
from_list(completions: Vec<String>) -> Self43     fn from_list(completions: Vec<String>) -> Self {
44         if completions.is_empty() {
45             return Self::None;
46         }
47         let mut iter = completions.iter();
48         let mut common: &str = match iter.next() {
49             Some(s) => s,
50             _ => { return Self::None; }
51         };
52         for c in iter {
53             common = common_start(common, c);
54         }
55         if common.is_empty() {
56             Self::List(completions)
57         } else {
58             Self::Common(common.to_string())
59         }
60 
61     }
62 
63     /// the wholes are assumed to all start with start.
for_wholes( start: &str, wholes: Vec<&str>, ) -> Self64     fn for_wholes(
65         start: &str,
66         wholes: Vec<&str>,
67     ) -> Self {
68         let completions = wholes.iter()
69             .map(|w|
70                 if let Some(stripped) = w.strip_prefix(start) {
71                     stripped
72                 } else {
73                     // this might become a feature but right now it's a bug
74                     warn!("unexpected non completing whole: {:?}", w);
75                     *w
76                 }
77             )
78             .map(|c| c.to_string())
79             .collect();
80         Self::from_list(completions)
81     }
82 
for_verb( start: &str, con: &AppContext, sel_info: SelInfo<'_>, ) -> Self83     fn for_verb(
84         start: &str,
85         con: &AppContext,
86         sel_info: SelInfo<'_>,
87     ) -> Self {
88         match con.verb_store.search_sel_info(start, &sel_info) {
89             PrefixSearchResult::NoMatch => Self::None,
90             PrefixSearchResult::Match(name, _) => {
91                 if start.len() >= name.len() {
92                     debug_assert!(name == start);
93                     Self::None
94                 } else {
95                     Self::Common(name[start.len()..].to_string())
96                 }
97             }
98             PrefixSearchResult::Matches(completions) => Self::for_wholes(
99                 start,
100                 completions,
101             ),
102         }
103     }
104 
list_for_path( verb_name: &str, arg: &str, path: &Path, sel_info: &SelInfo<'_>, con: &AppContext, ) -> io::Result<Vec<String>>105     fn list_for_path(
106         verb_name: &str,
107         arg: &str,
108         path: &Path,
109         sel_info: &SelInfo<'_>,
110         con: &AppContext,
111     ) -> io::Result<Vec<String>> {
112         let anchor = match con.verb_store.search_sel_info(verb_name, sel_info) {
113             PrefixSearchResult::Match(_, verb) => verb.get_arg_anchor(),
114             _ => PathAnchor::Unspecified,
115         };
116         let (_, parent_part, child_part) = regex_captures!(r"^(.*?)([^/]*)$", arg).unwrap();
117         let parent = path::path_from(path, anchor, parent_part);
118         let mut children = Vec::new();
119         if !parent.exists() {
120             debug!("no path completion possible because {:?} doesn't exist", &parent);
121         } else {
122             for entry in parent.read_dir()? {
123                 let entry = entry?;
124                 let mut name = entry.file_name().to_string_lossy().to_string();
125                 if !child_part.is_empty() {
126                     if !name.starts_with(child_part) {
127                         continue;
128                     }
129                     if name == child_part && entry.file_type()?.is_dir() {
130                         name = "/".to_string();
131                     } else {
132                         name.drain(0..child_part.len());
133                     }
134                 }
135                 children.push(name);
136             }
137         }
138         Ok(children)
139     }
140 
for_arg( verb_name: &str, arg: &str, con: &AppContext, sel_info: SelInfo<'_>, ) -> Self141     fn for_arg(
142         verb_name: &str,
143         arg: &str,
144         con: &AppContext,
145         sel_info: SelInfo<'_>,
146     ) -> Self {
147         // in the future we might offer completion of other types
148         // of arguments, maybe user supplied, but there's no use case
149         // now so we'll just assume the user wants to complete a path.
150         if arg.contains(' ') {
151             return Self::None;
152         }
153         match &sel_info {
154             SelInfo::None => Self::None,
155             SelInfo::One(sel) => {
156                 match Self::list_for_path(verb_name, arg, sel.path, &sel_info, con) {
157                     Ok(list) => Self::from_list(list),
158                     Err(e) => {
159                         warn!("Error while trying to complete path: {:?}", e);
160                         Self::None
161                     }
162                 }
163             }
164             SelInfo::More(stage) => {
165                 // We're looking for the possible completions which
166                 // are valid for all elements of the stage
167                 let mut lists = stage.paths()
168                     .iter()
169                     .filter_map(|path| {
170                         Self::list_for_path(
171                                 verb_name,
172                                 arg,
173                                 path,
174                                 &sel_info,
175                                 con
176                         ).ok()
177                     });
178                 let mut list = match lists.next() {
179                     Some(list) => list,
180                     None => {
181                         // can happen if there were IO errors on paths in stage, for example
182                         // on removals
183                         return Self::None;
184                     }
185                 };
186                 for ol in lists {
187                     list = list.iter().filter(|c| ol.contains(c)).cloned().collect();
188                     if list.is_empty() {
189                         break;
190                     }
191                 }
192                 Self::from_list(list)
193             }
194         }
195     }
196 
for_input( parts: &CommandParts, con: &AppContext, sel_info: SelInfo<'_>, ) -> Self197     pub fn for_input(
198         parts: &CommandParts,
199         con: &AppContext,
200         sel_info: SelInfo<'_>,
201     ) -> Self {
202         match &parts.verb_invocation {
203             Some(invocation) if !invocation.is_empty() => {
204                 match &invocation.args {
205                     None => {
206                         // looking into verb completion
207                         Self::for_verb(&invocation.name, con, sel_info)
208                     }
209                     Some(args) if !args.is_empty() => {
210                         // looking into arg completion
211                         Self::for_arg(&invocation.name, args, con, sel_info)
212                     }
213                     _ => {
214                         // nothing possible
215                         Self::None
216                     }
217                 }
218             }
219             _ => Self::None, // no possible completion if no verb invocation
220         }
221     }
222 
223 }
224