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}