diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index e8fc0593e7..0ad3bed29e 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -709,6 +709,7 @@ pub struct Editor { /// Used to prevent flickering as the user types while the menu is open stale_inline_completion_in_menu: Option, edit_prediction_settings: EditPredictionSettings, + edit_prediction_cursor_on_leading_whitespace: bool, inline_completions_hidden_for_vim_mode: bool, show_inline_completions_override: Option, menu_inline_completions_policy: MenuInlineCompletionsPolicy, @@ -1423,6 +1424,7 @@ impl Editor { show_inline_completions_override: None, menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider, edit_prediction_settings: EditPredictionSettings::Disabled, + edit_prediction_cursor_on_leading_whitespace: false, custom_context_menu: None, show_git_blame_gutter: false, show_git_blame_inline: false, @@ -1567,8 +1569,12 @@ impl Editor { if has_active_edit_prediction { key_context.add("copilot_suggestion"); key_context.add(EDIT_PREDICTION_KEY_CONTEXT); - - if showing_completions || self.edit_prediction_requires_modifier() { + if showing_completions + || self.edit_prediction_requires_modifier() + // Require modifier key when the cursor is on leading whitespace, to allow `tab` + // bindings to insert tab characters. + || self.edit_prediction_cursor_on_leading_whitespace + { key_context.add(EDIT_PREDICTION_REQUIRES_MODIFIER_KEY_CONTEXT); } } @@ -4931,23 +4937,6 @@ impl Editor { window: &mut Window, cx: &mut Context, ) { - 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(), window, cx); - return; - } - } - if self.show_edit_predictions_in_menu() { self.hide_context_menu(window, cx); } @@ -5298,6 +5287,9 @@ impl Editor { return None; } + self.edit_prediction_cursor_on_leading_whitespace = + multibuffer.is_line_whitespace_upto(cursor); + let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?; let edits = inline_completion .edits diff --git a/crates/editor/src/inline_completion_tests.rs b/crates/editor/src/inline_completion_tests.rs index c74de1fc93..10736adda6 100644 --- a/crates/editor/src/inline_completion_tests.rs +++ b/crates/editor/src/inline_completion_tests.rs @@ -1,10 +1,9 @@ use gpui::{prelude::*, Entity}; use indoc::indoc; use inline_completion::EditPredictionProvider; -use language::{Language, LanguageConfig}; use multi_buffer::{Anchor, MultiBufferSnapshot, ToPoint}; use project::Project; -use std::{num::NonZeroU32, ops::Range, sync::Arc}; +use std::ops::Range; use text::{Point, ToOffset}; use crate::{ @@ -124,54 +123,6 @@ 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(|_| 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, window, cx| editor.update_visible_inline_completion(window, 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, |_| {}); diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index ca52eadd9a..0306011b3a 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -4236,6 +4236,21 @@ impl MultiBufferSnapshot { indent } + pub fn is_line_whitespace_upto(&self, position: T) -> bool + where + T: ToOffset, + { + for char in self.reversed_chars_at(position) { + if !char.is_whitespace() { + return false; + } + if char == '\n' { + return true; + } + } + return true; + } + pub fn prev_non_blank_row(&self, mut row: MultiBufferRow) -> Option { while row.0 > 0 { row.0 -= 1;