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.rs103
1 files changed, 62 insertions, 41 deletions
diff --git a/crates/ra_ide_api/src/completion/completion_item.rs b/crates/ra_ide_api/src/completion/completion_item.rs
index 1cdcde211..9aa9768d1 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,47 @@ 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
39impl fmt::Debug for CompletionItem { 55impl fmt::Debug for CompletionItem {
40 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 56 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41 let mut s = f.debug_struct("CompletionItem"); 57 let mut s = f.debug_struct("CompletionItem");
42 s.field("label", &self.label()).field("source_range", &self.source_range()); 58 s.field("label", &self.label())
59 .field("source_range", &self.source_range())
60 .field("text_edit", &self.text_edit);
43 if let Some(kind) = self.kind().as_ref() { 61 if let Some(kind) = self.kind().as_ref() {
44 s.field("kind", kind); 62 s.field("kind", kind);
45 } 63 }
@@ -52,13 +70,6 @@ impl fmt::Debug for CompletionItem {
52 if let Some(documentation) = self.documentation() { 70 if let Some(documentation) = self.documentation() {
53 s.field("documentation", &documentation); 71 s.field("documentation", &documentation);
54 } 72 }
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() 73 s.finish()
63 } 74 }
64} 75}
@@ -103,12 +114,12 @@ pub enum InsertTextFormat {
103impl CompletionItem { 114impl CompletionItem {
104 pub(crate) fn new( 115 pub(crate) fn new(
105 completion_kind: CompletionKind, 116 completion_kind: CompletionKind,
106 replace_range: TextRange, 117 source_range: TextRange,
107 label: impl Into<String>, 118 label: impl Into<String>,
108 ) -> Builder { 119 ) -> Builder {
109 let label = label.into(); 120 let label = label.into();
110 Builder { 121 Builder {
111 source_range: replace_range, 122 source_range,
112 completion_kind, 123 completion_kind,
113 label, 124 label,
114 insert_text: None, 125 insert_text: None,
@@ -124,6 +135,18 @@ impl CompletionItem {
124 pub fn label(&self) -> &str { 135 pub fn label(&self) -> &str {
125 &self.label 136 &self.label
126 } 137 }
138 pub fn source_range(&self) -> TextRange {
139 self.source_range
140 }
141
142 pub fn insert_text_format(&self) -> InsertTextFormat {
143 self.insert_text_format
144 }
145
146 pub fn text_edit(&self) -> &TextEdit {
147 &self.text_edit
148 }
149
127 /// Short one-line additional information, like a type 150 /// Short one-line additional information, like a type
128 pub fn detail(&self) -> Option<&str> { 151 pub fn detail(&self) -> Option<&str> {
129 self.detail.as_ref().map(|it| it.as_str()) 152 self.detail.as_ref().map(|it| it.as_str())
@@ -137,24 +160,9 @@ impl CompletionItem {
137 self.lookup.as_ref().map(|it| it.as_str()).unwrap_or_else(|| self.label()) 160 self.lookup.as_ref().map(|it| it.as_str()).unwrap_or_else(|| self.label())
138 } 161 }
139 162
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> { 163 pub fn kind(&self) -> Option<CompletionItemKind> {
150 self.kind 164 self.kind
151 } 165 }
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} 166}
159 167
160/// A helper to make `CompletionItem`s. 168/// A helper to make `CompletionItem`s.
@@ -178,17 +186,27 @@ impl Builder {
178 } 186 }
179 187
180 pub(crate) fn build(self) -> CompletionItem { 188 pub(crate) fn build(self) -> CompletionItem {
189 let label = self.label;
190 let text_edit = match self.text_edit {
191 Some(it) => it,
192 None => {
193 let mut builder = TextEditBuilder::default();
194 builder
195 .replace(self.source_range, self.insert_text.unwrap_or_else(|| label.clone()));
196 builder.finish()
197 }
198 };
199
181 CompletionItem { 200 CompletionItem {
182 source_range: self.source_range, 201 source_range: self.source_range,
183 label: self.label, 202 label,
203 insert_text_format: self.insert_text_format,
204 text_edit,
184 detail: self.detail, 205 detail: self.detail,
185 documentation: self.documentation, 206 documentation: self.documentation,
186 insert_text_format: self.insert_text_format,
187 lookup: self.lookup, 207 lookup: self.lookup,
188 kind: self.kind, 208 kind: self.kind,
189 completion_kind: self.completion_kind, 209 completion_kind: self.completion_kind,
190 text_edit: self.text_edit,
191 insert_text: self.insert_text,
192 } 210 }
193 } 211 }
194 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { 212 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
@@ -207,11 +225,14 @@ impl Builder {
207 self.kind = Some(kind); 225 self.kind = Some(kind);
208 self 226 self
209 } 227 }
210 #[allow(unused)]
211 pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder { 228 pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder {
212 self.text_edit = Some(edit); 229 self.text_edit = Some(edit);
213 self 230 self
214 } 231 }
232 pub(crate) fn snippet_edit(mut self, edit: TextEdit) -> Builder {
233 self.insert_text_format = InsertTextFormat::Snippet;
234 self.text_edit(edit)
235 }
215 #[allow(unused)] 236 #[allow(unused)]
216 pub(crate) fn detail(self, detail: impl Into<String>) -> Builder { 237 pub(crate) fn detail(self, detail: impl Into<String>) -> Builder {
217 self.set_detail(Some(detail)) 238 self.set_detail(Some(detail))