editor: Respect search case sensitivity when selecting occurrences (#42121)
Update how the editor's `select_*` methods work in order to respect the `search.case_sensitive` setting, or to be overriden by the `BufferSearchBar` search options. - Update both the `SearchableItem` and `SearchableItemHandle` traits with a new `set_search_is_case_sensitive` method that allows callers to set the case sensitivity of the search - Update the `BufferSearchBar` to leverage `SearchableItemHandle.set_search_is_case_sensitive` in order to sync its case sensitivity options with the searchable item - Update the implementation of the `SearchableItem` trait for `Editor` so as to store the argument provided to the `set_search_is_case_sensitive` method - Update the way search queries are built by `Editor` so as to rely on `SearchableItem.set_search_is_case_sensitive` argument, if not `None`, or default to the editor's `search.case_sensitive` settings Closes #41070 Release Notes: - Improved the "Select Next Occurrence", "Select Previous Occurrence" and "Select All Occurrences" actions in order to respect the case sensitivity search settings --------- Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
@@ -616,9 +616,13 @@
|
||||
"search": {
|
||||
// Whether to show the project search button in the status bar.
|
||||
"button": true,
|
||||
// Whether to only match on whole words.
|
||||
"whole_word": false,
|
||||
// Whether to match case sensitively.
|
||||
"case_sensitive": false,
|
||||
// Whether to include gitignored files in search results.
|
||||
"include_ignored": false,
|
||||
// Whether to interpret the search query as a regular expression.
|
||||
"regex": false,
|
||||
// Whether to center the cursor on each search match when navigating.
|
||||
"center_on_match": false
|
||||
|
||||
@@ -74,7 +74,7 @@ use ::git::{
|
||||
blame::{BlameEntry, ParsedCommitMessage},
|
||||
status::FileStatus,
|
||||
};
|
||||
use aho_corasick::AhoCorasick;
|
||||
use aho_corasick::{AhoCorasick, AhoCorasickBuilder, BuildError};
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
use blink_manager::BlinkManager;
|
||||
use buffer_diff::DiffHunkStatus;
|
||||
@@ -1190,6 +1190,7 @@ pub struct Editor {
|
||||
refresh_colors_task: Task<()>,
|
||||
inlay_hints: Option<LspInlayHintData>,
|
||||
folding_newlines: Task<()>,
|
||||
select_next_is_case_sensitive: Option<bool>,
|
||||
pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
|
||||
}
|
||||
|
||||
@@ -2333,6 +2334,7 @@ impl Editor {
|
||||
selection_drag_state: SelectionDragState::None,
|
||||
folding_newlines: Task::ready(()),
|
||||
lookup_key: None,
|
||||
select_next_is_case_sensitive: None,
|
||||
};
|
||||
|
||||
if is_minimap {
|
||||
@@ -14645,7 +14647,7 @@ impl Editor {
|
||||
.collect::<String>();
|
||||
let is_empty = query.is_empty();
|
||||
let select_state = SelectNextState {
|
||||
query: AhoCorasick::new(&[query])?,
|
||||
query: self.build_query(&[query], cx)?,
|
||||
wordwise: true,
|
||||
done: is_empty,
|
||||
};
|
||||
@@ -14655,7 +14657,7 @@ impl Editor {
|
||||
}
|
||||
} else if let Some(selected_text) = selected_text {
|
||||
self.select_next_state = Some(SelectNextState {
|
||||
query: AhoCorasick::new(&[selected_text])?,
|
||||
query: self.build_query(&[selected_text], cx)?,
|
||||
wordwise: false,
|
||||
done: false,
|
||||
});
|
||||
@@ -14863,7 +14865,7 @@ impl Editor {
|
||||
.collect::<String>();
|
||||
let is_empty = query.is_empty();
|
||||
let select_state = SelectNextState {
|
||||
query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
|
||||
query: self.build_query(&[query.chars().rev().collect::<String>()], cx)?,
|
||||
wordwise: true,
|
||||
done: is_empty,
|
||||
};
|
||||
@@ -14873,7 +14875,8 @@ impl Editor {
|
||||
}
|
||||
} else if let Some(selected_text) = selected_text {
|
||||
self.select_prev_state = Some(SelectNextState {
|
||||
query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
|
||||
query: self
|
||||
.build_query(&[selected_text.chars().rev().collect::<String>()], cx)?,
|
||||
wordwise: false,
|
||||
done: false,
|
||||
});
|
||||
@@ -14883,6 +14886,25 @@ impl Editor {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Builds an `AhoCorasick` automaton from the provided patterns, while
|
||||
/// setting the case sensitivity based on the global
|
||||
/// `SelectNextCaseSensitive` setting, if set, otherwise based on the
|
||||
/// editor's settings.
|
||||
fn build_query<I, P>(&self, patterns: I, cx: &Context<Self>) -> Result<AhoCorasick, BuildError>
|
||||
where
|
||||
I: IntoIterator<Item = P>,
|
||||
P: AsRef<[u8]>,
|
||||
{
|
||||
let case_sensitive = self.select_next_is_case_sensitive.map_or_else(
|
||||
|| EditorSettings::get_global(cx).search.case_sensitive,
|
||||
|value| value,
|
||||
);
|
||||
|
||||
let mut builder = AhoCorasickBuilder::new();
|
||||
builder.ascii_case_insensitive(!case_sensitive);
|
||||
builder.build(patterns)
|
||||
}
|
||||
|
||||
pub fn find_next_match(
|
||||
&mut self,
|
||||
_: &FindNextMatch,
|
||||
|
||||
@@ -162,10 +162,15 @@ pub struct DragAndDropSelection {
|
||||
pub struct SearchSettings {
|
||||
/// Whether to show the project search button in the status bar.
|
||||
pub button: bool,
|
||||
/// Whether to only match on whole words.
|
||||
pub whole_word: bool,
|
||||
/// Whether to match case sensitively.
|
||||
pub case_sensitive: bool,
|
||||
/// Whether to include gitignored files in search results.
|
||||
pub include_ignored: bool,
|
||||
/// Whether to interpret the search query as a regular expression.
|
||||
pub regex: bool,
|
||||
/// Whether to center the cursor on each search match when navigating.
|
||||
pub center_on_match: bool,
|
||||
}
|
||||
|
||||
|
||||
@@ -44,8 +44,8 @@ use project::{
|
||||
};
|
||||
use serde_json::{self, json};
|
||||
use settings::{
|
||||
AllLanguageSettingsContent, IndentGuideBackgroundColoring, IndentGuideColoring,
|
||||
ProjectSettingsContent,
|
||||
AllLanguageSettingsContent, EditorSettingsContent, IndentGuideBackgroundColoring,
|
||||
IndentGuideColoring, ProjectSettingsContent, SearchSettingsContent,
|
||||
};
|
||||
use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
|
||||
use std::{
|
||||
@@ -8314,8 +8314,15 @@ async fn test_add_selection_above_below_multi_cursor_existing_state(cx: &mut Tes
|
||||
#[gpui::test]
|
||||
async fn test_select_next(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx).await;
|
||||
|
||||
// Enable case sensitive search.
|
||||
update_test_editor_settings(&mut cx, |settings| {
|
||||
let mut search_settings = SearchSettingsContent::default();
|
||||
search_settings.case_sensitive = Some(true);
|
||||
settings.search = Some(search_settings);
|
||||
});
|
||||
|
||||
cx.set_state("abc\nˇabc abc\ndefabc\nabc");
|
||||
|
||||
cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
|
||||
@@ -8346,14 +8353,41 @@ async fn test_select_next(cx: &mut TestAppContext) {
|
||||
cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
|
||||
.unwrap();
|
||||
cx.assert_editor_state("abc\n«ˇabc» «ˇabc»\ndefabc\nabc");
|
||||
|
||||
// Test case sensitivity
|
||||
cx.set_state("«ˇfoo»\nFOO\nFoo\nfoo");
|
||||
cx.update_editor(|e, window, cx| {
|
||||
e.select_next(&SelectNext::default(), window, cx).unwrap();
|
||||
});
|
||||
cx.assert_editor_state("«ˇfoo»\nFOO\nFoo\n«ˇfoo»");
|
||||
|
||||
// Disable case sensitive search.
|
||||
update_test_editor_settings(&mut cx, |settings| {
|
||||
let mut search_settings = SearchSettingsContent::default();
|
||||
search_settings.case_sensitive = Some(false);
|
||||
settings.search = Some(search_settings);
|
||||
});
|
||||
|
||||
cx.set_state("«ˇfoo»\nFOO\nFoo");
|
||||
cx.update_editor(|e, window, cx| {
|
||||
e.select_next(&SelectNext::default(), window, cx).unwrap();
|
||||
e.select_next(&SelectNext::default(), window, cx).unwrap();
|
||||
});
|
||||
cx.assert_editor_state("«ˇfoo»\n«ˇFOO»\n«ˇFoo»");
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_select_all_matches(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx).await;
|
||||
|
||||
// Enable case sensitive search.
|
||||
update_test_editor_settings(&mut cx, |settings| {
|
||||
let mut search_settings = SearchSettingsContent::default();
|
||||
search_settings.case_sensitive = Some(true);
|
||||
settings.search = Some(search_settings);
|
||||
});
|
||||
|
||||
// Test caret-only selections
|
||||
cx.set_state("abc\nˇabc abc\ndefabc\nabc");
|
||||
cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
|
||||
@@ -8398,6 +8432,26 @@ async fn test_select_all_matches(cx: &mut TestAppContext) {
|
||||
e.set_clip_at_line_ends(false, cx);
|
||||
});
|
||||
cx.assert_editor_state("«abcˇ»");
|
||||
|
||||
// Test case sensitivity
|
||||
cx.set_state("fˇoo\nFOO\nFoo");
|
||||
cx.update_editor(|e, window, cx| {
|
||||
e.select_all_matches(&SelectAllMatches, window, cx).unwrap();
|
||||
});
|
||||
cx.assert_editor_state("«fooˇ»\nFOO\nFoo");
|
||||
|
||||
// Disable case sensitive search.
|
||||
update_test_editor_settings(&mut cx, |settings| {
|
||||
let mut search_settings = SearchSettingsContent::default();
|
||||
search_settings.case_sensitive = Some(false);
|
||||
settings.search = Some(search_settings);
|
||||
});
|
||||
|
||||
cx.set_state("fˇoo\nFOO\nFoo");
|
||||
cx.update_editor(|e, window, cx| {
|
||||
e.select_all_matches(&SelectAllMatches, window, cx).unwrap();
|
||||
});
|
||||
cx.assert_editor_state("«fooˇ»\n«FOOˇ»\n«Fooˇ»");
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
@@ -8769,8 +8823,15 @@ let foo = «2ˇ»;"#,
|
||||
#[gpui::test]
|
||||
async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx).await;
|
||||
|
||||
// Enable case sensitive search.
|
||||
update_test_editor_settings(&mut cx, |settings| {
|
||||
let mut search_settings = SearchSettingsContent::default();
|
||||
search_settings.case_sensitive = Some(true);
|
||||
settings.search = Some(search_settings);
|
||||
});
|
||||
|
||||
cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
|
||||
|
||||
cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
|
||||
@@ -8795,6 +8856,32 @@ async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
|
||||
cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
|
||||
.unwrap();
|
||||
cx.assert_editor_state("«ˇabc»\n«ˇabc» «ˇabc»\ndef«ˇabc»\n«ˇabc»");
|
||||
|
||||
// Test case sensitivity
|
||||
cx.set_state("foo\nFOO\nFoo\n«ˇfoo»");
|
||||
cx.update_editor(|e, window, cx| {
|
||||
e.select_previous(&SelectPrevious::default(), window, cx)
|
||||
.unwrap();
|
||||
e.select_previous(&SelectPrevious::default(), window, cx)
|
||||
.unwrap();
|
||||
});
|
||||
cx.assert_editor_state("«ˇfoo»\nFOO\nFoo\n«ˇfoo»");
|
||||
|
||||
// Disable case sensitive search.
|
||||
update_test_editor_settings(&mut cx, |settings| {
|
||||
let mut search_settings = SearchSettingsContent::default();
|
||||
search_settings.case_sensitive = Some(false);
|
||||
settings.search = Some(search_settings);
|
||||
});
|
||||
|
||||
cx.set_state("foo\nFOO\n«ˇFoo»");
|
||||
cx.update_editor(|e, window, cx| {
|
||||
e.select_previous(&SelectPrevious::default(), window, cx)
|
||||
.unwrap();
|
||||
e.select_previous(&SelectPrevious::default(), window, cx)
|
||||
.unwrap();
|
||||
});
|
||||
cx.assert_editor_state("«ˇfoo»\n«ˇFOO»\n«ˇFoo»");
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
@@ -25717,6 +25804,17 @@ pub(crate) fn update_test_project_settings(
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn update_test_editor_settings(
|
||||
cx: &mut TestAppContext,
|
||||
f: impl Fn(&mut EditorSettingsContent),
|
||||
) {
|
||||
cx.update(|cx| {
|
||||
SettingsStore::update_global(cx, |store, cx| {
|
||||
store.update_user_settings(cx, |settings| f(&mut settings.editor));
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
|
||||
cx.update(|cx| {
|
||||
assets::Assets.load_test_fonts(cx);
|
||||
|
||||
@@ -1796,6 +1796,14 @@ impl SearchableItem for Editor {
|
||||
fn search_bar_visibility_changed(&mut self, _: bool, _: &mut Window, _: &mut Context<Self>) {
|
||||
self.expect_bounds_change = self.last_bounds;
|
||||
}
|
||||
|
||||
fn set_search_is_case_sensitive(
|
||||
&mut self,
|
||||
case_sensitive: Option<bool>,
|
||||
_cx: &mut Context<Self>,
|
||||
) {
|
||||
self.select_next_is_case_sensitive = case_sensitive;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn active_match_index(
|
||||
|
||||
@@ -127,12 +127,6 @@ pub struct BufferSearchBar {
|
||||
regex_language: Option<Arc<Language>>,
|
||||
}
|
||||
|
||||
impl BufferSearchBar {
|
||||
pub fn query_editor_focused(&self) -> bool {
|
||||
self.query_editor_focused
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<Event> for BufferSearchBar {}
|
||||
impl EventEmitter<workspace::ToolbarItemEvent> for BufferSearchBar {}
|
||||
impl Render for BufferSearchBar {
|
||||
@@ -521,6 +515,10 @@ impl ToolbarItemView for BufferSearchBar {
|
||||
}
|
||||
|
||||
impl BufferSearchBar {
|
||||
pub fn query_editor_focused(&self) -> bool {
|
||||
self.query_editor_focused
|
||||
}
|
||||
|
||||
pub fn register(registrar: &mut impl SearchActionsRegistrar) {
|
||||
registrar.register_handler(ForDeployed(|this, _: &FocusSearch, window, cx| {
|
||||
this.query_editor.focus_handle(cx).focus(window);
|
||||
@@ -696,6 +694,8 @@ impl BufferSearchBar {
|
||||
pub fn dismiss(&mut self, _: &Dismiss, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.dismissed = true;
|
||||
self.query_error = None;
|
||||
self.sync_select_next_case_sensitivity(cx);
|
||||
|
||||
for searchable_item in self.searchable_items_with_matches.keys() {
|
||||
if let Some(searchable_item) =
|
||||
WeakSearchableItemHandle::upgrade(searchable_item.as_ref(), cx)
|
||||
@@ -711,6 +711,7 @@ impl BufferSearchBar {
|
||||
let handle = active_editor.item_focus_handle(cx);
|
||||
self.focus(&handle, window);
|
||||
}
|
||||
|
||||
cx.emit(Event::UpdateLocation);
|
||||
cx.emit(ToolbarItemEvent::ChangeLocation(
|
||||
ToolbarItemLocation::Hidden,
|
||||
@@ -730,6 +731,7 @@ impl BufferSearchBar {
|
||||
}
|
||||
self.search_suggested(window, cx);
|
||||
self.smartcase(window, cx);
|
||||
self.sync_select_next_case_sensitivity(cx);
|
||||
self.replace_enabled = deploy.replace_enabled;
|
||||
self.selection_search_enabled = if deploy.selection_search_enabled {
|
||||
Some(FilteredSearchRange::Default)
|
||||
@@ -919,6 +921,7 @@ impl BufferSearchBar {
|
||||
self.default_options = self.search_options;
|
||||
drop(self.update_matches(false, false, window, cx));
|
||||
self.adjust_query_regex_language(cx);
|
||||
self.sync_select_next_case_sensitivity(cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -953,6 +956,7 @@ impl BufferSearchBar {
|
||||
pub fn set_search_options(&mut self, search_options: SearchOptions, cx: &mut Context<Self>) {
|
||||
self.search_options = search_options;
|
||||
self.adjust_query_regex_language(cx);
|
||||
self.sync_select_next_case_sensitivity(cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -1507,6 +1511,7 @@ impl BufferSearchBar {
|
||||
.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| {
|
||||
@@ -1519,6 +1524,24 @@ impl BufferSearchBar {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the searchable item's case sensitivity option to match the
|
||||
/// search bar's current case sensitivity setting. This ensures that
|
||||
/// editor's `select_next`/ `select_previous` operations respect the buffer
|
||||
/// search bar's search options.
|
||||
///
|
||||
/// Clears the case sensitivity when the search bar is dismissed so that
|
||||
/// only the editor's settings are respected.
|
||||
fn sync_select_next_case_sensitivity(&self, cx: &mut Context<Self>) {
|
||||
let case_sensitive = match self.dismissed {
|
||||
true => None,
|
||||
false => Some(self.search_options.contains(SearchOptions::CASE_SENSITIVE)),
|
||||
};
|
||||
|
||||
if let Some(active_searchable_item) = self.active_searchable_item.as_ref() {
|
||||
active_searchable_item.set_search_is_case_sensitive(case_sensitive, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -1528,7 +1551,7 @@ mod tests {
|
||||
use super::*;
|
||||
use editor::{
|
||||
DisplayPoint, Editor, MultiBuffer, SearchSettings, SelectionEffects,
|
||||
display_map::DisplayRow,
|
||||
display_map::DisplayRow, test::editor_test_context::EditorTestContext,
|
||||
};
|
||||
use gpui::{Hsla, TestAppContext, UpdateGlobal, VisualTestContext};
|
||||
use language::{Buffer, Point};
|
||||
@@ -2963,6 +2986,61 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_select_occurrence_case_sensitivity(cx: &mut TestAppContext) {
|
||||
let (editor, search_bar, cx) = init_test(cx);
|
||||
let mut editor_cx = EditorTestContext::for_editor_in(editor, cx).await;
|
||||
|
||||
// Start with case sensitive search settings.
|
||||
let mut search_settings = SearchSettings::default();
|
||||
search_settings.case_sensitive = true;
|
||||
update_search_settings(search_settings, cx);
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
let mut search_options = search_bar.search_options;
|
||||
search_options.insert(SearchOptions::CASE_SENSITIVE);
|
||||
search_bar.set_search_options(search_options, cx);
|
||||
});
|
||||
|
||||
editor_cx.set_state("«ˇfoo»\nFOO\nFoo\nfoo");
|
||||
editor_cx.update_editor(|e, window, cx| {
|
||||
e.select_next(&Default::default(), window, cx).unwrap();
|
||||
});
|
||||
editor_cx.assert_editor_state("«ˇfoo»\nFOO\nFoo\n«ˇfoo»");
|
||||
|
||||
// Update the search bar's case sensitivite toggle, so we can later
|
||||
// confirm that `select_next` will now be case-insensitive.
|
||||
editor_cx.set_state("«ˇfoo»\nFOO\nFoo\nfoo");
|
||||
search_bar.update_in(cx, |search_bar, window, cx| {
|
||||
search_bar.toggle_case_sensitive(&Default::default(), window, cx);
|
||||
});
|
||||
editor_cx.update_editor(|e, window, cx| {
|
||||
e.select_next(&Default::default(), window, cx).unwrap();
|
||||
});
|
||||
editor_cx.assert_editor_state("«ˇfoo»\n«ˇFOO»\nFoo\nfoo");
|
||||
|
||||
// Confirm that, after dismissing the search bar, only the editor's
|
||||
// search settings actually affect the behavior of `select_next`.
|
||||
search_bar.update_in(cx, |search_bar, window, cx| {
|
||||
search_bar.dismiss(&Default::default(), window, cx);
|
||||
});
|
||||
editor_cx.set_state("«ˇfoo»\nFOO\nFoo\nfoo");
|
||||
editor_cx.update_editor(|e, window, cx| {
|
||||
e.select_next(&Default::default(), window, cx).unwrap();
|
||||
});
|
||||
editor_cx.assert_editor_state("«ˇfoo»\nFOO\nFoo\n«ˇfoo»");
|
||||
|
||||
// Update the editor's search settings, disabling case sensitivity, to
|
||||
// check that the value is respected.
|
||||
let mut search_settings = SearchSettings::default();
|
||||
search_settings.case_sensitive = false;
|
||||
update_search_settings(search_settings, cx);
|
||||
editor_cx.set_state("«ˇfoo»\nFOO\nFoo\nfoo");
|
||||
editor_cx.update_editor(|e, window, cx| {
|
||||
e.select_next(&Default::default(), window, cx).unwrap();
|
||||
});
|
||||
editor_cx.assert_editor_state("«ˇfoo»\n«ˇFOO»\nFoo\nfoo");
|
||||
}
|
||||
|
||||
fn update_search_settings(search_settings: SearchSettings, cx: &mut TestAppContext) {
|
||||
cx.update(|cx| {
|
||||
SettingsStore::update_global(cx, |store, cx| {
|
||||
|
||||
@@ -759,9 +759,13 @@ pub enum SnippetSortOrder {
|
||||
pub struct SearchSettingsContent {
|
||||
/// Whether to show the project search button in the status bar.
|
||||
pub button: Option<bool>,
|
||||
/// Whether to only match on whole words.
|
||||
pub whole_word: Option<bool>,
|
||||
/// Whether to match case sensitively.
|
||||
pub case_sensitive: Option<bool>,
|
||||
/// Whether to include gitignored files in search results.
|
||||
pub include_ignored: Option<bool>,
|
||||
/// Whether to interpret the search query as a regular expression.
|
||||
pub regex: Option<bool>,
|
||||
/// Whether to center the cursor on each search match when navigating.
|
||||
pub center_on_match: Option<bool>,
|
||||
|
||||
@@ -166,6 +166,7 @@ pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<usize>;
|
||||
fn set_search_is_case_sensitive(&mut self, _: Option<bool>, _: &mut Context<Self>) {}
|
||||
}
|
||||
|
||||
pub trait SearchableItemHandle: ItemHandle {
|
||||
@@ -234,6 +235,8 @@ pub trait SearchableItemHandle: ItemHandle {
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
);
|
||||
|
||||
fn set_search_is_case_sensitive(&self, is_case_sensitive: Option<bool>, cx: &mut App);
|
||||
}
|
||||
|
||||
impl<T: SearchableItem> SearchableItemHandle for Entity<T> {
|
||||
@@ -390,6 +393,11 @@ impl<T: SearchableItem> SearchableItemHandle for Entity<T> {
|
||||
this.toggle_filtered_search_ranges(enabled, window, cx)
|
||||
});
|
||||
}
|
||||
fn set_search_is_case_sensitive(&self, enabled: Option<bool>, cx: &mut App) {
|
||||
self.update(cx, |this, cx| {
|
||||
this.set_search_is_case_sensitive(enabled, cx)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<dyn SearchableItemHandle>> for AnyView {
|
||||
|
||||
@@ -3184,13 +3184,53 @@ Non-negative `integer` values
|
||||
|
||||
```json [settings]
|
||||
"search": {
|
||||
"button": true,
|
||||
"whole_word": false,
|
||||
"case_sensitive": false,
|
||||
"include_ignored": false,
|
||||
"regex": false
|
||||
"regex": false,
|
||||
"center_on_match": false
|
||||
},
|
||||
```
|
||||
|
||||
### Button
|
||||
|
||||
- Description: Whether to show the project search button in the status bar.
|
||||
- Setting: `button`
|
||||
- Default: `true`
|
||||
|
||||
### Whole Word
|
||||
|
||||
- Description: Whether to only match on whole words.
|
||||
- Setting: `whole_word`
|
||||
- Default: `false`
|
||||
|
||||
### Case Sensitive
|
||||
|
||||
- Description: Whether to match case sensitively. This setting affects both
|
||||
searches and editor actions like "Select Next Occurrence", "Select Previous
|
||||
Occurrence", and "Select All Occurrences".
|
||||
- Setting: `case_sensitive`
|
||||
- Default: `false`
|
||||
|
||||
### Include Ignore
|
||||
|
||||
- Description: Whether to include gitignored files in search results.
|
||||
- Setting: `include_ignored`
|
||||
- Default: `false`
|
||||
|
||||
### Regex
|
||||
|
||||
- Description: Whether to interpret the search query as a regular expression.
|
||||
- Setting: `regex`
|
||||
- Default: `false`
|
||||
|
||||
### Center On Match
|
||||
|
||||
- Description: Whether to center the cursor on each search match when navigating.
|
||||
- Setting: `center_on_match`
|
||||
- Default: `false`
|
||||
|
||||
## Search Wrap
|
||||
|
||||
- Description: If `search_wrap` is disabled, search result do not wrap around the end of the file
|
||||
|
||||
Reference in New Issue
Block a user