@@ -3230,10 +3230,6 @@ impl Editor {
}
pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
- if self.accept_copilot_suggestion(cx) {
- return;
- }
-
if self.move_to_next_snippet_tabstop(cx) {
return;
}
@@ -3263,8 +3259,8 @@ impl Editor {
// If the selection is empty and the cursor is in the leading whitespace before the
// suggested indentation, then auto-indent the line.
let cursor = selection.head();
+ let current_indent = snapshot.indent_size_for_line(cursor.row);
if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
- let current_indent = snapshot.indent_size_for_line(cursor.row);
if cursor.column < suggested_indent.len
&& cursor.column <= current_indent.len
&& current_indent.len <= suggested_indent.len
@@ -3283,6 +3279,16 @@ impl Editor {
}
}
+ // Accept copilot suggestion if there is only one selection and the cursor is
+ // in the leading whitespace.
+ if self.selections.count() == 1
+ && selection.start.column >= current_indent.len
+ && self.has_active_copilot_suggestion(cx)
+ {
+ self.accept_copilot_suggestion(cx);
+ return;
+ }
+
// Otherwise, insert a hard or soft tab.
let settings = cx.global::<Settings>();
let language_name = buffer.language_at(cursor, cx).map(|l| l.name());
@@ -3306,7 +3312,8 @@ impl Editor {
self.transact(cx, |this, cx| {
this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
- this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections))
+ this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
+ this.refresh_copilot_suggestions(cx);
});
}
@@ -5881,7 +5881,7 @@ async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
);
}
-#[gpui::test]
+#[gpui::test(iterations = 10)]
async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
let (copilot, copilot_lsp) = Copilot::fake(cx);
cx.update(|cx| cx.set_global(copilot));
@@ -5918,7 +5918,6 @@ async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppC
&copilot_lsp,
vec![copilot::request::Completion {
text: "copilot1".into(),
- position: lsp::Position::new(0, 5),
range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
..Default::default()
}],
@@ -5962,7 +5961,6 @@ async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppC
&copilot_lsp,
vec![copilot::request::Completion {
text: "one.copilot1".into(),
- position: lsp::Position::new(0, 4),
range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
..Default::default()
}],
@@ -5996,7 +5994,6 @@ async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppC
&copilot_lsp,
vec![copilot::request::Completion {
text: "one.copilot2".into(),
- position: lsp::Position::new(0, 5),
range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
..Default::default()
}],
@@ -6062,6 +6059,43 @@ async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppC
assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
});
+
+ // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
+ cx.update_editor(|editor, cx| {
+ editor.set_text("fn foo() {\n \n}", cx);
+ editor.change_selections(None, cx, |s| {
+ s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
+ });
+ });
+ handle_copilot_completion_request(
+ &copilot_lsp,
+ vec![copilot::request::Completion {
+ text: " let x = 4;".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_copilot_suggestion(&Default::default(), cx));
+ deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
+ cx.update_editor(|editor, cx| {
+ assert!(editor.has_active_copilot_suggestion(cx));
+ assert_eq!(editor.display_text(cx), "fn foo() {\n let x = 4;\n}");
+ assert_eq!(editor.text(cx), "fn foo() {\n \n}");
+
+ // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
+ editor.tab(&Default::default(), cx);
+ assert!(editor.has_active_copilot_suggestion(cx));
+ assert_eq!(editor.text(cx), "fn foo() {\n \n}");
+ assert_eq!(editor.display_text(cx), "fn foo() {\n let x = 4;\n}");
+
+ // Tabbing again accepts the suggestion.
+ editor.tab(&Default::default(), cx);
+ assert!(!editor.has_active_copilot_suggestion(cx));
+ assert_eq!(editor.text(cx), "fn foo() {\n let x = 4;\n}");
+ assert_eq!(editor.display_text(cx), "fn foo() {\n let x = 4;\n}");
+ });
}
fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {