mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-24 02:46:43 +00:00
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" } ```
This commit is contained in:
parent
67068faa1d
commit
2d3a34c864
11 changed files with 109 additions and 87 deletions
|
@ -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.
|
||||
|
|
|
@ -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<bool>,
|
||||
pub scrollbar: Option<ScrollbarContent>,
|
||||
pub relative_line_numbers: Option<bool>,
|
||||
pub seed_search_query_from_cursor: Option<SeedQuerySetting>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
|
|
|
@ -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<Self>) -> String {
|
||||
let display_map = self.snapshot(cx).display_snapshot;
|
||||
let setting = settings::get::<EditorSettings>(cx).seed_search_query_from_cursor;
|
||||
let snapshot = &self.snapshot(cx).buffer_snapshot;
|
||||
let selection = self.selections.newest::<usize>(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() {
|
||||
String::new()
|
||||
} else {
|
||||
text
|
||||
|
||||
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 {
|
||||
display_map
|
||||
.buffer_snapshot
|
||||
.text_for_range(selection.start..selection.end)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<bool>,
|
||||
pub scrollbar: Option<ScrollbarContent>,
|
||||
pub relative_line_numbers: Option<bool>,
|
||||
pub seed_search_query_from_selection: Option<SeedQuerySetting>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
|
|
|
@ -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<Self>) -> 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::<usize>(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() {
|
||||
String::new()
|
||||
} else {
|
||||
text
|
||||
|
||||
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 {
|
||||
display_map
|
||||
.buffer_snapshot
|
||||
.text_for_range(selection.start..selection.end)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ impl ToJson for RectF {
|
|||
}
|
||||
|
||||
#[derive(Refineable, Debug)]
|
||||
#[refineable(debug)]
|
||||
#[refineable(Debug)]
|
||||
pub struct Point<T: Clone + Default + Debug> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
|
@ -161,7 +161,7 @@ impl<T: Clone + Default + Debug> Into<taffy::geometry::Point<T>> for Point<T> {
|
|||
}
|
||||
|
||||
#[derive(Refineable, Clone, Debug)]
|
||||
#[refineable(debug)]
|
||||
#[refineable(Debug)]
|
||||
pub struct Size<T: Clone + Default + Debug> {
|
||||
pub width: T,
|
||||
pub height: T,
|
||||
|
@ -227,7 +227,7 @@ impl Size<Length> {
|
|||
}
|
||||
|
||||
#[derive(Clone, Default, Refineable, Debug)]
|
||||
#[refineable(debug)]
|
||||
#[refineable(Debug)]
|
||||
pub struct Edges<T: Clone + Default + Debug> {
|
||||
pub top: T,
|
||||
pub right: T,
|
||||
|
|
|
@ -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<T: Default + Clone + Debug> {
|
||||
pub x: T,
|
||||
|
@ -140,7 +140,7 @@ impl<T: Clone + Default + Debug> Clone for Point<T> {
|
|||
}
|
||||
|
||||
#[derive(Refineable, Default, Clone, Copy, PartialEq, Div, Hash, Serialize, Deserialize)]
|
||||
#[refineable(debug)]
|
||||
#[refineable(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct Size<T: Clone + Default + Debug> {
|
||||
pub width: T,
|
||||
|
@ -295,7 +295,7 @@ impl Size<Length> {
|
|||
}
|
||||
|
||||
#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
|
||||
#[refineable(debug)]
|
||||
#[refineable(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct Bounds<T: Clone + Default + Debug> {
|
||||
pub origin: Point<T>,
|
||||
|
@ -459,7 +459,7 @@ impl Bounds<Pixels> {
|
|||
impl<T: Clone + Debug + Copy + Default> Copy for Bounds<T> {}
|
||||
|
||||
#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
|
||||
#[refineable(debug)]
|
||||
#[refineable(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct Edges<T: Clone + Default + Debug> {
|
||||
pub top: T,
|
||||
|
@ -592,7 +592,7 @@ impl Edges<Pixels> {
|
|||
}
|
||||
|
||||
#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
|
||||
#[refineable(debug)]
|
||||
#[refineable(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct Corners<T: Clone + Default + Debug> {
|
||||
pub top_left: T,
|
||||
|
|
|
@ -14,7 +14,7 @@ pub use taffy::style::{
|
|||
pub type StyleCascade = Cascade<Style>;
|
||||
|
||||
#[derive(Clone, Refineable, Debug)]
|
||||
#[refineable(debug)]
|
||||
#[refineable(Debug)]
|
||||
pub struct Style {
|
||||
/// What layout strategy should be used?
|
||||
pub display: Display,
|
||||
|
@ -129,7 +129,7 @@ pub struct BoxShadow {
|
|||
}
|
||||
|
||||
#[derive(Refineable, Clone, Debug)]
|
||||
#[refineable(debug)]
|
||||
#[refineable(Debug)]
|
||||
pub struct TextStyle {
|
||||
pub color: Hsla,
|
||||
pub font_family: SharedString,
|
||||
|
@ -353,7 +353,7 @@ impl Default for Style {
|
|||
}
|
||||
|
||||
#[derive(Refineable, Copy, Clone, Default, Debug, PartialEq, Eq)]
|
||||
#[refineable(debug)]
|
||||
#[refineable(Debug)]
|
||||
pub struct UnderlineStyle {
|
||||
pub thickness: Pixels,
|
||||
pub color: Option<Hsla>,
|
||||
|
|
|
@ -19,8 +19,7 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
|
|||
let refineable_attr = attrs.iter().find(|attr| attr.path.is_ident("refineable"));
|
||||
|
||||
let mut impl_debug_on_refinement = false;
|
||||
let mut derive_serialize_on_refinement = false;
|
||||
let mut derive_deserialize_on_refinement = false;
|
||||
let mut refinement_traits_to_derive = vec![];
|
||||
|
||||
if let Some(refineable_attr) = refineable_attr {
|
||||
if let Ok(syn::Meta::List(meta_list)) = refineable_attr.parse_meta() {
|
||||
|
@ -29,16 +28,10 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
|
|||
continue;
|
||||
};
|
||||
|
||||
if path.is_ident("debug") {
|
||||
if path.is_ident("Debug") {
|
||||
impl_debug_on_refinement = true;
|
||||
}
|
||||
|
||||
if path.is_ident("serialize") {
|
||||
derive_serialize_on_refinement = true;
|
||||
}
|
||||
|
||||
if path.is_ident("deserialize") {
|
||||
derive_deserialize_on_refinement = true;
|
||||
} else {
|
||||
refinement_traits_to_derive.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -259,22 +252,14 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
|
|||
quote! {}
|
||||
};
|
||||
|
||||
let derive_serialize = if derive_serialize_on_refinement {
|
||||
quote! { #[derive(serde::Serialize)]}
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
let derive_deserialize = if derive_deserialize_on_refinement {
|
||||
quote! { #[derive(serde::Deserialize)]}
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
let mut derive_stream = quote! {};
|
||||
for trait_to_derive in refinement_traits_to_derive {
|
||||
derive_stream.extend(quote! { #[derive(#trait_to_derive)] })
|
||||
}
|
||||
|
||||
let gen = quote! {
|
||||
#[derive(Clone)]
|
||||
#derive_serialize
|
||||
#derive_deserialize
|
||||
#derive_stream
|
||||
pub struct #refinement_ident #impl_generics {
|
||||
#( #field_visibilities #field_names: #wrapped_types ),*
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::{PlayerColors, SyntaxTheme};
|
||||
use gpui::Hsla;
|
||||
use refineable::Refineable;
|
||||
|
||||
use crate::{PlayerColors, SyntaxTheme};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SystemColors {
|
||||
|
@ -14,7 +12,7 @@ pub struct SystemColors {
|
|||
}
|
||||
|
||||
#[derive(Refineable, Clone, Debug)]
|
||||
#[refineable(debug)]
|
||||
#[refineable(Debug)]
|
||||
pub struct StatusColors {
|
||||
pub conflict: Hsla,
|
||||
pub created: Hsla,
|
||||
|
@ -30,7 +28,7 @@ pub struct StatusColors {
|
|||
}
|
||||
|
||||
#[derive(Refineable, Clone, Debug)]
|
||||
#[refineable(debug)]
|
||||
#[refineable(Debug)]
|
||||
pub struct GitStatusColors {
|
||||
pub conflict: Hsla,
|
||||
pub created: Hsla,
|
||||
|
@ -41,7 +39,7 @@ pub struct GitStatusColors {
|
|||
}
|
||||
|
||||
#[derive(Refineable, Clone, Debug)]
|
||||
#[refineable(debug, deserialize)]
|
||||
#[refineable(Debug, serde::Deserialize)]
|
||||
pub struct ThemeColors {
|
||||
pub border: Hsla,
|
||||
/// Border color. Used for deemphasized borders, like a visual divider between two sections
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use crate::{Appearance, ThemeColors, ThemeColorsRefinement};
|
||||
use refineable::Refineable;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{Appearance, ThemeColors, ThemeColorsRefinement};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct UserThemeFamily {
|
||||
pub name: String,
|
||||
|
@ -18,7 +17,7 @@ pub struct UserTheme {
|
|||
}
|
||||
|
||||
#[derive(Refineable, Clone)]
|
||||
#[refineable(deserialize)]
|
||||
#[refineable(Deserialize)]
|
||||
pub struct UserThemeStyles {
|
||||
#[refineable]
|
||||
pub colors: ThemeColors,
|
||||
|
|
Loading…
Reference in a new issue