lsp_core/feature/
completion.rs

1use bevy_ecs::{prelude::*, schedule::ScheduleLabel};
2use derive_more::{AsMut, AsRef, Deref, DerefMut};
3use lsp_types::{
4    CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionTextEdit,
5    Documentation, InsertTextFormat, TextEdit,
6};
7
8pub use crate::{
9    systems::{
10        complete_class, complete_properties, keyword_complete, prefix::defined_prefix_completion,
11    },
12    util::{token::get_current_token, triple::get_current_triple},
13};
14
15/// [`ScheduleLabel`] related to the Completion schedule
16#[derive(ScheduleLabel, Clone, Eq, PartialEq, Debug, Hash)]
17pub struct Label;
18
19/// [`Component`] indicating that the current document is currently handling a Completion request.
20#[derive(Component, AsRef, Deref, AsMut, DerefMut, Debug)]
21pub struct CompletionRequest(pub Vec<SimpleCompletion>);
22
23pub fn setup_schedule(world: &mut World) {
24    let mut completion = Schedule::new(Label);
25    completion.add_systems((
26        get_current_token,
27        keyword_complete.after(get_current_token),
28        get_current_triple.after(get_current_token),
29        complete_class.after(get_current_triple),
30        complete_properties.after(get_current_triple),
31        defined_prefix_completion.after(get_current_token),
32    ));
33    world.add_schedule(completion);
34}
35
36#[derive(Debug)]
37pub struct SimpleCompletion {
38    pub kind: CompletionItemKind,
39    pub label: String,
40    pub _label_details: Option<CompletionItemLabelDetails>,
41    pub _documentation: Option<String>,
42    pub _sort_text: Option<String>,
43    pub _filter_text: Option<String>,
44    pub edits: Vec<TextEdit>,
45    pub _commit_char: Option<String>,
46}
47
48impl SimpleCompletion {
49    pub fn new(kind: CompletionItemKind, label: String, edit: TextEdit) -> Self {
50        Self {
51            kind,
52            label,
53            edits: vec![edit],
54            _label_details: None,
55            _documentation: None,
56            _sort_text: None,
57            _filter_text: None,
58            _commit_char: None,
59        }
60    }
61
62    pub fn label_detail(mut self, detail: impl Into<String>) -> Self {
63        if let Some(ref mut t) = self._label_details {
64            t.detail = Some(detail.into());
65        } else {
66            self._label_details = Some(CompletionItemLabelDetails {
67                detail: Some(detail.into()),
68                description: None,
69            });
70        }
71
72        self
73    }
74
75    pub fn label_description(mut self, description: impl Into<String>) -> Self {
76        if let Some(ref mut t) = self._label_details {
77            t.description = Some(description.into());
78        } else {
79            self._label_details = Some(CompletionItemLabelDetails {
80                description: Some(description.into()),
81                detail: None,
82            });
83        }
84
85        self
86    }
87
88    pub fn text_edit(mut self, edit: TextEdit) -> Self {
89        self.edits.push(edit);
90        self
91    }
92
93    pub fn documentation(mut self, documentation: impl Into<String>) -> Self {
94        self._documentation = Some(documentation.into());
95        self
96    }
97
98    pub fn m_documentation<S: Into<String>>(mut self, documentation: Option<S>) -> Self {
99        self._documentation = documentation.map(|x| x.into());
100        self
101    }
102
103    pub fn sort_text(mut self, sort_text: impl Into<String>) -> Self {
104        self._sort_text = Some(sort_text.into());
105        self
106    }
107
108    pub fn filter_text(mut self, filter_text: impl Into<String>) -> Self {
109        self._filter_text = Some(filter_text.into());
110        self
111    }
112
113    pub fn commit_char(mut self, commit_char: impl Into<String>) -> Self {
114        self._commit_char = Some(commit_char.into());
115        self
116    }
117}
118
119impl Into<CompletionItem> for SimpleCompletion {
120    fn into(self) -> CompletionItem {
121        let SimpleCompletion {
122            _filter_text: filter_text,
123            _sort_text: sort_text,
124            label,
125            _label_details,
126            _documentation: documentation,
127            kind,
128            edits,
129            _commit_char: commit_char,
130        } = self;
131
132        let text_edit = edits
133            .iter()
134            .next()
135            .map(|x| CompletionTextEdit::Edit(x.clone()));
136
137        let additional_text_edits = edits.into_iter().skip(1).collect();
138
139        CompletionItem {
140            label,
141            kind: Some(kind),
142            sort_text,
143            insert_text_format: (kind == CompletionItemKind::SNIPPET)
144                .then_some(InsertTextFormat::SNIPPET),
145            filter_text,
146            label_details: _label_details,
147            documentation: documentation.map(|st| Documentation::String(st)),
148            text_edit,
149            additional_text_edits: Some(additional_text_edits),
150            commit_characters: commit_char.map(|x| vec![String::from(x)]),
151            ..Default::default()
152        }
153    }
154}