mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
project_search: Add ability to search only for opened files (#16580)
Any suggestion? Release Notes: - add ability for project search only for opend files. --------- Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
parent
403fdd6018
commit
400e503d9b
@ -4920,6 +4920,7 @@ async fn test_project_search(
|
|||||||
false,
|
false,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
cx,
|
cx,
|
||||||
|
@ -883,6 +883,7 @@ impl RandomizedTest for ProjectCollaborationTest {
|
|||||||
false,
|
false,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
cx,
|
cx,
|
||||||
|
@ -31,6 +31,7 @@ static MENTIONS_SEARCH: LazyLock<SearchQuery> = LazyLock::new(|| {
|
|||||||
false,
|
false,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
});
|
});
|
||||||
|
@ -7238,8 +7238,11 @@ impl Project {
|
|||||||
) -> Receiver<SearchResult> {
|
) -> Receiver<SearchResult> {
|
||||||
let (result_tx, result_rx) = smol::channel::unbounded();
|
let (result_tx, result_rx) = smol::channel::unbounded();
|
||||||
|
|
||||||
let matching_buffers_rx =
|
let matching_buffers_rx = if query.is_opened_only() {
|
||||||
self.search_for_candidate_buffers(&query, MAX_SEARCH_RESULT_FILES + 1, cx);
|
self.sort_candidate_buffers(&query, cx)
|
||||||
|
} else {
|
||||||
|
self.search_for_candidate_buffers(&query, MAX_SEARCH_RESULT_FILES + 1, cx)
|
||||||
|
};
|
||||||
|
|
||||||
cx.spawn(|_, cx| async move {
|
cx.spawn(|_, cx| async move {
|
||||||
let mut range_count = 0;
|
let mut range_count = 0;
|
||||||
@ -7317,6 +7320,48 @@ impl Project {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sort_candidate_buffers(
|
||||||
|
&mut self,
|
||||||
|
search_query: &SearchQuery,
|
||||||
|
cx: &mut ModelContext<Project>,
|
||||||
|
) -> Receiver<Model<Buffer>> {
|
||||||
|
let worktree_store = self.worktree_store.read(cx);
|
||||||
|
let mut buffers = search_query
|
||||||
|
.buffers()
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.filter(|buffer| {
|
||||||
|
let b = buffer.read(cx);
|
||||||
|
if let Some(file) = b.file() {
|
||||||
|
if !search_query.file_matches(file.path()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if let Some(entry) = b
|
||||||
|
.entry_id(cx)
|
||||||
|
.and_then(|entry_id| worktree_store.entry_for_id(entry_id, cx))
|
||||||
|
{
|
||||||
|
if entry.is_ignored && !search_query.include_ignored() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let (tx, rx) = smol::channel::unbounded();
|
||||||
|
buffers.sort_by(|a, b| match (a.read(cx).file(), b.read(cx).file()) {
|
||||||
|
(None, None) => a.read(cx).remote_id().cmp(&b.read(cx).remote_id()),
|
||||||
|
(None, Some(_)) => std::cmp::Ordering::Less,
|
||||||
|
(Some(_), None) => std::cmp::Ordering::Greater,
|
||||||
|
(Some(a), Some(b)) => compare_paths((a.path(), true), (b.path(), true)),
|
||||||
|
});
|
||||||
|
for buffer in buffers {
|
||||||
|
tx.send_blocking(buffer.clone()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
rx
|
||||||
|
}
|
||||||
|
|
||||||
fn search_for_candidate_buffers_remote(
|
fn search_for_candidate_buffers_remote(
|
||||||
&mut self,
|
&mut self,
|
||||||
query: &SearchQuery,
|
query: &SearchQuery,
|
||||||
|
@ -3941,7 +3941,8 @@ async fn test_search(cx: &mut gpui::TestAppContext) {
|
|||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
Default::default()
|
Default::default(),
|
||||||
|
None
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
cx
|
cx
|
||||||
@ -3974,7 +3975,8 @@ async fn test_search(cx: &mut gpui::TestAppContext) {
|
|||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
Default::default()
|
Default::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
cx
|
cx
|
||||||
@ -4017,7 +4019,8 @@ async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) {
|
|||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
PathMatcher::new(&["*.odd".to_owned()]).unwrap(),
|
PathMatcher::new(&["*.odd".to_owned()]).unwrap(),
|
||||||
Default::default()
|
Default::default(),
|
||||||
|
None
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
cx
|
cx
|
||||||
@ -4037,7 +4040,8 @@ async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) {
|
|||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
PathMatcher::new(&["*.rs".to_owned()]).unwrap(),
|
PathMatcher::new(&["*.rs".to_owned()]).unwrap(),
|
||||||
Default::default()
|
Default::default(),
|
||||||
|
None
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
cx
|
cx
|
||||||
@ -4063,6 +4067,7 @@ async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) {
|
|||||||
PathMatcher::new(&["*.ts".to_owned(), "*.odd".to_owned()]).unwrap(),
|
PathMatcher::new(&["*.ts".to_owned(), "*.odd".to_owned()]).unwrap(),
|
||||||
|
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
None,
|
||||||
).unwrap(),
|
).unwrap(),
|
||||||
cx
|
cx
|
||||||
)
|
)
|
||||||
@ -4087,6 +4092,7 @@ async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) {
|
|||||||
PathMatcher::new(&["*.rs".to_owned(), "*.ts".to_owned(), "*.odd".to_owned()]).unwrap(),
|
PathMatcher::new(&["*.rs".to_owned(), "*.ts".to_owned(), "*.odd".to_owned()]).unwrap(),
|
||||||
|
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
None,
|
||||||
).unwrap(),
|
).unwrap(),
|
||||||
cx
|
cx
|
||||||
)
|
)
|
||||||
@ -4131,6 +4137,7 @@ async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) {
|
|||||||
false,
|
false,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
PathMatcher::new(&["*.odd".to_owned()]).unwrap(),
|
PathMatcher::new(&["*.odd".to_owned()]).unwrap(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
cx
|
cx
|
||||||
@ -4155,7 +4162,8 @@ async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) {
|
|||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
PathMatcher::new(&["*.rs".to_owned()]).unwrap()
|
PathMatcher::new(&["*.rs".to_owned()]).unwrap(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
cx
|
cx
|
||||||
@ -4180,6 +4188,7 @@ async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) {
|
|||||||
Default::default(),
|
Default::default(),
|
||||||
|
|
||||||
PathMatcher::new(&["*.ts".to_owned(), "*.odd".to_owned()]).unwrap(),
|
PathMatcher::new(&["*.ts".to_owned(), "*.odd".to_owned()]).unwrap(),
|
||||||
|
None,
|
||||||
|
|
||||||
).unwrap(),
|
).unwrap(),
|
||||||
cx
|
cx
|
||||||
@ -4204,6 +4213,7 @@ async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) {
|
|||||||
Default::default(),
|
Default::default(),
|
||||||
|
|
||||||
PathMatcher::new(&["*.rs".to_owned(), "*.ts".to_owned(), "*.odd".to_owned()]).unwrap(),
|
PathMatcher::new(&["*.rs".to_owned(), "*.ts".to_owned(), "*.odd".to_owned()]).unwrap(),
|
||||||
|
None,
|
||||||
|
|
||||||
).unwrap(),
|
).unwrap(),
|
||||||
cx
|
cx
|
||||||
@ -4243,6 +4253,7 @@ async fn test_search_with_exclusions_and_inclusions(cx: &mut gpui::TestAppContex
|
|||||||
false,
|
false,
|
||||||
PathMatcher::new(&["*.odd".to_owned()]).unwrap(),
|
PathMatcher::new(&["*.odd".to_owned()]).unwrap(),
|
||||||
PathMatcher::new(&["*.odd".to_owned()]).unwrap(),
|
PathMatcher::new(&["*.odd".to_owned()]).unwrap(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
cx
|
cx
|
||||||
@ -4263,6 +4274,7 @@ async fn test_search_with_exclusions_and_inclusions(cx: &mut gpui::TestAppContex
|
|||||||
false,
|
false,
|
||||||
PathMatcher::new(&["*.ts".to_owned()]).unwrap(),
|
PathMatcher::new(&["*.ts".to_owned()]).unwrap(),
|
||||||
PathMatcher::new(&["*.ts".to_owned()]).unwrap(),
|
PathMatcher::new(&["*.ts".to_owned()]).unwrap(),
|
||||||
|
None,
|
||||||
).unwrap(),
|
).unwrap(),
|
||||||
cx
|
cx
|
||||||
)
|
)
|
||||||
@ -4282,6 +4294,7 @@ async fn test_search_with_exclusions_and_inclusions(cx: &mut gpui::TestAppContex
|
|||||||
false,
|
false,
|
||||||
PathMatcher::new(&["*.ts".to_owned(), "*.odd".to_owned()]).unwrap(),
|
PathMatcher::new(&["*.ts".to_owned(), "*.odd".to_owned()]).unwrap(),
|
||||||
PathMatcher::new(&["*.ts".to_owned(), "*.odd".to_owned()]).unwrap(),
|
PathMatcher::new(&["*.ts".to_owned(), "*.odd".to_owned()]).unwrap(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
cx
|
cx
|
||||||
@ -4302,6 +4315,7 @@ async fn test_search_with_exclusions_and_inclusions(cx: &mut gpui::TestAppContex
|
|||||||
false,
|
false,
|
||||||
PathMatcher::new(&["*.ts".to_owned(), "*.odd".to_owned()]).unwrap(),
|
PathMatcher::new(&["*.ts".to_owned(), "*.odd".to_owned()]).unwrap(),
|
||||||
PathMatcher::new(&["*.rs".to_owned(), "*.odd".to_owned()]).unwrap(),
|
PathMatcher::new(&["*.rs".to_owned(), "*.odd".to_owned()]).unwrap(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
cx
|
cx
|
||||||
@ -4354,7 +4368,8 @@ async fn test_search_multiple_worktrees_with_inclusions(cx: &mut gpui::TestAppCo
|
|||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
PathMatcher::new(&["worktree-a/*.rs".to_owned()]).unwrap(),
|
PathMatcher::new(&["worktree-a/*.rs".to_owned()]).unwrap(),
|
||||||
Default::default()
|
Default::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
cx
|
cx
|
||||||
@ -4373,7 +4388,8 @@ async fn test_search_multiple_worktrees_with_inclusions(cx: &mut gpui::TestAppCo
|
|||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
PathMatcher::new(&["worktree-b/*.rs".to_owned()]).unwrap(),
|
PathMatcher::new(&["worktree-b/*.rs".to_owned()]).unwrap(),
|
||||||
Default::default()
|
Default::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
cx
|
cx
|
||||||
@ -4393,7 +4409,8 @@ async fn test_search_multiple_worktrees_with_inclusions(cx: &mut gpui::TestAppCo
|
|||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
PathMatcher::new(&["*.ts".to_owned()]).unwrap(),
|
PathMatcher::new(&["*.ts".to_owned()]).unwrap(),
|
||||||
Default::default()
|
Default::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
cx
|
cx
|
||||||
@ -4447,7 +4464,8 @@ async fn test_search_in_gitignored_dirs(cx: &mut gpui::TestAppContext) {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
Default::default()
|
Default::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
cx
|
cx
|
||||||
@ -4468,7 +4486,8 @@ async fn test_search_in_gitignored_dirs(cx: &mut gpui::TestAppContext) {
|
|||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
Default::default()
|
Default::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
cx
|
cx
|
||||||
@ -4508,6 +4527,7 @@ async fn test_search_in_gitignored_dirs(cx: &mut gpui::TestAppContext) {
|
|||||||
true,
|
true,
|
||||||
files_to_include,
|
files_to_include,
|
||||||
files_to_exclude,
|
files_to_exclude,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
cx
|
cx
|
||||||
@ -4554,6 +4574,7 @@ async fn test_search_ordering(cx: &mut gpui::TestAppContext) {
|
|||||||
true,
|
true,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
cx,
|
cx,
|
||||||
|
@ -30,6 +30,7 @@ pub struct SearchInputs {
|
|||||||
query: Arc<str>,
|
query: Arc<str>,
|
||||||
files_to_include: PathMatcher,
|
files_to_include: PathMatcher,
|
||||||
files_to_exclude: PathMatcher,
|
files_to_exclude: PathMatcher,
|
||||||
|
buffers: Option<Vec<Model<Buffer>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SearchInputs {
|
impl SearchInputs {
|
||||||
@ -42,6 +43,9 @@ impl SearchInputs {
|
|||||||
pub fn files_to_exclude(&self) -> &PathMatcher {
|
pub fn files_to_exclude(&self) -> &PathMatcher {
|
||||||
&self.files_to_exclude
|
&self.files_to_exclude
|
||||||
}
|
}
|
||||||
|
pub fn buffers(&self) -> &Option<Vec<Model<Buffer>>> {
|
||||||
|
&self.buffers
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum SearchQuery {
|
pub enum SearchQuery {
|
||||||
@ -73,6 +77,7 @@ impl SearchQuery {
|
|||||||
include_ignored: bool,
|
include_ignored: bool,
|
||||||
files_to_include: PathMatcher,
|
files_to_include: PathMatcher,
|
||||||
files_to_exclude: PathMatcher,
|
files_to_exclude: PathMatcher,
|
||||||
|
buffers: Option<Vec<Model<Buffer>>>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let query = query.to_string();
|
let query = query.to_string();
|
||||||
let search = AhoCorasickBuilder::new()
|
let search = AhoCorasickBuilder::new()
|
||||||
@ -82,6 +87,7 @@ impl SearchQuery {
|
|||||||
query: query.into(),
|
query: query.into(),
|
||||||
files_to_exclude,
|
files_to_exclude,
|
||||||
files_to_include,
|
files_to_include,
|
||||||
|
buffers,
|
||||||
};
|
};
|
||||||
Ok(Self::Text {
|
Ok(Self::Text {
|
||||||
search: Arc::new(search),
|
search: Arc::new(search),
|
||||||
@ -100,6 +106,7 @@ impl SearchQuery {
|
|||||||
include_ignored: bool,
|
include_ignored: bool,
|
||||||
files_to_include: PathMatcher,
|
files_to_include: PathMatcher,
|
||||||
files_to_exclude: PathMatcher,
|
files_to_exclude: PathMatcher,
|
||||||
|
buffers: Option<Vec<Model<Buffer>>>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let mut query = query.to_string();
|
let mut query = query.to_string();
|
||||||
let initial_query = Arc::from(query.as_str());
|
let initial_query = Arc::from(query.as_str());
|
||||||
@ -120,6 +127,7 @@ impl SearchQuery {
|
|||||||
query: initial_query,
|
query: initial_query,
|
||||||
files_to_exclude,
|
files_to_exclude,
|
||||||
files_to_include,
|
files_to_include,
|
||||||
|
buffers,
|
||||||
};
|
};
|
||||||
Ok(Self::Regex {
|
Ok(Self::Regex {
|
||||||
regex,
|
regex,
|
||||||
@ -141,6 +149,7 @@ impl SearchQuery {
|
|||||||
message.include_ignored,
|
message.include_ignored,
|
||||||
deserialize_path_matches(&message.files_to_include)?,
|
deserialize_path_matches(&message.files_to_include)?,
|
||||||
deserialize_path_matches(&message.files_to_exclude)?,
|
deserialize_path_matches(&message.files_to_exclude)?,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Self::text(
|
Self::text(
|
||||||
@ -150,6 +159,7 @@ impl SearchQuery {
|
|||||||
message.include_ignored,
|
message.include_ignored,
|
||||||
deserialize_path_matches(&message.files_to_include)?,
|
deserialize_path_matches(&message.files_to_include)?,
|
||||||
deserialize_path_matches(&message.files_to_exclude)?,
|
deserialize_path_matches(&message.files_to_exclude)?,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,6 +173,7 @@ impl SearchQuery {
|
|||||||
message.include_ignored,
|
message.include_ignored,
|
||||||
deserialize_path_matches(&message.files_to_include)?,
|
deserialize_path_matches(&message.files_to_include)?,
|
||||||
deserialize_path_matches(&message.files_to_exclude)?,
|
deserialize_path_matches(&message.files_to_exclude)?,
|
||||||
|
None, // search opened only don't need search remote
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Self::text(
|
Self::text(
|
||||||
@ -172,6 +183,7 @@ impl SearchQuery {
|
|||||||
message.include_ignored,
|
message.include_ignored,
|
||||||
deserialize_path_matches(&message.files_to_include)?,
|
deserialize_path_matches(&message.files_to_include)?,
|
||||||
deserialize_path_matches(&message.files_to_exclude)?,
|
deserialize_path_matches(&message.files_to_exclude)?,
|
||||||
|
None, // search opened only don't need search remote
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -420,6 +432,14 @@ impl SearchQuery {
|
|||||||
self.as_inner().files_to_exclude()
|
self.as_inner().files_to_exclude()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn buffers(&self) -> Option<&Vec<Model<Buffer>>> {
|
||||||
|
self.as_inner().buffers.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_opened_only(&self) -> bool {
|
||||||
|
self.as_inner().buffers.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn filters_path(&self) -> bool {
|
pub fn filters_path(&self) -> bool {
|
||||||
!(self.files_to_exclude().sources().is_empty()
|
!(self.files_to_exclude().sources().is_empty()
|
||||||
&& self.files_to_include().sources().is_empty())
|
&& self.files_to_include().sources().is_empty())
|
||||||
|
@ -142,6 +142,7 @@ async fn test_remote_project_search(cx: &mut TestAppContext, server_cx: &mut Tes
|
|||||||
false,
|
false,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
cx,
|
cx,
|
||||||
|
@ -936,6 +936,7 @@ impl BufferSearchBar {
|
|||||||
false,
|
false,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
None,
|
||||||
) {
|
) {
|
||||||
Ok(query) => query.with_replacement(self.replacement(cx)),
|
Ok(query) => query.with_replacement(self.replacement(cx)),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
@ -953,6 +954,7 @@ impl BufferSearchBar {
|
|||||||
false,
|
false,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
None,
|
||||||
) {
|
) {
|
||||||
Ok(query) => query.with_replacement(self.replacement(cx)),
|
Ok(query) => query.with_replacement(self.replacement(cx)),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
@ -16,8 +16,9 @@ use gpui::{
|
|||||||
actions, div, Action, AnyElement, AnyView, AppContext, Context as _, EntityId, EventEmitter,
|
actions, div, Action, AnyElement, AnyView, AppContext, Context as _, EntityId, EventEmitter,
|
||||||
FocusHandle, FocusableView, Global, Hsla, InteractiveElement, IntoElement, KeyContext, Model,
|
FocusHandle, FocusableView, Global, Hsla, InteractiveElement, IntoElement, KeyContext, Model,
|
||||||
ModelContext, ParentElement, Point, Render, SharedString, Styled, Subscription, Task,
|
ModelContext, ParentElement, Point, Render, SharedString, Styled, Subscription, Task,
|
||||||
TextStyle, UpdateGlobal, View, ViewContext, VisualContext, WeakModel, WindowContext,
|
TextStyle, UpdateGlobal, View, ViewContext, VisualContext, WeakModel, WeakView, WindowContext,
|
||||||
};
|
};
|
||||||
|
use language::Buffer;
|
||||||
use menu::Confirm;
|
use menu::Confirm;
|
||||||
use project::{search::SearchQuery, search_history::SearchHistoryCursor, Project, ProjectPath};
|
use project::{search::SearchQuery, search_history::SearchHistoryCursor, Project, ProjectPath};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
@ -134,6 +135,7 @@ enum InputPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct ProjectSearchView {
|
pub struct ProjectSearchView {
|
||||||
|
workspace: WeakView<Workspace>,
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
model: Model<ProjectSearch>,
|
model: Model<ProjectSearch>,
|
||||||
query_editor: View<Editor>,
|
query_editor: View<Editor>,
|
||||||
@ -147,6 +149,7 @@ pub struct ProjectSearchView {
|
|||||||
excluded_files_editor: View<Editor>,
|
excluded_files_editor: View<Editor>,
|
||||||
filters_enabled: bool,
|
filters_enabled: bool,
|
||||||
replace_enabled: bool,
|
replace_enabled: bool,
|
||||||
|
included_opened_only: bool,
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -478,7 +481,7 @@ impl Item for ProjectSearchView {
|
|||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
let model = self.model.update(cx, |model, cx| model.clone(cx));
|
let model = self.model.update(cx, |model, cx| model.clone(cx));
|
||||||
Some(cx.new_view(|cx| Self::new(model, cx, None)))
|
Some(cx.new_view(|cx| Self::new(self.workspace.clone(), model, cx, None)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
|
fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
|
||||||
@ -556,6 +559,10 @@ impl ProjectSearchView {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn toggle_opened_only(&mut self, _cx: &mut ViewContext<Self>) {
|
||||||
|
self.included_opened_only = !self.included_opened_only;
|
||||||
|
}
|
||||||
|
|
||||||
fn replace_next(&mut self, _: &ReplaceNext, cx: &mut ViewContext<Self>) {
|
fn replace_next(&mut self, _: &ReplaceNext, cx: &mut ViewContext<Self>) {
|
||||||
if self.model.read(cx).match_ranges.is_empty() {
|
if self.model.read(cx).match_ranges.is_empty() {
|
||||||
return;
|
return;
|
||||||
@ -606,6 +613,7 @@ impl ProjectSearchView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn new(
|
fn new(
|
||||||
|
workspace: WeakView<Workspace>,
|
||||||
model: Model<ProjectSearch>,
|
model: Model<ProjectSearch>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
settings: Option<ProjectSearchSettings>,
|
settings: Option<ProjectSearchSettings>,
|
||||||
@ -711,6 +719,7 @@ impl ProjectSearchView {
|
|||||||
|
|
||||||
// Check if Worktrees have all been previously indexed
|
// Check if Worktrees have all been previously indexed
|
||||||
let mut this = ProjectSearchView {
|
let mut this = ProjectSearchView {
|
||||||
|
workspace,
|
||||||
focus_handle,
|
focus_handle,
|
||||||
replacement_editor,
|
replacement_editor,
|
||||||
search_id: model.read(cx).search_id,
|
search_id: model.read(cx).search_id,
|
||||||
@ -724,6 +733,7 @@ impl ProjectSearchView {
|
|||||||
excluded_files_editor,
|
excluded_files_editor,
|
||||||
filters_enabled,
|
filters_enabled,
|
||||||
replace_enabled: false,
|
replace_enabled: false,
|
||||||
|
included_opened_only: false,
|
||||||
_subscriptions: subscriptions,
|
_subscriptions: subscriptions,
|
||||||
};
|
};
|
||||||
this.model_changed(cx);
|
this.model_changed(cx);
|
||||||
@ -739,8 +749,10 @@ impl ProjectSearchView {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let weak_workspace = cx.view().downgrade();
|
||||||
|
|
||||||
let model = cx.new_model(|cx| ProjectSearch::new(workspace.project().clone(), cx));
|
let model = cx.new_model(|cx| ProjectSearch::new(workspace.project().clone(), cx));
|
||||||
let search = cx.new_view(|cx| ProjectSearchView::new(model, cx, None));
|
let search = cx.new_view(|cx| ProjectSearchView::new(weak_workspace, model, cx, None));
|
||||||
workspace.add_item_to_active_pane(Box::new(search.clone()), None, true, cx);
|
workspace.add_item_to_active_pane(Box::new(search.clone()), None, true, cx);
|
||||||
search.update(cx, |search, cx| {
|
search.update(cx, |search, cx| {
|
||||||
search
|
search
|
||||||
@ -790,8 +802,11 @@ impl ProjectSearchView {
|
|||||||
model.search(new_query, cx);
|
model.search(new_query, cx);
|
||||||
model
|
model
|
||||||
});
|
});
|
||||||
|
let weak_workspace = cx.view().downgrade();
|
||||||
workspace.add_item_to_active_pane(
|
workspace.add_item_to_active_pane(
|
||||||
Box::new(cx.new_view(|cx| ProjectSearchView::new(model, cx, None))),
|
Box::new(
|
||||||
|
cx.new_view(|cx| ProjectSearchView::new(weak_workspace, model, cx, None)),
|
||||||
|
),
|
||||||
None,
|
None,
|
||||||
true,
|
true,
|
||||||
cx,
|
cx,
|
||||||
@ -840,8 +855,11 @@ impl ProjectSearchView {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let weak_workspace = cx.view().downgrade();
|
||||||
|
|
||||||
let model = cx.new_model(|cx| ProjectSearch::new(workspace.project().clone(), cx));
|
let model = cx.new_model(|cx| ProjectSearch::new(workspace.project().clone(), cx));
|
||||||
let view = cx.new_view(|cx| ProjectSearchView::new(model, cx, settings));
|
let view =
|
||||||
|
cx.new_view(|cx| ProjectSearchView::new(weak_workspace, model, cx, settings));
|
||||||
|
|
||||||
workspace.add_item_to_active_pane(Box::new(view.clone()), None, true, cx);
|
workspace.add_item_to_active_pane(Box::new(view.clone()), None, true, cx);
|
||||||
view
|
view
|
||||||
@ -869,6 +887,11 @@ impl ProjectSearchView {
|
|||||||
fn build_search_query(&mut self, cx: &mut ViewContext<Self>) -> Option<SearchQuery> {
|
fn build_search_query(&mut self, cx: &mut ViewContext<Self>) -> Option<SearchQuery> {
|
||||||
// Do not bail early in this function, as we want to fill out `self.panels_with_errors`.
|
// Do not bail early in this function, as we want to fill out `self.panels_with_errors`.
|
||||||
let text = self.query_editor.read(cx).text(cx);
|
let text = self.query_editor.read(cx).text(cx);
|
||||||
|
let open_buffers = if self.included_opened_only {
|
||||||
|
Some(self.open_buffers(cx))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let included_files =
|
let included_files =
|
||||||
match Self::parse_path_matches(&self.included_files_editor.read(cx).text(cx)) {
|
match Self::parse_path_matches(&self.included_files_editor.read(cx).text(cx)) {
|
||||||
Ok(included_files) => {
|
Ok(included_files) => {
|
||||||
@ -913,6 +936,7 @@ impl ProjectSearchView {
|
|||||||
self.search_options.contains(SearchOptions::INCLUDE_IGNORED),
|
self.search_options.contains(SearchOptions::INCLUDE_IGNORED),
|
||||||
included_files,
|
included_files,
|
||||||
excluded_files,
|
excluded_files,
|
||||||
|
open_buffers,
|
||||||
) {
|
) {
|
||||||
Ok(query) => {
|
Ok(query) => {
|
||||||
let should_unmark_error = self.panels_with_errors.remove(&InputPanel::Query);
|
let should_unmark_error = self.panels_with_errors.remove(&InputPanel::Query);
|
||||||
@ -939,6 +963,7 @@ impl ProjectSearchView {
|
|||||||
self.search_options.contains(SearchOptions::INCLUDE_IGNORED),
|
self.search_options.contains(SearchOptions::INCLUDE_IGNORED),
|
||||||
included_files,
|
included_files,
|
||||||
excluded_files,
|
excluded_files,
|
||||||
|
open_buffers,
|
||||||
) {
|
) {
|
||||||
Ok(query) => {
|
Ok(query) => {
|
||||||
let should_unmark_error = self.panels_with_errors.remove(&InputPanel::Query);
|
let should_unmark_error = self.panels_with_errors.remove(&InputPanel::Query);
|
||||||
@ -967,6 +992,20 @@ impl ProjectSearchView {
|
|||||||
query
|
query
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn open_buffers(&self, cx: &mut ViewContext<Self>) -> Vec<Model<Buffer>> {
|
||||||
|
let mut buffers = Vec::new();
|
||||||
|
self.workspace
|
||||||
|
.update(cx, |workspace, cx| {
|
||||||
|
for editor in workspace.items_of_type::<Editor>(cx) {
|
||||||
|
if let Some(buffer) = editor.read(cx).buffer().read(cx).as_singleton() {
|
||||||
|
buffers.push(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
buffers
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_path_matches(text: &str) -> anyhow::Result<PathMatcher> {
|
fn parse_path_matches(text: &str) -> anyhow::Result<PathMatcher> {
|
||||||
let queries = text
|
let queries = text
|
||||||
.split(',')
|
.split(',')
|
||||||
@ -1272,6 +1311,30 @@ impl ProjectSearchBar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn toggle_opened_only(&mut self, cx: &mut ViewContext<Self>) -> bool {
|
||||||
|
if let Some(search_view) = self.active_project_search.as_ref() {
|
||||||
|
search_view.update(cx, |search_view, cx| {
|
||||||
|
search_view.toggle_opened_only(cx);
|
||||||
|
if search_view.model.read(cx).active_query.is_some() {
|
||||||
|
search_view.search(cx);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.notify();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_opened_only_enabled(&self, cx: &AppContext) -> bool {
|
||||||
|
if let Some(search_view) = self.active_project_search.as_ref() {
|
||||||
|
search_view.read(cx).included_opened_only
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn move_focus_to_results(&self, cx: &mut ViewContext<Self>) {
|
fn move_focus_to_results(&self, cx: &mut ViewContext<Self>) {
|
||||||
if let Some(search_view) = self.active_project_search.as_ref() {
|
if let Some(search_view) = self.active_project_search.as_ref() {
|
||||||
search_view.update(cx, |search_view, cx| {
|
search_view.update(cx, |search_view, cx| {
|
||||||
@ -1607,6 +1670,14 @@ impl Render for ProjectSearchBar {
|
|||||||
.rounded_lg()
|
.rounded_lg()
|
||||||
.child(self.render_text_input(&search.excluded_files_editor, cx)),
|
.child(self.render_text_input(&search.excluded_files_editor, cx)),
|
||||||
)
|
)
|
||||||
|
.child(
|
||||||
|
IconButton::new("project-search-opened-only", IconName::FileDoc)
|
||||||
|
.selected(self.is_opened_only_enabled(cx))
|
||||||
|
.tooltip(|cx| Tooltip::text("Only search open files", cx))
|
||||||
|
.on_click(cx.listener(|this, _, cx| {
|
||||||
|
this.toggle_opened_only(cx);
|
||||||
|
})),
|
||||||
|
)
|
||||||
.child(
|
.child(
|
||||||
SearchOptions::INCLUDE_IGNORED.as_button(
|
SearchOptions::INCLUDE_IGNORED.as_button(
|
||||||
search
|
search
|
||||||
@ -1779,8 +1850,12 @@ pub mod tests {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
|
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
|
||||||
let search = cx.new_model(|cx| ProjectSearch::new(project, cx));
|
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||||
let search_view = cx.add_window(|cx| ProjectSearchView::new(search.clone(), cx, None));
|
let workspace = window.root(cx).unwrap();
|
||||||
|
let search = cx.new_model(|cx| ProjectSearch::new(project.clone(), cx));
|
||||||
|
let search_view = cx.add_window(|cx| {
|
||||||
|
ProjectSearchView::new(workspace.downgrade(), search.clone(), cx, None)
|
||||||
|
});
|
||||||
|
|
||||||
perform_search(search_view, "TWO", cx);
|
perform_search(search_view, "TWO", cx);
|
||||||
search_view.update(cx, |search_view, cx| {
|
search_view.update(cx, |search_view, cx| {
|
||||||
@ -3253,8 +3328,12 @@ pub mod tests {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
|
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
|
||||||
|
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||||
|
let workspace = window.root(cx).unwrap();
|
||||||
let search = cx.new_model(|cx| ProjectSearch::new(project, cx));
|
let search = cx.new_model(|cx| ProjectSearch::new(project, cx));
|
||||||
let search_view = cx.add_window(|cx| ProjectSearchView::new(search.clone(), cx, None));
|
let search_view = cx.add_window(|cx| {
|
||||||
|
ProjectSearchView::new(workspace.downgrade(), search.clone(), cx, None)
|
||||||
|
});
|
||||||
|
|
||||||
// First search
|
// First search
|
||||||
perform_search(search_view, "A", cx);
|
perform_search(search_view, "A", cx);
|
||||||
|
@ -1245,6 +1245,7 @@ impl SearchableItem for TerminalView {
|
|||||||
query.include_ignored(),
|
query.include_ignored(),
|
||||||
query.files_to_include().clone(),
|
query.files_to_include().clone(),
|
||||||
query.files_to_exclude().clone(),
|
query.files_to_exclude().clone(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap()),
|
.unwrap()),
|
||||||
),
|
),
|
||||||
|
Loading…
Reference in New Issue
Block a user