Skip to content

Commit 7586ef1

Browse files
authored
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" } ```
2 parents 31a6409 + b6eef26 commit 7586ef1

File tree

11 files changed

+106
-84
lines changed

11 files changed

+106
-84
lines changed

assets/settings/default.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,16 @@
102102
"selections": true
103103
},
104104
"relative_line_numbers": false,
105+
// When to populate a new search's query based on the text under the cursor.
106+
// This setting can take the following three values:
107+
//
108+
// 1. Always populate the search query with the word under the cursor (default).
109+
// "always"
110+
// 2. Only populate the search query when there is text selected
111+
// "selection"
112+
// 3. Never populate the search query
113+
// "never"
114+
"seed_search_query_from_cursor": "always",
105115
// Inlay hint related settings
106116
"inlay_hints": {
107117
// Global switch to toggle hints on and off, switched off by default.

crates/editor/src/editor_settings.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use schemars::JsonSchema;
22
use serde::{Deserialize, Serialize};
33
use settings::Setting;
44

5-
#[derive(Deserialize)]
5+
#[derive(Clone, Deserialize)]
66
pub struct EditorSettings {
77
pub cursor_blink: bool,
88
pub hover_popover_enabled: bool,
@@ -11,6 +11,15 @@ pub struct EditorSettings {
1111
pub use_on_type_format: bool,
1212
pub scrollbar: Scrollbar,
1313
pub relative_line_numbers: bool,
14+
pub seed_search_query_from_cursor: SeedQuerySetting,
15+
}
16+
17+
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
18+
#[serde(rename_all = "snake_case")]
19+
pub enum SeedQuerySetting {
20+
Always,
21+
Selection,
22+
Never,
1423
}
1524

1625
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
@@ -38,6 +47,7 @@ pub struct EditorSettingsContent {
3847
pub use_on_type_format: Option<bool>,
3948
pub scrollbar: Option<ScrollbarContent>,
4049
pub relative_line_numbers: Option<bool>,
50+
pub seed_search_query_from_cursor: Option<SeedQuerySetting>,
4151
}
4252

4353
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]

crates/editor/src/items.rs

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::{
2-
display_map::ToDisplayPoint, link_go_to_definition::hide_link_definition,
3-
movement::surrounding_word, persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor,
4-
Event, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _,
2+
editor_settings::SeedQuerySetting, link_go_to_definition::hide_link_definition,
3+
persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor, EditorSettings, Event,
4+
ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _,
55
};
66
use anyhow::{Context, Result};
77
use collections::HashSet;
@@ -13,8 +13,8 @@ use gpui::{
1313
ViewHandle, WeakViewHandle,
1414
};
1515
use language::{
16-
proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, OffsetRangeExt, Point,
17-
SelectionGoal,
16+
proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, CharKind, OffsetRangeExt,
17+
Point, SelectionGoal,
1818
};
1919
use project::{search::SearchQuery, FormatTrigger, Item as _, Project, ProjectPath};
2020
use rpc::proto::{self, update_view, PeerId};
@@ -937,24 +937,28 @@ impl SearchableItem for Editor {
937937
}
938938

939939
fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String {
940-
let display_map = self.snapshot(cx).display_snapshot;
940+
let setting = settings::get::<EditorSettings>(cx).seed_search_query_from_cursor;
941+
let snapshot = &self.snapshot(cx).buffer_snapshot;
941942
let selection = self.selections.newest::<usize>(cx);
942-
if selection.start == selection.end {
943-
let point = selection.start.to_display_point(&display_map);
944-
let range = surrounding_word(&display_map, point);
945-
let range = range.start.to_offset(&display_map, Bias::Left)
946-
..range.end.to_offset(&display_map, Bias::Right);
947-
let text: String = display_map.buffer_snapshot.text_for_range(range).collect();
948-
if text.trim().is_empty() {
943+
944+
match setting {
945+
SeedQuerySetting::Never => String::new(),
946+
SeedQuerySetting::Selection | SeedQuerySetting::Always if !selection.is_empty() => {
947+
snapshot
948+
.text_for_range(selection.start..selection.end)
949+
.collect()
950+
}
951+
SeedQuerySetting::Selection => String::new(),
952+
SeedQuerySetting::Always => {
953+
let (range, kind) = snapshot.surrounding_word(selection.start);
954+
if kind == Some(CharKind::Word) {
955+
let text: String = snapshot.text_for_range(range).collect();
956+
if !text.trim().is_empty() {
957+
return text;
958+
}
959+
}
949960
String::new()
950-
} else {
951-
text
952961
}
953-
} else {
954-
display_map
955-
.buffer_snapshot
956-
.text_for_range(selection.start..selection.end)
957-
.collect()
958962
}
959963
}
960964

crates/editor2/src/editor_settings.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ pub struct EditorSettings {
1111
pub use_on_type_format: bool,
1212
pub scrollbar: Scrollbar,
1313
pub relative_line_numbers: bool,
14+
pub seed_search_query_from_cursor: SeedQuerySetting,
15+
}
16+
17+
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
18+
#[serde(rename_all = "snake_case")]
19+
pub enum SeedQuerySetting {
20+
Always,
21+
Selection,
22+
Never,
1423
}
1524

1625
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
@@ -38,6 +47,7 @@ pub struct EditorSettingsContent {
3847
pub use_on_type_format: Option<bool>,
3948
pub scrollbar: Option<ScrollbarContent>,
4049
pub relative_line_numbers: Option<bool>,
50+
pub seed_search_query_from_selection: Option<SeedQuerySetting>,
4151
}
4252

4353
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]

crates/editor2/src/items.rs

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use crate::{
2-
display_map::ToDisplayPoint, link_go_to_definition::hide_link_definition,
2+
editor_settings::SeedQuerySetting, link_go_to_definition::hide_link_definition,
33
movement::surrounding_word, persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor,
4-
Event, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _,
4+
EditorSettings, Event, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot,
5+
NavigationData, ToPoint as _,
56
};
67
use anyhow::{anyhow, Context, Result};
78
use collections::HashSet;
@@ -12,11 +13,12 @@ use gpui::{
1213
ViewContext, VisualContext, WeakView,
1314
};
1415
use language::{
15-
proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, OffsetRangeExt, Point,
16-
SelectionGoal,
16+
proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, CharKind, OffsetRangeExt,
17+
Point, SelectionGoal,
1718
};
1819
use project::{search::SearchQuery, FormatTrigger, Item as _, Project, ProjectPath};
1920
use rpc::proto::{self, update_view, PeerId};
21+
use settings::Settings;
2022
use smallvec::SmallVec;
2123
use std::{
2224
borrow::Cow,
@@ -918,24 +920,28 @@ impl SearchableItem for Editor {
918920
}
919921

920922
fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String {
921-
let display_map = self.snapshot(cx).display_snapshot;
923+
let setting = EditorSettings::get_global(cx).seed_search_query_from_cursor;
924+
let snapshot = &self.snapshot(cx).buffer_snapshot;
922925
let selection = self.selections.newest::<usize>(cx);
923-
if selection.start == selection.end {
924-
let point = selection.start.to_display_point(&display_map);
925-
let range = surrounding_word(&display_map, point);
926-
let range = range.start.to_offset(&display_map, Bias::Left)
927-
..range.end.to_offset(&display_map, Bias::Right);
928-
let text: String = display_map.buffer_snapshot.text_for_range(range).collect();
929-
if text.trim().is_empty() {
926+
927+
match setting {
928+
SeedQuerySetting::Never => String::new(),
929+
SeedQuerySetting::Selection | SeedQuerySetting::Always if !selection.is_empty() => {
930+
snapshot
931+
.text_for_range(selection.start..selection.end)
932+
.collect()
933+
}
934+
SeedQuerySetting::Selection => String::new(),
935+
SeedQuerySetting::Always => {
936+
let (range, kind) = snapshot.surrounding_word(selection.start);
937+
if kind == Some(CharKind::Word) {
938+
let text: String = snapshot.text_for_range(range).collect();
939+
if !text.trim().is_empty() {
940+
return text;
941+
}
942+
}
930943
String::new()
931-
} else {
932-
text
933944
}
934-
} else {
935-
display_map
936-
.buffer_snapshot
937-
.text_for_range(selection.start..selection.end)
938-
.collect()
939945
}
940946
}
941947

crates/gpui/src/geometry.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ impl ToJson for RectF {
136136
}
137137

138138
#[derive(Refineable, Debug)]
139-
#[refineable(debug)]
139+
#[refineable(Debug)]
140140
pub struct Point<T: Clone + Default + Debug> {
141141
pub x: T,
142142
pub y: T,
@@ -161,7 +161,7 @@ impl<T: Clone + Default + Debug> Into<taffy::geometry::Point<T>> for Point<T> {
161161
}
162162

163163
#[derive(Refineable, Clone, Debug)]
164-
#[refineable(debug)]
164+
#[refineable(Debug)]
165165
pub struct Size<T: Clone + Default + Debug> {
166166
pub width: T,
167167
pub height: T,
@@ -227,7 +227,7 @@ impl Size<Length> {
227227
}
228228

229229
#[derive(Clone, Default, Refineable, Debug)]
230-
#[refineable(debug)]
230+
#[refineable(Debug)]
231231
pub struct Edges<T: Clone + Default + Debug> {
232232
pub top: T,
233233
pub right: T,

crates/gpui2/src/geometry.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::{
99
};
1010

1111
#[derive(Refineable, Default, Add, AddAssign, Sub, SubAssign, Copy, Debug, PartialEq, Eq, Hash)]
12-
#[refineable(debug)]
12+
#[refineable(Debug)]
1313
#[repr(C)]
1414
pub struct Point<T: Default + Clone + Debug> {
1515
pub x: T,
@@ -140,7 +140,7 @@ impl<T: Clone + Default + Debug> Clone for Point<T> {
140140
}
141141

142142
#[derive(Refineable, Default, Clone, Copy, PartialEq, Div, Hash, Serialize, Deserialize)]
143-
#[refineable(debug)]
143+
#[refineable(Debug)]
144144
#[repr(C)]
145145
pub struct Size<T: Clone + Default + Debug> {
146146
pub width: T,
@@ -313,7 +313,7 @@ impl Size<Length> {
313313
}
314314

315315
#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
316-
#[refineable(debug)]
316+
#[refineable(Debug)]
317317
#[repr(C)]
318318
pub struct Bounds<T: Clone + Default + Debug> {
319319
pub origin: Point<T>,
@@ -477,7 +477,7 @@ impl Bounds<Pixels> {
477477
impl<T: Clone + Debug + Copy + Default> Copy for Bounds<T> {}
478478

479479
#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
480-
#[refineable(debug)]
480+
#[refineable(Debug)]
481481
#[repr(C)]
482482
pub struct Edges<T: Clone + Default + Debug> {
483483
pub top: T,
@@ -619,7 +619,7 @@ impl Edges<Pixels> {
619619
}
620620

621621
#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
622-
#[refineable(debug)]
622+
#[refineable(Debug)]
623623
#[repr(C)]
624624
pub struct Corners<T: Clone + Default + Debug> {
625625
pub top_left: T,

crates/gpui2/src/style.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub use taffy::style::{
1414
pub type StyleCascade = Cascade<Style>;
1515

1616
#[derive(Clone, Refineable, Debug)]
17-
#[refineable(debug)]
17+
#[refineable(Debug)]
1818
pub struct Style {
1919
/// What layout strategy should be used?
2020
pub display: Display,
@@ -129,7 +129,7 @@ pub struct BoxShadow {
129129
}
130130

131131
#[derive(Refineable, Clone, Debug)]
132-
#[refineable(debug)]
132+
#[refineable(Debug)]
133133
pub struct TextStyle {
134134
pub color: Hsla,
135135
pub font_family: SharedString,
@@ -353,7 +353,7 @@ impl Default for Style {
353353
}
354354

355355
#[derive(Refineable, Copy, Clone, Default, Debug, PartialEq, Eq)]
356-
#[refineable(debug)]
356+
#[refineable(Debug)]
357357
pub struct UnderlineStyle {
358358
pub thickness: Pixels,
359359
pub color: Option<Hsla>,

crates/refineable/derive_refineable/src/derive_refineable.rs

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
1919
let refineable_attr = attrs.iter().find(|attr| attr.path.is_ident("refineable"));
2020

2121
let mut impl_debug_on_refinement = false;
22-
let mut derive_serialize_on_refinement = false;
23-
let mut derive_deserialize_on_refinement = false;
22+
let mut refinement_traits_to_derive = vec![];
2423

2524
if let Some(refineable_attr) = refineable_attr {
2625
if let Ok(syn::Meta::List(meta_list)) = refineable_attr.parse_meta() {
@@ -29,16 +28,10 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
2928
continue;
3029
};
3130

32-
if path.is_ident("debug") {
31+
if path.is_ident("Debug") {
3332
impl_debug_on_refinement = true;
34-
}
35-
36-
if path.is_ident("serialize") {
37-
derive_serialize_on_refinement = true;
38-
}
39-
40-
if path.is_ident("deserialize") {
41-
derive_deserialize_on_refinement = true;
33+
} else {
34+
refinement_traits_to_derive.push(path);
4235
}
4336
}
4437
}
@@ -259,22 +252,14 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
259252
quote! {}
260253
};
261254

262-
let derive_serialize = if derive_serialize_on_refinement {
263-
quote! { #[derive(serde::Serialize)]}
264-
} else {
265-
quote! {}
266-
};
267-
268-
let derive_deserialize = if derive_deserialize_on_refinement {
269-
quote! { #[derive(serde::Deserialize)]}
270-
} else {
271-
quote! {}
272-
};
255+
let mut derive_stream = quote! {};
256+
for trait_to_derive in refinement_traits_to_derive {
257+
derive_stream.extend(quote! { #[derive(#trait_to_derive)] })
258+
}
273259

274260
let gen = quote! {
275261
#[derive(Clone)]
276-
#derive_serialize
277-
#derive_deserialize
262+
#derive_stream
278263
pub struct #refinement_ident #impl_generics {
279264
#( #field_visibilities #field_names: #wrapped_types ),*
280265
}

crates/theme2/src/colors.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
use std::sync::Arc;
2-
1+
use crate::{PlayerColors, SyntaxTheme};
32
use gpui::Hsla;
43
use refineable::Refineable;
5-
6-
use crate::{PlayerColors, SyntaxTheme};
4+
use std::sync::Arc;
75

86
#[derive(Clone)]
97
pub struct SystemColors {
@@ -14,7 +12,7 @@ pub struct SystemColors {
1412
}
1513

1614
#[derive(Refineable, Clone, Debug)]
17-
#[refineable(debug, deserialize)]
15+
#[refineable(Debug, serde::Deserialize)]
1816
pub struct StatusColors {
1917
pub conflict: Hsla,
2018
pub created: Hsla,
@@ -30,7 +28,7 @@ pub struct StatusColors {
3028
}
3129

3230
#[derive(Refineable, Clone, Debug)]
33-
#[refineable(debug, deserialize)]
31+
#[refineable(Debug, serde::Deserialize)]
3432
pub struct ThemeColors {
3533
pub border: Hsla,
3634
/// Border color. Used for deemphasized borders, like a visual divider between two sections

0 commit comments

Comments
 (0)