mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-10 20:41:59 +00:00
Respect LSP completion triggers when copilot suggestion is on (#11401)
This commit is contained in:
parent
89039f6f34
commit
9ec0927701
4 changed files with 137 additions and 13 deletions
|
@ -53,6 +53,7 @@ clock.workspace = true
|
|||
indoc.workspace = true
|
||||
serde_json.workspace = true
|
||||
collections = { workspace = true, features = ["test-support"] }
|
||||
editor = { workspace = true, features = ["test-support"] }
|
||||
fs = { workspace = true, features = ["test-support"] }
|
||||
gpui = { workspace = true, features = ["test-support"] }
|
||||
language = { workspace = true, features = ["test-support"] }
|
||||
|
|
|
@ -840,6 +840,124 @@ mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_copilot_does_not_prevent_completion_triggers(
|
||||
executor: BackgroundExecutor,
|
||||
cx: &mut TestAppContext,
|
||||
) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (copilot, copilot_lsp) = Copilot::fake(cx);
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
completion_provider: Some(lsp::CompletionOptions {
|
||||
trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
|
||||
..lsp::CompletionOptions::default()
|
||||
}),
|
||||
..lsp::ServerCapabilities::default()
|
||||
},
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
let copilot_provider = cx.new_model(|_| CopilotCompletionProvider::new(copilot));
|
||||
cx.update_editor(|editor, cx| {
|
||||
editor.set_inline_completion_provider(Some(copilot_provider), cx)
|
||||
});
|
||||
|
||||
cx.set_state(indoc! {"
|
||||
one
|
||||
twˇ
|
||||
three
|
||||
"});
|
||||
|
||||
let _ = handle_completion_request(
|
||||
&mut cx,
|
||||
indoc! {"
|
||||
one
|
||||
tw|<>
|
||||
three
|
||||
"},
|
||||
vec!["completion_a", "completion_b"],
|
||||
);
|
||||
handle_copilot_completion_request(
|
||||
&copilot_lsp,
|
||||
vec![crate::request::Completion {
|
||||
text: "two.foo()".into(),
|
||||
range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
|
||||
..Default::default()
|
||||
}],
|
||||
vec![],
|
||||
);
|
||||
cx.update_editor(|editor, cx| editor.next_inline_completion(&Default::default(), cx));
|
||||
executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
|
||||
cx.update_editor(|editor, cx| {
|
||||
assert!(!editor.context_menu_visible(), "Even there are some completions available, those are not triggered when active copilot suggestion is present");
|
||||
assert!(editor.has_active_inline_completion(cx));
|
||||
assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
|
||||
assert_eq!(editor.text(cx), "one\ntw\nthree\n");
|
||||
});
|
||||
|
||||
cx.simulate_keystroke("o");
|
||||
let _ = handle_completion_request(
|
||||
&mut cx,
|
||||
indoc! {"
|
||||
one
|
||||
two|<>
|
||||
three
|
||||
"},
|
||||
vec!["completion_a_2", "completion_b_2"],
|
||||
);
|
||||
handle_copilot_completion_request(
|
||||
&copilot_lsp,
|
||||
vec![crate::request::Completion {
|
||||
text: "two.foo()".into(),
|
||||
range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 3)),
|
||||
..Default::default()
|
||||
}],
|
||||
vec![],
|
||||
);
|
||||
executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
|
||||
cx.update_editor(|editor, cx| {
|
||||
assert!(!editor.context_menu_visible());
|
||||
assert!(editor.has_active_inline_completion(cx));
|
||||
assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
|
||||
assert_eq!(editor.text(cx), "one\ntwo\nthree\n");
|
||||
});
|
||||
|
||||
cx.simulate_keystroke(".");
|
||||
let _ = handle_completion_request(
|
||||
&mut cx,
|
||||
indoc! {"
|
||||
one
|
||||
two.|<>
|
||||
three
|
||||
"},
|
||||
vec!["something_else()"],
|
||||
);
|
||||
handle_copilot_completion_request(
|
||||
&copilot_lsp,
|
||||
vec![crate::request::Completion {
|
||||
text: "two.foo()".into(),
|
||||
range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 4)),
|
||||
..Default::default()
|
||||
}],
|
||||
vec![],
|
||||
);
|
||||
executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
|
||||
cx.update_editor(|editor, cx| {
|
||||
assert!(
|
||||
editor.context_menu_visible(),
|
||||
"On completion trigger input, the completions should be fetched and visible"
|
||||
);
|
||||
assert!(
|
||||
!editor.has_active_inline_completion(cx),
|
||||
"On completion trigger input, copilot suggestion should be dismissed"
|
||||
);
|
||||
assert_eq!(editor.display_text(cx), "one\ntwo.\nthree\n");
|
||||
assert_eq!(editor.text(cx), "one\ntwo.\nthree\n");
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_copilot_disabled_globs(executor: BackgroundExecutor, cx: &mut TestAppContext) {
|
||||
init_test(cx, |settings| {
|
||||
|
|
|
@ -2695,15 +2695,9 @@ impl Editor {
|
|||
}
|
||||
}
|
||||
|
||||
if had_active_inline_completion {
|
||||
this.refresh_inline_completion(true, cx);
|
||||
if !this.has_active_inline_completion(cx) {
|
||||
this.trigger_completion_on_input(&text, cx);
|
||||
}
|
||||
} else {
|
||||
this.trigger_completion_on_input(&text, cx);
|
||||
this.refresh_inline_completion(true, cx);
|
||||
}
|
||||
let trigger_in_words = !had_active_inline_completion;
|
||||
this.trigger_completion_on_input(&text, trigger_in_words, cx);
|
||||
this.refresh_inline_completion(true, cx);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -3056,7 +3050,12 @@ impl Editor {
|
|||
});
|
||||
}
|
||||
|
||||
fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
|
||||
fn trigger_completion_on_input(
|
||||
&mut self,
|
||||
text: &str,
|
||||
trigger_in_words: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
if !EditorSettings::get_global(cx).show_completions_on_input {
|
||||
return;
|
||||
}
|
||||
|
@ -3065,7 +3064,7 @@ impl Editor {
|
|||
if self
|
||||
.buffer
|
||||
.read(cx)
|
||||
.is_completion_trigger(selection.head(), text, cx)
|
||||
.is_completion_trigger(selection.head(), text, trigger_in_words, cx)
|
||||
{
|
||||
self.show_completions(&ShowCompletions, cx);
|
||||
} else {
|
||||
|
|
|
@ -1522,7 +1522,13 @@ impl MultiBuffer {
|
|||
.map(|state| state.buffer.clone())
|
||||
}
|
||||
|
||||
pub fn is_completion_trigger(&self, position: Anchor, text: &str, cx: &AppContext) -> bool {
|
||||
pub fn is_completion_trigger(
|
||||
&self,
|
||||
position: Anchor,
|
||||
text: &str,
|
||||
trigger_in_words: bool,
|
||||
cx: &AppContext,
|
||||
) -> bool {
|
||||
let mut chars = text.chars();
|
||||
let char = if let Some(char) = chars.next() {
|
||||
char
|
||||
|
@ -1536,7 +1542,7 @@ impl MultiBuffer {
|
|||
let snapshot = self.snapshot(cx);
|
||||
let position = position.to_offset(&snapshot);
|
||||
let scope = snapshot.language_scope_at(position);
|
||||
if char_kind(&scope, char) == CharKind::Word {
|
||||
if trigger_in_words && char_kind(&scope, char) == CharKind::Word {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue