From 2d3a34c86438c219ab5fd04c57cc7f847361bad9 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 9 Nov 2023 14:25:20 -0800 Subject: [PATCH] Refine search query suggestions (#3293) This PR fixes some issues in response to feedback from Dan Abramov and Jose Valim. To do: * [x] fix non-word search suggestions * [x] add setting for disabling search suggestions Release Notes: - Fixed an issue where opening a search without text selected would populate the search query with non-word characters adjacent to the cursor. - Added a setting, `seed_search_query_from_cursor`, which controls whether the search query is automatically populated from the buffer when starting a new buffer search or project search. By default, the search query will always be set to the word under the cursor. If you want to only populate the search query when text is selected, you can add the following to your `~/.zed/settings.json`: ```json { "seed_search_query_from_cursor": "selection" } ``` If you don't want the search query to be automatically populated, even when there is text selected, add the following: ```json { "seed_search_query_from_cursor": "never" } ``` --- assets/settings/default.json | 10 +++++ crates/editor/src/editor_settings.rs | 12 ++++- crates/editor/src/items.rs | 44 ++++++++++--------- crates/editor2/src/editor_settings.rs | 10 +++++ crates/editor2/src/items.rs | 44 +++++++++++-------- crates/gpui/src/geometry.rs | 6 +-- crates/gpui2/src/geometry.rs | 10 ++--- crates/gpui2/src/style.rs | 6 +-- .../src/derive_refineable.rs | 33 ++++---------- crates/theme2/src/colors.rs | 12 +++-- crates/theme2/src/user_theme.rs | 5 +-- 11 files changed, 107 insertions(+), 85 deletions(-) diff --git a/assets/settings/default.json b/assets/settings/default.json index 19c73ca0212016bf61b7fcf519d62fee33a4417b..9a6c7587d6266a9a80f0ef36b46e678d878f4bd6 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -102,6 +102,16 @@ "selections": true }, "relative_line_numbers": false, + // When to populate a new search's query based on the text under the cursor. + // This setting can take the following three values: + // + // 1. Always populate the search query with the word under the cursor (default). + // "always" + // 2. Only populate the search query when there is text selected + // "selection" + // 3. Never populate the search query + // "never" + "seed_search_query_from_cursor": "always", // Inlay hint related settings "inlay_hints": { // Global switch to toggle hints on and off, switched off by default. diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index 75f8b800f93757bec3bcbbf01b68226880267d57..b885e065a1a722564314d157a097a97a990ac971 100644 --- a/crates/editor/src/editor_settings.rs +++ b/crates/editor/src/editor_settings.rs @@ -2,7 +2,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use settings::Setting; -#[derive(Deserialize)] +#[derive(Clone, Deserialize)] pub struct EditorSettings { pub cursor_blink: bool, pub hover_popover_enabled: bool, @@ -11,6 +11,15 @@ pub struct EditorSettings { pub use_on_type_format: bool, pub scrollbar: Scrollbar, pub relative_line_numbers: bool, + pub seed_search_query_from_cursor: SeedQuerySetting, +} + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum SeedQuerySetting { + Always, + Selection, + Never, } #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] @@ -38,6 +47,7 @@ pub struct EditorSettingsContent { pub use_on_type_format: Option, pub scrollbar: Option, pub relative_line_numbers: Option, + pub seed_search_query_from_cursor: Option, } #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 1b922848e061b8b162fc5c1f25babf5c3ce96c0e..4c45904c502285cb217b7e61193ef8fdc95dc99b 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -1,7 +1,7 @@ use crate::{ - display_map::ToDisplayPoint, link_go_to_definition::hide_link_definition, - movement::surrounding_word, persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor, - Event, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _, + editor_settings::SeedQuerySetting, link_go_to_definition::hide_link_definition, + persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor, EditorSettings, Event, + ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _, }; use anyhow::{Context, Result}; use collections::HashSet; @@ -13,8 +13,8 @@ use gpui::{ ViewHandle, WeakViewHandle, }; use language::{ - proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, OffsetRangeExt, Point, - SelectionGoal, + proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, CharKind, OffsetRangeExt, + Point, SelectionGoal, }; use project::{search::SearchQuery, FormatTrigger, Item as _, Project, ProjectPath}; use rpc::proto::{self, update_view, PeerId}; @@ -937,24 +937,28 @@ impl SearchableItem for Editor { } fn query_suggestion(&mut self, cx: &mut ViewContext) -> String { - let display_map = self.snapshot(cx).display_snapshot; + let setting = settings::get::(cx).seed_search_query_from_cursor; + let snapshot = &self.snapshot(cx).buffer_snapshot; let selection = self.selections.newest::(cx); - if selection.start == selection.end { - let point = selection.start.to_display_point(&display_map); - let range = surrounding_word(&display_map, point); - let range = range.start.to_offset(&display_map, Bias::Left) - ..range.end.to_offset(&display_map, Bias::Right); - let text: String = display_map.buffer_snapshot.text_for_range(range).collect(); - if text.trim().is_empty() { + + match setting { + SeedQuerySetting::Never => String::new(), + SeedQuerySetting::Selection | SeedQuerySetting::Always if !selection.is_empty() => { + snapshot + .text_for_range(selection.start..selection.end) + .collect() + } + SeedQuerySetting::Selection => String::new(), + SeedQuerySetting::Always => { + let (range, kind) = snapshot.surrounding_word(selection.start); + if kind == Some(CharKind::Word) { + let text: String = snapshot.text_for_range(range).collect(); + if !text.trim().is_empty() { + return text; + } + } String::new() - } else { - text } - } else { - display_map - .buffer_snapshot - .text_for_range(selection.start..selection.end) - .collect() } } diff --git a/crates/editor2/src/editor_settings.rs b/crates/editor2/src/editor_settings.rs index 45c797598f51c4bde2f0008d02ac62d55accb3c7..c23bf76aea8a442e343925b06bfdcf1457236f76 100644 --- a/crates/editor2/src/editor_settings.rs +++ b/crates/editor2/src/editor_settings.rs @@ -11,6 +11,15 @@ pub struct EditorSettings { pub use_on_type_format: bool, pub scrollbar: Scrollbar, pub relative_line_numbers: bool, + pub seed_search_query_from_cursor: SeedQuerySetting, +} + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum SeedQuerySetting { + Always, + Selection, + Never, } #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] @@ -38,6 +47,7 @@ pub struct EditorSettingsContent { pub use_on_type_format: Option, pub scrollbar: Option, pub relative_line_numbers: Option, + pub seed_search_query_from_selection: Option, } #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] diff --git a/crates/editor2/src/items.rs b/crates/editor2/src/items.rs index 9c49e5f14317579eb1ca6e103c5058de97e76d40..b7e14244c30218cedf4c5c8ede9fe06151d3b9db 100644 --- a/crates/editor2/src/items.rs +++ b/crates/editor2/src/items.rs @@ -1,7 +1,8 @@ use crate::{ - display_map::ToDisplayPoint, link_go_to_definition::hide_link_definition, + editor_settings::SeedQuerySetting, link_go_to_definition::hide_link_definition, movement::surrounding_word, persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor, - Event, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _, + EditorSettings, Event, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, + NavigationData, ToPoint as _, }; use anyhow::{anyhow, Context, Result}; use collections::HashSet; @@ -12,11 +13,12 @@ use gpui::{ VisualContext, WeakView, }; use language::{ - proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, OffsetRangeExt, Point, - SelectionGoal, + proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, CharKind, OffsetRangeExt, + Point, SelectionGoal, }; use project::{search::SearchQuery, FormatTrigger, Item as _, Project, ProjectPath}; use rpc::proto::{self, update_view, PeerId}; +use settings::Settings; use smallvec::SmallVec; use std::{ borrow::Cow, @@ -950,24 +952,28 @@ impl SearchableItem for Editor { } fn query_suggestion(&mut self, cx: &mut ViewContext) -> String { - let display_map = self.snapshot(cx).display_snapshot; + let setting = EditorSettings::get_global(cx).seed_search_query_from_cursor; + let snapshot = &self.snapshot(cx).buffer_snapshot; let selection = self.selections.newest::(cx); - if selection.start == selection.end { - let point = selection.start.to_display_point(&display_map); - let range = surrounding_word(&display_map, point); - let range = range.start.to_offset(&display_map, Bias::Left) - ..range.end.to_offset(&display_map, Bias::Right); - let text: String = display_map.buffer_snapshot.text_for_range(range).collect(); - if text.trim().is_empty() { + + match setting { + SeedQuerySetting::Never => String::new(), + SeedQuerySetting::Selection | SeedQuerySetting::Always if !selection.is_empty() => { + snapshot + .text_for_range(selection.start..selection.end) + .collect() + } + SeedQuerySetting::Selection => String::new(), + SeedQuerySetting::Always => { + let (range, kind) = snapshot.surrounding_word(selection.start); + if kind == Some(CharKind::Word) { + let text: String = snapshot.text_for_range(range).collect(); + if !text.trim().is_empty() { + return text; + } + } String::new() - } else { - text } - } else { - display_map - .buffer_snapshot - .text_for_range(selection.start..selection.end) - .collect() } } diff --git a/crates/gpui/src/geometry.rs b/crates/gpui/src/geometry.rs index 40b06827872edec469d82cfc1cc79c377a00633b..fe197af5d2670146de94198abbc0db3dccc02135 100644 --- a/crates/gpui/src/geometry.rs +++ b/crates/gpui/src/geometry.rs @@ -136,7 +136,7 @@ impl ToJson for RectF { } #[derive(Refineable, Debug)] -#[refineable(debug)] +#[refineable(Debug)] pub struct Point { pub x: T, pub y: T, @@ -161,7 +161,7 @@ impl Into> for Point { } #[derive(Refineable, Clone, Debug)] -#[refineable(debug)] +#[refineable(Debug)] pub struct Size { pub width: T, pub height: T, @@ -227,7 +227,7 @@ impl Size { } #[derive(Clone, Default, Refineable, Debug)] -#[refineable(debug)] +#[refineable(Debug)] pub struct Edges { pub top: T, pub right: T, diff --git a/crates/gpui2/src/geometry.rs b/crates/gpui2/src/geometry.rs index e16635be362f6bc59370805a6c93413da50a7c64..f36432dff54e65badefc63e9187f09be67426d69 100644 --- a/crates/gpui2/src/geometry.rs +++ b/crates/gpui2/src/geometry.rs @@ -9,7 +9,7 @@ use std::{ }; #[derive(Refineable, Default, Add, AddAssign, Sub, SubAssign, Copy, Debug, PartialEq, Eq, Hash)] -#[refineable(debug)] +#[refineable(Debug)] #[repr(C)] pub struct Point { pub x: T, @@ -140,7 +140,7 @@ impl Clone for Point { } #[derive(Refineable, Default, Clone, Copy, PartialEq, Div, Hash, Serialize, Deserialize)] -#[refineable(debug)] +#[refineable(Debug)] #[repr(C)] pub struct Size { pub width: T, @@ -295,7 +295,7 @@ impl Size { } #[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)] -#[refineable(debug)] +#[refineable(Debug)] #[repr(C)] pub struct Bounds { pub origin: Point, @@ -459,7 +459,7 @@ impl Bounds { impl Copy for Bounds {} #[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)] -#[refineable(debug)] +#[refineable(Debug)] #[repr(C)] pub struct Edges { pub top: T, @@ -592,7 +592,7 @@ impl Edges { } #[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)] -#[refineable(debug)] +#[refineable(Debug)] #[repr(C)] pub struct Corners { pub top_left: T, diff --git a/crates/gpui2/src/style.rs b/crates/gpui2/src/style.rs index 5de173c2d4f729e90f9ba691bccb02bab664214a..44bf1c1118b12872af03be032756cdfe12459b36 100644 --- a/crates/gpui2/src/style.rs +++ b/crates/gpui2/src/style.rs @@ -14,7 +14,7 @@ pub use taffy::style::{ pub type StyleCascade = Cascade