Revert "Remove semantic search UI"

This reverts commit c0f042b39a.
This commit is contained in:
Mikayla 2023-08-18 15:59:26 -07:00
parent cb55204e22
commit 48553d7c8f
No known key found for this signature in database
7 changed files with 309 additions and 91 deletions

View file

@ -1,3 +1,5 @@
use std::marker::PhantomData;
use pathfinder_geometry::{rect::RectF, vector::Vector2F}; use pathfinder_geometry::{rect::RectF, vector::Vector2F};
use crate::{ use crate::{
@ -103,26 +105,22 @@ impl<V: View> Component<V> for ElementAdapter<V> {
} }
// Component -> Element // Component -> Element
pub struct ComponentAdapter<V: View, E> { pub struct ComponentAdapter<V, E> {
component: Option<E>, component: Option<E>,
element: Option<AnyElement<V>>, phantom: PhantomData<V>,
#[cfg(debug_assertions)]
_component_name: &'static str,
} }
impl<E, V: View> ComponentAdapter<V, E> { impl<E, V> ComponentAdapter<V, E> {
pub fn new(e: E) -> Self { pub fn new(e: E) -> Self {
Self { Self {
component: Some(e), component: Some(e),
element: None, phantom: PhantomData,
#[cfg(debug_assertions)]
_component_name: std::any::type_name::<E>(),
} }
} }
} }
impl<V: View, C: Component<V> + 'static> Element<V> for ComponentAdapter<V, C> { impl<V: View, C: Component<V> + 'static> Element<V> for ComponentAdapter<V, C> {
type LayoutState = (); type LayoutState = AnyElement<V>;
type PaintState = (); type PaintState = ();
@ -132,12 +130,10 @@ impl<V: View, C: Component<V> + 'static> Element<V> for ComponentAdapter<V, C> {
view: &mut V, view: &mut V,
cx: &mut LayoutContext<V>, cx: &mut LayoutContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
if self.element.is_none() { let component = self.component.take().unwrap();
let component = self.component.take().unwrap(); let mut element = component.render(view, cx.view_context());
self.element = Some(component.render(view, cx.view_context())); let constraint = element.layout(constraint, view, cx);
} (constraint, element)
let constraint = self.element.as_mut().unwrap().layout(constraint, view, cx);
(constraint, ())
} }
fn paint( fn paint(
@ -145,14 +141,11 @@ impl<V: View, C: Component<V> + 'static> Element<V> for ComponentAdapter<V, C> {
scene: &mut SceneBuilder, scene: &mut SceneBuilder,
bounds: RectF, bounds: RectF,
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, layout: &mut Self::LayoutState,
view: &mut V, view: &mut V,
cx: &mut PaintContext<V>, cx: &mut PaintContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
self.element layout.paint(scene, bounds.origin(), visible_bounds, view, cx)
.as_mut()
.unwrap()
.paint(scene, bounds.origin(), visible_bounds, view, cx)
} }
fn rect_for_text_range( fn rect_for_text_range(
@ -160,35 +153,25 @@ impl<V: View, C: Component<V> + 'static> Element<V> for ComponentAdapter<V, C> {
range_utf16: std::ops::Range<usize>, range_utf16: std::ops::Range<usize>,
_: RectF, _: RectF,
_: RectF, _: RectF,
_: &Self::LayoutState, element: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
view: &V, view: &V,
cx: &ViewContext<V>, cx: &ViewContext<V>,
) -> Option<RectF> { ) -> Option<RectF> {
self.element element.rect_for_text_range(range_utf16, view, cx)
.as_ref()
.unwrap()
.rect_for_text_range(range_utf16, view, cx)
} }
fn debug( fn debug(
&self, &self,
_: RectF, _: RectF,
_: &Self::LayoutState, element: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
view: &V, view: &V,
cx: &ViewContext<V>, cx: &ViewContext<V>,
) -> serde_json::Value { ) -> serde_json::Value {
#[cfg(debug_assertions)]
let component_name = self._component_name;
#[cfg(not(debug_assertions))]
let component_name = "Unknown";
serde_json::json!({ serde_json::json!({
"type": "ComponentAdapter", "type": "ComponentAdapter",
"child": self.element.as_ref().unwrap().debug(view, cx), "child": element.debug(view, cx),
"component_name": component_name
}) })
} }
} }

