tinymist_query/completion/
proto.rs

1use ecow::EcoString;
2use lsp_types::InsertTextFormat;
3use serde::{Deserialize, Serialize};
4
5use crate::StrRef;
6
7use super::LspRange;
8
9/// A kind of item that can be completed.
10#[derive(Debug, Clone, PartialEq, Eq, Default)]
11pub enum CompletionKind {
12    /// A syntactical structure.
13    Syntax,
14    /// A function.
15    Func,
16    /// A type.
17    Type,
18    /// A function parameter.
19    Param,
20    /// A field.
21    Field,
22    /// A constant.
23    #[default]
24    Constant,
25    /// A reference.
26    Reference,
27    /// A symbol.
28    Symbol(EcoString),
29    /// A variable.
30    Variable,
31    /// A module.
32    Module,
33    /// A file.
34    File,
35    /// A folder.
36    Folder,
37}
38
39impl From<&CompletionKind> for lsp_types::CompletionItemKind {
40    fn from(value: &CompletionKind) -> Self {
41        match value {
42            CompletionKind::Syntax => Self::SNIPPET,
43            CompletionKind::Func => Self::FUNCTION,
44            CompletionKind::Param => Self::VARIABLE,
45            CompletionKind::Field => Self::FIELD,
46            CompletionKind::Variable => Self::VARIABLE,
47            CompletionKind::Constant => Self::CONSTANT,
48            CompletionKind::Reference => Self::REFERENCE,
49            CompletionKind::Symbol(_) => Self::FIELD,
50            CompletionKind::Type => Self::CLASS,
51            CompletionKind::Module => Self::MODULE,
52            CompletionKind::File => Self::FILE,
53            CompletionKind::Folder => Self::FOLDER,
54        }
55    }
56}
57
58impl serde::Serialize for CompletionKind {
59    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
60    where
61        S: serde::Serializer,
62    {
63        <&Self as Into<lsp_types::CompletionItemKind>>::into(self).serialize(serializer)
64    }
65}
66
67impl<'de> serde::Deserialize<'de> for CompletionKind {
68    fn deserialize<D>(deserializer: D) -> Result<CompletionKind, D::Error>
69    where
70        D: serde::Deserializer<'de>,
71    {
72        let kind = lsp_types::CompletionItemKind::deserialize(deserializer)?;
73        Ok(match kind {
74            lsp_types::CompletionItemKind::SNIPPET => CompletionKind::Syntax,
75            lsp_types::CompletionItemKind::FUNCTION => CompletionKind::Func,
76            lsp_types::CompletionItemKind::VARIABLE => CompletionKind::Param,
77            lsp_types::CompletionItemKind::FIELD => CompletionKind::Field,
78            lsp_types::CompletionItemKind::CONSTANT => CompletionKind::Constant,
79            lsp_types::CompletionItemKind::REFERENCE => CompletionKind::Reference,
80            lsp_types::CompletionItemKind::CLASS => CompletionKind::Type,
81            lsp_types::CompletionItemKind::MODULE => CompletionKind::Module,
82            lsp_types::CompletionItemKind::FILE => CompletionKind::File,
83            lsp_types::CompletionItemKind::FOLDER => CompletionKind::Folder,
84            _ => CompletionKind::Variable,
85        })
86    }
87}
88
89/// An autocompletion option.
90#[derive(Debug, Clone, Serialize, Deserialize, Default)]
91pub struct Completion {
92    /// The kind of item this completes to.
93    pub kind: CompletionKind,
94    /// The label the completion is shown with.
95    pub label: EcoString,
96    /// The label the completion is shown with.
97    pub label_details: Option<EcoString>,
98    /// The label the completion is shown with.
99    pub sort_text: Option<EcoString>,
100    /// The composed text used for filtering.
101    pub filter_text: Option<EcoString>,
102    /// The completed version of the input, possibly described with snippet
103    /// syntax like `${lhs} + ${rhs}`.
104    ///
105    /// Should default to the `label` if `None`.
106    pub apply: Option<EcoString>,
107    /// Whether the completion may capture the right-side identifier suffix as
108    /// the first snippet argument.
109    #[serde(skip)]
110    pub capture_suffix: bool,
111    /// An optional short description, at most one sentence.
112    pub detail: Option<EcoString>,
113    /// An optional array of additional text edits that are applied when
114    /// selecting this completion. Edits must not overlap with the main edit
115    /// nor with themselves.
116    pub additional_text_edits: Option<Vec<EcoTextEdit>>,
117    /// An optional command to run when the completion is selected.
118    pub command: Option<LspCompletionCommand>,
119}
120
121/// Represents a collection of [completion items](#CompletionItem) to be
122/// presented in the editor.
123#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
124#[serde(rename_all = "camelCase")]
125pub struct CompletionList {
126    /// This list it not complete. Further typing should result in recomputing
127    /// this list.
128    pub is_incomplete: bool,
129
130    /// The completion items.
131    pub items: Vec<CompletionItem>,
132}
133
134/// Additional details for a completion item label.
135///
136/// @since 3.17.0
137#[derive(Debug, PartialEq, Default, Deserialize, Serialize, Clone)]
138#[serde(rename_all = "camelCase")]
139pub struct CompletionItemLabelDetails {
140    /// An optional string which is rendered less prominently directly after
141    /// {@link CompletionItemLabel.label label}, without any spacing. Should be
142    /// used for function signatures or type annotations.
143    #[serde(skip_serializing_if = "Option::is_none")]
144    pub detail: Option<EcoString>,
145
146    /// An optional string which is rendered less prominently after
147    /// {@link CompletionItemLabel.detail}. Should be used for fully qualified
148    /// names or file path.
149    #[serde(skip_serializing_if = "Option::is_none")]
150    pub description: Option<EcoString>,
151}
152
153impl From<EcoString> for CompletionItemLabelDetails {
154    fn from(description: EcoString) -> Self {
155        Self {
156            detail: None,
157            description: Some(description),
158        }
159    }
160}
161
162/// A textual edit applicable to a text document.
163///
164/// If n `EcoTextEdit`s are applied to a text document all text edits describe
165/// changes to the initial document version. Execution wise text edits should
166/// applied from the bottom to the top of the text document. Overlapping text
167/// edits are not supported.
168#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
169#[serde(rename_all = "camelCase")]
170pub struct EcoTextEdit {
171    /// The range of the text document to be manipulated. To insert
172    /// text into a document create a range where start === end.
173    pub range: LspRange,
174    /// The string to be inserted. For delete operations use an
175    /// empty string.
176    pub new_text: EcoString,
177}
178
179impl EcoTextEdit {
180    pub fn new(range: LspRange, new_text: EcoString) -> EcoTextEdit {
181        EcoTextEdit { range, new_text }
182    }
183}
184
185/// A textual edit with separate insert and replace ranges.
186#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
187#[serde(rename_all = "camelCase")]
188pub struct EcoInsertReplaceEdit {
189    /// The string to be inserted.
190    pub new_text: EcoString,
191    /// The range if insert mode is requested.
192    pub insert: LspRange,
193    /// The range if replace mode is requested.
194    pub replace: LspRange,
195}
196
197impl EcoInsertReplaceEdit {
198    pub fn new(insert: LspRange, replace: LspRange, new_text: EcoString) -> EcoInsertReplaceEdit {
199        EcoInsertReplaceEdit {
200            new_text,
201            insert,
202            replace,
203        }
204    }
205}
206
207/// The main edit for a completion item.
208#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
209#[serde(untagged)]
210pub enum EcoCompletionTextEdit {
211    Edit(EcoTextEdit),
212    InsertAndReplace(EcoInsertReplaceEdit),
213}
214
215impl EcoCompletionTextEdit {
216    pub fn new_text(&self) -> &EcoString {
217        match self {
218            EcoCompletionTextEdit::Edit(edit) => &edit.new_text,
219            EcoCompletionTextEdit::InsertAndReplace(edit) => &edit.new_text,
220        }
221    }
222
223    pub fn new_text_mut(&mut self) -> &mut EcoString {
224        match self {
225            EcoCompletionTextEdit::Edit(edit) => &mut edit.new_text,
226            EcoCompletionTextEdit::InsertAndReplace(edit) => &mut edit.new_text,
227        }
228    }
229}
230
231impl From<EcoTextEdit> for EcoCompletionTextEdit {
232    fn from(edit: EcoTextEdit) -> Self {
233        EcoCompletionTextEdit::Edit(edit)
234    }
235}
236
237impl From<EcoInsertReplaceEdit> for EcoCompletionTextEdit {
238    fn from(edit: EcoInsertReplaceEdit) -> Self {
239        EcoCompletionTextEdit::InsertAndReplace(edit)
240    }
241}
242
243/// Represents a completion item.
244#[derive(Debug, PartialEq, Default, Deserialize, Serialize, Clone)]
245#[serde(rename_all = "camelCase")]
246pub struct CompletionItem {
247    /// The label of this completion item. By default
248    /// also the text that is inserted when selecting
249    /// this completion.
250    pub label: EcoString,
251
252    /// Additional details for the label
253    ///
254    /// @since 3.17.0
255    #[serde(skip_serializing_if = "Option::is_none")]
256    pub label_details: Option<CompletionItemLabelDetails>,
257
258    /// The kind of this completion item. Based of the kind
259    /// an icon is chosen by the editor.
260    pub kind: CompletionKind,
261
262    /// A human-readable string with additional information
263    /// about this item, like type or symbol information.
264    #[serde(skip_serializing_if = "Option::is_none")]
265    pub detail: Option<EcoString>,
266
267    /// A string that should be used when comparing this item
268    /// with other items. When `falsy` the label is used
269    /// as the sort text for this item.
270    #[serde(skip_serializing_if = "Option::is_none")]
271    pub sort_text: Option<EcoString>,
272
273    /// A string that should be used when filtering a set of
274    /// completion items. When `falsy` the label is used as the
275    /// filter text for this item.
276    #[serde(skip_serializing_if = "Option::is_none")]
277    pub filter_text: Option<EcoString>,
278
279    /// A string that should be inserted into a document when selecting
280    /// this completion. When `falsy` the label is used as the insert text
281    /// for this item.
282    ///
283    /// The `insertText` is subject to interpretation by the client side.
284    /// Some tools might not take the string literally. For example
285    /// VS Code when code complete is requested in this example
286    /// `con<cursor position>` and a completion item with an `insertText` of
287    /// `console` is provided it will only insert `sole`. Therefore it is
288    /// recommended to use `textEdit` instead since it avoids additional client
289    /// side interpretation.
290    #[serde(skip_serializing_if = "Option::is_none")]
291    pub insert_text: Option<EcoString>,
292
293    /// The format of the insert text. The format applies to both the
294    /// `insertText` property and the `newText` property of a provided
295    /// `textEdit`. If omitted defaults to `InsertTextFormat.PlainText`.
296    ///
297    /// @since 3.16.0
298    #[serde(skip_serializing_if = "Option::is_none")]
299    pub insert_text_format: Option<InsertTextFormat>,
300
301    /// An edit which is applied to a document when selecting
302    /// this completion. When an edit is provided the value of
303    /// insertText is ignored.
304    ///
305    /// Most editors support two different operation when accepting a completion
306    /// item. One is to insert a completion text and the other is to replace an
307    /// existing text with a completion text. Since this can usually not
308    /// predetermined by a server it can report both ranges. Clients need to
309    /// signal support for `InsertReplaceEdits` via the
310    /// `textDocument.completion.insertReplaceSupport` client capability
311    /// property.
312    ///
313    /// *Note 1:* The text edit's range as well as both ranges from a insert
314    /// replace edit must be a [single line] and they must contain the
315    /// position at which completion has been requested. *Note 2:* If an
316    /// `InsertReplaceEdit` is returned the edit's insert range must be a prefix
317    /// of the edit's replace range, that means it must be contained and
318    /// starting at the same position.
319    ///
320    /// @since 3.16.0 additional type `InsertReplaceEdit`
321    #[serde(skip_serializing_if = "Option::is_none")]
322    pub text_edit: Option<EcoCompletionTextEdit>,
323
324    /// An optional array of additional text edits that are applied when
325    /// selecting this completion. Edits must not overlap with the main edit
326    /// nor with themselves.
327    #[serde(skip_serializing_if = "Option::is_none")]
328    pub additional_text_edits: Option<Vec<EcoTextEdit>>,
329
330    /// An optional command that is executed *after* inserting this completion.
331    /// *Note* that additional modifications to the current document should
332    /// be described with the additionalTextEdits-property.
333    #[serde(skip_serializing_if = "Option::is_none")]
334    pub command: Option<LspCompletionCommand>,
335}
336
337/// Represents a reference to a command. Provides a title which will be used to
338/// represent a command in the UI. Commands are identified by a string
339/// identifier. The recommended way to handle commands is to implement
340/// their execution on the server side if the client and server provides the
341/// corresponding capabilities. Alternatively the tool extension code could
342/// handle the command. The protocol currently doesn’t specify a set of
343/// well-known commands.
344#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
345pub struct LspCompletionCommand {
346    /// The title of command.
347    pub title: EcoString,
348    /// The identifier of the actual command handler.
349    pub command: StrRef,
350}
351
352impl From<StrRef> for LspCompletionCommand {
353    fn from(command: StrRef) -> Self {
354        Self {
355            title: EcoString::default(),
356            command,
357        }
358    }
359}