Allow buffer search in project search (#23819)
Closes #13437 Closes #19993 Release Notes: - Allow searching within the results of a project search - vim: Fix `/`/`?`, `n`/`N`, `gn`/`gN`,`*`/`#` in project search results --------- Co-authored-by: Nico <nico.lehmann@gmail.com>
This commit is contained in:
parent
e1af35aa15
commit
f2b3f3a9ab
10 changed files with 191 additions and 105 deletions
|
@ -136,7 +136,7 @@
|
||||||
"ctrl-k z": "editor::ToggleSoftWrap",
|
"ctrl-k z": "editor::ToggleSoftWrap",
|
||||||
"find": "buffer_search::Deploy",
|
"find": "buffer_search::Deploy",
|
||||||
"ctrl-f": "buffer_search::Deploy",
|
"ctrl-f": "buffer_search::Deploy",
|
||||||
"ctrl-h": ["buffer_search::Deploy", { "replace_enabled": true }],
|
"ctrl-h": "buffer_search::DeployReplace",
|
||||||
// "cmd-e": ["buffer_search::Deploy", { "focus": false }],
|
// "cmd-e": ["buffer_search::Deploy", { "focus": false }],
|
||||||
"ctrl->": "assistant::QuoteSelection",
|
"ctrl->": "assistant::QuoteSelection",
|
||||||
"ctrl-<": "assistant::InsertIntoEditor",
|
"ctrl-<": "assistant::InsertIntoEditor",
|
||||||
|
|
|
@ -145,7 +145,7 @@
|
||||||
"cmd-shift-enter": "editor::NewlineAbove",
|
"cmd-shift-enter": "editor::NewlineAbove",
|
||||||
"cmd-k z": "editor::ToggleSoftWrap",
|
"cmd-k z": "editor::ToggleSoftWrap",
|
||||||
"cmd-f": "buffer_search::Deploy",
|
"cmd-f": "buffer_search::Deploy",
|
||||||
"cmd-alt-f": ["buffer_search::Deploy", { "replace_enabled": true }],
|
"cmd-alt-f": "buffer_search::DeployReplace",
|
||||||
"cmd-alt-l": ["buffer_search::Deploy", { "selection_search_enabled": true }],
|
"cmd-alt-l": ["buffer_search::Deploy", { "selection_search_enabled": true }],
|
||||||
"cmd-e": ["buffer_search::Deploy", { "focus": false }],
|
"cmd-e": ["buffer_search::Deploy", { "focus": false }],
|
||||||
"cmd->": "assistant::QuoteSelection",
|
"cmd->": "assistant::QuoteSelection",
|
||||||
|
|
|
@ -728,6 +728,7 @@ pub struct Editor {
|
||||||
expect_bounds_change: Option<Bounds<Pixels>>,
|
expect_bounds_change: Option<Bounds<Pixels>>,
|
||||||
tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
|
tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
|
||||||
tasks_update_task: Option<Task<()>>,
|
tasks_update_task: Option<Task<()>>,
|
||||||
|
in_project_search: bool,
|
||||||
previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
|
previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
|
||||||
breadcrumb_header: Option<String>,
|
breadcrumb_header: Option<String>,
|
||||||
focused_block: Option<FocusedBlock>,
|
focused_block: Option<FocusedBlock>,
|
||||||
|
@ -1426,6 +1427,7 @@ impl Editor {
|
||||||
],
|
],
|
||||||
tasks_update_task: None,
|
tasks_update_task: None,
|
||||||
linked_edit_ranges: Default::default(),
|
linked_edit_ranges: Default::default(),
|
||||||
|
in_project_search: false,
|
||||||
previous_search_ranges: None,
|
previous_search_ranges: None,
|
||||||
breadcrumb_header: None,
|
breadcrumb_header: None,
|
||||||
focused_block: None,
|
focused_block: None,
|
||||||
|
@ -1703,6 +1705,10 @@ impl Editor {
|
||||||
self.collaboration_hub = Some(hub);
|
self.collaboration_hub = Some(hub);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_in_project_search(&mut self, in_project_search: bool) {
|
||||||
|
self.in_project_search = in_project_search;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_custom_context_menu(
|
pub fn set_custom_context_menu(
|
||||||
&mut self,
|
&mut self,
|
||||||
f: impl 'static
|
f: impl 'static
|
||||||
|
|
|
@ -38,8 +38,11 @@ use text::{BufferId, Selection};
|
||||||
use theme::{Theme, ThemeSettings};
|
use theme::{Theme, ThemeSettings};
|
||||||
use ui::{h_flex, prelude::*, IconDecorationKind, Label};
|
use ui::{h_flex, prelude::*, IconDecorationKind, Label};
|
||||||
use util::{paths::PathExt, ResultExt, TryFutureExt};
|
use util::{paths::PathExt, ResultExt, TryFutureExt};
|
||||||
use workspace::item::{BreadcrumbText, FollowEvent};
|
|
||||||
use workspace::item::{Dedup, ItemSettings, SerializableItem, TabContentParams};
|
use workspace::item::{Dedup, ItemSettings, SerializableItem, TabContentParams};
|
||||||
|
use workspace::{
|
||||||
|
item::{BreadcrumbText, FollowEvent},
|
||||||
|
searchable::SearchOptions,
|
||||||
|
};
|
||||||
use workspace::{
|
use workspace::{
|
||||||
item::{FollowableItem, Item, ItemEvent, ProjectItem},
|
item::{FollowableItem, Item, ItemEvent, ProjectItem},
|
||||||
searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle},
|
searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle},
|
||||||
|
@ -1324,6 +1327,28 @@ impl SearchableItem for Editor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn supported_options(&self) -> SearchOptions {
|
||||||
|
if self.in_project_search {
|
||||||
|
SearchOptions {
|
||||||
|
case: true,
|
||||||
|
word: true,
|
||||||
|
regex: true,
|
||||||
|
replacement: false,
|
||||||
|
selection: false,
|
||||||
|
find_in_results: true,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SearchOptions {
|
||||||
|
case: true,
|
||||||
|
word: true,
|
||||||
|
regex: true,
|
||||||
|
replacement: true,
|
||||||
|
selection: true,
|
||||||
|
find_in_results: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn query_suggestion(&mut self, window: &mut Window, cx: &mut Context<Self>) -> String {
|
fn query_suggestion(&mut self, window: &mut Window, cx: &mut Context<Self>) -> String {
|
||||||
let setting = EditorSettings::get_global(cx).seed_search_query_from_cursor;
|
let setting = EditorSettings::get_global(cx).seed_search_query_from_cursor;
|
||||||
let snapshot = &self.snapshot(window, cx).buffer_snapshot;
|
let snapshot = &self.snapshot(window, cx).buffer_snapshot;
|
||||||
|
|
|
@ -1148,11 +1148,12 @@ impl SearchableItem for LspLogView {
|
||||||
) {
|
) {
|
||||||
// Since LSP Log is read-only, it doesn't make sense to support replace operation.
|
// Since LSP Log is read-only, it doesn't make sense to support replace operation.
|
||||||
}
|
}
|
||||||
fn supported_options() -> workspace::searchable::SearchOptions {
|
fn supported_options(&self) -> workspace::searchable::SearchOptions {
|
||||||
workspace::searchable::SearchOptions {
|
workspace::searchable::SearchOptions {
|
||||||
case: true,
|
case: true,
|
||||||
word: true,
|
word: true,
|
||||||
regex: true,
|
regex: true,
|
||||||
|
find_in_results: false,
|
||||||
// LSP log is read-only.
|
// LSP log is read-only.
|
||||||
replacement: false,
|
replacement: false,
|
||||||
selection: false,
|
selection: false,
|
||||||
|
|
|
@ -55,7 +55,7 @@ pub struct Deploy {
|
||||||
|
|
||||||
impl_actions!(buffer_search, [Deploy]);
|
impl_actions!(buffer_search, [Deploy]);
|
||||||
|
|
||||||
actions!(buffer_search, [Dismiss, FocusEditor]);
|
actions!(buffer_search, [DeployReplace, Dismiss, FocusEditor]);
|
||||||
|
|
||||||
impl Deploy {
|
impl Deploy {
|
||||||
pub fn find() -> Self {
|
pub fn find() -> Self {
|
||||||
|
@ -65,6 +65,14 @@ impl Deploy {
|
||||||
selection_search_enabled: false,
|
selection_search_enabled: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn replace() -> Self {
|
||||||
|
Self {
|
||||||
|
focus: true,
|
||||||
|
replace_enabled: true,
|
||||||
|
selection_search_enabled: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
|
@ -156,7 +164,7 @@ impl Render for BufferSearchBar {
|
||||||
let hide_inline_icons = self.editor_needed_width
|
let hide_inline_icons = self.editor_needed_width
|
||||||
> self.editor_scroll_handle.bounds().size.width - window.rem_size() * 6.;
|
> self.editor_scroll_handle.bounds().size.width - window.rem_size() * 6.;
|
||||||
|
|
||||||
let supported_options = self.supported_options();
|
let supported_options = self.supported_options(cx);
|
||||||
|
|
||||||
if self.query_editor.update(cx, |query_editor, _cx| {
|
if self.query_editor.update(cx, |query_editor, _cx| {
|
||||||
query_editor.placeholder_text().is_none()
|
query_editor.placeholder_text().is_none()
|
||||||
|
@ -223,6 +231,9 @@ impl Render for BufferSearchBar {
|
||||||
|
|
||||||
let search_line = h_flex()
|
let search_line = h_flex()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
|
.when(supported_options.find_in_results, |el| {
|
||||||
|
el.child(Label::new("Find in results").color(Color::Hint))
|
||||||
|
})
|
||||||
.child(
|
.child(
|
||||||
input_base_styles()
|
input_base_styles()
|
||||||
.id("editor-scroll")
|
.id("editor-scroll")
|
||||||
|
@ -328,56 +339,70 @@ impl Render for BufferSearchBar {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.child(
|
.when(!supported_options.find_in_results, |el| {
|
||||||
IconButton::new("select-all", ui::IconName::SelectAll)
|
el.child(
|
||||||
.on_click(|_, window, cx| {
|
IconButton::new("select-all", ui::IconName::SelectAll)
|
||||||
window.dispatch_action(SelectAllMatches.boxed_clone(), cx)
|
.on_click(|_, window, cx| {
|
||||||
})
|
window.dispatch_action(SelectAllMatches.boxed_clone(), cx)
|
||||||
.shape(IconButtonShape::Square)
|
})
|
||||||
.tooltip({
|
.shape(IconButtonShape::Square)
|
||||||
let focus_handle = focus_handle.clone();
|
.tooltip({
|
||||||
move |window, cx| {
|
let focus_handle = focus_handle.clone();
|
||||||
Tooltip::for_action_in(
|
move |window, cx| {
|
||||||
"Select All Matches",
|
Tooltip::for_action_in(
|
||||||
&SelectAllMatches,
|
"Select All Matches",
|
||||||
&focus_handle,
|
&SelectAllMatches,
|
||||||
window,
|
&focus_handle,
|
||||||
cx,
|
window,
|
||||||
)
|
cx,
|
||||||
}
|
)
|
||||||
}),
|
}
|
||||||
)
|
}),
|
||||||
.child(
|
)
|
||||||
h_flex()
|
.child(
|
||||||
.pl_2()
|
h_flex()
|
||||||
.ml_1()
|
.pl_2()
|
||||||
.border_l_1()
|
.ml_1()
|
||||||
.border_color(cx.theme().colors().border_variant)
|
.border_l_1()
|
||||||
.child(render_nav_button(
|
.border_color(cx.theme().colors().border_variant)
|
||||||
ui::IconName::ChevronLeft,
|
.child(render_nav_button(
|
||||||
self.active_match_index.is_some(),
|
ui::IconName::ChevronLeft,
|
||||||
"Select Previous Match",
|
self.active_match_index.is_some(),
|
||||||
&SelectPrevMatch,
|
"Select Previous Match",
|
||||||
focus_handle.clone(),
|
&SelectPrevMatch,
|
||||||
|
focus_handle.clone(),
|
||||||
|
))
|
||||||
|
.child(render_nav_button(
|
||||||
|
ui::IconName::ChevronRight,
|
||||||
|
self.active_match_index.is_some(),
|
||||||
|
"Select Next Match",
|
||||||
|
&SelectNextMatch,
|
||||||
|
focus_handle.clone(),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.when(!narrow_mode, |this| {
|
||||||
|
this.child(h_flex().ml_2().min_w(rems_from_px(40.)).child(
|
||||||
|
Label::new(match_text).size(LabelSize::Small).color(
|
||||||
|
if self.active_match_index.is_some() {
|
||||||
|
Color::Default
|
||||||
|
} else {
|
||||||
|
Color::Disabled
|
||||||
|
},
|
||||||
|
),
|
||||||
))
|
))
|
||||||
.child(render_nav_button(
|
})
|
||||||
ui::IconName::ChevronRight,
|
})
|
||||||
self.active_match_index.is_some(),
|
.when(supported_options.find_in_results, |el| {
|
||||||
"Select Next Match",
|
el.child(
|
||||||
&SelectNextMatch,
|
IconButton::new(SharedString::from("Close"), IconName::Close)
|
||||||
focus_handle.clone(),
|
.shape(IconButtonShape::Square)
|
||||||
)),
|
.tooltip(move |window, cx| {
|
||||||
)
|
Tooltip::for_action("Close Search Bar", &Dismiss, window, cx)
|
||||||
.when(!narrow_mode, |this| {
|
})
|
||||||
this.child(h_flex().ml_2().min_w(rems_from_px(40.)).child(
|
.on_click(cx.listener(|this, _: &ClickEvent, window, cx| {
|
||||||
Label::new(match_text).size(LabelSize::Small).color(
|
this.dismiss(&Dismiss, window, cx)
|
||||||
if self.active_match_index.is_some() {
|
})),
|
||||||
Color::Default
|
)
|
||||||
} else {
|
|
||||||
Color::Disabled
|
|
||||||
},
|
|
||||||
),
|
|
||||||
))
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -447,49 +472,43 @@ impl Render for BufferSearchBar {
|
||||||
.on_action(cx.listener(Self::dismiss))
|
.on_action(cx.listener(Self::dismiss))
|
||||||
.on_action(cx.listener(Self::select_next_match))
|
.on_action(cx.listener(Self::select_next_match))
|
||||||
.on_action(cx.listener(Self::select_prev_match))
|
.on_action(cx.listener(Self::select_prev_match))
|
||||||
.when(self.supported_options().replacement, |this| {
|
.when(self.supported_options(cx).replacement, |this| {
|
||||||
this.on_action(cx.listener(Self::toggle_replace))
|
this.on_action(cx.listener(Self::toggle_replace))
|
||||||
.when(in_replace, |this| {
|
.when(in_replace, |this| {
|
||||||
this.on_action(cx.listener(Self::replace_next))
|
this.on_action(cx.listener(Self::replace_next))
|
||||||
.on_action(cx.listener(Self::replace_all))
|
.on_action(cx.listener(Self::replace_all))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.when(self.supported_options().case, |this| {
|
.when(self.supported_options(cx).case, |this| {
|
||||||
this.on_action(cx.listener(Self::toggle_case_sensitive))
|
this.on_action(cx.listener(Self::toggle_case_sensitive))
|
||||||
})
|
})
|
||||||
.when(self.supported_options().word, |this| {
|
.when(self.supported_options(cx).word, |this| {
|
||||||
this.on_action(cx.listener(Self::toggle_whole_word))
|
this.on_action(cx.listener(Self::toggle_whole_word))
|
||||||
})
|
})
|
||||||
.when(self.supported_options().regex, |this| {
|
.when(self.supported_options(cx).regex, |this| {
|
||||||
this.on_action(cx.listener(Self::toggle_regex))
|
this.on_action(cx.listener(Self::toggle_regex))
|
||||||
})
|
})
|
||||||
.when(self.supported_options().selection, |this| {
|
.when(self.supported_options(cx).selection, |this| {
|
||||||
this.on_action(cx.listener(Self::toggle_selection))
|
this.on_action(cx.listener(Self::toggle_selection))
|
||||||
})
|
})
|
||||||
.child(
|
.child(h_flex().relative().child(search_line.w_full()).when(
|
||||||
h_flex()
|
!narrow_mode && !supported_options.find_in_results,
|
||||||
.relative()
|
|div| {
|
||||||
.child(search_line.w_full())
|
div.child(
|
||||||
.when(!narrow_mode, |div| {
|
h_flex().absolute().right_0().child(
|
||||||
div.child(
|
IconButton::new(SharedString::from("Close"), IconName::Close)
|
||||||
h_flex().absolute().right_0().child(
|
.shape(IconButtonShape::Square)
|
||||||
IconButton::new(SharedString::from("Close"), IconName::Close)
|
.tooltip(move |window, cx| {
|
||||||
.shape(IconButtonShape::Square)
|
Tooltip::for_action("Close Search Bar", &Dismiss, window, cx)
|
||||||
.tooltip(move |window, cx| {
|
})
|
||||||
Tooltip::for_action(
|
.on_click(cx.listener(|this, _: &ClickEvent, window, cx| {
|
||||||
"Close Search Bar",
|
this.dismiss(&Dismiss, window, cx)
|
||||||
&Dismiss,
|
})),
|
||||||
window,
|
),
|
||||||
cx,
|
)
|
||||||
)
|
.w_full()
|
||||||
})
|
},
|
||||||
.on_click(cx.listener(|this, _: &ClickEvent, window, cx| {
|
))
|
||||||
this.dismiss(&Dismiss, window, cx)
|
|
||||||
})),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.children(replace_line)
|
.children(replace_line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -531,10 +550,15 @@ impl ToolbarItemView for BufferSearchBar {
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let is_project_search = searchable_item_handle.supported_options(cx).find_in_results;
|
||||||
self.active_searchable_item = Some(searchable_item_handle);
|
self.active_searchable_item = Some(searchable_item_handle);
|
||||||
drop(self.update_matches(true, window, cx));
|
drop(self.update_matches(true, window, cx));
|
||||||
if !self.dismissed {
|
if !self.dismissed {
|
||||||
return ToolbarItemLocation::Secondary;
|
if is_project_search {
|
||||||
|
self.dismiss(&Default::default(), window, cx);
|
||||||
|
} else {
|
||||||
|
return ToolbarItemLocation::Secondary;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToolbarItemLocation::Hidden
|
ToolbarItemLocation::Hidden
|
||||||
|
@ -549,40 +573,56 @@ impl BufferSearchBar {
|
||||||
}));
|
}));
|
||||||
registrar.register_handler(ForDeployed(
|
registrar.register_handler(ForDeployed(
|
||||||
|this, action: &ToggleCaseSensitive, window, cx| {
|
|this, action: &ToggleCaseSensitive, window, cx| {
|
||||||
if this.supported_options().case {
|
if this.supported_options(cx).case {
|
||||||
this.toggle_case_sensitive(action, window, cx);
|
this.toggle_case_sensitive(action, window, cx);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
registrar.register_handler(ForDeployed(|this, action: &ToggleWholeWord, window, cx| {
|
registrar.register_handler(ForDeployed(|this, action: &ToggleWholeWord, window, cx| {
|
||||||
if this.supported_options().word {
|
if this.supported_options(cx).word {
|
||||||
this.toggle_whole_word(action, window, cx);
|
this.toggle_whole_word(action, window, cx);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
registrar.register_handler(ForDeployed(|this, action: &ToggleRegex, window, cx| {
|
registrar.register_handler(ForDeployed(|this, action: &ToggleRegex, window, cx| {
|
||||||
if this.supported_options().regex {
|
if this.supported_options(cx).regex {
|
||||||
this.toggle_regex(action, window, cx);
|
this.toggle_regex(action, window, cx);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
registrar.register_handler(ForDeployed(|this, action: &ToggleSelection, window, cx| {
|
registrar.register_handler(ForDeployed(|this, action: &ToggleSelection, window, cx| {
|
||||||
if this.supported_options().selection {
|
if this.supported_options(cx).selection {
|
||||||
this.toggle_selection(action, window, cx);
|
this.toggle_selection(action, window, cx);
|
||||||
|
} else {
|
||||||
|
cx.propagate();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
registrar.register_handler(ForDeployed(|this, action: &ToggleReplace, window, cx| {
|
registrar.register_handler(ForDeployed(|this, action: &ToggleReplace, window, cx| {
|
||||||
if this.supported_options().replacement {
|
if this.supported_options(cx).replacement {
|
||||||
this.toggle_replace(action, window, cx);
|
this.toggle_replace(action, window, cx);
|
||||||
|
} else {
|
||||||
|
cx.propagate();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
registrar.register_handler(WithResults(|this, action: &SelectNextMatch, window, cx| {
|
registrar.register_handler(WithResults(|this, action: &SelectNextMatch, window, cx| {
|
||||||
this.select_next_match(action, window, cx);
|
if this.supported_options(cx).find_in_results {
|
||||||
|
cx.propagate();
|
||||||
|
} else {
|
||||||
|
this.select_next_match(action, window, cx);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
registrar.register_handler(WithResults(|this, action: &SelectPrevMatch, window, cx| {
|
registrar.register_handler(WithResults(|this, action: &SelectPrevMatch, window, cx| {
|
||||||
this.select_prev_match(action, window, cx);
|
if this.supported_options(cx).find_in_results {
|
||||||
|
cx.propagate();
|
||||||
|
} else {
|
||||||
|
this.select_prev_match(action, window, cx);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
registrar.register_handler(WithResults(
|
registrar.register_handler(WithResults(
|
||||||
|this, action: &SelectAllMatches, window, cx| {
|
|this, action: &SelectAllMatches, window, cx| {
|
||||||
this.select_all_matches(action, window, cx);
|
if this.supported_options(cx).find_in_results {
|
||||||
|
cx.propagate();
|
||||||
|
} else {
|
||||||
|
this.select_all_matches(action, window, cx);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
registrar.register_handler(ForDeployed(
|
registrar.register_handler(ForDeployed(
|
||||||
|
@ -599,6 +639,13 @@ impl BufferSearchBar {
|
||||||
registrar.register_handler(ForDeployed(|this, deploy, window, cx| {
|
registrar.register_handler(ForDeployed(|this, deploy, window, cx| {
|
||||||
this.deploy(deploy, window, cx);
|
this.deploy(deploy, window, cx);
|
||||||
}));
|
}));
|
||||||
|
registrar.register_handler(ForDismissed(|this, _: &DeployReplace, window, cx| {
|
||||||
|
if this.supported_options(cx).find_in_results {
|
||||||
|
cx.propagate();
|
||||||
|
} else {
|
||||||
|
this.deploy(&Deploy::replace(), window, cx);
|
||||||
|
}
|
||||||
|
}));
|
||||||
registrar.register_handler(ForDismissed(|this, deploy, window, cx| {
|
registrar.register_handler(ForDismissed(|this, deploy, window, cx| {
|
||||||
this.deploy(deploy, window, cx);
|
this.deploy(deploy, window, cx);
|
||||||
}))
|
}))
|
||||||
|
@ -735,10 +782,10 @@ impl BufferSearchBar {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn supported_options(&self) -> workspace::searchable::SearchOptions {
|
fn supported_options(&self, cx: &mut Context<Self>) -> workspace::searchable::SearchOptions {
|
||||||
self.active_searchable_item
|
self.active_searchable_item
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.map(SearchableItemHandle::supported_options)
|
.map(|item| item.supported_options(cx))
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -421,6 +421,9 @@ impl Item for ProjectSearchView {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn as_searchable(&self, _: &Entity<Self>) -> Option<Box<dyn SearchableItemHandle>> {
|
||||||
|
Some(Box::new(self.results_editor.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
fn deactivated(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
fn deactivated(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
self.results_editor
|
self.results_editor
|
||||||
|
@ -736,6 +739,7 @@ impl ProjectSearchView {
|
||||||
let mut editor =
|
let mut editor =
|
||||||
Editor::for_multibuffer(excerpts, Some(project.clone()), true, window, cx);
|
Editor::for_multibuffer(excerpts, Some(project.clone()), true, window, cx);
|
||||||
editor.set_searchable(false);
|
editor.set_searchable(false);
|
||||||
|
editor.set_in_project_search(true);
|
||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
subscriptions.push(cx.observe(&results_editor, |_, _, cx| cx.emit(ViewEvent::UpdateTab)));
|
subscriptions.push(cx.observe(&results_editor, |_, _, cx| cx.emit(ViewEvent::UpdateTab)));
|
||||||
|
|
|
@ -1479,13 +1479,14 @@ impl SerializableItem for TerminalView {
|
||||||
impl SearchableItem for TerminalView {
|
impl SearchableItem for TerminalView {
|
||||||
type Match = RangeInclusive<Point>;
|
type Match = RangeInclusive<Point>;
|
||||||
|
|
||||||
fn supported_options() -> SearchOptions {
|
fn supported_options(&self) -> SearchOptions {
|
||||||
SearchOptions {
|
SearchOptions {
|
||||||
case: false,
|
case: false,
|
||||||
word: false,
|
word: false,
|
||||||
regex: true,
|
regex: true,
|
||||||
replacement: false,
|
replacement: false,
|
||||||
selection: false,
|
selection: false,
|
||||||
|
find_in_results: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,18 +42,20 @@ pub struct SearchOptions {
|
||||||
/// Specifies whether the supports search & replace.
|
/// Specifies whether the supports search & replace.
|
||||||
pub replacement: bool,
|
pub replacement: bool,
|
||||||
pub selection: bool,
|
pub selection: bool,
|
||||||
|
pub find_in_results: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
|
pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
|
||||||
type Match: Any + Sync + Send + Clone;
|
type Match: Any + Sync + Send + Clone;
|
||||||
|
|
||||||
fn supported_options() -> SearchOptions {
|
fn supported_options(&self) -> SearchOptions {
|
||||||
SearchOptions {
|
SearchOptions {
|
||||||
case: true,
|
case: true,
|
||||||
word: true,
|
word: true,
|
||||||
regex: true,
|
regex: true,
|
||||||
replacement: true,
|
replacement: true,
|
||||||
selection: true,
|
selection: true,
|
||||||
|
find_in_results: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +68,7 @@ pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_filtered_search_ranges(&mut self) -> bool {
|
fn has_filtered_search_ranges(&mut self) -> bool {
|
||||||
Self::supported_options().selection
|
self.supported_options().selection
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_filtered_search_ranges(
|
fn toggle_filtered_search_ranges(
|
||||||
|
@ -157,7 +159,7 @@ pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
|
||||||
pub trait SearchableItemHandle: ItemHandle {
|
pub trait SearchableItemHandle: ItemHandle {
|
||||||
fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle>;
|
fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle>;
|
||||||
fn boxed_clone(&self) -> Box<dyn SearchableItemHandle>;
|
fn boxed_clone(&self) -> Box<dyn SearchableItemHandle>;
|
||||||
fn supported_options(&self) -> SearchOptions;
|
fn supported_options(&self, cx: &App) -> SearchOptions;
|
||||||
fn subscribe_to_search_events(
|
fn subscribe_to_search_events(
|
||||||
&self,
|
&self,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -224,8 +226,8 @@ impl<T: SearchableItem> SearchableItemHandle for Entity<T> {
|
||||||
Box::new(self.clone())
|
Box::new(self.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn supported_options(&self) -> SearchOptions {
|
fn supported_options(&self, cx: &App) -> SearchOptions {
|
||||||
T::supported_options()
|
self.read(cx).supported_options()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscribe_to_search_events(
|
fn subscribe_to_search_events(
|
||||||
|
|
|
@ -82,7 +82,7 @@ impl Toolbar {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn secondary_items(&self) -> impl Iterator<Item = &dyn ToolbarItemViewHandle> {
|
fn secondary_items(&self) -> impl Iterator<Item = &dyn ToolbarItemViewHandle> {
|
||||||
self.items.iter().filter_map(|(item, location)| {
|
self.items.iter().rev().filter_map(|(item, location)| {
|
||||||
if *location == ToolbarItemLocation::Secondary {
|
if *location == ToolbarItemLocation::Secondary {
|
||||||
Some(item.as_ref())
|
Some(item.as_ref())
|
||||||
} else {
|
} else {
|
||||||
|
@ -98,7 +98,7 @@ impl Render for Toolbar {
|
||||||
return div();
|
return div();
|
||||||
}
|
}
|
||||||
|
|
||||||
let secondary_item = self.secondary_items().next().map(|item| item.to_any());
|
let secondary_items = self.secondary_items().map(|item| item.to_any());
|
||||||
|
|
||||||
let has_left_items = self.left_items().count() > 0;
|
let has_left_items = self.left_items().count() > 0;
|
||||||
let has_right_items = self.right_items().count() > 0;
|
let has_right_items = self.right_items().count() > 0;
|
||||||
|
@ -145,7 +145,7 @@ impl Render for Toolbar {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.children(secondary_item)
|
.children(secondary_items)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue