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