Skip to main content

kode_core/
completion.rs

1use crate::selection::Position;
2
3/// The kind of a completion item, used for icon display and sorting.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
5pub enum CompletionKind {
6    #[default]
7    Text,
8    Keyword,
9    Variable,
10    Function,
11    Field,
12    Property,
13    Method,
14    Module,
15    Snippet,
16    Other,
17}
18
19/// A single completion suggestion.
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct CompletionItem {
22    pub label: String,
23    pub insert_text: Option<String>,
24    pub detail: Option<String>,
25    pub sort_order: i32,
26    pub kind: CompletionKind,
27}
28
29impl Default for CompletionItem {
30    fn default() -> Self {
31        Self {
32            label: String::new(),
33            insert_text: None,
34            detail: None,
35            sort_order: 0,
36            kind: CompletionKind::Text,
37        }
38    }
39}
40
41/// Context passed to completion providers.
42#[derive(Debug, Clone)]
43pub struct CompletionContext {
44    pub text: String,
45    pub cursor: Position,
46    pub version: u64,
47    pub trigger: CompletionTrigger,
48}
49
50/// What triggered the completion request.
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub enum CompletionTrigger {
53    Invoke,
54    TriggerCharacter(char),
55    Typing,
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn completion_item_all_fields() {
64        let item = CompletionItem {
65            label: "my_func".to_string(),
66            insert_text: Some("my_func()".to_string()),
67            detail: Some("fn my_func()".to_string()),
68            sort_order: 5,
69            kind: CompletionKind::Function,
70        };
71        assert_eq!(item.label, "my_func");
72        assert_eq!(item.insert_text.as_deref(), Some("my_func()"));
73        assert_eq!(item.detail.as_deref(), Some("fn my_func()"));
74        assert_eq!(item.sort_order, 5);
75        assert_eq!(item.kind, CompletionKind::Function);
76    }
77
78    #[test]
79    fn completion_item_default() {
80        let item = CompletionItem::default();
81        assert_eq!(item.label, "");
82        assert!(item.insert_text.is_none());
83        assert!(item.detail.is_none());
84        assert_eq!(item.sort_order, 0);
85        assert_eq!(item.kind, CompletionKind::Text);
86    }
87
88    #[test]
89    fn completion_context_holds_position_and_version() {
90        let ctx = CompletionContext {
91            text: "hello world".to_string(),
92            cursor: Position::new(0, 5),
93            version: 42,
94            trigger: CompletionTrigger::Typing,
95        };
96        assert_eq!(ctx.cursor, Position::new(0, 5));
97        assert_eq!(ctx.version, 42);
98        assert_eq!(ctx.text, "hello world");
99        assert_eq!(ctx.trigger, CompletionTrigger::Typing);
100    }
101
102    #[test]
103    fn completion_kind_variants_compare_equal() {
104        let variants = [
105            CompletionKind::Text,
106            CompletionKind::Keyword,
107            CompletionKind::Variable,
108            CompletionKind::Function,
109            CompletionKind::Field,
110            CompletionKind::Property,
111            CompletionKind::Method,
112            CompletionKind::Module,
113            CompletionKind::Snippet,
114            CompletionKind::Other,
115        ];
116        for (i, a) in variants.iter().enumerate() {
117            for (j, b) in variants.iter().enumerate() {
118                if i == j {
119                    assert_eq!(a, b);
120                } else {
121                    assert_ne!(a, b);
122                }
123            }
124        }
125    }
126}