Replace newlines in search bar (#33504)

Release Notes:

- search: Pasted newlines are now rendered as "\n" (with an underline),
instead of line-wrapping. This should make it much clearer what you're
searching for.
 
<img width="675" alt="Screenshot 2025-06-27 at 00 34 52"
src="https://github.com/user-attachments/assets/67275bc6-bec1-463f-b351-6b9ed0a6df81"
/>
This commit is contained in:
Conrad Irwin 2025-06-27 09:39:38 -06:00 committed by GitHub
parent d74f3f4ea6
commit 157199b65b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 92 additions and 10 deletions

View file

@ -1143,6 +1143,7 @@ pub struct Editor {
drag_and_drop_selection_enabled: bool,
next_color_inlay_id: usize,
colors: Option<LspColorData>,
folding_newlines: Task<()>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
@ -2159,6 +2160,7 @@ impl Editor {
mode,
selection_drag_state: SelectionDragState::None,
drag_and_drop_selection_enabled: EditorSettings::get_global(cx).drag_and_drop_selection,
folding_newlines: Task::ready(()),
};
if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
editor
@ -6717,6 +6719,77 @@ impl Editor {
})
}
fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
struct NewlineFold;
let type_id = std::any::TypeId::of::<NewlineFold>();
if !self.mode.is_single_line() {
return;
}
let snapshot = self.snapshot(window, cx);
if snapshot.buffer_snapshot.max_point().row == 0 {
return;
}
let task = cx.background_spawn(async move {
let new_newlines = snapshot
.buffer_chars_at(0)
.filter_map(|(c, i)| {
if c == '\n' {
Some(
snapshot.buffer_snapshot.anchor_after(i)
..snapshot.buffer_snapshot.anchor_before(i + 1),
)
} else {
None
}
})
.collect::<Vec<_>>();
let existing_newlines = snapshot
.folds_in_range(0..snapshot.buffer_snapshot.len())
.filter_map(|fold| {
if fold.placeholder.type_tag == Some(type_id) {
Some(fold.range.start..fold.range.end)
} else {
None
}
})
.collect::<Vec<_>>();
(new_newlines, existing_newlines)
});
self.folding_newlines = cx.spawn(async move |this, cx| {
let (new_newlines, existing_newlines) = task.await;
if new_newlines == existing_newlines {
return;
}
let placeholder = FoldPlaceholder {
render: Arc::new(move |_, _, cx| {
div()
.bg(cx.theme().status().hint_background)
.border_b_1()
.size_full()
.font(ThemeSettings::get_global(cx).buffer_font.clone())
.border_color(cx.theme().status().hint)
.child("\\n")
.into_any()
}),
constrain_width: false,
merge_adjacent: false,
type_tag: Some(type_id),
};
let creases = new_newlines
.into_iter()
.map(|range| Crease::simple(range, placeholder.clone()))
.collect();
this.update(cx, |this, cx| {
this.display_map.update(cx, |display_map, cx| {
display_map.remove_folds_with_type(existing_newlines, type_id, cx);
display_map.fold(creases, cx);
});
})
.ok();
});
}
fn refresh_selected_text_highlights(
&mut self,
on_buffer_edit: bool,
@ -17100,16 +17173,6 @@ impl Editor {
return;
}
let mut buffers_affected = HashSet::default();
let multi_buffer = self.buffer().read(cx);
for crease in &creases {
if let Some((_, buffer, _)) =
multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
{
buffers_affected.insert(buffer.read(cx).remote_id());
};
}
self.display_map.update(cx, |map, cx| map.fold(creases, cx));
if auto_scroll {
@ -19435,6 +19498,7 @@ impl Editor {
self.refresh_active_diagnostics(cx);
self.refresh_code_actions(window, cx);
self.refresh_selected_text_highlights(true, window, cx);
self.refresh_single_line_folds(window, cx);
refresh_matching_bracket_highlights(self, window, cx);
if self.has_active_inline_completion() {
self.update_visible_inline_completion(window, cx);

View file

@ -22770,6 +22770,24 @@ async fn test_mtime_and_document_colors(cx: &mut TestAppContext) {
});
}
#[gpui::test]
async fn test_newline_replacement_in_single_line(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let (editor, cx) = cx.add_window_view(Editor::single_line);
editor.update_in(cx, |editor, window, cx| {
editor.set_text("oops\n\nwow\n", window, cx)
});
cx.run_until_parked();
editor.update(cx, |editor, cx| {
assert_eq!(editor.display_text(cx), "oops⋯⋯wow⋯");
});
editor.update(cx, |editor, cx| editor.edit([(3..5, "")], cx));
cx.run_until_parked();
editor.update(cx, |editor, cx| {
assert_eq!(editor.display_text(cx), "oop⋯wow⋯");
});
}
#[track_caller]
fn extract_color_inlays(editor: &Editor, cx: &App) -> Vec<Rgba> {
editor