mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-01 19:23:50 +00:00
Merge #8410
8410: Use CompletionTextEdit::InsertAndReplace if supported by the client r=Veykril a=Veykril Fixes #8404, Fixes #3130 Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
commit
0fac165052
@ -29,7 +29,7 @@ pub struct CompletionItem {
|
|||||||
/// Range of identifier that is being completed.
|
/// Range of identifier that is being completed.
|
||||||
///
|
///
|
||||||
/// It should be used primarily for UI, but we also use this to convert
|
/// It should be used primarily for UI, but we also use this to convert
|
||||||
/// genetic TextEdit into LSP's completion edit (see conv.rs).
|
/// generic TextEdit into LSP's completion edit (see conv.rs).
|
||||||
///
|
///
|
||||||
/// `source_range` must contain the completion offset. `insert_text` should
|
/// `source_range` must contain the completion offset. `insert_text` should
|
||||||
/// start with what `source_range` points to, or VSCode will filter out the
|
/// start with what `source_range` points to, or VSCode will filter out the
|
||||||
|
@ -656,6 +656,19 @@ impl Config {
|
|||||||
pub fn code_lens_refresh(&self) -> bool {
|
pub fn code_lens_refresh(&self) -> bool {
|
||||||
try_or!(self.caps.workspace.as_ref()?.code_lens.as_ref()?.refresh_support?, false)
|
try_or!(self.caps.workspace.as_ref()?.code_lens.as_ref()?.refresh_support?, false)
|
||||||
}
|
}
|
||||||
|
pub fn insert_replace_support(&self) -> bool {
|
||||||
|
try_or!(
|
||||||
|
self.caps
|
||||||
|
.text_document
|
||||||
|
.as_ref()?
|
||||||
|
.completion
|
||||||
|
.as_ref()?
|
||||||
|
.completion_item
|
||||||
|
.as_ref()?
|
||||||
|
.insert_replace_support?,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
|
@ -664,10 +664,13 @@ pub(crate) fn handle_completion(
|
|||||||
};
|
};
|
||||||
let line_index = snap.file_line_index(position.file_id)?;
|
let line_index = snap.file_line_index(position.file_id)?;
|
||||||
|
|
||||||
|
let insert_replace_support =
|
||||||
|
snap.config.insert_replace_support().then(|| text_document_position.position);
|
||||||
let items: Vec<CompletionItem> = items
|
let items: Vec<CompletionItem> = items
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|item| {
|
.flat_map(|item| {
|
||||||
let mut new_completion_items = to_proto::completion_item(&line_index, item.clone());
|
let mut new_completion_items =
|
||||||
|
to_proto::completion_item(insert_replace_support, &line_index, item.clone());
|
||||||
|
|
||||||
if completion_config.enable_imports_on_the_fly {
|
if completion_config.enable_imports_on_the_fly {
|
||||||
for new_item in &mut new_completion_items {
|
for new_item in &mut new_completion_items {
|
||||||
|
@ -150,8 +150,16 @@ pub(crate) fn all_edits_are_disjoint(
|
|||||||
edit_ranges.push(edit.range);
|
edit_ranges.push(edit.range);
|
||||||
}
|
}
|
||||||
Some(lsp_types::CompletionTextEdit::InsertAndReplace(edit)) => {
|
Some(lsp_types::CompletionTextEdit::InsertAndReplace(edit)) => {
|
||||||
edit_ranges.push(edit.insert);
|
let replace = edit.replace;
|
||||||
edit_ranges.push(edit.replace);
|
let insert = edit.insert;
|
||||||
|
if replace.start != insert.start
|
||||||
|
|| insert.start > insert.end
|
||||||
|
|| insert.end > replace.end
|
||||||
|
{
|
||||||
|
// insert has to be a prefix of replace but it is not
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
edit_ranges.push(replace);
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
@ -310,18 +318,6 @@ mod tests {
|
|||||||
"Completion with disjoint edits fails the validation even with empty extra edits"
|
"Completion with disjoint edits fails the validation even with empty extra edits"
|
||||||
);
|
);
|
||||||
|
|
||||||
completion_with_joint_edits.text_edit =
|
|
||||||
Some(CompletionTextEdit::InsertAndReplace(InsertReplaceEdit {
|
|
||||||
new_text: "new_text".to_string(),
|
|
||||||
insert: disjoint_edit.range,
|
|
||||||
replace: joint_edit.range,
|
|
||||||
}));
|
|
||||||
completion_with_joint_edits.additional_text_edits = None;
|
|
||||||
assert!(
|
|
||||||
!all_edits_are_disjoint(&completion_with_joint_edits, &[]),
|
|
||||||
"Completion with disjoint edits fails the validation even with empty extra edits"
|
|
||||||
);
|
|
||||||
|
|
||||||
completion_with_joint_edits.text_edit =
|
completion_with_joint_edits.text_edit =
|
||||||
Some(CompletionTextEdit::InsertAndReplace(InsertReplaceEdit {
|
Some(CompletionTextEdit::InsertAndReplace(InsertReplaceEdit {
|
||||||
new_text: "new_text".to_string(),
|
new_text: "new_text".to_string(),
|
||||||
|
@ -145,6 +145,23 @@ pub(crate) fn text_edit(line_index: &LineIndex, indel: Indel) -> lsp_types::Text
|
|||||||
lsp_types::TextEdit { range, new_text }
|
lsp_types::TextEdit { range, new_text }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn completion_text_edit(
|
||||||
|
line_index: &LineIndex,
|
||||||
|
insert_replace_support: Option<lsp_types::Position>,
|
||||||
|
indel: Indel,
|
||||||
|
) -> lsp_types::CompletionTextEdit {
|
||||||
|
let text_edit = text_edit(line_index, indel);
|
||||||
|
match insert_replace_support {
|
||||||
|
Some(cursor_pos) => lsp_types::InsertReplaceEdit {
|
||||||
|
new_text: text_edit.new_text,
|
||||||
|
insert: lsp_types::Range { start: text_edit.range.start, end: cursor_pos },
|
||||||
|
replace: text_edit.range,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
None => text_edit.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn snippet_text_edit(
|
pub(crate) fn snippet_text_edit(
|
||||||
line_index: &LineIndex,
|
line_index: &LineIndex,
|
||||||
is_snippet: bool,
|
is_snippet: bool,
|
||||||
@ -179,6 +196,7 @@ pub(crate) fn snippet_text_edit_vec(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn completion_item(
|
pub(crate) fn completion_item(
|
||||||
|
insert_replace_support: Option<lsp_types::Position>,
|
||||||
line_index: &LineIndex,
|
line_index: &LineIndex,
|
||||||
item: CompletionItem,
|
item: CompletionItem,
|
||||||
) -> Vec<lsp_types::CompletionItem> {
|
) -> Vec<lsp_types::CompletionItem> {
|
||||||
@ -190,7 +208,7 @@ pub(crate) fn completion_item(
|
|||||||
for indel in item.text_edit().iter() {
|
for indel in item.text_edit().iter() {
|
||||||
if indel.delete.contains_range(source_range) {
|
if indel.delete.contains_range(source_range) {
|
||||||
text_edit = Some(if indel.delete == source_range {
|
text_edit = Some(if indel.delete == source_range {
|
||||||
self::text_edit(line_index, indel.clone())
|
self::completion_text_edit(line_index, insert_replace_support, indel.clone())
|
||||||
} else {
|
} else {
|
||||||
assert!(source_range.end() == indel.delete.end());
|
assert!(source_range.end() == indel.delete.end());
|
||||||
let range1 = TextRange::new(indel.delete.start(), source_range.start());
|
let range1 = TextRange::new(indel.delete.start(), source_range.start());
|
||||||
@ -198,7 +216,7 @@ pub(crate) fn completion_item(
|
|||||||
let indel1 = Indel::replace(range1, String::new());
|
let indel1 = Indel::replace(range1, String::new());
|
||||||
let indel2 = Indel::replace(range2, indel.insert.clone());
|
let indel2 = Indel::replace(range2, indel.insert.clone());
|
||||||
additional_text_edits.push(self::text_edit(line_index, indel1));
|
additional_text_edits.push(self::text_edit(line_index, indel1));
|
||||||
self::text_edit(line_index, indel2)
|
self::completion_text_edit(line_index, insert_replace_support, indel2)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
assert!(source_range.intersect(indel.delete).is_none());
|
assert!(source_range.intersect(indel.delete).is_none());
|
||||||
@ -213,7 +231,7 @@ pub(crate) fn completion_item(
|
|||||||
detail: item.detail().map(|it| it.to_string()),
|
detail: item.detail().map(|it| it.to_string()),
|
||||||
filter_text: Some(item.lookup().to_string()),
|
filter_text: Some(item.lookup().to_string()),
|
||||||
kind: item.kind().map(completion_item_kind),
|
kind: item.kind().map(completion_item_kind),
|
||||||
text_edit: Some(text_edit.into()),
|
text_edit: Some(text_edit),
|
||||||
additional_text_edits: Some(additional_text_edits),
|
additional_text_edits: Some(additional_text_edits),
|
||||||
documentation: item.documentation().map(documentation),
|
documentation: item.documentation().map(documentation),
|
||||||
deprecated: Some(item.deprecated()),
|
deprecated: Some(item.deprecated()),
|
||||||
@ -1136,7 +1154,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|c| c.label().ends_with("arg"))
|
.filter(|c| c.label().ends_with("arg"))
|
||||||
.map(|c| completion_item(&line_index, c))
|
.map(|c| completion_item(None, &line_index, c))
|
||||||
.flat_map(|comps| comps.into_iter().map(|c| (c.label, c.sort_text)))
|
.flat_map(|comps| comps.into_iter().map(|c| (c.label, c.sort_text)))
|
||||||
.collect();
|
.collect();
|
||||||
expect_test::expect![[r#"
|
expect_test::expect![[r#"
|
||||||
|
Loading…
Reference in New Issue
Block a user