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 0a5649bdb24bf141da369e7481d3a0b9d7ec5bdb..8eb18f6c3bfa0f21f756243c0e5b1c6984de6253 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 47bee57b58fa7c1faecd076dd9879a4d2e6301f5..4882d8c217dfd17853a06232f5a64ec526b6a022 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 1daaacbcb389fb25d48d79ec3e570a2a673e1c4b..343102b99edf2a361e9a2286f1088072d2e04369 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; };