editor: Highlight all matching occurrences of text in selection (#24835)
Closes #12635 - [x] Get it working - [x] Disable for multi cursor - [x] Disable for vim visual line selection - [x] Add setting to disable it - [x] Add scrollbar marker - [x] Handle delete state capturing selection Preview: https://github.com/user-attachments/assets/a76cde64-4f6c-4575-91cc-3a03a954e7a9 Release Notes: - Added support to highlight all matching occurrences of text within the selection in editor. --------- Co-authored-by: Agus Zubiaga <agus@zed.dev> Co-authored-by: Danilo <danilo@zed.dev>
This commit is contained in:
parent
1e1b637b50
commit
3e9722685b
5 changed files with 144 additions and 2 deletions
|
@ -283,6 +283,7 @@ impl InlayId {
|
|||
enum DocumentHighlightRead {}
|
||||
enum DocumentHighlightWrite {}
|
||||
enum InputComposition {}
|
||||
enum SelectedTextHighlight {}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Navigated {
|
||||
|
@ -681,6 +682,7 @@ pub struct Editor {
|
|||
next_completion_id: CompletionId,
|
||||
available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
|
||||
code_actions_task: Option<Task<Result<()>>>,
|
||||
selection_highlight_task: Option<Task<()>>,
|
||||
document_highlights_task: Option<Task<()>>,
|
||||
linked_editing_range_task: Option<Task<Option<()>>>,
|
||||
linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
|
||||
|
@ -1384,6 +1386,7 @@ impl Editor {
|
|||
code_action_providers,
|
||||
available_code_actions: Default::default(),
|
||||
code_actions_task: Default::default(),
|
||||
selection_highlight_task: Default::default(),
|
||||
document_highlights_task: Default::default(),
|
||||
linked_editing_range_task: Default::default(),
|
||||
pending_rename: Default::default(),
|
||||
|
@ -2165,6 +2168,7 @@ impl Editor {
|
|||
}
|
||||
self.refresh_code_actions(window, cx);
|
||||
self.refresh_document_highlights(cx);
|
||||
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;
|
||||
|
@ -4722,6 +4726,93 @@ impl Editor {
|
|||
None
|
||||
}
|
||||
|
||||
pub fn refresh_selected_text_highlights(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Editor>,
|
||||
) {
|
||||
self.selection_highlight_task.take();
|
||||
if !EditorSettings::get_global(cx).selection_highlight {
|
||||
self.clear_background_highlights::<SelectedTextHighlight>(cx);
|
||||
return;
|
||||
}
|
||||
if self.selections.count() != 1 || self.selections.line_mode {
|
||||
self.clear_background_highlights::<SelectedTextHighlight>(cx);
|
||||
return;
|
||||
}
|
||||
let selection = self.selections.newest::<Point>(cx);
|
||||
if selection.is_empty() || selection.start.row != selection.end.row {
|
||||
self.clear_background_highlights::<SelectedTextHighlight>(cx);
|
||||
return;
|
||||
}
|
||||
let debounce = EditorSettings::get_global(cx).selection_highlight_debounce;
|
||||
self.selection_highlight_task = Some(cx.spawn_in(window, |editor, mut cx| async move {
|
||||
cx.background_executor()
|
||||
.timer(Duration::from_millis(debounce))
|
||||
.await;
|
||||
let Some(matches_task) = editor
|
||||
.read_with(&mut cx, |editor, cx| {
|
||||
let buffer = editor.buffer().read(cx).snapshot(cx);
|
||||
cx.background_executor().spawn(async move {
|
||||
let mut ranges = Vec::new();
|
||||
let buffer_ranges =
|
||||
vec![buffer.anchor_before(0)..buffer.anchor_after(buffer.len())];
|
||||
let query = buffer.text_for_range(selection.range()).collect::<String>();
|
||||
for range in buffer_ranges {
|
||||
for (search_buffer, search_range, excerpt_id) in
|
||||
buffer.range_to_buffer_ranges(range)
|
||||
{
|
||||
ranges.extend(
|
||||
project::search::SearchQuery::text(
|
||||
query.clone(),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
None,
|
||||
)
|
||||
.unwrap()
|
||||
.search(search_buffer, Some(search_range.clone()))
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|match_range| {
|
||||
let start = search_buffer
|
||||
.anchor_after(search_range.start + match_range.start);
|
||||
let end = search_buffer
|
||||
.anchor_before(search_range.start + match_range.end);
|
||||
Anchor::range_in_buffer(
|
||||
excerpt_id,
|
||||
search_buffer.remote_id(),
|
||||
start..end,
|
||||
)
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
ranges
|
||||
})
|
||||
})
|
||||
.log_err()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let matches = matches_task.await;
|
||||
editor
|
||||
.update_in(&mut cx, |editor, _, cx| {
|
||||
editor.clear_background_highlights::<SelectedTextHighlight>(cx);
|
||||
if !matches.is_empty() {
|
||||
editor.highlight_background::<SelectedTextHighlight>(
|
||||
&matches,
|
||||
|theme| theme.editor_document_highlight_bracket_background,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
})
|
||||
.log_err();
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn refresh_inline_completion(
|
||||
&mut self,
|
||||
debounce: bool,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue