diff options
Diffstat (limited to 'crates/ra_ide_api/src/completion/completion_item.rs')
-rw-r--r-- | crates/ra_ide_api/src/completion/completion_item.rs | 114 |
1 files changed, 68 insertions, 46 deletions
diff --git a/crates/ra_ide_api/src/completion/completion_item.rs b/crates/ra_ide_api/src/completion/completion_item.rs index 7b93b5df8..95bdd59b4 100644 --- a/crates/ra_ide_api/src/completion/completion_item.rs +++ b/crates/ra_ide_api/src/completion/completion_item.rs | |||
@@ -2,7 +2,7 @@ use std::fmt; | |||
2 | 2 | ||
3 | use hir::{Docs, Documentation, PerNs, Resolution}; | 3 | use hir::{Docs, Documentation, PerNs, Resolution}; |
4 | use ra_syntax::TextRange; | 4 | use ra_syntax::TextRange; |
5 | use ra_text_edit::TextEdit; | 5 | use ra_text_edit::{ TextEditBuilder, TextEdit}; |
6 | use test_utils::tested_by; | 6 | use test_utils::tested_by; |
7 | 7 | ||
8 | use crate::completion::{ | 8 | use crate::completion::{ |
@@ -17,29 +17,53 @@ use crate::completion::{ | |||
17 | /// `CompletionItem`, use `new` method and the `Builder` struct. | 17 | /// `CompletionItem`, use `new` method and the `Builder` struct. |
18 | pub struct CompletionItem { | 18 | pub struct CompletionItem { |
19 | /// Used only internally in tests, to check only specific kind of | 19 | /// Used only internally in tests, to check only specific kind of |
20 | /// completion. | 20 | /// completion (postfix, keyword, reference, etc). |
21 | #[allow(unused)] | 21 | #[allow(unused)] |
22 | completion_kind: CompletionKind, | 22 | completion_kind: CompletionKind, |
23 | /// Label in the completion pop up which identifies completion. | ||
23 | label: String, | 24 | label: String, |
25 | /// Range of identifier that is being completed. | ||
26 | /// | ||
27 | /// It should be used primarily for UI, but we also use this to convert | ||
28 | /// genetic TextEdit into LSP's completion edit (see conv.rs). | ||
29 | /// | ||
30 | /// `source_range` must contain the completion offset. `insert_text` should | ||
31 | /// start with what `source_range` points to, or VSCode will filter out the | ||
32 | /// completion silently. | ||
33 | source_range: TextRange, | ||
34 | /// What happens when user selects this item. | ||
35 | /// | ||
36 | /// Typically, replaces `source_range` with new identifier. | ||
37 | text_edit: TextEdit, | ||
38 | insert_text_format: InsertTextFormat, | ||
39 | |||
40 | /// What item (struct, function, etc) are we completing. | ||
24 | kind: Option<CompletionItemKind>, | 41 | kind: Option<CompletionItemKind>, |
42 | |||
43 | /// Lookup is used to check if completion item indeed can complete current | ||
44 | /// ident. | ||
45 | /// | ||
46 | /// That is, in `foo.bar<|>` lookup of `abracadabra` will be accepted (it | ||
47 | /// contains `bar` sub sequence), and `quux` will rejected. | ||
25 | lookup: Option<String>, | 48 | lookup: Option<String>, |
49 | |||
50 | /// Additional info to show in the UI pop up. | ||
26 | detail: Option<String>, | 51 | detail: Option<String>, |
27 | documentation: Option<Documentation>, | 52 | documentation: Option<Documentation>, |
28 | insert_text: Option<String>, | ||
29 | insert_text_format: InsertTextFormat, | ||
30 | /// Where completion occurs. `source_range` must contain the completion offset. | ||
31 | /// `insert_text` should start with what `source_range` points to, or VSCode | ||
32 | /// will filter out the completion silently. | ||
33 | source_range: TextRange, | ||
34 | /// Additional text edit, ranges in `text_edit` must never intersect with `source_range`. | ||
35 | /// Or VSCode will drop it silently. | ||
36 | text_edit: Option<TextEdit>, | ||
37 | } | 53 | } |
38 | 54 | ||
55 | // We use custom debug for CompletionItem to make `insta`'s diffs more readable. | ||
39 | impl fmt::Debug for CompletionItem { | 56 | impl fmt::Debug for CompletionItem { |
40 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 57 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
41 | let mut s = f.debug_struct("CompletionItem"); | 58 | let mut s = f.debug_struct("CompletionItem"); |
42 | s.field("label", &self.label()).field("source_range", &self.source_range()); | 59 | s.field("label", &self.label()).field("source_range", &self.source_range()); |
60 | if self.text_edit().as_atoms().len() == 1 { | ||
61 | let atom = &self.text_edit().as_atoms()[0]; | ||
62 | s.field("delete", &atom.delete); | ||
63 | s.field("insert", &atom.insert); | ||
64 | } else { | ||
65 | s.field("text_edit", &self.text_edit); | ||
66 | } | ||
43 | if let Some(kind) = self.kind().as_ref() { | 67 | if let Some(kind) = self.kind().as_ref() { |
44 | s.field("kind", kind); | 68 | s.field("kind", kind); |
45 | } | 69 | } |
@@ -52,13 +76,6 @@ impl fmt::Debug for CompletionItem { | |||
52 | if let Some(documentation) = self.documentation() { | 76 | if let Some(documentation) = self.documentation() { |
53 | s.field("documentation", &documentation); | 77 | s.field("documentation", &documentation); |
54 | } | 78 | } |
55 | if self.insert_text() != self.label() { | ||
56 | s.field("insert_text", &self.insert_text()) | ||
57 | .field("insert_text_format", &self.insert_text_format()); | ||
58 | } | ||
59 | if let Some(edit) = self.text_edit.as_ref() { | ||
60 | s.field("text_edit", edit); | ||
61 | } | ||
62 | s.finish() | 79 | s.finish() |
63 | } | 80 | } |
64 | } | 81 | } |
@@ -103,12 +120,12 @@ pub enum InsertTextFormat { | |||
103 | impl CompletionItem { | 120 | impl CompletionItem { |
104 | pub(crate) fn new( | 121 | pub(crate) fn new( |
105 | completion_kind: CompletionKind, | 122 | completion_kind: CompletionKind, |
106 | replace_range: TextRange, | 123 | source_range: TextRange, |
107 | label: impl Into<String>, | 124 | label: impl Into<String>, |
108 | ) -> Builder { | 125 | ) -> Builder { |
109 | let label = label.into(); | 126 | let label = label.into(); |
110 | Builder { | 127 | Builder { |
111 | source_range: replace_range, | 128 | source_range, |
112 | completion_kind, | 129 | completion_kind, |
113 | label, | 130 | label, |
114 | insert_text: None, | 131 | insert_text: None, |
@@ -124,6 +141,18 @@ impl CompletionItem { | |||
124 | pub fn label(&self) -> &str { | 141 | pub fn label(&self) -> &str { |
125 | &self.label | 142 | &self.label |
126 | } | 143 | } |
144 | pub fn source_range(&self) -> TextRange { | ||
145 | self.source_range | ||
146 | } | ||
147 | |||
148 | pub fn insert_text_format(&self) -> InsertTextFormat { | ||
149 | self.insert_text_format | ||
150 | } | ||
151 | |||
152 | pub fn text_edit(&self) -> &TextEdit { | ||
153 | &self.text_edit | ||
154 | } | ||
155 | |||
127 | /// Short one-line additional information, like a type | 156 | /// Short one-line additional information, like a type |
128 | pub fn detail(&self) -> Option<&str> { | 157 | pub fn detail(&self) -> Option<&str> { |
129 | self.detail.as_ref().map(|it| it.as_str()) | 158 | self.detail.as_ref().map(|it| it.as_str()) |
@@ -137,24 +166,9 @@ impl CompletionItem { | |||
137 | self.lookup.as_ref().map(|it| it.as_str()).unwrap_or_else(|| self.label()) | 166 | self.lookup.as_ref().map(|it| it.as_str()).unwrap_or_else(|| self.label()) |
138 | } | 167 | } |
139 | 168 | ||
140 | pub fn insert_text_format(&self) -> InsertTextFormat { | ||
141 | self.insert_text_format | ||
142 | } | ||
143 | pub fn insert_text(&self) -> String { | ||
144 | match &self.insert_text { | ||
145 | Some(t) => t.clone(), | ||
146 | None => self.label.clone(), | ||
147 | } | ||
148 | } | ||
149 | pub fn kind(&self) -> Option<CompletionItemKind> { | 169 | pub fn kind(&self) -> Option<CompletionItemKind> { |
150 | self.kind | 170 | self.kind |
151 | } | 171 | } |
152 | pub fn take_text_edit(&mut self) -> Option<TextEdit> { | ||
153 | self.text_edit.take() | ||
154 | } | ||
155 | pub fn source_range(&self) -> TextRange { | ||
156 | self.source_range | ||
157 | } | ||
158 | } | 172 | } |
159 | 173 | ||
160 | /// A helper to make `CompletionItem`s. | 174 | /// A helper to make `CompletionItem`s. |
@@ -178,17 +192,27 @@ impl Builder { | |||
178 | } | 192 | } |
179 | 193 | ||
180 | pub(crate) fn build(self) -> CompletionItem { | 194 | pub(crate) fn build(self) -> CompletionItem { |
195 | let label = self.label; | ||
196 | let text_edit = match self.text_edit { | ||
197 | Some(it) => it, | ||
198 | None => { | ||
199 | let mut builder = TextEditBuilder::default(); | ||
200 | builder | ||
201 | .replace(self.source_range, self.insert_text.unwrap_or_else(|| label.clone())); | ||
202 | builder.finish() | ||
203 | } | ||
204 | }; | ||
205 | |||
181 | CompletionItem { | 206 | CompletionItem { |
182 | source_range: self.source_range, | 207 | source_range: self.source_range, |
183 | label: self.label, | 208 | label, |
209 | insert_text_format: self.insert_text_format, | ||
210 | text_edit, | ||
184 | detail: self.detail, | 211 | detail: self.detail, |
185 | documentation: self.documentation, | 212 | documentation: self.documentation, |
186 | insert_text_format: self.insert_text_format, | ||
187 | lookup: self.lookup, | 213 | lookup: self.lookup, |
188 | kind: self.kind, | 214 | kind: self.kind, |
189 | completion_kind: self.completion_kind, | 215 | completion_kind: self.completion_kind, |
190 | text_edit: self.text_edit, | ||
191 | insert_text: self.insert_text, | ||
192 | } | 216 | } |
193 | } | 217 | } |
194 | pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { | 218 | pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { |
@@ -199,12 +223,7 @@ impl Builder { | |||
199 | self.insert_text = Some(insert_text.into()); | 223 | self.insert_text = Some(insert_text.into()); |
200 | self | 224 | self |
201 | } | 225 | } |
202 | #[allow(unused)] | 226 | pub(crate) fn insert_snippet(mut self, snippet: impl Into<String>) -> Builder { |
203 | pub(crate) fn insert_text_format(mut self, insert_text_format: InsertTextFormat) -> Builder { | ||
204 | self.insert_text_format = insert_text_format; | ||
205 | self | ||
206 | } | ||
207 | pub(crate) fn snippet(mut self, snippet: impl Into<String>) -> Builder { | ||
208 | self.insert_text_format = InsertTextFormat::Snippet; | 227 | self.insert_text_format = InsertTextFormat::Snippet; |
209 | self.insert_text(snippet) | 228 | self.insert_text(snippet) |
210 | } | 229 | } |
@@ -212,11 +231,14 @@ impl Builder { | |||
212 | self.kind = Some(kind); | 231 | self.kind = Some(kind); |
213 | self | 232 | self |
214 | } | 233 | } |
215 | #[allow(unused)] | ||
216 | pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder { | 234 | pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder { |
217 | self.text_edit = Some(edit); | 235 | self.text_edit = Some(edit); |
218 | self | 236 | self |
219 | } | 237 | } |
238 | pub(crate) fn snippet_edit(mut self, edit: TextEdit) -> Builder { | ||
239 | self.insert_text_format = InsertTextFormat::Snippet; | ||
240 | self.text_edit(edit) | ||
241 | } | ||
220 | #[allow(unused)] | 242 | #[allow(unused)] |
221 | pub(crate) fn detail(self, detail: impl Into<String>) -> Builder { | 243 | pub(crate) fn detail(self, detail: impl Into<String>) -> Builder { |
222 | self.set_detail(Some(detail)) | 244 | self.set_detail(Some(detail)) |