1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
use crate::completions::{CompletionOptions, SortBy};
use nu_protocol::{
    engine::{Stack, StateWorkingSet},
    levenshtein_distance, Span,
};
use reedline::Suggestion;

// Completer trait represents the three stages of the completion
// fetch, filter and sort
pub trait Completer {
    #[allow(clippy::too_many_arguments)]
    fn fetch(
        &mut self,
        working_set: &StateWorkingSet,
        stack: &Stack,
        prefix: Vec<u8>,
        span: Span,
        offset: usize,
        pos: usize,
        options: &CompletionOptions,
    ) -> Vec<SemanticSuggestion>;

    fn get_sort_by(&self) -> SortBy {
        SortBy::Ascending
    }

    fn sort(&self, items: Vec<SemanticSuggestion>, prefix: Vec<u8>) -> Vec<SemanticSuggestion> {
        let prefix_str = String::from_utf8_lossy(&prefix).to_string();
        let mut filtered_items = items;

        // Sort items
        match self.get_sort_by() {
            SortBy::LevenshteinDistance => {
                filtered_items.sort_by(|a, b| {
                    let a_distance = levenshtein_distance(&prefix_str, &a.suggestion.value);
                    let b_distance = levenshtein_distance(&prefix_str, &b.suggestion.value);
                    a_distance.cmp(&b_distance)
                });
            }
            SortBy::Ascending => {
                filtered_items.sort_by(|a, b| a.suggestion.value.cmp(&b.suggestion.value));
            }
            SortBy::None => {}
        };

        filtered_items
    }
}

#[derive(Debug, Default, PartialEq)]
pub struct SemanticSuggestion {
    pub suggestion: Suggestion,
    pub kind: Option<SuggestionKind>,
}

// TODO: think about name: maybe suggestion context?
#[derive(Clone, Debug, PartialEq)]
pub enum SuggestionKind {
    Command(nu_protocol::engine::CommandType),
    Type(nu_protocol::Type),
}

impl From<Suggestion> for SemanticSuggestion {
    fn from(suggestion: Suggestion) -> Self {
        Self {
            suggestion,
            ..Default::default()
        }
    }
}