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