]> git.lizzy.rs Git - rust.git/blob - crates/completion/src/item.rs
f23913935c0e018e7d3287422c6efedd02a70512
[rust.git] / crates / completion / src / item.rs
1 //! See `CompletionItem` structure.
2
3 use std::fmt;
4
5 use hir::{Documentation, Mutability};
6 use syntax::TextRange;
7 use text_edit::TextEdit;
8
9 use crate::config::SnippetCap;
10
11 /// `CompletionItem` describes a single completion variant in the editor pop-up.
12 /// It is basically a POD with various properties. To construct a
13 /// `CompletionItem`, use `new` method and the `Builder` struct.
14 pub struct CompletionItem {
15     /// Used only internally in tests, to check only specific kind of
16     /// completion (postfix, keyword, reference, etc).
17     #[allow(unused)]
18     pub(crate) completion_kind: CompletionKind,
19     /// Label in the completion pop up which identifies completion.
20     label: String,
21     /// Range of identifier that is being completed.
22     ///
23     /// It should be used primarily for UI, but we also use this to convert
24     /// genetic TextEdit into LSP's completion edit (see conv.rs).
25     ///
26     /// `source_range` must contain the completion offset. `insert_text` should
27     /// start with what `source_range` points to, or VSCode will filter out the
28     /// completion silently.
29     source_range: TextRange,
30     /// What happens when user selects this item.
31     ///
32     /// Typically, replaces `source_range` with new identifier.
33     text_edit: TextEdit,
34
35     insert_text_format: InsertTextFormat,
36
37     /// What item (struct, function, etc) are we completing.
38     kind: Option<CompletionItemKind>,
39
40     /// Lookup is used to check if completion item indeed can complete current
41     /// ident.
42     ///
43     /// That is, in `foo.bar<|>` lookup of `abracadabra` will be accepted (it
44     /// contains `bar` sub sequence), and `quux` will rejected.
45     lookup: Option<String>,
46
47     /// Additional info to show in the UI pop up.
48     detail: Option<String>,
49     documentation: Option<Documentation>,
50
51     /// Whether this item is marked as deprecated
52     deprecated: bool,
53
54     /// If completing a function call, ask the editor to show parameter popup
55     /// after completion.
56     trigger_call_info: bool,
57
58     /// Score is useful to pre select or display in better order completion items
59     score: Option<CompletionScore>,
60
61     /// Indicates that a reference or mutable reference to this variable is a
62     /// possible match.
63     ref_match: Option<(Mutability, CompletionScore)>,
64 }
65
66 // We use custom debug for CompletionItem to make snapshot tests more readable.
67 impl fmt::Debug for CompletionItem {
68     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69         let mut s = f.debug_struct("CompletionItem");
70         s.field("label", &self.label()).field("source_range", &self.source_range());
71         if self.text_edit().len() == 1 {
72             let atom = &self.text_edit().iter().next().unwrap();
73             s.field("delete", &atom.delete);
74             s.field("insert", &atom.insert);
75         } else {
76             s.field("text_edit", &self.text_edit);
77         }
78         if let Some(kind) = self.kind().as_ref() {
79             s.field("kind", kind);
80         }
81         if self.lookup() != self.label() {
82             s.field("lookup", &self.lookup());
83         }
84         if let Some(detail) = self.detail() {
85             s.field("detail", &detail);
86         }
87         if let Some(documentation) = self.documentation() {
88             s.field("documentation", &documentation);
89         }
90         if self.deprecated {
91             s.field("deprecated", &true);
92         }
93         if let Some(score) = &self.score {
94             s.field("score", score);
95         }
96         if self.trigger_call_info {
97             s.field("trigger_call_info", &true);
98         }
99         s.finish()
100     }
101 }
102
103 #[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
104 pub enum CompletionScore {
105     /// If only type match
106     TypeMatch,
107     /// If type and name match
108     TypeAndNameMatch,
109 }
110
111 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
112 pub enum CompletionItemKind {
113     Snippet,
114     Keyword,
115     Module,
116     Function,
117     BuiltinType,
118     Struct,
119     Enum,
120     EnumVariant,
121     Binding,
122     Field,
123     Static,
124     Const,
125     Trait,
126     TypeAlias,
127     Method,
128     TypeParam,
129     Macro,
130     Attribute,
131     UnresolvedReference,
132 }
133
134 impl CompletionItemKind {
135     #[cfg(test)]
136     pub(crate) fn tag(&self) -> &'static str {
137         match self {
138             CompletionItemKind::Attribute => "at",
139             CompletionItemKind::Binding => "bn",
140             CompletionItemKind::BuiltinType => "bt",
141             CompletionItemKind::Const => "ct",
142             CompletionItemKind::Enum => "en",
143             CompletionItemKind::EnumVariant => "ev",
144             CompletionItemKind::Field => "fd",
145             CompletionItemKind::Function => "fn",
146             CompletionItemKind::Keyword => "kw",
147             CompletionItemKind::Macro => "ma",
148             CompletionItemKind::Method => "me",
149             CompletionItemKind::Module => "md",
150             CompletionItemKind::Snippet => "sn",
151             CompletionItemKind::Static => "sc",
152             CompletionItemKind::Struct => "st",
153             CompletionItemKind::Trait => "tt",
154             CompletionItemKind::TypeAlias => "ta",
155             CompletionItemKind::TypeParam => "tp",
156             CompletionItemKind::UnresolvedReference => "??",
157         }
158     }
159 }
160
161 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
162 pub(crate) enum CompletionKind {
163     /// Parser-based keyword completion.
164     Keyword,
165     /// Your usual "complete all valid identifiers".
166     Reference,
167     /// "Secret sauce" completions.
168     Magic,
169     Snippet,
170     Postfix,
171     BuiltinType,
172     Attribute,
173 }
174
175 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
176 pub enum InsertTextFormat {
177     PlainText,
178     Snippet,
179 }
180
181 impl CompletionItem {
182     pub(crate) fn new(
183         completion_kind: CompletionKind,
184         source_range: TextRange,
185         label: impl Into<String>,
186     ) -> Builder {
187         let label = label.into();
188         Builder {
189             source_range,
190             completion_kind,
191             label,
192             insert_text: None,
193             insert_text_format: InsertTextFormat::PlainText,
194             detail: None,
195             documentation: None,
196             lookup: None,
197             kind: None,
198             text_edit: None,
199             deprecated: None,
200             trigger_call_info: None,
201             score: None,
202             ref_match: None,
203         }
204     }
205     /// What user sees in pop-up in the UI.
206     pub fn label(&self) -> &str {
207         &self.label
208     }
209     pub fn source_range(&self) -> TextRange {
210         self.source_range
211     }
212
213     pub fn insert_text_format(&self) -> InsertTextFormat {
214         self.insert_text_format
215     }
216
217     pub fn text_edit(&self) -> &TextEdit {
218         &self.text_edit
219     }
220
221     /// Short one-line additional information, like a type
222     pub fn detail(&self) -> Option<&str> {
223         self.detail.as_deref()
224     }
225     /// A doc-comment
226     pub fn documentation(&self) -> Option<Documentation> {
227         self.documentation.clone()
228     }
229     /// What string is used for filtering.
230     pub fn lookup(&self) -> &str {
231         self.lookup.as_deref().unwrap_or(&self.label)
232     }
233
234     pub fn kind(&self) -> Option<CompletionItemKind> {
235         self.kind
236     }
237
238     pub fn deprecated(&self) -> bool {
239         self.deprecated
240     }
241
242     pub fn score(&self) -> Option<CompletionScore> {
243         self.score
244     }
245
246     pub fn trigger_call_info(&self) -> bool {
247         self.trigger_call_info
248     }
249
250     pub fn ref_match(&self) -> Option<(Mutability, CompletionScore)> {
251         self.ref_match
252     }
253 }
254
255 /// A helper to make `CompletionItem`s.
256 #[must_use]
257 #[derive(Clone)]
258 pub(crate) struct Builder {
259     source_range: TextRange,
260     completion_kind: CompletionKind,
261     label: String,
262     insert_text: Option<String>,
263     insert_text_format: InsertTextFormat,
264     detail: Option<String>,
265     documentation: Option<Documentation>,
266     lookup: Option<String>,
267     kind: Option<CompletionItemKind>,
268     text_edit: Option<TextEdit>,
269     deprecated: Option<bool>,
270     trigger_call_info: Option<bool>,
271     score: Option<CompletionScore>,
272     ref_match: Option<(Mutability, CompletionScore)>,
273 }
274
275 impl Builder {
276     pub(crate) fn build(self) -> CompletionItem {
277         let label = self.label;
278         let text_edit = match self.text_edit {
279             Some(it) => it,
280             None => TextEdit::replace(
281                 self.source_range,
282                 self.insert_text.unwrap_or_else(|| label.clone()),
283             ),
284         };
285
286         CompletionItem {
287             source_range: self.source_range,
288             label,
289             insert_text_format: self.insert_text_format,
290             text_edit,
291             detail: self.detail,
292             documentation: self.documentation,
293             lookup: self.lookup,
294             kind: self.kind,
295             completion_kind: self.completion_kind,
296             deprecated: self.deprecated.unwrap_or(false),
297             trigger_call_info: self.trigger_call_info.unwrap_or(false),
298             score: self.score,
299             ref_match: self.ref_match,
300         }
301     }
302     pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
303         self.lookup = Some(lookup.into());
304         self
305     }
306     pub(crate) fn label(mut self, label: impl Into<String>) -> Builder {
307         self.label = label.into();
308         self
309     }
310     pub(crate) fn insert_text(mut self, insert_text: impl Into<String>) -> Builder {
311         self.insert_text = Some(insert_text.into());
312         self
313     }
314     pub(crate) fn insert_snippet(
315         mut self,
316         _cap: SnippetCap,
317         snippet: impl Into<String>,
318     ) -> Builder {
319         self.insert_text_format = InsertTextFormat::Snippet;
320         self.insert_text(snippet)
321     }
322     pub(crate) fn kind(mut self, kind: CompletionItemKind) -> Builder {
323         self.kind = Some(kind);
324         self
325     }
326     pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder {
327         self.text_edit = Some(edit);
328         self
329     }
330     pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder {
331         self.insert_text_format = InsertTextFormat::Snippet;
332         self.text_edit(edit)
333     }
334     #[allow(unused)]
335     pub(crate) fn detail(self, detail: impl Into<String>) -> Builder {
336         self.set_detail(Some(detail))
337     }
338     pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder {
339         self.detail = detail.map(Into::into);
340         self
341     }
342     #[allow(unused)]
343     pub(crate) fn documentation(self, docs: Documentation) -> Builder {
344         self.set_documentation(Some(docs))
345     }
346     pub(crate) fn set_documentation(mut self, docs: Option<Documentation>) -> Builder {
347         self.documentation = docs.map(Into::into);
348         self
349     }
350     pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder {
351         self.deprecated = Some(deprecated);
352         self
353     }
354     pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder {
355         self.score = Some(score);
356         self
357     }
358     pub(crate) fn trigger_call_info(mut self) -> Builder {
359         self.trigger_call_info = Some(true);
360         self
361     }
362     pub(crate) fn set_ref_match(
363         mut self,
364         ref_match: Option<(Mutability, CompletionScore)>,
365     ) -> Builder {
366         self.ref_match = ref_match;
367         self
368     }
369 }
370
371 impl<'a> Into<CompletionItem> for Builder {
372     fn into(self) -> CompletionItem {
373         self.build()
374     }
375 }