edit predictions: Refine leading whitespace behavior (#25491)

Closes https://github.com/zed-industries/zed/issues/25406

### Problem

Users have been confused about requiring `alt-tab` instead of just `tab`
in cases where they don't have a completions menu open (see issue
above). When they insert a newline and are in leading whitespace, they
expect to be able to accept a prediction with just `tab`, but doing so
increasing the indentation instead.

This PR changes the behavior in so a modifier is only required if the
cursor isn't already at the right indentation level based on the
surrounding block. In this case, `tab` would increase the indentation
and the prediction would get interpolated, allowing the user to press
`tab` again to accept it.

We also updated the docs to break down this behavior:
https://github.com/zed-industries/zed/pull/25493

### Before


https://github.com/user-attachments/assets/91fe6193-dddd-43c1-8c26-0f4648bdc3fa

### After


https://github.com/user-attachments/assets/671041bf-bf22-46a3-8466-b19b3e7dd6a0


Release Notes:

- edit predictions: Do not require a modifier key when indentation is
correct according to its surrounding block

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
This commit is contained in:
Agus Zubiaga 2025-02-24 18:37:18 -03:00 committed by GitHub
parent f1e6b144e8
commit 2f7a62780a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -677,8 +677,8 @@ pub struct Editor {
show_inline_completions_override: Option<bool>,
menu_inline_completions_policy: MenuInlineCompletionsPolicy,
edit_prediction_preview: EditPredictionPreview,
edit_prediction_cursor_on_leading_whitespace: bool,
edit_prediction_requires_modifier_in_leading_space: bool,
edit_prediction_indent_conflict: bool,
edit_prediction_requires_modifier_in_indent_conflict: bool,
inlay_hint_cache: InlayHintCache,
next_inlay_id: usize,
_subscriptions: Vec<Subscription>,
@ -1403,8 +1403,8 @@ 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,
edit_prediction_requires_modifier_in_leading_space: true,
edit_prediction_indent_conflict: false,
edit_prediction_requires_modifier_in_indent_conflict: true,
custom_context_menu: None,
show_git_blame_gutter: false,
show_git_blame_inline: false,
@ -1578,7 +1578,7 @@ impl Editor {
|| 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_requires_modifier_in_leading_space && self.edit_prediction_cursor_on_leading_whitespace)
|| (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
}
pub fn accept_edit_prediction_keybind(
@ -2150,7 +2150,7 @@ impl Editor {
self.refresh_selected_text_highlights(window, cx);
refresh_matching_bracket_highlights(self, window, cx);
self.update_visible_inline_completion(window, cx);
self.edit_prediction_requires_modifier_in_leading_space = true;
self.edit_prediction_requires_modifier_in_indent_conflict = true;
linked_editing_ranges::refresh_linked_ranges(self, window, cx);
if self.git_blame_inline_enabled {
self.start_inline_blame_timer(window, cx);
@ -5136,7 +5136,7 @@ impl Editor {
}
}
self.edit_prediction_requires_modifier_in_leading_space = false;
self.edit_prediction_requires_modifier_in_indent_conflict = false;
}
pub fn accept_partial_inline_completion(
@ -5434,8 +5434,19 @@ impl Editor {
self.edit_prediction_settings =
self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
self.edit_prediction_cursor_on_leading_whitespace =
multibuffer.is_line_whitespace_upto(cursor);
self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
if self.edit_prediction_indent_conflict {
let cursor_point = cursor.to_point(&multibuffer);
let indents = multibuffer.suggested_indents(cursor_point.row..cursor_point.row + 1, cx);
if let Some((_, indent)) = indents.iter().next() {
if indent.len == cursor_point.column {
self.edit_prediction_indent_conflict = false;
}
}
}
let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
let edits = inline_completion