Skip to content

Commit

Permalink
Improve handling tab when inline completion is visible (#22892)
Browse files Browse the repository at this point in the history
This changes the behaviour of `<tab>` when inline completion is visible.
When the cursor is before the suggested indentation level, accepting a
completion should just indent.

cc @nathansobo @maxdeviant 

Release Notes:

- Changed the behavior of `<tab>` at start of line when an inline
completion (Copilot, Supermaven, ...) is visible. If the cursor is
before the suggested indentation, `<tab>` now indents the line instead
of accepting the visible completion.

Co-authored-by: Antonio <antonio@zed.dev>
  • Loading branch information
mrnugget and as-cii authored Jan 9, 2025
1 parent 6c50659 commit 38fbc73
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 1 deletion.
17 changes: 17 additions & 0 deletions crates/editor/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4571,6 +4571,23 @@ impl Editor {
_: &AcceptInlineCompletion,
cx: &mut ViewContext<Self>,
) {
let buffer = self.buffer.read(cx);
let snapshot = buffer.snapshot(cx);
let selection = self.selections.newest_adjusted(cx);
let cursor = selection.head();
let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
let suggested_indents = snapshot.suggested_indents([cursor.row], cx);
if let Some(suggested_indent) = suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
{
if cursor.column < suggested_indent.len
&& cursor.column <= current_indent.len
&& current_indent.len <= suggested_indent.len
{
self.tab(&Default::default(), cx);
return;
}
}

if self.show_inline_completions_in_menu(cx) {
self.hide_context_menu(cx);
}
Expand Down
51 changes: 50 additions & 1 deletion crates/editor/src/inline_completion_tests.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use gpui::{prelude::*, Model};
use indoc::indoc;
use inline_completion::InlineCompletionProvider;
use language::{Language, LanguageConfig};
use multi_buffer::{Anchor, MultiBufferSnapshot, ToPoint};
use std::ops::Range;
use std::{num::NonZeroU32, ops::Range, sync::Arc};
use text::{Point, ToOffset};

use crate::{
Expand Down Expand Up @@ -122,6 +123,54 @@ async fn test_inline_completion_jump_button(cx: &mut gpui::TestAppContext) {
"});
}

#[gpui::test]
async fn test_indentation(cx: &mut gpui::TestAppContext) {
init_test(cx, |settings| {
settings.defaults.tab_size = NonZeroU32::new(4)
});

let language = Arc::new(
Language::new(
LanguageConfig::default(),
Some(tree_sitter_rust::LANGUAGE.into()),
)
.with_indents_query(r#"(_ "(" ")" @end) @indent"#)
.unwrap(),
);

let mut cx = EditorTestContext::new(cx).await;
cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
let provider = cx.new_model(|_| FakeInlineCompletionProvider::default());
assign_editor_completion_provider(provider.clone(), &mut cx);

cx.set_state(indoc! {"
const a: A = (
ˇ
);
"});

propose_edits(
&provider,
vec![(Point::new(1, 0)..Point::new(1, 0), " const function()")],
&mut cx,
);
cx.update_editor(|editor, cx| editor.update_visible_inline_completion(cx));

assert_editor_active_edit_completion(&mut cx, |_, edits| {
assert_eq!(edits.len(), 1);
assert_eq!(edits[0].1.as_str(), " const function()");
});

// When the cursor is before the suggested indentation level, accepting a
// completion should just indent.
accept_completion(&mut cx);
cx.assert_editor_state(indoc! {"
const a: A = (
ˇ
);
"});
}

#[gpui::test]
async fn test_inline_completion_invalidation_range(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
Expand Down

0 comments on commit 38fbc73

Please sign in to comment.