fix semantic search panic which is created via incompatible build_search_query path

Co-authored-by: Piotr <piotr@zed.dev>
This commit is contained in:
KCaverly 2023-08-16 15:50:54 +01:00
parent 6e3e61ec95
commit aeda5d9842
4 changed files with 75 additions and 64 deletions

View file

@ -13,24 +13,39 @@ use std::{
sync::Arc, sync::Arc,
}; };
#[derive(Clone, Debug)]
pub struct SearchInputs {
query: Arc<str>,
files_to_include: Vec<PathMatcher>,
files_to_exclude: Vec<PathMatcher>,
}
impl SearchInputs {
pub fn as_str(&self) -> &str {
self.query.as_ref()
}
pub fn files_to_include(&self) -> &[PathMatcher] {
&self.files_to_include
}
pub fn files_to_exclude(&self) -> &[PathMatcher] {
&self.files_to_exclude
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum SearchQuery { pub enum SearchQuery {
Text { Text {
search: Arc<AhoCorasick<usize>>, search: Arc<AhoCorasick<usize>>,
query: Arc<str>,
whole_word: bool, whole_word: bool,
case_sensitive: bool, case_sensitive: bool,
files_to_include: Vec<PathMatcher>, inner: SearchInputs,
files_to_exclude: Vec<PathMatcher>,
}, },
Regex { Regex {
regex: Regex, regex: Regex,
query: Arc<str>,
multiline: bool, multiline: bool,
whole_word: bool, whole_word: bool,
case_sensitive: bool, case_sensitive: bool,
files_to_include: Vec<PathMatcher>, inner: SearchInputs,
files_to_exclude: Vec<PathMatcher>,
}, },
} }
@ -72,13 +87,16 @@ impl SearchQuery {
.auto_configure(&[&query]) .auto_configure(&[&query])
.ascii_case_insensitive(!case_sensitive) .ascii_case_insensitive(!case_sensitive)
.build(&[&query]); .build(&[&query]);
let inner = SearchInputs {
query: query.into(),
files_to_exclude,
files_to_include,
};
Self::Text { Self::Text {
search: Arc::new(search), search: Arc::new(search),
query: Arc::from(query),
whole_word, whole_word,
case_sensitive, case_sensitive,
files_to_include, inner,
files_to_exclude,
} }
} }
@ -104,14 +122,17 @@ impl SearchQuery {
.case_insensitive(!case_sensitive) .case_insensitive(!case_sensitive)
.multi_line(multiline) .multi_line(multiline)
.build()?; .build()?;
let inner = SearchInputs {
query: initial_query,
files_to_exclude,
files_to_include,
};
Ok(Self::Regex { Ok(Self::Regex {
regex, regex,
query: initial_query,
multiline, multiline,
whole_word, whole_word,
case_sensitive, case_sensitive,
files_to_include, inner,
files_to_exclude,
}) })
} }
@ -267,10 +288,7 @@ impl SearchQuery {
} }
pub fn as_str(&self) -> &str { pub fn as_str(&self) -> &str {
match self { self.as_inner().as_str()
Self::Text { query, .. } => query.as_ref(),
Self::Regex { query, .. } => query.as_ref(),
}
} }
pub fn whole_word(&self) -> bool { pub fn whole_word(&self) -> bool {
@ -292,25 +310,11 @@ impl SearchQuery {
} }
pub fn files_to_include(&self) -> &[PathMatcher] { pub fn files_to_include(&self) -> &[PathMatcher] {
match self { self.as_inner().files_to_include()
Self::Text {
files_to_include, ..
} => files_to_include,
Self::Regex {
files_to_include, ..
} => files_to_include,
}
} }
pub fn files_to_exclude(&self) -> &[PathMatcher] { pub fn files_to_exclude(&self) -> &[PathMatcher] {
match self { self.as_inner().files_to_exclude()
Self::Text {
files_to_exclude, ..
} => files_to_exclude,
Self::Regex {
files_to_exclude, ..
} => files_to_exclude,
}
} }
pub fn file_matches(&self, file_path: Option<&Path>) -> bool { pub fn file_matches(&self, file_path: Option<&Path>) -> bool {
@ -329,6 +333,11 @@ impl SearchQuery {
None => self.files_to_include().is_empty(), None => self.files_to_include().is_empty(),
} }
} }
pub fn as_inner(&self) -> &SearchInputs {
match self {
Self::Regex { inner, .. } | Self::Text { inner, .. } => inner,
}
}
} }
fn deserialize_path_matches(glob_set: &str) -> anyhow::Result<Vec<PathMatcher>> { fn deserialize_path_matches(glob_set: &str) -> anyhow::Result<Vec<PathMatcher>> {

View file

@ -24,7 +24,7 @@ use gpui::{
use menu::Confirm; use menu::Confirm;
use postage::stream::Stream; use postage::stream::Stream;
use project::{ use project::{
search::{PathMatcher, SearchQuery}, search::{PathMatcher, SearchInputs, SearchQuery},
Entry, Project, Entry, Project,
}; };
use semantic_index::SemanticIndex; use semantic_index::SemanticIndex;
@ -177,10 +177,12 @@ impl ProjectSearch {
} }
fn kill_search(&mut self) { fn kill_search(&mut self) {
dbg!("Killing search");
self.active_query = None; self.active_query = None;
self.match_ranges.clear(); self.match_ranges.clear();
self.pending_search = None; self.pending_search = None;
self.no_results = None; self.no_results = None;
dbg!("Killed search");
} }
fn search(&mut self, query: SearchQuery, cx: &mut ModelContext<Self>) { fn search(&mut self, query: SearchQuery, cx: &mut ModelContext<Self>) {
@ -226,22 +228,22 @@ impl ProjectSearch {
cx.notify(); cx.notify();
} }
fn semantic_search(&mut self, query: SearchQuery, cx: &mut ModelContext<Self>) { fn semantic_search(&mut self, inputs: &SearchInputs, cx: &mut ModelContext<Self>) {
let search = SemanticIndex::global(cx).map(|index| { let search = SemanticIndex::global(cx).map(|index| {
index.update(cx, |semantic_index, cx| { index.update(cx, |semantic_index, cx| {
semantic_index.search_project( semantic_index.search_project(
self.project.clone(), self.project.clone(),
query.as_str().to_owned(), inputs.as_str().to_owned(),
10, 10,
query.files_to_include().to_vec(), inputs.files_to_include().to_vec(),
query.files_to_exclude().to_vec(), inputs.files_to_exclude().to_vec(),
cx, cx,
) )
}) })
}); });
self.search_id += 1; self.search_id += 1;
self.match_ranges.clear(); self.match_ranges.clear();
self.search_history.add(query.as_str().to_string()); self.search_history.add(inputs.as_str().to_string());
self.no_results = Some(true); self.no_results = Some(true);
self.pending_search = Some(cx.spawn(|this, mut cx| async move { self.pending_search = Some(cx.spawn(|this, mut cx| async move {
let results = search?.await.log_err()?; let results = search?.await.log_err()?;
@ -682,6 +684,7 @@ impl ProjectSearchView {
fn activate_search_mode(&mut self, mode: SearchMode, cx: &mut ViewContext<Self>) { fn activate_search_mode(&mut self, mode: SearchMode, cx: &mut ViewContext<Self>) {
let previous_mode = self.current_mode; let previous_mode = self.current_mode;
log::error!("Going from {previous_mode:?} to {:?}", mode);
if previous_mode == mode { if previous_mode == mode {
return; return;
} }
@ -690,6 +693,7 @@ impl ProjectSearchView {
match mode { match mode {
SearchMode::Semantic => { SearchMode::Semantic => {
dbg!("Matched on Semantic");
let has_permission = self.semantic_permissioned(cx); let has_permission = self.semantic_permissioned(cx);
self.active_match_index = None; self.active_match_index = None;
cx.spawn(|this, mut cx| async move { cx.spawn(|this, mut cx| async move {
@ -947,7 +951,7 @@ impl ProjectSearchView {
if let Some(query) = self.build_search_query(cx) { if let Some(query) = self.build_search_query(cx) {
self.model self.model
.update(cx, |model, cx| model.semantic_search(query, cx)); .update(cx, |model, cx| model.semantic_search(query.as_inner(), cx));
} }
} }
} }
@ -986,7 +990,9 @@ impl ProjectSearchView {
return None; return None;
} }
}; };
if self.current_mode == SearchMode::Regex { let current_mode = self.current_mode;
match current_mode {
SearchMode::Regex => {
match SearchQuery::regex( match SearchQuery::regex(
text, text,
self.search_options.contains(SearchOptions::WHOLE_WORD), self.search_options.contains(SearchOptions::WHOLE_WORD),
@ -1004,15 +1010,14 @@ impl ProjectSearchView {
None None
} }
} }
} else { }
debug_assert_ne!(self.current_mode, SearchMode::Semantic); _ => Some(SearchQuery::text(
Some(SearchQuery::text(
text, text,
self.search_options.contains(SearchOptions::WHOLE_WORD), self.search_options.contains(SearchOptions::WHOLE_WORD),
self.search_options.contains(SearchOptions::CASE_SENSITIVE), self.search_options.contains(SearchOptions::CASE_SENSITIVE),
included_files, included_files,
excluded_files, excluded_files,
)) )),
} }
} }

View file

@ -483,10 +483,8 @@ fn possible_open_targets(
} }
pub fn regex_search_for_query(query: project::search::SearchQuery) -> Option<RegexSearch> { pub fn regex_search_for_query(query: project::search::SearchQuery) -> Option<RegexSearch> {
let searcher = match query { let query = query.as_str();
project::search::SearchQuery::Text { query, .. } => RegexSearch::new(&query), let searcher = RegexSearch::new(&query);
project::search::SearchQuery::Regex { query, .. } => RegexSearch::new(&query),
};
searcher.ok() searcher.ok()
} }

View file

@ -2,7 +2,6 @@ import { with_opacity } from "../theme/color"
import { background, border, foreground, text } from "./components" import { background, border, foreground, text } from "./components"
import { interactive, toggleable } from "../element" import { interactive, toggleable } from "../element"
import { useTheme } from "../theme" import { useTheme } from "../theme"
import { toggleable_icon_button } from "../component/icon_button"
export default function search(): any { export default function search(): any {
const theme = useTheme() const theme = useTheme()