aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorLukas Wirth <[email protected]>2021-04-08 13:22:54 +0100
committerLukas Wirth <[email protected]>2021-04-08 14:21:27 +0100
commit8fa3011908e3b6cc74cfa4d7e896f5d329e4228c (patch)
tree9c6f1e2a87d045a2cd65c9f9597f0b7285d0303b /crates
parent3191a93185b34c6deebca2aad0584d2840ad6d43 (diff)
Use CompletionTextEdit::InsertAndReplace if supported by the client
Diffstat (limited to 'crates')
-rw-r--r--crates/ide_completion/src/item.rs2
-rw-r--r--crates/rust-analyzer/src/config.rs13
-rw-r--r--crates/rust-analyzer/src/handlers.rs5
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs24
-rw-r--r--crates/rust-analyzer/src/to_proto.rs26
5 files changed, 50 insertions, 20 deletions
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs
index cc4ac9ea2..16991b688 100644
--- a/crates/ide_completion/src/item.rs
+++ b/crates/ide_completion/src/item.rs
@@ -29,7 +29,7 @@ pub struct CompletionItem {
29 /// Range of identifier that is being completed. 29 /// Range of identifier that is being completed.
30 /// 30 ///
31 /// It should be used primarily for UI, but we also use this to convert 31 /// It should be used primarily for UI, but we also use this to convert
32 /// genetic TextEdit into LSP's completion edit (see conv.rs). 32 /// generic TextEdit into LSP's completion edit (see conv.rs).
33 /// 33 ///
34 /// `source_range` must contain the completion offset. `insert_text` should 34 /// `source_range` must contain the completion offset. `insert_text` should
35 /// start with what `source_range` points to, or VSCode will filter out the 35 /// start with what `source_range` points to, or VSCode will filter out the
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index e012b4452..f809667e9 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -656,6 +656,19 @@ impl Config {
656 pub fn code_lens_refresh(&self) -> bool { 656 pub fn code_lens_refresh(&self) -> bool {
657 try_or!(self.caps.workspace.as_ref()?.code_lens.as_ref()?.refresh_support?, false) 657 try_or!(self.caps.workspace.as_ref()?.code_lens.as_ref()?.refresh_support?, false)
658 } 658 }
659 pub fn insert_replace_support(&self) -> bool {
660 try_or!(
661 self.caps
662 .text_document
663 .as_ref()?
664 .completion
665 .as_ref()?
666 .completion_item
667 .as_ref()?
668 .insert_replace_support?,
669 false
670 )
671 }
659} 672}
660 673
661#[derive(Deserialize, Debug, Clone)] 674#[derive(Deserialize, Debug, Clone)]
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 31d8c487b..edfa42eb5 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -664,10 +664,13 @@ pub(crate) fn handle_completion(
664 }; 664 };
665 let line_index = snap.file_line_index(position.file_id)?; 665 let line_index = snap.file_line_index(position.file_id)?;
666 666
667 let insert_replace_support =
668 snap.config.insert_replace_support().then(|| text_document_position.position);
667 let items: Vec<CompletionItem> = items 669 let items: Vec<CompletionItem> = items
668 .into_iter() 670 .into_iter()
669 .flat_map(|item| { 671 .flat_map(|item| {
670 let mut new_completion_items = to_proto::completion_item(&line_index, item.clone()); 672 let mut new_completion_items =
673 to_proto::completion_item(insert_replace_support, &line_index, item.clone());
671 674
672 if completion_config.enable_imports_on_the_fly { 675 if completion_config.enable_imports_on_the_fly {
673 for new_item in &mut new_completion_items { 676 for new_item in &mut new_completion_items {
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
index 2ac487632..73c4193e8 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -150,8 +150,16 @@ pub(crate) fn all_edits_are_disjoint(
150 edit_ranges.push(edit.range); 150 edit_ranges.push(edit.range);
151 } 151 }
152 Some(lsp_types::CompletionTextEdit::InsertAndReplace(edit)) => { 152 Some(lsp_types::CompletionTextEdit::InsertAndReplace(edit)) => {
153 edit_ranges.push(edit.insert); 153 let replace = edit.replace;
154 edit_ranges.push(edit.replace); 154 let insert = edit.insert;
155 if replace.start != insert.start
156 || insert.start > insert.end
157 || insert.end > replace.end
158 {
159 // insert has to be a prefix of replace but it is not
160 return false;
161 }
162 edit_ranges.push(replace);
155 } 163 }
156 None => {} 164 None => {}
157 } 165 }
@@ -314,18 +322,6 @@ mod tests {
314 Some(CompletionTextEdit::InsertAndReplace(InsertReplaceEdit { 322 Some(CompletionTextEdit::InsertAndReplace(InsertReplaceEdit {
315 new_text: "new_text".to_string(), 323 new_text: "new_text".to_string(),
316 insert: disjoint_edit.range, 324 insert: disjoint_edit.range,
317 replace: joint_edit.range,
318 }));
319 completion_with_joint_edits.additional_text_edits = None;
320 assert!(
321 !all_edits_are_disjoint(&completion_with_joint_edits, &[]),
322 "Completion with disjoint edits fails the validation even with empty extra edits"
323 );
324
325 completion_with_joint_edits.text_edit =
326 Some(CompletionTextEdit::InsertAndReplace(InsertReplaceEdit {
327 new_text: "new_text".to_string(),
328 insert: disjoint_edit.range,
329 replace: disjoint_edit_2.range, 325 replace: disjoint_edit_2.range,
330 })); 326 }));
331 completion_with_joint_edits.additional_text_edits = Some(vec![joint_edit]); 327 completion_with_joint_edits.additional_text_edits = Some(vec![joint_edit]);
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index c3820944b..d5c0d22b8 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -145,6 +145,23 @@ pub(crate) fn text_edit(line_index: &LineIndex, indel: Indel) -> lsp_types::Text
145 lsp_types::TextEdit { range, new_text } 145 lsp_types::TextEdit { range, new_text }
146} 146}
147 147
148pub(crate) fn completion_text_edit(
149 line_index: &LineIndex,
150 insert_replace_support: Option<lsp_types::Position>,
151 indel: Indel,
152) -> lsp_types::CompletionTextEdit {
153 let text_edit = text_edit(line_index, indel);
154 match insert_replace_support {
155 Some(cursor_pos) => lsp_types::InsertReplaceEdit {
156 new_text: text_edit.new_text,
157 insert: lsp_types::Range { start: text_edit.range.start, end: cursor_pos },
158 replace: text_edit.range,
159 }
160 .into(),
161 None => text_edit.into(),
162 }
163}
164
148pub(crate) fn snippet_text_edit( 165pub(crate) fn snippet_text_edit(
149 line_index: &LineIndex, 166 line_index: &LineIndex,
150 is_snippet: bool, 167 is_snippet: bool,
@@ -179,6 +196,7 @@ pub(crate) fn snippet_text_edit_vec(
179} 196}
180 197
181pub(crate) fn completion_item( 198pub(crate) fn completion_item(
199 insert_replace_support: Option<lsp_types::Position>,
182 line_index: &LineIndex, 200 line_index: &LineIndex,
183 item: CompletionItem, 201 item: CompletionItem,
184) -> Vec<lsp_types::CompletionItem> { 202) -> Vec<lsp_types::CompletionItem> {
@@ -190,7 +208,7 @@ pub(crate) fn completion_item(
190 for indel in item.text_edit().iter() { 208 for indel in item.text_edit().iter() {
191 if indel.delete.contains_range(source_range) { 209 if indel.delete.contains_range(source_range) {
192 text_edit = Some(if indel.delete == source_range { 210 text_edit = Some(if indel.delete == source_range {
193 self::text_edit(line_index, indel.clone()) 211 self::completion_text_edit(line_index, insert_replace_support, indel.clone())
194 } else { 212 } else {
195 assert!(source_range.end() == indel.delete.end()); 213 assert!(source_range.end() == indel.delete.end());
196 let range1 = TextRange::new(indel.delete.start(), source_range.start()); 214 let range1 = TextRange::new(indel.delete.start(), source_range.start());
@@ -198,7 +216,7 @@ pub(crate) fn completion_item(
198 let indel1 = Indel::replace(range1, String::new()); 216 let indel1 = Indel::replace(range1, String::new());
199 let indel2 = Indel::replace(range2, indel.insert.clone()); 217 let indel2 = Indel::replace(range2, indel.insert.clone());
200 additional_text_edits.push(self::text_edit(line_index, indel1)); 218 additional_text_edits.push(self::text_edit(line_index, indel1));
201 self::text_edit(line_index, indel2) 219 self::completion_text_edit(line_index, insert_replace_support, indel2)
202 }) 220 })
203 } else { 221 } else {
204 assert!(source_range.intersect(indel.delete).is_none()); 222 assert!(source_range.intersect(indel.delete).is_none());
@@ -213,7 +231,7 @@ pub(crate) fn completion_item(
213 detail: item.detail().map(|it| it.to_string()), 231 detail: item.detail().map(|it| it.to_string()),
214 filter_text: Some(item.lookup().to_string()), 232 filter_text: Some(item.lookup().to_string()),
215 kind: item.kind().map(completion_item_kind), 233 kind: item.kind().map(completion_item_kind),
216 text_edit: Some(text_edit.into()), 234 text_edit: Some(text_edit),
217 additional_text_edits: Some(additional_text_edits), 235 additional_text_edits: Some(additional_text_edits),
218 documentation: item.documentation().map(documentation), 236 documentation: item.documentation().map(documentation),
219 deprecated: Some(item.deprecated()), 237 deprecated: Some(item.deprecated()),
@@ -1135,7 +1153,7 @@ mod tests {
1135 .unwrap() 1153 .unwrap()
1136 .into_iter() 1154 .into_iter()
1137 .filter(|c| c.label().ends_with("arg")) 1155 .filter(|c| c.label().ends_with("arg"))
1138 .map(|c| completion_item(&line_index, c)) 1156 .map(|c| completion_item(None, &line_index, c))
1139 .flat_map(|comps| comps.into_iter().map(|c| (c.label, c.sort_text))) 1157 .flat_map(|comps| comps.into_iter().map(|c| (c.label, c.sort_text)))
1140 .collect(); 1158 .collect();
1141 expect_test::expect![[r#" 1159 expect_test::expect![[r#"