View file

@ -523,6 +523,11 @@ impl BufferSearchBar {
} }
pub fn activate_search_mode(&mut self, mode: SearchMode, cx: &mut ViewContext<Self>) { pub fn activate_search_mode(&mut self, mode: SearchMode, cx: &mut ViewContext<Self>) {
assert_ne!(
mode,
SearchMode::Semantic,
"Semantic search is not supported in buffer search"
);
if mode == self.current_mode { if mode == self.current_mode {
return; return;
} }
@ -797,7 +802,7 @@ impl BufferSearchBar {
} }
} }
fn cycle_mode(&mut self, _: &CycleMode, cx: &mut ViewContext<Self>) { fn cycle_mode(&mut self, _: &CycleMode, cx: &mut ViewContext<Self>) {
self.activate_search_mode(next_mode(&self.current_mode), cx); self.activate_search_mode(next_mode(&self.current_mode, false), cx);
} }
fn cycle_mode_on_pane(pane: &mut Pane, action: &CycleMode, cx: &mut ViewContext<Pane>) { fn cycle_mode_on_pane(pane: &mut Pane, action: &CycleMode, cx: &mut ViewContext<Pane>) {
let mut should_propagate = true; let mut should_propagate = true;

View file

@ -1,11 +1,12 @@
use gpui::Action; use gpui::Action;
use crate::{ActivateRegexMode, ActivateTextMode}; use crate::{ActivateRegexMode, ActivateSemanticMode, ActivateTextMode};
// TODO: Update the default search mode to get from config // TODO: Update the default search mode to get from config
#[derive(Copy, Clone, Debug, Default, PartialEq)] #[derive(Copy, Clone, Debug, Default, PartialEq)]
pub enum SearchMode { pub enum SearchMode {
#[default] #[default]
Text, Text,
Semantic,
Regex, Regex,
} }
@ -19,6 +20,7 @@ impl SearchMode {
pub(crate) fn label(&self) -> &'static str { pub(crate) fn label(&self) -> &'static str {
match self { match self {
SearchMode::Text => "Text", SearchMode::Text => "Text",
SearchMode::Semantic => "Semantic",
SearchMode::Regex => "Regex", SearchMode::Regex => "Regex",
} }
} }
@ -26,6 +28,7 @@ impl SearchMode {
pub(crate) fn region_id(&self) -> usize { pub(crate) fn region_id(&self) -> usize {
match self { match self {
SearchMode::Text => 3, SearchMode::Text => 3,
SearchMode::Semantic => 4,
SearchMode::Regex => 5, SearchMode::Regex => 5,
} }
} }
@ -33,6 +36,7 @@ impl SearchMode {
pub(crate) fn tooltip_text(&self) -> &'static str { pub(crate) fn tooltip_text(&self) -> &'static str {
match self { match self {
SearchMode::Text => "Activate Text Search", SearchMode::Text => "Activate Text Search",
SearchMode::Semantic => "Activate Semantic Search",
SearchMode::Regex => "Activate Regex Search", SearchMode::Regex => "Activate Regex Search",
} }
} }
@ -40,6 +44,7 @@ impl SearchMode {
pub(crate) fn activate_action(&self) -> Box<dyn Action> { pub(crate) fn activate_action(&self) -> Box<dyn Action> {
match self { match self {
SearchMode::Text => Box::new(ActivateTextMode), SearchMode::Text => Box::new(ActivateTextMode),
SearchMode::Semantic => Box::new(ActivateSemanticMode),
SearchMode::Regex => Box::new(ActivateRegexMode), SearchMode::Regex => Box::new(ActivateRegexMode),
} }
} }
@ -48,6 +53,7 @@ impl SearchMode {
match self { match self {
SearchMode::Regex => true, SearchMode::Regex => true,
SearchMode::Text => true, SearchMode::Text => true,
SearchMode::Semantic => true,
} }
} }
@ -61,14 +67,22 @@ impl SearchMode {
pub(crate) fn button_side(&self) -> Option<Side> { pub(crate) fn button_side(&self) -> Option<Side> {
match self { match self {
SearchMode::Text => Some(Side::Left), SearchMode::Text => Some(Side::Left),
SearchMode::Semantic => None,
SearchMode::Regex => Some(Side::Right), SearchMode::Regex => Some(Side::Right),
} }
} }
} }
pub(crate) fn next_mode(mode: &SearchMode) -> SearchMode { pub(crate) fn next_mode(mode: &SearchMode, semantic_enabled: bool) -> SearchMode {
let next_text_state = if semantic_enabled {
SearchMode::Semantic
} else {
SearchMode::Regex
};
match mode { match mode {
SearchMode::Text => SearchMode::Regex, SearchMode::Text => next_text_state,
SearchMode::Semantic => SearchMode::Regex,
SearchMode::Regex => SearchMode::Text, SearchMode::Regex => SearchMode::Text,
} }
} }

View file

@ -2,10 +2,10 @@ use crate::{
history::SearchHistory, history::SearchHistory,
mode::SearchMode, mode::SearchMode,
search_bar::{render_nav_button, render_option_button_icon, render_search_mode_button}, search_bar::{render_nav_button, render_option_button_icon, render_search_mode_button},
CycleMode, NextHistoryQuery, PreviousHistoryQuery, SearchOptions, SelectNextMatch, ActivateRegexMode, CycleMode, NextHistoryQuery, PreviousHistoryQuery, SearchOptions,
SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleWholeWord,
}; };
use anyhow::Context; use anyhow::{Context, Result};
use collections::HashMap; use collections::HashMap;
use editor::{ use editor::{
items::active_match_index, scroll::autoscroll::Autoscroll, Anchor, Editor, MultiBuffer, items::active_match_index, scroll::autoscroll::Autoscroll, Anchor, Editor, MultiBuffer,
@ -13,6 +13,8 @@ use editor::{
}; };
use futures::StreamExt; use futures::StreamExt;
use gpui::platform::PromptLevel;
use gpui::{ use gpui::{
actions, elements::*, platform::MouseButton, Action, AnyElement, AnyViewHandle, AppContext, actions, elements::*, platform::MouseButton, Action, AnyElement, AnyViewHandle, AppContext,
Entity, ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, Entity, ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle,
@ -20,10 +22,12 @@ use gpui::{
}; };
use menu::Confirm; use menu::Confirm;
use postage::stream::Stream;
use project::{ use project::{
search::{PathMatcher, SearchQuery}, search::{PathMatcher, SearchInputs, SearchQuery},
Entry, Project, Entry, Project,
}; };
use semantic_index::SemanticIndex;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{ use std::{
any::{Any, TypeId}, any::{Any, TypeId},
@ -60,7 +64,7 @@ pub fn init(cx: &mut AppContext) {
cx.add_action(ProjectSearchBar::cycle_mode); cx.add_action(ProjectSearchBar::cycle_mode);
cx.add_action(ProjectSearchBar::next_history_query); cx.add_action(ProjectSearchBar::next_history_query);
cx.add_action(ProjectSearchBar::previous_history_query); cx.add_action(ProjectSearchBar::previous_history_query);
// cx.add_action(ProjectSearchBar::activate_regex_mode); cx.add_action(ProjectSearchBar::activate_regex_mode);
cx.capture_action(ProjectSearchBar::tab); cx.capture_action(ProjectSearchBar::tab);
cx.capture_action(ProjectSearchBar::tab_previous); cx.capture_action(ProjectSearchBar::tab_previous);
add_toggle_option_action::<ToggleCaseSensitive>(SearchOptions::CASE_SENSITIVE, cx); add_toggle_option_action::<ToggleCaseSensitive>(SearchOptions::CASE_SENSITIVE, cx);
@ -114,6 +118,8 @@ pub struct ProjectSearchView {
model: ModelHandle<ProjectSearch>, model: ModelHandle<ProjectSearch>,
query_editor: ViewHandle<Editor>, query_editor: ViewHandle<Editor>,
results_editor: ViewHandle<Editor>, results_editor: ViewHandle<Editor>,
semantic_state: Option<SemanticSearchState>,
semantic_permissioned: Option<bool>,
search_options: SearchOptions, search_options: SearchOptions,
panels_with_errors: HashSet<InputPanel>, panels_with_errors: HashSet<InputPanel>,
active_match_index: Option<usize>, active_match_index: Option<usize>,
@ -125,6 +131,12 @@ pub struct ProjectSearchView {
current_mode: SearchMode, current_mode: SearchMode,
} }
struct SemanticSearchState {
file_count: usize,
outstanding_file_count: usize,
_progress_task: Task<()>,
}
pub struct ProjectSearchBar { pub struct ProjectSearchBar {
active_project_search: Option<ViewHandle<ProjectSearchView>>, active_project_search: Option<ViewHandle<ProjectSearchView>>,
subscription: Option<Subscription>, subscription: Option<Subscription>,
@ -206,6 +218,60 @@ impl ProjectSearch {
})); }));
cx.notify(); cx.notify();
} }
fn semantic_search(&mut self, inputs: &SearchInputs, cx: &mut ModelContext<Self>) {
let search = SemanticIndex::global(cx).map(|index| {
index.update(cx, |semantic_index, cx| {
semantic_index.search_project(
self.project.clone(),
inputs.as_str().to_owned(),
10,
inputs.files_to_include().to_vec(),
inputs.files_to_exclude().to_vec(),
cx,
)
})
});
self.search_id += 1;
self.match_ranges.clear();
self.search_history.add(inputs.as_str().to_string());
self.no_results = Some(true);
self.pending_search = Some(cx.spawn(|this, mut cx| async move {
let results = search?.await.log_err()?;
let (_task, mut match_ranges) = this.update(&mut cx, |this, cx| {
this.excerpts.update(cx, |excerpts, cx| {
excerpts.clear(cx);
let matches = results
.into_iter()
.map(|result| (result.buffer, vec![result.range.start..result.range.start]))
.collect();
excerpts.stream_excerpts_with_context_lines(matches, 3, cx)
})
});
while let Some(match_range) = match_ranges.next().await {
this.update(&mut cx, |this, cx| {
this.match_ranges.push(match_range);
while let Ok(Some(match_range)) = match_ranges.try_next() {
this.match_ranges.push(match_range);
}
this.no_results = Some(false);
cx.notify();
});
}
this.update(&mut cx, |this, cx| {
this.pending_search.take();
cx.notify();
});
None
}));
cx.notify();
}
} }
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
@ -245,10 +311,27 @@ impl View for ProjectSearchView {
} else { } else {
match current_mode { match current_mode {
SearchMode::Text => Cow::Borrowed("Text search all files and folders"), SearchMode::Text => Cow::Borrowed("Text search all files and folders"),
SearchMode::Semantic => {
Cow::Borrowed("Search all code objects using Natural Language")
}
SearchMode::Regex => Cow::Borrowed("Regex search all files and folders"), SearchMode::Regex => Cow::Borrowed("Regex search all files and folders"),
} }
}; };
let semantic_status = if let Some(semantic) = &self.semantic_state {
if semantic.outstanding_file_count > 0 {
format!(
"Indexing: {} of {}...",
semantic.file_count - semantic.outstanding_file_count,
semantic.file_count
)
} else {
"Indexing complete".to_string()
}
} else {
"Indexing: ...".to_string()
};
let minor_text = if let Some(no_results) = model.no_results { let minor_text = if let Some(no_results) = model.no_results {
if model.pending_search.is_none() && no_results { if model.pending_search.is_none() && no_results {
vec!["No results found in this project for the provided query".to_owned()] vec!["No results found in this project for the provided query".to_owned()]
@ -256,11 +339,19 @@ impl View for ProjectSearchView {
vec![] vec![]
} }
} else { } else {
vec![ match current_mode {
"".to_owned(), SearchMode::Semantic => vec![
"Include/exclude specific paths with the filter option.".to_owned(), "".to_owned(),
"Matching exact word and/or casing is available too.".to_owned(), semantic_status,
] "Simply explain the code you are looking to find.".to_owned(),
"ex. 'prompt user for permissions to index their project'".to_owned(),
],
_ => vec![
"".to_owned(),
"Include/exclude specific paths with the filter option.".to_owned(),
"Matching exact word and/or casing is available too.".to_owned(),
],
}
}; };
let previous_query_keystrokes = let previous_query_keystrokes =
@ -539,6 +630,49 @@ impl ProjectSearchView {
self.search_options.toggle(option); self.search_options.toggle(option);
} }
fn index_project(&mut self, cx: &mut ViewContext<Self>) {
if let Some(semantic_index) = SemanticIndex::global(cx) {
// Semantic search uses no options
self.search_options = SearchOptions::none();
let project = self.model.read(cx).project.clone();
let index_task = semantic_index.update(cx, |semantic_index, cx| {
semantic_index.index_project(project, cx)
});
cx.spawn(|search_view, mut cx| async move {
let (files_to_index, mut files_remaining_rx) = index_task.await?;
search_view.update(&mut cx, |search_view, cx| {
cx.notify();
search_view.semantic_state = Some(SemanticSearchState {
file_count: files_to_index,
outstanding_file_count: files_to_index,
_progress_task: cx.spawn(|search_view, mut cx| async move {
while let Some(count) = files_remaining_rx.recv().await {
search_view
.update(&mut cx, |search_view, cx| {
if let Some(semantic_search_state) =
&mut search_view.semantic_state
{
semantic_search_state.outstanding_file_count = count;
cx.notify();
if count == 0 {
return;
}
}
})
.ok();
}
}),
});
})?;
anyhow::Ok(())
})
.detach_and_log_err(cx);
}
}
fn clear_search(&mut self, cx: &mut ViewContext<Self>) { fn clear_search(&mut self, cx: &mut ViewContext<Self>) {
self.model.update(cx, |model, cx| { self.model.update(cx, |model, cx| {
model.pending_search = None; model.pending_search = None;
@ -561,7 +695,61 @@ impl ProjectSearchView {
self.current_mode = mode; self.current_mode = mode;
self.active_match_index = None; self.active_match_index = None;
self.search(cx); match mode {
SearchMode::Semantic => {
let has_permission = self.semantic_permissioned(cx);
self.active_match_index = None;
cx.spawn(|this, mut cx| async move {
let has_permission = has_permission.await?;
if !has_permission {
let mut answer = this.update(&mut cx, |this, cx| {
let project = this.model.read(cx).project.clone();
let project_name = project
.read(cx)
.worktree_root_names(cx)
.collect::<Vec<&str>>()
.join("/");
let is_plural =
project_name.chars().filter(|letter| *letter == '/').count() > 0;
let prompt_text = format!("Would you like to index the '{}' project{} for semantic search? This requires sending code to the OpenAI API", project_name,
if is_plural {
"s"
} else {""});
cx.prompt(
PromptLevel::Info,
prompt_text.as_str(),
&["Continue", "Cancel"],
)
})?;
if answer.next().await == Some(0) {
this.update(&mut cx, |this, _| {
this.semantic_permissioned = Some(true);
})?;
} else {
this.update(&mut cx, |this, cx| {
this.semantic_permissioned = Some(false);
debug_assert_ne!(previous_mode, SearchMode::Semantic, "Tried to re-enable semantic search mode after user modal was rejected");
this.activate_search_mode(previous_mode, cx);
})?;
return anyhow::Ok(());
}
}
this.update(&mut cx, |this, cx| {
this.index_project(cx);
})?;
anyhow::Ok(())
}).detach_and_log_err(cx);
}
SearchMode::Regex | SearchMode::Text => {
self.semantic_state = None;
self.active_match_index = None;
self.search(cx);
}
}
cx.notify(); cx.notify();
} }
@ -657,6 +845,8 @@ impl ProjectSearchView {
model, model,
query_editor, query_editor,
results_editor, results_editor,
semantic_state: None,
semantic_permissioned: None,
search_options: options, search_options: options,
panels_with_errors: HashSet::new(), panels_with_errors: HashSet::new(),
active_match_index: None, active_match_index: None,
@ -670,6 +860,18 @@ impl ProjectSearchView {
this this
} }
fn semantic_permissioned(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<bool>> {
if let Some(value) = self.semantic_permissioned {
return Task::ready(Ok(value));
}
SemanticIndex::global(cx)
.map(|semantic| {
let project = self.model.read(cx).project.clone();
semantic.update(cx, |this, cx| this.project_previously_indexed(project, cx))
})
.unwrap_or(Task::ready(Ok(false)))
}
pub fn new_search_in_directory( pub fn new_search_in_directory(
workspace: &mut Workspace, workspace: &mut Workspace,
dir_entry: &Entry, dir_entry: &Entry,
@ -745,8 +947,26 @@ impl ProjectSearchView {
} }
fn search(&mut self, cx: &mut ViewContext<Self>) { fn search(&mut self, cx: &mut ViewContext<Self>) {
if let Some(query) = self.build_search_query(cx) { let mode = self.current_mode;
self.model.update(cx, |model, cx| model.search(query, cx)); match mode {
SearchMode::Semantic => {
if let Some(semantic) = &mut self.semantic_state {
if semantic.outstanding_file_count > 0 {
return;
}
if let Some(query) = self.build_search_query(cx) {
self.model
.update(cx, |model, cx| model.semantic_search(query.as_inner(), cx));
}
}
}
_ => {
if let Some(query) = self.build_search_query(cx) {
self.model.update(cx, |model, cx| model.search(query, cx));
}
}
} }
} }
@ -946,7 +1166,8 @@ impl ProjectSearchBar {
.and_then(|item| item.downcast::<ProjectSearchView>()) .and_then(|item| item.downcast::<ProjectSearchView>())
{ {
search_view.update(cx, |this, cx| { search_view.update(cx, |this, cx| {
let new_mode = crate::mode::next_mode(&this.current_mode); let new_mode =
crate::mode::next_mode(&this.current_mode, SemanticIndex::enabled(cx));
this.activate_search_mode(new_mode, cx); this.activate_search_mode(new_mode, cx);
cx.focus(&this.query_editor); cx.focus(&this.query_editor);
}) })
@ -1071,18 +1292,18 @@ impl ProjectSearchBar {
} }
} }
// fn activate_regex_mode(pane: &mut Pane, _: &ActivateRegexMode, cx: &mut ViewContext<Pane>) { fn activate_regex_mode(pane: &mut Pane, _: &ActivateRegexMode, cx: &mut ViewContext<Pane>) {
// if let Some(search_view) = pane if let Some(search_view) = pane
// .active_item() .active_item()
// .and_then(|item| item.downcast::<ProjectSearchView>()) .and_then(|item| item.downcast::<ProjectSearchView>())
// { {
// search_view.update(cx, |view, cx| { search_view.update(cx, |view, cx| {
// view.activate_search_mode(SearchMode::Regex, cx) view.activate_search_mode(SearchMode::Regex, cx)
// }); });
// } else { } else {
// cx.propagate_action(); cx.propagate_action();
// } }
// } }
fn toggle_filters(&mut self, cx: &mut ViewContext<Self>) -> bool { fn toggle_filters(&mut self, cx: &mut ViewContext<Self>) -> bool {
if let Some(search_view) = self.active_project_search.as_ref() { if let Some(search_view) = self.active_project_search.as_ref() {
@ -1195,7 +1416,8 @@ impl View for ProjectSearchBar {
}, },
cx, cx,
); );
let search = _search.read(cx);
let is_semantic_disabled = search.semantic_state.is_none();
let render_option_button_icon = |path, option, cx: &mut ViewContext<Self>| { let render_option_button_icon = |path, option, cx: &mut ViewContext<Self>| {
crate::search_bar::render_option_button_icon( crate::search_bar::render_option_button_icon(
self.is_option_enabled(option, cx), self.is_option_enabled(option, cx),
@ -1209,17 +1431,17 @@ impl View for ProjectSearchBar {
cx, cx,
) )
}; };
let case_sensitive = render_option_button_icon( let case_sensitive = is_semantic_disabled.then(|| {
"icons/case_insensitive_12.svg", render_option_button_icon(
SearchOptions::CASE_SENSITIVE, "icons/case_insensitive_12.svg",
cx, SearchOptions::CASE_SENSITIVE,
); cx,
)
});
let whole_word = render_option_button_icon( let whole_word = is_semantic_disabled.then(|| {
"icons/word_search_12.svg", render_option_button_icon("icons/word_search_12.svg", SearchOptions::WHOLE_WORD, cx)
SearchOptions::WHOLE_WORD, });
cx,
);
let search = _search.read(cx); let search = _search.read(cx);
let icon_style = theme.search.editor_icon.clone(); let icon_style = theme.search.editor_icon.clone();
@ -1235,8 +1457,8 @@ impl View for ProjectSearchBar {
.with_child( .with_child(
Flex::row() Flex::row()
.with_child(filter_button) .with_child(filter_button)
.with_child(case_sensitive) .with_children(case_sensitive)
.with_child(whole_word) .with_children(whole_word)
.flex(1., false) .flex(1., false)
.constrained() .constrained()
.contained(), .contained(),
@ -1335,7 +1557,8 @@ impl View for ProjectSearchBar {
) )
}; };
let is_active = search.active_match_index.is_some(); let is_active = search.active_match_index.is_some();
let semantic_index = SemanticIndex::enabled(cx)
.then(|| search_button_for_mode(SearchMode::Semantic, cx));
let nav_button_for_direction = |label, direction, cx: &mut ViewContext<Self>| { let nav_button_for_direction = |label, direction, cx: &mut ViewContext<Self>| {
render_nav_button( render_nav_button(
label, label,
@ -1361,6 +1584,7 @@ impl View for ProjectSearchBar {
.with_child( .with_child(
Flex::row() Flex::row()
.with_child(search_button_for_mode(SearchMode::Text, cx)) .with_child(search_button_for_mode(SearchMode::Text, cx))
.with_children(semantic_index)
.with_child(search_button_for_mode(SearchMode::Regex, cx)) .with_child(search_button_for_mode(SearchMode::Regex, cx))
.contained() .contained()
.with_style(theme.search.modes_container), .with_style(theme.search.modes_container),

View file

@ -8,9 +8,7 @@ use gpui::{
pub use mode::SearchMode; pub use mode::SearchMode;
use project::search::SearchQuery; use project::search::SearchQuery;
pub use project_search::{ProjectSearchBar, ProjectSearchView}; pub use project_search::{ProjectSearchBar, ProjectSearchView};
use theme::components::{ use theme::components::{action_button::ActionButton, ComponentExt, ToggleIconButtonStyle};
action_button::ActionButton, svg::Svg, ComponentExt, ToggleIconButtonStyle,
};
pub mod buffer_search; pub mod buffer_search;
mod history; mod history;
@ -35,6 +33,7 @@ actions!(
NextHistoryQuery, NextHistoryQuery,
PreviousHistoryQuery, PreviousHistoryQuery,
ActivateTextMode, ActivateTextMode,
ActivateSemanticMode,
ActivateRegexMode ActivateRegexMode
] ]
); );
@ -95,7 +94,7 @@ impl SearchOptions {
format!("Toggle {}", self.label()), format!("Toggle {}", self.label()),
tooltip_style, tooltip_style,
) )
.with_contents(Svg::new(self.icon())) .with_contents(theme::components::svg::Svg::new(self.icon()))
.toggleable(active) .toggleable(active)
.with_style(button_style) .with_style(button_style)
.element() .element()

View file

@ -1,5 +1,3 @@
#![allow(non_snake_case, non_upper_case_globals)]
mod keymap_file; mod keymap_file;
mod settings_file; mod settings_file;
mod settings_store; mod settings_store;

View file

@ -175,13 +175,8 @@ pub mod action_button {
.on_click(MouseButton::Left, { .on_click(MouseButton::Left, {
let action = self.action.boxed_clone(); let action = self.action.boxed_clone();
move |_, _, cx| { move |_, _, cx| {
let window = cx.window(); cx.window()
let view = cx.view_id(); .dispatch_action(cx.view_id(), action.as_ref(), cx);
let action = action.boxed_clone();
cx.spawn(|_, mut cx| async move {
window.dispatch_action(view, action.as_ref(), &mut cx);
})
.detach();
} }
}) })
.with_cursor_style(CursorStyle::PointingHand) .with_cursor_style(CursorStyle::PointingHand)