Add regex highlights in the query input (#25005)
Closes https://github.com/zed-industries/zed/discussions/24994 Release Notes: - Added regex highlights in the query input
This commit is contained in:
parent
91c99baaf2
commit
b5d85b638a
6 changed files with 117 additions and 35 deletions
|
@ -279,7 +279,17 @@ impl AssistantPanel {
|
||||||
});
|
});
|
||||||
pane.toolbar().update(cx, |toolbar, cx| {
|
pane.toolbar().update(cx, |toolbar, cx| {
|
||||||
toolbar.add_item(context_editor_toolbar.clone(), window, cx);
|
toolbar.add_item(context_editor_toolbar.clone(), window, cx);
|
||||||
toolbar.add_item(cx.new(|cx| BufferSearchBar::new(window, cx)), window, cx)
|
toolbar.add_item(
|
||||||
|
cx.new(|cx| {
|
||||||
|
BufferSearchBar::new(
|
||||||
|
Some(workspace.project().read(cx).languages().clone()),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
});
|
});
|
||||||
pane
|
pane
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,6 +6,7 @@ use crate::{
|
||||||
ToggleCaseSensitive, ToggleRegex, ToggleReplace, ToggleSelection, ToggleWholeWord,
|
ToggleCaseSensitive, ToggleRegex, ToggleReplace, ToggleSelection, ToggleWholeWord,
|
||||||
};
|
};
|
||||||
use any_vec::AnyVec;
|
use any_vec::AnyVec;
|
||||||
|
use anyhow::Context as _;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use editor::{
|
use editor::{
|
||||||
actions::{Tab, TabPrev},
|
actions::{Tab, TabPrev},
|
||||||
|
@ -14,9 +15,10 @@ use editor::{
|
||||||
use futures::channel::oneshot;
|
use futures::channel::oneshot;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, div, impl_actions, Action, App, ClickEvent, Context, Entity, EventEmitter,
|
actions, div, impl_actions, Action, App, ClickEvent, Context, Entity, EventEmitter,
|
||||||
FocusHandle, Focusable, Hsla, InteractiveElement as _, IntoElement, KeyContext,
|
FocusHandle, Focusable, InteractiveElement as _, IntoElement, KeyContext, ParentElement as _,
|
||||||
ParentElement as _, Render, ScrollHandle, Styled, Subscription, Task, TextStyle, Window,
|
Render, ScrollHandle, Styled, Subscription, Task, TextStyle, Window,
|
||||||
};
|
};
|
||||||
|
use language::{Language, LanguageRegistry};
|
||||||
use project::{
|
use project::{
|
||||||
search::SearchQuery,
|
search::SearchQuery,
|
||||||
search_history::{SearchHistory, SearchHistoryCursor},
|
search_history::{SearchHistory, SearchHistoryCursor},
|
||||||
|
@ -108,41 +110,48 @@ pub struct BufferSearchBar {
|
||||||
scroll_handle: ScrollHandle,
|
scroll_handle: ScrollHandle,
|
||||||
editor_scroll_handle: ScrollHandle,
|
editor_scroll_handle: ScrollHandle,
|
||||||
editor_needed_width: Pixels,
|
editor_needed_width: Pixels,
|
||||||
|
regex_language: Option<Arc<Language>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BufferSearchBar {
|
impl BufferSearchBar {
|
||||||
fn render_text_input(
|
fn render_text_input(
|
||||||
&self,
|
&self,
|
||||||
editor: &Entity<Editor>,
|
editor: &Entity<Editor>,
|
||||||
color: Hsla,
|
color_override: Option<Color>,
|
||||||
|
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement {
|
||||||
|
let (color, use_syntax) = if editor.read(cx).read_only(cx) {
|
||||||
|
(cx.theme().colors().text_disabled, false)
|
||||||
|
} else {
|
||||||
|
match color_override {
|
||||||
|
Some(color_override) => (color_override.color(cx), false),
|
||||||
|
None => (cx.theme().colors().text, true),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let settings = ThemeSettings::get_global(cx);
|
let settings = ThemeSettings::get_global(cx);
|
||||||
let text_style = TextStyle {
|
let text_style = TextStyle {
|
||||||
color: if editor.read(cx).read_only(cx) {
|
color,
|
||||||
cx.theme().colors().text_disabled
|
|
||||||
} else {
|
|
||||||
color
|
|
||||||
},
|
|
||||||
font_family: settings.buffer_font.family.clone(),
|
font_family: settings.buffer_font.family.clone(),
|
||||||
font_features: settings.buffer_font.features.clone(),
|
font_features: settings.buffer_font.features.clone(),
|
||||||
font_fallbacks: settings.buffer_font.fallbacks.clone(),
|
font_fallbacks: settings.buffer_font.fallbacks.clone(),
|
||||||
font_size: rems(0.875).into(),
|
font_size: rems(0.875).into(),
|
||||||
font_weight: settings.buffer_font.weight,
|
font_weight: settings.buffer_font.weight,
|
||||||
line_height: relative(1.3),
|
line_height: relative(1.3),
|
||||||
..Default::default()
|
..TextStyle::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
EditorElement::new(
|
let mut editor_style = EditorStyle {
|
||||||
editor,
|
background: cx.theme().colors().editor_background,
|
||||||
EditorStyle {
|
local_player: cx.theme().players().local(),
|
||||||
background: cx.theme().colors().editor_background,
|
text: text_style,
|
||||||
local_player: cx.theme().players().local(),
|
..EditorStyle::default()
|
||||||
text: text_style,
|
};
|
||||||
..Default::default()
|
if use_syntax {
|
||||||
},
|
editor_style.syntax = cx.theme().syntax().clone();
|
||||||
)
|
}
|
||||||
|
|
||||||
|
EditorElement::new(editor, editor_style)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn query_editor_focused(&self) -> bool {
|
pub fn query_editor_focused(&self) -> bool {
|
||||||
|
@ -179,7 +188,7 @@ impl Render for BufferSearchBar {
|
||||||
editor.set_placeholder_text("Replace with…", cx);
|
editor.set_placeholder_text("Replace with…", cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut text_color = Color::Default;
|
let mut color_override = None;
|
||||||
let match_text = self
|
let match_text = self
|
||||||
.active_searchable_item
|
.active_searchable_item
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -195,7 +204,7 @@ impl Render for BufferSearchBar {
|
||||||
if let Some(match_ix) = self.active_match_index {
|
if let Some(match_ix) = self.active_match_index {
|
||||||
Some(format!("{}/{}", match_ix + 1, matches_count))
|
Some(format!("{}/{}", match_ix + 1, matches_count))
|
||||||
} else {
|
} else {
|
||||||
text_color = Color::Error; // No matches found
|
color_override = Some(Color::Error); // No matches found
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -239,7 +248,7 @@ impl Render for BufferSearchBar {
|
||||||
input_base_styles()
|
input_base_styles()
|
||||||
.id("editor-scroll")
|
.id("editor-scroll")
|
||||||
.track_scroll(&self.editor_scroll_handle)
|
.track_scroll(&self.editor_scroll_handle)
|
||||||
.child(self.render_text_input(&self.query_editor, text_color.color(cx), cx))
|
.child(self.render_text_input(&self.query_editor, color_override, cx))
|
||||||
.when(!hide_inline_icons, |div| {
|
.when(!hide_inline_icons, |div| {
|
||||||
div.child(
|
div.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
|
@ -412,7 +421,7 @@ impl Render for BufferSearchBar {
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.child(input_base_styles().child(self.render_text_input(
|
.child(input_base_styles().child(self.render_text_input(
|
||||||
&self.replacement_editor,
|
&self.replacement_editor,
|
||||||
cx.theme().colors().text,
|
None,
|
||||||
cx,
|
cx,
|
||||||
)))
|
)))
|
||||||
.child(
|
.child(
|
||||||
|
@ -652,7 +661,11 @@ impl BufferSearchBar {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
pub fn new(
|
||||||
|
languages: Option<Arc<LanguageRegistry>>,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) -> Self {
|
||||||
let query_editor = cx.new(|cx| Editor::single_line(window, cx));
|
let query_editor = cx.new(|cx| Editor::single_line(window, cx));
|
||||||
cx.subscribe_in(&query_editor, window, Self::on_query_editor_event)
|
cx.subscribe_in(&query_editor, window, Self::on_query_editor_event)
|
||||||
.detach();
|
.detach();
|
||||||
|
@ -661,6 +674,32 @@ impl BufferSearchBar {
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
let search_options = SearchOptions::from_settings(&EditorSettings::get_global(cx).search);
|
let search_options = SearchOptions::from_settings(&EditorSettings::get_global(cx).search);
|
||||||
|
if let Some(languages) = languages {
|
||||||
|
let query_buffer = query_editor
|
||||||
|
.read(cx)
|
||||||
|
.buffer()
|
||||||
|
.read(cx)
|
||||||
|
.as_singleton()
|
||||||
|
.expect("query editor should be backed by a singleton buffer");
|
||||||
|
query_buffer.update(cx, |query_buffer, _| {
|
||||||
|
query_buffer.set_language_registry(languages.clone());
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.spawn(|buffer_search_bar, mut cx| async move {
|
||||||
|
let regex_language = languages
|
||||||
|
.language_for_name("regex")
|
||||||
|
.await
|
||||||
|
.context("loading regex language")?;
|
||||||
|
buffer_search_bar
|
||||||
|
.update(&mut cx, |buffer_search_bar, cx| {
|
||||||
|
buffer_search_bar.regex_language = Some(regex_language);
|
||||||
|
buffer_search_bar.adjust_query_regex_language(cx);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
anyhow::Ok(())
|
||||||
|
})
|
||||||
|
.detach_and_log_err(cx);
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
query_editor,
|
query_editor,
|
||||||
|
@ -688,6 +727,7 @@ impl BufferSearchBar {
|
||||||
scroll_handle: ScrollHandle::new(),
|
scroll_handle: ScrollHandle::new(),
|
||||||
editor_scroll_handle: ScrollHandle::new(),
|
editor_scroll_handle: ScrollHandle::new(),
|
||||||
editor_needed_width: px(0.),
|
editor_needed_width: px(0.),
|
||||||
|
regex_language: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -910,6 +950,7 @@ impl BufferSearchBar {
|
||||||
self.search_options.toggle(search_option);
|
self.search_options.toggle(search_option);
|
||||||
self.default_options = self.search_options;
|
self.default_options = self.search_options;
|
||||||
drop(self.update_matches(false, window, cx));
|
drop(self.update_matches(false, window, cx));
|
||||||
|
self.adjust_query_regex_language(cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1429,6 +1470,28 @@ impl BufferSearchBar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn adjust_query_regex_language(&self, cx: &mut App) {
|
||||||
|
let enable = self.search_options.contains(SearchOptions::REGEX);
|
||||||
|
let query_buffer = self
|
||||||
|
.query_editor
|
||||||
|
.read(cx)
|
||||||
|
.buffer()
|
||||||
|
.read(cx)
|
||||||
|
.as_singleton()
|
||||||
|
.expect("query editor should be backed by a singleton buffer");
|
||||||
|
if enable {
|
||||||
|
if let Some(regex_language) = self.regex_language.clone() {
|
||||||
|
query_buffer.update(cx, |query_buffer, cx| {
|
||||||
|
query_buffer.set_language(Some(regex_language), cx);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
query_buffer.update(cx, |query_buffer, cx| {
|
||||||
|
query_buffer.set_language(None, cx);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -1482,7 +1545,7 @@ mod tests {
|
||||||
cx.new_window_entity(|window, cx| Editor::for_buffer(buffer.clone(), None, window, cx));
|
cx.new_window_entity(|window, cx| Editor::for_buffer(buffer.clone(), None, window, cx));
|
||||||
|
|
||||||
let search_bar = cx.new_window_entity(|window, cx| {
|
let search_bar = cx.new_window_entity(|window, cx| {
|
||||||
let mut search_bar = BufferSearchBar::new(window, cx);
|
let mut search_bar = BufferSearchBar::new(None, window, cx);
|
||||||
search_bar.set_active_pane_item(Some(&editor), window, cx);
|
search_bar.set_active_pane_item(Some(&editor), window, cx);
|
||||||
search_bar.show(window, cx);
|
search_bar.show(window, cx);
|
||||||
search_bar
|
search_bar
|
||||||
|
@ -1851,7 +1914,7 @@ mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
let search_bar = window.build_entity(cx, |window, cx| {
|
let search_bar = window.build_entity(cx, |window, cx| {
|
||||||
let mut search_bar = BufferSearchBar::new(window, cx);
|
let mut search_bar = BufferSearchBar::new(None, window, cx);
|
||||||
search_bar.set_active_pane_item(Some(&editor), window, cx);
|
search_bar.set_active_pane_item(Some(&editor), window, cx);
|
||||||
search_bar.show(window, cx);
|
search_bar.show(window, cx);
|
||||||
search_bar
|
search_bar
|
||||||
|
@ -2059,7 +2122,7 @@ mod tests {
|
||||||
cx.new_window_entity(|window, cx| Editor::for_buffer(buffer.clone(), None, window, cx));
|
cx.new_window_entity(|window, cx| Editor::for_buffer(buffer.clone(), None, window, cx));
|
||||||
|
|
||||||
let search_bar = cx.new_window_entity(|window, cx| {
|
let search_bar = cx.new_window_entity(|window, cx| {
|
||||||
let mut search_bar = BufferSearchBar::new(window, cx);
|
let mut search_bar = BufferSearchBar::new(None, window, cx);
|
||||||
search_bar.set_active_pane_item(Some(&editor), window, cx);
|
search_bar.set_active_pane_item(Some(&editor), window, cx);
|
||||||
search_bar.show(window, cx);
|
search_bar.show(window, cx);
|
||||||
search_bar
|
search_bar
|
||||||
|
@ -2133,7 +2196,7 @@ mod tests {
|
||||||
cx.new_window_entity(|window, cx| Editor::for_buffer(buffer.clone(), None, window, cx));
|
cx.new_window_entity(|window, cx| Editor::for_buffer(buffer.clone(), None, window, cx));
|
||||||
|
|
||||||
let search_bar = cx.new_window_entity(|window, cx| {
|
let search_bar = cx.new_window_entity(|window, cx| {
|
||||||
let mut search_bar = BufferSearchBar::new(window, cx);
|
let mut search_bar = BufferSearchBar::new(None, window, cx);
|
||||||
search_bar.set_active_pane_item(Some(&editor), window, cx);
|
search_bar.set_active_pane_item(Some(&editor), window, cx);
|
||||||
search_bar.show(window, cx);
|
search_bar.show(window, cx);
|
||||||
search_bar
|
search_bar
|
||||||
|
@ -2513,7 +2576,7 @@ mod tests {
|
||||||
cx.new_window_entity(|window, cx| Editor::for_buffer(buffer.clone(), None, window, cx));
|
cx.new_window_entity(|window, cx| Editor::for_buffer(buffer.clone(), None, window, cx));
|
||||||
|
|
||||||
let search_bar = cx.new_window_entity(|window, cx| {
|
let search_bar = cx.new_window_entity(|window, cx| {
|
||||||
let mut search_bar = BufferSearchBar::new(window, cx);
|
let mut search_bar = BufferSearchBar::new(None, window, cx);
|
||||||
search_bar.set_active_pane_item(Some(&editor), window, cx);
|
search_bar.set_active_pane_item(Some(&editor), window, cx);
|
||||||
search_bar.show(window, cx);
|
search_bar.show(window, cx);
|
||||||
search_bar
|
search_bar
|
||||||
|
@ -2596,7 +2659,7 @@ mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
let search_bar = cx.new_window_entity(|window, cx| {
|
let search_bar = cx.new_window_entity(|window, cx| {
|
||||||
let mut search_bar = BufferSearchBar::new(window, cx);
|
let mut search_bar = BufferSearchBar::new(None, window, cx);
|
||||||
search_bar.set_active_pane_item(Some(&editor), window, cx);
|
search_bar.set_active_pane_item(Some(&editor), window, cx);
|
||||||
search_bar.show(window, cx);
|
search_bar.show(window, cx);
|
||||||
search_bar
|
search_bar
|
||||||
|
|
|
@ -3804,7 +3804,8 @@ pub mod tests {
|
||||||
cx.run_until_parked();
|
cx.run_until_parked();
|
||||||
|
|
||||||
let buffer_search_bar = cx.new_window_entity(|window, cx| {
|
let buffer_search_bar = cx.new_window_entity(|window, cx| {
|
||||||
let mut search_bar = BufferSearchBar::new(window, cx);
|
let mut search_bar =
|
||||||
|
BufferSearchBar::new(Some(project.read(cx).languages().clone()), window, cx);
|
||||||
search_bar.set_active_pane_item(Some(&editor), window, cx);
|
search_bar.set_active_pane_item(Some(&editor), window, cx);
|
||||||
search_bar.show(window, cx);
|
search_bar.show(window, cx);
|
||||||
search_bar
|
search_bar
|
||||||
|
|
|
@ -980,7 +980,9 @@ pub fn new_terminal_pane(
|
||||||
false
|
false
|
||||||
})));
|
})));
|
||||||
|
|
||||||
let buffer_search_bar = cx.new(|cx| search::BufferSearchBar::new(window, cx));
|
let buffer_search_bar = cx.new(|cx| {
|
||||||
|
search::BufferSearchBar::new(Some(project.read(cx).languages().clone()), window, cx)
|
||||||
|
});
|
||||||
let breadcrumbs = cx.new(|_| Breadcrumbs::new());
|
let breadcrumbs = cx.new(|_| Breadcrumbs::new());
|
||||||
pane.toolbar().update(cx, |toolbar, cx| {
|
pane.toolbar().update(cx, |toolbar, cx| {
|
||||||
toolbar.add_item(buffer_search_bar, window, cx);
|
toolbar.add_item(buffer_search_bar, window, cx);
|
||||||
|
|
|
@ -78,7 +78,7 @@ impl VimTestContext {
|
||||||
cx.update_workspace(|workspace, window, cx| {
|
cx.update_workspace(|workspace, window, cx| {
|
||||||
workspace.active_pane().update(cx, |pane, cx| {
|
workspace.active_pane().update(cx, |pane, cx| {
|
||||||
pane.toolbar().update(cx, |toolbar, cx| {
|
pane.toolbar().update(cx, |toolbar, cx| {
|
||||||
let buffer_search_bar = cx.new(|cx| BufferSearchBar::new(window, cx));
|
let buffer_search_bar = cx.new(|cx| BufferSearchBar::new(None, window, cx));
|
||||||
toolbar.add_item(buffer_search_bar, window, cx);
|
toolbar.add_item(buffer_search_bar, window, cx);
|
||||||
|
|
||||||
let project_search_bar = cx.new(|_| ProjectSearchBar::new());
|
let project_search_bar = cx.new(|_| ProjectSearchBar::new());
|
||||||
|
|
|
@ -885,7 +885,13 @@ fn initialize_pane(
|
||||||
toolbar.add_item(multibuffer_hint, window, cx);
|
toolbar.add_item(multibuffer_hint, window, cx);
|
||||||
let breadcrumbs = cx.new(|_| Breadcrumbs::new());
|
let breadcrumbs = cx.new(|_| Breadcrumbs::new());
|
||||||
toolbar.add_item(breadcrumbs, window, cx);
|
toolbar.add_item(breadcrumbs, window, cx);
|
||||||
let buffer_search_bar = cx.new(|cx| search::BufferSearchBar::new(window, cx));
|
let buffer_search_bar = cx.new(|cx| {
|
||||||
|
search::BufferSearchBar::new(
|
||||||
|
Some(workspace.project().read(cx).languages().clone()),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
toolbar.add_item(buffer_search_bar.clone(), window, cx);
|
toolbar.add_item(buffer_search_bar.clone(), window, cx);
|
||||||
let proposed_change_bar = cx.new(|_| ProposedChangesEditorToolbar::new());
|
let proposed_change_bar = cx.new(|_| ProposedChangesEditorToolbar::new());
|
||||||
toolbar.add_item(proposed_change_bar, window, cx);
|
toolbar.add_item(proposed_change_bar, window, cx);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue