Skip to main content

nu_protocol/config/
completions.rs

1use super::{config_update_string_enum, prelude::*};
2use crate as nu_protocol;
3use crate::engine::Closure;
4
5#[derive(Clone, Copy, Debug, Default, IntoValue, PartialEq, Eq, Serialize, Deserialize)]
6pub enum CompletionAlgorithm {
7    #[default]
8    Prefix,
9    Substring,
10    Fuzzy,
11}
12
13impl FromStr for CompletionAlgorithm {
14    type Err = &'static str;
15
16    fn from_str(s: &str) -> Result<Self, Self::Err> {
17        match s.to_ascii_lowercase().as_str() {
18            "prefix" => Ok(Self::Prefix),
19            "substring" => Ok(Self::Substring),
20            "fuzzy" => Ok(Self::Fuzzy),
21            _ => Err("'prefix' or 'fuzzy' or 'substring'"),
22        }
23    }
24}
25
26impl UpdateFromValue for CompletionAlgorithm {
27    fn update(&mut self, value: &Value, path: &mut ConfigPath, errors: &mut ConfigErrors) {
28        config_update_string_enum(self, value, path, errors)
29    }
30}
31
32#[derive(Clone, Copy, Debug, Default, IntoValue, PartialEq, Eq, Serialize, Deserialize)]
33pub enum CompletionSort {
34    #[default]
35    Smart,
36    Alphabetical,
37}
38
39impl FromStr for CompletionSort {
40    type Err = &'static str;
41
42    fn from_str(s: &str) -> Result<Self, Self::Err> {
43        match s.to_ascii_lowercase().as_str() {
44            "smart" => Ok(Self::Smart),
45            "alphabetical" => Ok(Self::Alphabetical),
46            _ => Err("'smart' or 'alphabetical'"),
47        }
48    }
49}
50
51impl UpdateFromValue for CompletionSort {
52    fn update(&mut self, value: &Value, path: &mut ConfigPath, errors: &mut ConfigErrors) {
53        config_update_string_enum(self, value, path, errors)
54    }
55}
56
57#[derive(Clone, Debug, IntoValue, Serialize, Deserialize)]
58pub struct ExternalCompleterConfig {
59    pub enable: bool,
60    pub max_results: i64,
61    /// A closure completing external-command arguments. To drive a picker like `fzf`, have it
62    /// call a command carrying the `@interactive` attribute; interactivity is seen through the
63    /// closure to that command, so there is no separate switch to set.
64    pub completer: Option<Closure>,
65}
66
67impl Default for ExternalCompleterConfig {
68    fn default() -> Self {
69        Self {
70            enable: true,
71            max_results: 100,
72            completer: None,
73        }
74    }
75}
76
77impl UpdateFromValue for ExternalCompleterConfig {
78    fn update<'a>(
79        &mut self,
80        value: &'a Value,
81        path: &mut ConfigPath<'a>,
82        errors: &mut ConfigErrors,
83    ) {
84        let Value::Record { val: record, .. } = value else {
85            errors.type_mismatch(path, Type::record(), value);
86            return;
87        };
88
89        for (col, val) in record.iter() {
90            let path = &mut path.push(col);
91            match col.as_str() {
92                "completer" => match val {
93                    Value::Nothing { .. } => self.completer = None,
94                    Value::Closure { val, .. } => self.completer = Some(val.as_ref().clone()),
95                    _ => errors.type_mismatch(path, Type::custom("closure or nothing"), val),
96                },
97                "max_results" => self.max_results.update(val, path, errors),
98                "enable" => self.enable.update(val, path, errors),
99                _ => errors.unknown_option(path, val),
100            }
101        }
102    }
103}
104
105#[derive(Clone, Debug, IntoValue, Serialize, Deserialize)]
106pub struct CompletionConfig {
107    pub sort: CompletionSort,
108    pub case_sensitive: bool,
109    pub quick: bool,
110    pub partial: bool,
111    pub persistent_menus: bool,
112    pub algorithm: CompletionAlgorithm,
113    pub external: ExternalCompleterConfig,
114    pub use_ls_colors: bool,
115    /// Completion results the cache keeps before evicting the least recently used entry; `0` disables it.
116    pub cache_size: i64,
117}
118
119impl Default for CompletionConfig {
120    fn default() -> Self {
121        Self {
122            sort: CompletionSort::default(),
123            case_sensitive: false,
124            quick: true,
125            partial: true,
126            persistent_menus: false,
127            algorithm: CompletionAlgorithm::default(),
128            external: ExternalCompleterConfig::default(),
129            use_ls_colors: true,
130            cache_size: 100,
131        }
132    }
133}
134
135impl UpdateFromValue for CompletionConfig {
136    fn update<'a>(
137        &mut self,
138        value: &'a Value,
139        path: &mut ConfigPath<'a>,
140        errors: &mut ConfigErrors,
141    ) {
142        let Value::Record { val: record, .. } = value else {
143            errors.type_mismatch(path, Type::record(), value);
144            return;
145        };
146
147        for (col, val) in record.iter() {
148            let path = &mut path.push(col);
149            match col.as_str() {
150                "sort" => self.sort.update(val, path, errors),
151                "quick" => self.quick.update(val, path, errors),
152                "partial" => self.partial.update(val, path, errors),
153                "persistent_menus" => self.persistent_menus.update(val, path, errors),
154                "algorithm" => self.algorithm.update(val, path, errors),
155                "case_sensitive" => self.case_sensitive.update(val, path, errors),
156                "external" => self.external.update(val, path, errors),
157                "use_ls_colors" => self.use_ls_colors.update(val, path, errors),
158                "cache_size" => self.cache_size.update(val, path, errors),
159                _ => errors.unknown_option(path, val),
160            }
161        }
162    }
163}