Fix single line edit prediction detection (#23456)

#23411 introduced an "Accept" callout for single line edits, but the
logic to detect them was incorrect causing it to trigger for multiline
insertions, this PR fixes that.

Release Notes:

- N/A
This commit is contained in:
Agus Zubiaga 2025-01-22 10:00:43 -03:00 committed by GitHub
parent f0b5b0b4df
commit 55721c65d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 35 additions and 43 deletions

View file

@ -1757,7 +1757,6 @@ impl<'a> BlockChunks<'a> {
pub struct StickyHeaderExcerpt<'a> { pub struct StickyHeaderExcerpt<'a> {
pub excerpt: &'a ExcerptInfo, pub excerpt: &'a ExcerptInfo,
pub next_excerpt_controls_present: bool, pub next_excerpt_controls_present: bool,
// TODO az remove option
pub next_buffer_row: Option<u32>, pub next_buffer_row: Option<u32>,
} }

View file

@ -486,7 +486,10 @@ enum InlineCompletionText {
} }
enum InlineCompletion { enum InlineCompletion {
Edit(Vec<(Range<Anchor>, String)>), Edit {
edits: Vec<(Range<Anchor>, String)>,
single_line: bool,
},
Move(Anchor), Move(Anchor),
} }
@ -4686,7 +4689,10 @@ impl Editor {
selections.select_anchor_ranges([position..position]); selections.select_anchor_ranges([position..position]);
}); });
} }
InlineCompletion::Edit(edits) => { InlineCompletion::Edit {
edits,
single_line: _,
} => {
if let Some(provider) = self.inline_completion_provider() { if let Some(provider) = self.inline_completion_provider() {
provider.accept(cx); provider.accept(cx);
} }
@ -4733,7 +4739,10 @@ impl Editor {
selections.select_anchor_ranges([position..position]); selections.select_anchor_ranges([position..position]);
}); });
} }
InlineCompletion::Edit(edits) => { InlineCompletion::Edit {
edits,
single_line: _,
} => {
// Find an insertion that starts at the cursor position. // Find an insertion that starts at the cursor position.
let snapshot = self.buffer.read(cx).snapshot(cx); let snapshot = self.buffer.read(cx).snapshot(cx);
let cursor_offset = self.selections.newest::<usize>(cx).head(); let cursor_offset = self.selections.newest::<usize>(cx).head();
@ -4883,16 +4892,12 @@ impl Editor {
} }
let first_edit_start = edits.first().unwrap().0.start; let first_edit_start = edits.first().unwrap().0.start;
let edit_start_row = first_edit_start let first_edit_start_point = first_edit_start.to_point(&multibuffer);
.to_point(&multibuffer) let edit_start_row = first_edit_start_point.row.saturating_sub(2);
.row
.saturating_sub(2);
let last_edit_end = edits.last().unwrap().0.end; let last_edit_end = edits.last().unwrap().0.end;
let edit_end_row = cmp::min( let last_edit_end_point = last_edit_end.to_point(&multibuffer);
multibuffer.max_point().row, let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
last_edit_end.to_point(&multibuffer).row + 2,
);
let cursor_row = cursor.to_point(&multibuffer).row; let cursor_row = cursor.to_point(&multibuffer).row;
@ -4935,7 +4940,11 @@ impl Editor {
} }
invalidation_row_range = edit_start_row..edit_end_row; invalidation_row_range = edit_start_row..edit_end_row;
completion = InlineCompletion::Edit(edits);
let single_line = first_edit_start_point.row == last_edit_end_point.row
&& !edits.iter().any(|(_, edit)| edit.contains('\n'));
completion = InlineCompletion::Edit { edits, single_line };
}; };
let invalidation_range = multibuffer let invalidation_range = multibuffer
@ -4976,9 +4985,10 @@ impl Editor {
let editor_snapshot = self.snapshot(cx); let editor_snapshot = self.snapshot(cx);
let text = match &self.active_inline_completion.as_ref()?.completion { let text = match &self.active_inline_completion.as_ref()?.completion {
InlineCompletion::Edit(edits) => { InlineCompletion::Edit {
inline_completion_edit_text(&editor_snapshot, edits, true, cx) edits,
} single_line: _,
} => inline_completion_edit_text(&editor_snapshot, edits, true, cx),
InlineCompletion::Move(target) => { InlineCompletion::Move(target) => {
let target_point = let target_point =
target.to_point(&editor_snapshot.display_snapshot.buffer_snapshot); target.to_point(&editor_snapshot.display_snapshot.buffer_snapshot);

View file

@ -1592,7 +1592,6 @@ impl EditorElement {
&self, &self,
display_row: DisplayRow, display_row: DisplayRow,
display_snapshot: &DisplaySnapshot, display_snapshot: &DisplaySnapshot,
buffer_snapshot: &MultiBufferSnapshot,
line_layout: &LineWithInvisibles, line_layout: &LineWithInvisibles,
crease_trailer: Option<&CreaseTrailerLayout>, crease_trailer: Option<&CreaseTrailerLayout>,
em_width: Pixels, em_width: Pixels,
@ -1628,11 +1627,9 @@ impl EditorElement {
if let Some(inline_completion) = editor.active_inline_completion.as_ref() { if let Some(inline_completion) = editor.active_inline_completion.as_ref() {
match &inline_completion.completion { match &inline_completion.completion {
InlineCompletion::Edit(edits) InlineCompletion::Edit {
if single_line_edit(&edits, buffer_snapshot).is_some() => single_line: true, ..
{ } => padding += INLINE_ACCEPT_SUGGESTION_EM_WIDTHS,
padding += INLINE_ACCEPT_SUGGESTION_EM_WIDTHS
}
_ => {} _ => {}
} }
} }
@ -3389,7 +3386,7 @@ impl EditorElement {
Some(element) Some(element)
} }
} }
InlineCompletion::Edit(edits) => { InlineCompletion::Edit { edits, single_line } => {
if self.editor.read(cx).has_active_completions_menu() { if self.editor.read(cx).has_active_completions_menu() {
return None; return None;
} }
@ -3413,7 +3410,7 @@ impl EditorElement {
return None; return None;
} }
if let Some(range) = single_line_edit(&edits, &editor_snapshot.buffer_snapshot) { if let (true, Some((range, _))) = (single_line, edits.first()) {
let mut element = inline_completion_tab_indicator("Accept", None, cx); let mut element = inline_completion_tab_indicator("Accept", None, cx);
let target_display_point = range.end.to_display_point(editor_snapshot); let target_display_point = range.end.to_display_point(editor_snapshot);
@ -5250,22 +5247,6 @@ fn inline_completion_tab_indicator(
.into_any() .into_any()
} }
fn single_line_edit<'a>(
edits: &'a [(Range<Anchor>, String)],
snapshot: &MultiBufferSnapshot,
) -> Option<&'a Range<Anchor>> {
let [(range, _)] = edits else {
return None;
};
let point_range = range.to_point(&snapshot);
if point_range.start.row == point_range.end.row {
Some(range)
} else {
None
}
}
fn all_edits_insertions_or_deletions( fn all_edits_insertions_or_deletions(
edits: &Vec<(Range<Anchor>, String)>, edits: &Vec<(Range<Anchor>, String)>,
snapshot: &MultiBufferSnapshot, snapshot: &MultiBufferSnapshot,
@ -6550,7 +6531,6 @@ impl Element for EditorElement {
inline_blame = self.layout_inline_blame( inline_blame = self.layout_inline_blame(
display_row, display_row,
&snapshot.display_snapshot, &snapshot.display_snapshot,
&snapshot.buffer_snapshot,
line_layout, line_layout,
crease_trailer_layout, crease_trailer_layout,
em_width, em_width,

View file

@ -286,7 +286,11 @@ fn assert_editor_active_edit_completion(
.as_ref() .as_ref()
.expect("editor has no active completion"); .expect("editor has no active completion");
if let InlineCompletion::Edit(edits) = &completion_state.completion { if let InlineCompletion::Edit {
edits,
single_line: _,
} = &completion_state.completion
{
assert(editor.buffer().read(cx).snapshot(cx), edits); assert(editor.buffer().read(cx).snapshot(cx), edits);
} else { } else {
panic!("expected edit completion"); panic!("expected edit completion");

View file

@ -1211,7 +1211,6 @@ impl Element for Div {
state.child_bounds = Vec::with_capacity(request_layout.child_layout_ids.len()); state.child_bounds = Vec::with_capacity(request_layout.child_layout_ids.len());
state.bounds = bounds; state.bounds = bounds;
let requested = state.requested_scroll_top.take(); let requested = state.requested_scroll_top.take();
// TODO az
for (ix, child_layout_id) in request_layout.child_layout_ids.iter().enumerate() { for (ix, child_layout_id) in request_layout.child_layout_ids.iter().enumerate() {
let child_bounds = cx.layout_bounds(*child_layout_id); let child_bounds = cx.layout_bounds(*child_layout_id);