]> git.lizzy.rs Git - rust.git/blob - crates/completion/src/item.rs
Reuse existing element rendering
[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     pub fn update_text_edit(&mut self, new_text_edit: TextEdit) {
222         self.text_edit = new_text_edit;
223     }
224
225     /// Short one-line additional information, like a type
226     pub fn detail(&self) -> Option<&str> {
227         self.detail.as_deref()
228     }
229     /// A doc-comment
230     pub fn documentation(&self) -> Option<Documentation> {
231         self.documentation.clone()
232     }
233     /// What string is used for filtering.
234     pub fn lookup(&self) -> &str {
235         self.lookup.as_deref().unwrap_or(&self.label)
236     }
237
238     pub fn kind(&self) -> Option<CompletionItemKind> {
239         self.kind
240     }
241
242     pub fn deprecated(&self) -> bool {
243         self.deprecated
244     }
245
246     pub fn score(&self) -> Option<CompletionScore> {
247         self.score
248     }
249
250     pub fn trigger_call_info(&self) -> bool {
251         self.trigger_call_info
252     }
253
254     pub fn ref_match(&self) -> Option<(Mutability, CompletionScore)> {
255         self.ref_match
256     }
257 }
258
259 /// A helper to make `CompletionItem`s.
260 #[must_use]
261 #[derive(Clone)]
262 pub(crate) struct Builder {
263     source_range: TextRange,
264     completion_kind: CompletionKind,
265     label: String,
266     insert_text: Option<String>,
267     insert_text_format: InsertTextFormat,
268     detail: Option<String>,
269     documentation: Option<Documentation>,
270     lookup: Option<String>,
271     kind: Option<CompletionItemKind>,
272     text_edit: Option<TextEdit>,
273     deprecated: Option<bool>,
274     trigger_call_info: Option<bool>,
275     score: Option<CompletionScore>,
276     ref_match: Option<(Mutability, CompletionScore)>,
277 }
278
279 impl Builder {
280     pub(crate) fn build(self) -> CompletionItem {
281         let label = self.label;
282         let text_edit = match self.text_edit {
283             Some(it) => it,
284             None => TextEdit::replace(
285                 self.source_range,
286                 self.insert_text.unwrap_or_else(|| label.clone()),
287             ),
288         };
289
290         CompletionItem {
291             source_range: self.source_range,
292             label,
293             insert_text_format: self.insert_text_format,
294             text_edit,
295             detail: self.detail,
296             documentation: self.documentation,
297             lookup: self.lookup,
298             kind: self.kind,
299             completion_kind: self.completion_kind,
300             deprecated: self.deprecated.unwrap_or(false),
301             trigger_call_info: self.trigger_call_info.unwrap_or(false),
302             score: self.score,
303             ref_match: self.ref_match,
304         }
305     }
306     pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
307         self.lookup = Some(lookup.into());
308         self
309     }
310     pub(crate) fn label(mut self, label: impl Into<String>) -> Builder {
311         self.label = label.into();
312         self
313     }
314     pub(crate) fn insert_text(mut self, insert_text: impl Into<String>) -> Builder {
315         self.insert_text = Some(insert_text.into());
316         self
317     }
318     pub(crate) fn insert_snippet(
319         mut self,
320         _cap: SnippetCap,
321         snippet: impl Into<String>,
322     ) -> Builder {
323         self.insert_text_format = InsertTextFormat::Snippet;
324         self.insert_text(snippet)
325     }
326     pub(crate) fn kind(mut self, kind: CompletionItemKind) -> Builder {
327         self.kind = Some(kind);
328         self
329     }
330     pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder {
331         self.text_edit = Some(edit);
332         self
333     }
334     pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder {
335         self.insert_text_format = InsertTextFormat::Snippet;
336         self.text_edit(edit)
337     }
338     #[allow(unused)]
339     pub(crate) fn detail(self, detail: impl Into<String>) -> Builder {
340         self.set_detail(Some(detail))
341     }
342     pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder {
343         self.detail = detail.map(Into::into);
344         self
345     }
346     #[allow(unused)]
347     pub(crate) fn documentation(self, docs: Documentation) -> Builder {
348         self.set_documentation(Some(docs))
349     }
350     pub(crate) fn set_documentation(mut self, docs: Option<Documentation>) -> Builder {
351         self.documentation = docs.map(Into::into);
352         self
353     }
354     pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder {
355         self.deprecated = Some(deprecated);
356         self
357     }
358     pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder {
359         self.score = Some(score);
360         self
361     }
362     pub(crate) fn trigger_call_info(mut self) -> Builder {
363         self.trigger_call_info = Some(true);
364         self
365     }
366     pub(crate) fn set_ref_match(
367         mut self,
368         ref_match: Option<(Mutability, CompletionScore)>,
369     ) -> Builder {
370         self.ref_match = ref_match;
371         self
372     }
373 }
374
375 impl<'a> Into<CompletionItem> for Builder {
376     fn into(self) -> CompletionItem {
377         self.build()
378     }
379 }