rs_complete/completion_tree.rs
1use std::collections::{BTreeMap, BTreeSet};
2use std::str::Chars;
3use std::sync::Arc;
4
5/// Word separation type used by CompletionTree
6#[derive(Debug, Clone, PartialEq)]
7pub enum WordSeparator {
8 Whitespace,
9 Separator(&'static str),
10}
11
12/// A completion tree that holds and handles completions
13#[derive(Debug, Clone)]
14pub struct CompletionTree {
15 root: CompletionNode,
16 inclusions: Arc<BTreeSet<char>>,
17 min_word_len: usize,
18 separator: WordSeparator,
19}
20
21impl Default for CompletionTree {
22 fn default() -> Self {
23 let inclusions = Arc::new(BTreeSet::new());
24 Self {
25 root: CompletionNode::new(inclusions.clone()),
26 inclusions,
27 min_word_len: 5,
28 separator: WordSeparator::Whitespace,
29 }
30 }
31}
32
33impl CompletionTree {
34 /// Create a new CompletionTree with provided non alphabet characters whitelisted.
35 /// The default CompletionTree will only parse alphabet characters (a-z, A-Z). Use this to
36 /// introduce additional accepted special characters.
37 ///
38 /// # Arguments
39 ///
40 /// * `incl` An array slice with allowed characters
41 ///
42 /// # Example
43 /// ```
44 /// extern crate rs_complete;
45 /// use rs_complete::CompletionTree;
46 ///
47 /// let mut completions = CompletionTree::default();
48 /// completions.insert("test-hyphen test_underscore");
49 /// assert_eq!(
50 /// completions.complete("te"),
51 /// Some(vec!["test".to_string()]));
52 ///
53 /// let mut completions = CompletionTree::with_inclusions(&['-', '_']);
54 /// completions.insert("test-hyphen test_underscore");
55 /// assert_eq!(
56 /// completions.complete("te"),
57 /// Some(vec!["test-hyphen".to_string(), "test_underscore".to_string()]));
58 /// ```
59 pub fn with_inclusions(incl: &[char]) -> Self {
60 let mut set = BTreeSet::new();
61 incl.iter().for_each(|c| {
62 set.insert(*c);
63 });
64 let inclusions = Arc::new(set);
65 Self {
66 root: CompletionNode::new(inclusions.clone()),
67 inclusions,
68 ..Self::default()
69 }
70 }
71
72 /// Inserts one or more words into the completion tree for later use.
73 /// Input is automatically split using the defined [WordSeparator] (see [CompletionTree::separator]).
74 ///
75 /// # Arguments
76 ///
77 /// * `line` A str slice containing one or more words
78 ///
79 /// # Example
80 /// ```
81 /// extern crate rs_complete;
82 /// use rs_complete::CompletionTree;
83 ///
84 /// let mut completions = CompletionTree::default();
85 /// completions.set_min_word_len(1);
86 ///
87 /// // Insert multiple words
88 /// completions.insert("a line with many words");
89 /// assert_eq!(completions.word_count(), 5);
90 /// completions.clear();
91 /// assert_eq!(completions.word_count(), 0);
92 ///
93 /// // The above line is equal to the following:
94 /// completions.insert("a");
95 /// completions.insert("line");
96 /// completions.insert("with");
97 /// completions.insert("many");
98 /// completions.insert("words");
99 /// assert_eq!(completions.word_count(), 5);
100 /// ```
101 pub fn insert(&mut self, line: &str) {
102 match self.separator {
103 WordSeparator::Whitespace => line.split_whitespace().for_each(|w| self.insert_word(w)),
104 WordSeparator::Separator(sep) => line.split(sep).for_each(|w| self.insert_word(w)),
105 };
106 }
107
108 fn insert_word(&mut self, word: &str) {
109 if word.len() >= self.min_word_len {
110 self.root.insert(word.chars());
111 }
112 }
113
114 /// Changes the word separator used by CompletionTree::insert()
115 /// If left unchanged the default is [WordSeparator::Whitespace]
116 ///
117 /// # Arguments
118 ///
119 /// * `separator` A WordSeparator
120 ///
121 /// # Example
122 ///
123 /// ```
124 /// extern crate rs_complete;
125 /// use rs_complete::{CompletionTree, WordSeparator};
126 ///
127 /// let mut completions = CompletionTree::default();
128 /// completions.separator(WordSeparator::Separator("|"));
129 /// completions.set_min_word_len(1);
130 ///
131 /// // Insert multiple words
132 /// completions.insert("a|line|with|many|words");
133 /// assert_eq!(completions.word_count(), 5);
134 /// completions.clear();
135 /// assert_eq!(completions.word_count(), 0);
136 ///
137 /// // The above line is equal to the following:
138 /// completions.insert("a");
139 /// completions.insert("line");
140 /// completions.insert("with");
141 /// completions.insert("many");
142 /// completions.insert("words");
143 /// assert_eq!(completions.word_count(), 5);
144 /// ```
145 pub fn separator(&mut self, separator: WordSeparator) {
146 self.separator = separator;
147 }
148
149 /// Returns an optional vector of completions based on the provided input
150 ///
151 /// # Arguments
152 ///
153 /// * `line` The line to complete
154 /// In case of multiple words, only the last will be completed
155 ///
156 /// # Example
157 /// ```
158 /// extern crate rs_complete;
159 /// use rs_complete::CompletionTree;
160 ///
161 /// let mut completions = CompletionTree::default();
162 /// completions.insert("batman robin batmobile batcave robber");
163 /// assert_eq!(
164 /// completions.complete("bat"),
165 /// Some(vec!["batcave", "batman", "batmobile"].iter().map(|s| s.to_string()).collect()));
166 ///
167 /// assert_eq!(
168 /// completions.complete("to the bat"),
169 /// Some(vec!["to the batcave", "to the batman", "to the batmobile"].iter().map(|s| s.to_string()).collect()));
170 /// ```
171 pub fn complete(&self, line: &str) -> Option<Vec<String>> {
172 if !line.is_empty() {
173 let last_word = line.split_whitespace().last().unwrap_or("");
174 if let Some(mut extensions) = self.root.complete(last_word.chars()) {
175 extensions.sort();
176 return Some(
177 extensions
178 .iter()
179 .map(|ext| format!("{}{}", line, ext))
180 .collect::<Vec<String>>(),
181 );
182 } else {
183 return None;
184 }
185 }
186 None
187 }
188
189 /// Clears all the data from the tree
190 /// # Example
191 /// ```
192 /// extern crate rs_complete;
193 /// use rs_complete::CompletionTree;
194 ///
195 /// let mut completions = CompletionTree::default();
196 /// completions.insert("batman robin batmobile batcave robber");
197 /// assert_eq!(completions.word_count(), 5);
198 /// assert_eq!(completions.size(), 24);
199 /// completions.clear();
200 /// assert_eq!(completions.size(), 1);
201 /// assert_eq!(completions.word_count(), 0);
202 /// ```
203 pub fn clear(&mut self) {
204 self.root.clear();
205 }
206
207 /// Returns a count of how many words that exist in the tree
208 /// # Example
209 /// ```
210 /// extern crate rs_complete;
211 /// use rs_complete::CompletionTree;
212 ///
213 /// let mut completions = CompletionTree::default();
214 /// completions.insert("batman robin batmobile batcave robber");
215 /// assert_eq!(completions.word_count(), 5);
216 /// ```
217 pub fn word_count(&self) -> u32 {
218 self.root.word_count()
219 }
220
221 /// Returns the size of the tree, the amount of nodes, not words
222 /// # Example
223 /// ```
224 /// extern crate rs_complete;
225 /// use rs_complete::CompletionTree;
226 ///
227 /// let mut completions = CompletionTree::default();
228 /// completions.insert("batman robin batmobile batcave robber");
229 /// assert_eq!(completions.size(), 24);
230 /// ```
231 pub fn size(&self) -> u32 {
232 self.root.subnode_count()
233 }
234
235 /// Returns the minimum word length to complete. This allows you
236 /// to pass full sentences to `insert()` and not worry about
237 /// pruning out small words like "a" or "to", because they will be
238 /// ignored.
239 /// # Example
240 /// ```
241 /// extern crate rs_complete;
242 /// use rs_complete::CompletionTree;
243 ///
244 /// let mut completions = CompletionTree::default();
245 /// completions.set_min_word_len(4);
246 /// completions.insert("one two three four five");
247 /// assert_eq!(completions.word_count(), 3);
248 ///
249 /// let mut completions = CompletionTree::default();
250 /// completions.set_min_word_len(1);
251 /// completions.insert("one two three four five");
252 /// assert_eq!(completions.word_count(), 5);
253 /// ```
254 pub fn min_word_len(&self) -> usize {
255 self.min_word_len
256 }
257
258 /// Sets the minimum word length to complete on. Smaller words are
259 /// ignored. This only affects future calls to `insert()` -
260 /// changing this won't start completing on smaller words that
261 /// were added in the past, nor will it exclude larger words
262 /// already inserted into the completion tree.
263 pub fn set_min_word_len(&mut self, len: usize) {
264 self.min_word_len = len;
265 }
266}
267
268#[derive(Debug, Clone)]
269struct CompletionNode {
270 subnodes: BTreeMap<char, CompletionNode>,
271 leaf: bool,
272 inclusions: Arc<BTreeSet<char>>,
273}
274
275impl CompletionNode {
276 fn new(incl: Arc<BTreeSet<char>>) -> Self {
277 Self {
278 subnodes: BTreeMap::new(),
279 leaf: false,
280 inclusions: incl,
281 }
282 }
283
284 fn clear(&mut self) {
285 self.subnodes.clear();
286 }
287
288 fn word_count(&self) -> u32 {
289 let mut count = self.subnodes.values().map(|n| n.word_count()).sum();
290 if self.leaf {
291 count += 1;
292 }
293 count
294 }
295
296 fn subnode_count(&self) -> u32 {
297 self.subnodes
298 .values()
299 .map(|n| n.subnode_count())
300 .sum::<u32>()
301 + 1
302 }
303
304 fn insert(&mut self, mut iter: Chars) {
305 if let Some(c) = iter.next() {
306 if self.inclusions.contains(&c) || c.is_alphanumeric() {
307 let inclusions = self.inclusions.clone();
308 let subnode = self
309 .subnodes
310 .entry(c)
311 .or_insert_with(|| CompletionNode::new(inclusions));
312 subnode.insert(iter);
313 } else {
314 self.leaf = true;
315 }
316 } else {
317 self.leaf = true;
318 }
319 }
320
321 fn complete(&self, mut iter: Chars) -> Option<Vec<String>> {
322 if let Some(c) = iter.next() {
323 if let Some(subnode) = self.subnodes.get(&c) {
324 subnode.complete(iter)
325 } else {
326 None
327 }
328 } else {
329 Some(self.collect("".to_string()))
330 }
331 }
332
333 fn collect(&self, partial: String) -> Vec<String> {
334 let mut completions = vec![];
335 if self.leaf {
336 completions.push(partial.clone());
337 }
338
339 if !self.subnodes.is_empty() {
340 for (c, node) in &self.subnodes {
341 let mut partial = partial.clone();
342 partial.push(*c);
343 completions.append(&mut node.collect(partial));
344 }
345 }
346 completions
347 }
348}