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:
parent
6e3e61ec95
commit
aeda5d9842
4 changed files with 75 additions and 64 deletions
|
@ -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>> {
|
||||||
|
|
|
@ -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,
|
||||||
))
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue