edit predictions: Refine leading whitespace behavior (#25491)

Agus Zubiaga and Danilo Leal created

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>

Change summary

crates/editor/src/editor.rs | 29 ++++++++++++++++++++---------
1 file changed, 20 insertions(+), 9 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -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