project: Add tests for more cases in LSP completions (#27650)
This PR adds two more cases to existing LSP completion cases. - When text_edit exists: (New test) 1. we use text_edit, over insert_text and label - When edit range exists (and text_edit is None): (New test) 1. insert_text is used over label if exists 2. label is used otherwise - When not edit range exists (and text_edit is None): (Existing test) 1. insert_text is used over label if exists 2. label is used otherwise Release Notes: - N/A
This commit is contained in:
parent
c8105863c8
commit
7ac51e4c82
1 changed files with 206 additions and 0 deletions
|
@ -2776,6 +2776,210 @@ async fn test_definition(cx: &mut gpui::TestAppContext) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_completions_with_text_edit(cx: &mut gpui::TestAppContext) {
|
||||||
|
init_test(cx);
|
||||||
|
|
||||||
|
let fs = FakeFs::new(cx.executor());
|
||||||
|
fs.insert_tree(
|
||||||
|
path!("/dir"),
|
||||||
|
json!({
|
||||||
|
"a.ts": "",
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
|
||||||
|
|
||||||
|
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||||
|
language_registry.add(typescript_lang());
|
||||||
|
let mut fake_language_servers = language_registry.register_fake_lsp(
|
||||||
|
"TypeScript",
|
||||||
|
FakeLspAdapter {
|
||||||
|
capabilities: lsp::ServerCapabilities {
|
||||||
|
completion_provider: Some(lsp::CompletionOptions {
|
||||||
|
trigger_characters: Some(vec![".".to_string()]),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let (buffer, _handle) = project
|
||||||
|
.update(cx, |p, cx| {
|
||||||
|
p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let fake_server = fake_language_servers.next().await.unwrap();
|
||||||
|
|
||||||
|
// When text_edit exists, it takes precedence over insert_text and label
|
||||||
|
let text = "let a = obj.fqn";
|
||||||
|
buffer.update(cx, |buffer, cx| buffer.set_text(text, cx));
|
||||||
|
let completions = project.update(cx, |project, cx| {
|
||||||
|
project.completions(&buffer, text.len(), DEFAULT_COMPLETION_CONTEXT, cx)
|
||||||
|
});
|
||||||
|
|
||||||
|
fake_server
|
||||||
|
.set_request_handler::<lsp::request::Completion, _, _>(|_, _| async {
|
||||||
|
Ok(Some(lsp::CompletionResponse::Array(vec![
|
||||||
|
lsp::CompletionItem {
|
||||||
|
label: "labelText".into(),
|
||||||
|
insert_text: Some("insertText".into()),
|
||||||
|
text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
|
||||||
|
range: lsp::Range::new(
|
||||||
|
lsp::Position::new(0, text.len() as u32 - 3),
|
||||||
|
lsp::Position::new(0, text.len() as u32),
|
||||||
|
),
|
||||||
|
new_text: "textEditText".into(),
|
||||||
|
})),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
])))
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let completions = completions.await.unwrap().unwrap();
|
||||||
|
let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
|
||||||
|
|
||||||
|
assert_eq!(completions.len(), 1);
|
||||||
|
assert_eq!(completions[0].new_text, "textEditText");
|
||||||
|
assert_eq!(
|
||||||
|
completions[0].old_range.to_offset(&snapshot),
|
||||||
|
text.len() - 3..text.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_completions_with_edit_ranges(cx: &mut gpui::TestAppContext) {
|
||||||
|
init_test(cx);
|
||||||
|
|
||||||
|
let fs = FakeFs::new(cx.executor());
|
||||||
|
fs.insert_tree(
|
||||||
|
path!("/dir"),
|
||||||
|
json!({
|
||||||
|
"a.ts": "",
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
|
||||||
|
|
||||||
|
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||||
|
language_registry.add(typescript_lang());
|
||||||
|
let mut fake_language_servers = language_registry.register_fake_lsp(
|
||||||
|
"TypeScript",
|
||||||
|
FakeLspAdapter {
|
||||||
|
capabilities: lsp::ServerCapabilities {
|
||||||
|
completion_provider: Some(lsp::CompletionOptions {
|
||||||
|
trigger_characters: Some(vec![".".to_string()]),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let (buffer, _handle) = project
|
||||||
|
.update(cx, |p, cx| {
|
||||||
|
p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let fake_server = fake_language_servers.next().await.unwrap();
|
||||||
|
let text = "let a = obj.fqn";
|
||||||
|
|
||||||
|
// Test 1: When text_edit is None but insert_text exists with default edit_range
|
||||||
|
{
|
||||||
|
buffer.update(cx, |buffer, cx| buffer.set_text(text, cx));
|
||||||
|
let completions = project.update(cx, |project, cx| {
|
||||||
|
project.completions(&buffer, text.len(), DEFAULT_COMPLETION_CONTEXT, cx)
|
||||||
|
});
|
||||||
|
|
||||||
|
fake_server
|
||||||
|
.set_request_handler::<lsp::request::Completion, _, _>(|_, _| async {
|
||||||
|
Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
|
||||||
|
is_incomplete: false,
|
||||||
|
item_defaults: Some(lsp::CompletionListItemDefaults {
|
||||||
|
edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
|
||||||
|
lsp::Range::new(
|
||||||
|
lsp::Position::new(0, text.len() as u32 - 3),
|
||||||
|
lsp::Position::new(0, text.len() as u32),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
items: vec![lsp::CompletionItem {
|
||||||
|
label: "labelText".into(),
|
||||||
|
insert_text: Some("insertText".into()),
|
||||||
|
text_edit: None,
|
||||||
|
..Default::default()
|
||||||
|
}],
|
||||||
|
})))
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let completions = completions.await.unwrap().unwrap();
|
||||||
|
let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
|
||||||
|
|
||||||
|
assert_eq!(completions.len(), 1);
|
||||||
|
assert_eq!(completions[0].new_text, "insertText");
|
||||||
|
assert_eq!(
|
||||||
|
completions[0].old_range.to_offset(&snapshot),
|
||||||
|
text.len() - 3..text.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 2: When both text_edit and insert_text are None with default edit_range
|
||||||
|
{
|
||||||
|
buffer.update(cx, |buffer, cx| buffer.set_text(text, cx));
|
||||||
|
let completions = project.update(cx, |project, cx| {
|
||||||
|
project.completions(&buffer, text.len(), DEFAULT_COMPLETION_CONTEXT, cx)
|
||||||
|
});
|
||||||
|
|
||||||
|
fake_server
|
||||||
|
.set_request_handler::<lsp::request::Completion, _, _>(|_, _| async {
|
||||||
|
Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
|
||||||
|
is_incomplete: false,
|
||||||
|
item_defaults: Some(lsp::CompletionListItemDefaults {
|
||||||
|
edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
|
||||||
|
lsp::Range::new(
|
||||||
|
lsp::Position::new(0, text.len() as u32 - 3),
|
||||||
|
lsp::Position::new(0, text.len() as u32),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
items: vec![lsp::CompletionItem {
|
||||||
|
label: "labelText".into(),
|
||||||
|
insert_text: None,
|
||||||
|
text_edit: None,
|
||||||
|
..Default::default()
|
||||||
|
}],
|
||||||
|
})))
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let completions = completions.await.unwrap().unwrap();
|
||||||
|
let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
|
||||||
|
|
||||||
|
assert_eq!(completions.len(), 1);
|
||||||
|
assert_eq!(completions[0].new_text, "labelText");
|
||||||
|
assert_eq!(
|
||||||
|
completions[0].old_range.to_offset(&snapshot),
|
||||||
|
text.len() - 3..text.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) {
|
async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) {
|
||||||
init_test(cx);
|
init_test(cx);
|
||||||
|
@ -2816,6 +3020,7 @@ async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) {
|
||||||
|
|
||||||
let fake_server = fake_language_servers.next().await.unwrap();
|
let fake_server = fake_language_servers.next().await.unwrap();
|
||||||
|
|
||||||
|
// Test 1: When text_edit is None but insert_text exists (no edit_range in defaults)
|
||||||
let text = "let a = b.fqn";
|
let text = "let a = b.fqn";
|
||||||
buffer.update(cx, |buffer, cx| buffer.set_text(text, cx));
|
buffer.update(cx, |buffer, cx| buffer.set_text(text, cx));
|
||||||
let completions = project.update(cx, |project, cx| {
|
let completions = project.update(cx, |project, cx| {
|
||||||
|
@ -2843,6 +3048,7 @@ async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) {
|
||||||
text.len() - 3..text.len()
|
text.len() - 3..text.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Test 2: When both text_edit and insert_text are None (no edit_range in defaults)
|
||||||
let text = "let a = \"atoms/cmp\"";
|
let text = "let a = \"atoms/cmp\"";
|
||||||
buffer.update(cx, |buffer, cx| buffer.set_text(text, cx));
|
buffer.update(cx, |buffer, cx| buffer.set_text(text, cx));
|
||||||
let completions = project.update(cx, |project, cx| {
|
let completions = project.update(cx, |project, cx| {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue