aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src/completion/completion_item.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api/src/completion/completion_item.rs')
-rw-r--r--crates/ra_ide_api/src/completion/completion_item.rs114
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
3use hir::{Docs, Documentation, PerNs, Resolution}; 3use hir::{Docs, Documentation, PerNs, Resolution};
4use ra_syntax::TextRange; 4use ra_syntax::TextRange;
5use ra_text_edit::TextEdit; 5use ra_text_edit::{ TextEditBuilder, TextEdit};
6use test_utils::tested_by; 6use test_utils::tested_by;
7 7
8use crate::completion::{ 8use 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.
18pub struct CompletionItem { 18pub 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.
39impl fmt::Debug for CompletionItem { 56impl 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 {
103impl CompletionItem { 120impl 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))