From a0a095c6a351638a56972a71fe0b2fc8af455b94 Mon Sep 17 00:00:00 2001 From: Thorsten Ball Date: Wed, 18 Dec 2024 14:46:51 +0100 Subject: [PATCH] zeta: Show deletions when inline completion is shown in menu (#22186) If an inline completion isn't shown in a menu, we highlight text in the editor as deleted. But if it's shown in the menu, we didn't even show deleted text, which makes it hard to understand what's going on. This fixes it. ![screenshot-2024-12-18-14 34 55@2x](https://github.com/user-attachments/assets/579639e4-5ed9-4fe6-8e21-65166d192432) Release Notes: - N/A --- crates/editor/src/editor.rs | 19 +++++-- crates/editor/src/editor_tests.rs | 85 ++++++++++++++++++++++++++++--- crates/editor/src/element.rs | 2 +- 3 files changed, 94 insertions(+), 12 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 0a5649bdb2..8eb18f6c3b 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -4864,7 +4864,7 @@ impl Editor { let text = match &self.active_inline_completion.as_ref()?.completion { InlineCompletion::Edit(edits) => { - inline_completion_edit_text(&editor_snapshot, edits, cx) + inline_completion_edit_text(&editor_snapshot, edits, true, cx) } InlineCompletion::Move(target) => { let target_point = @@ -14630,6 +14630,7 @@ pub fn diagnostic_block_renderer( fn inline_completion_edit_text( editor_snapshot: &EditorSnapshot, edits: &Vec<(Range, String)>, + include_deletions: bool, cx: &WindowContext, ) -> InlineCompletionText { let edit_start = edits @@ -14653,12 +14654,24 @@ fn inline_completion_edit_text( offset = old_offset_range.end; let start = text.len(); - text.push_str(new_text); + let color = if include_deletions && new_text.is_empty() { + text.extend( + editor_snapshot + .buffer_snapshot + .chunks(old_offset_range.start..offset, false) + .map(|chunk| chunk.text), + ); + cx.theme().status().deleted_background + } else { + text.push_str(new_text); + cx.theme().status().created_background + }; let end = text.len(); + highlights.push(( start..end, HighlightStyle { - background_color: Some(cx.theme().status().created_background), + background_color: Some(color), ..Default::default() }, )); diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 47bee57b58..4882d8c217 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -14367,7 +14367,7 @@ async fn test_multi_buffer_with_single_excerpt_folding(cx: &mut gpui::TestAppCon fn test_inline_completion_text(cx: &mut TestAppContext) { init_test(cx, |_| {}); - // Test case 1: Simple insertion + // Simple insertion { let window = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple("Hello, world!", cx); @@ -14383,7 +14383,7 @@ fn test_inline_completion_text(cx: &mut TestAppContext) { let edits = vec![(edit_range, " beautiful".to_string())]; let InlineCompletionText::Edit { text, highlights } = - inline_completion_edit_text(&snapshot, &edits, cx) + inline_completion_edit_text(&snapshot, &edits, false, cx) else { panic!("Failed to generate inline completion text"); }; @@ -14399,7 +14399,7 @@ fn test_inline_completion_text(cx: &mut TestAppContext) { .unwrap(); } - // Test case 2: Replacement + // Replacement { let window = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple("This is a test.", cx); @@ -14417,7 +14417,7 @@ fn test_inline_completion_text(cx: &mut TestAppContext) { )]; let InlineCompletionText::Edit { text, highlights } = - inline_completion_edit_text(&snapshot, &edits, cx) + inline_completion_edit_text(&snapshot, &edits, false, cx) else { panic!("Failed to generate inline completion text"); }; @@ -14433,7 +14433,7 @@ fn test_inline_completion_text(cx: &mut TestAppContext) { .unwrap(); } - // Test case 3: Multiple edits + // Multiple edits { let window = cx.add_window(|cx| { let buffer = MultiBuffer::build_simple("Hello, world!", cx); @@ -14458,7 +14458,7 @@ fn test_inline_completion_text(cx: &mut TestAppContext) { ]; let InlineCompletionText::Edit { text, highlights } = - inline_completion_edit_text(&snapshot, &edits, cx) + inline_completion_edit_text(&snapshot, &edits, false, cx) else { panic!("Failed to generate inline completion text"); }; @@ -14479,7 +14479,7 @@ fn test_inline_completion_text(cx: &mut TestAppContext) { .unwrap(); } - // Test case 4: Multiple lines with edits + // Multiple lines with edits { let window = cx.add_window(|cx| { let buffer = @@ -14510,7 +14510,7 @@ fn test_inline_completion_text(cx: &mut TestAppContext) { ]; let InlineCompletionText::Edit { text, highlights } = - inline_completion_edit_text(&snapshot, &edits, cx) + inline_completion_edit_text(&snapshot, &edits, false, cx) else { panic!("Failed to generate inline completion text"); }; @@ -14532,6 +14532,75 @@ fn test_inline_completion_text(cx: &mut TestAppContext) { } } +#[gpui::test] +fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) { + init_test(cx, |_| {}); + + // Deletion + { + let window = cx.add_window(|cx| { + let buffer = MultiBuffer::build_simple("Hello, world!", cx); + Editor::new(EditorMode::Full, buffer, None, true, cx) + }); + let cx = &mut VisualTestContext::from_window(*window, cx); + + window + .update(cx, |editor, cx| { + let snapshot = editor.snapshot(cx); + let edit_range = snapshot.buffer_snapshot.anchor_after(Point::new(0, 5)) + ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 11)); + let edits = vec![(edit_range, "".to_string())]; + + let InlineCompletionText::Edit { text, highlights } = + inline_completion_edit_text(&snapshot, &edits, true, cx) + else { + panic!("Failed to generate inline completion text"); + }; + + assert_eq!(text, "Hello, world!"); + assert_eq!(highlights.len(), 1); + assert_eq!(highlights[0].0, 5..11); + assert_eq!( + highlights[0].1.background_color, + Some(cx.theme().status().deleted_background) + ); + }) + .unwrap(); + } + + // Insertion + { + let window = cx.add_window(|cx| { + let buffer = MultiBuffer::build_simple("Hello, world!", cx); + Editor::new(EditorMode::Full, buffer, None, true, cx) + }); + let cx = &mut VisualTestContext::from_window(*window, cx); + + window + .update(cx, |editor, cx| { + let snapshot = editor.snapshot(cx); + let edit_range = snapshot.buffer_snapshot.anchor_after(Point::new(0, 6)) + ..snapshot.buffer_snapshot.anchor_before(Point::new(0, 6)); + let edits = vec![(edit_range, " digital".to_string())]; + + let InlineCompletionText::Edit { text, highlights } = + inline_completion_edit_text(&snapshot, &edits, true, cx) + else { + panic!("Failed to generate inline completion text"); + }; + + assert_eq!(text, "Hello, digital world!"); + assert_eq!(highlights.len(), 1); + assert_eq!(highlights[0].0, 6..14); + assert_eq!( + highlights[0].1.background_color, + Some(cx.theme().status().created_background) + ); + }) + .unwrap(); + } +} + fn empty_range(row: usize, column: usize) -> Range { let point = DisplayPoint::new(DisplayRow(row as u32), column as u32); point..point diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 1daaacbcb3..343102b99e 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -3221,7 +3221,7 @@ impl EditorElement { } let crate::InlineCompletionText::Edit { text, highlights } = - crate::inline_completion_edit_text(editor_snapshot, edits, cx) + crate::inline_completion_edit_text(editor_snapshot, edits, false, cx) else { return None; };