mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-07 20:39:04 +03:00
Rename worktree crate to project, pull in Project
Also, move the high-level fuzzy mathcing functions in zed::fuzzy into the fuzzy crate so that project can use them. This required defining a 'PathMatchCandidateSet' trait to avoid a circular dependency from fuzzy to worktree. Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
748598e419
commit
2f0212ee98
66
Cargo.lock
generated
66
Cargo.lock
generated
@ -2097,6 +2097,10 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "fuzzy"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"gpui",
|
||||
"util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generator"
|
||||
@ -3674,6 +3678,36 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"buffer",
|
||||
"clock",
|
||||
"fsevent",
|
||||
"futures",
|
||||
"fuzzy",
|
||||
"gpui",
|
||||
"ignore",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"postage",
|
||||
"rand 0.8.3",
|
||||
"rpc",
|
||||
"rpc_client",
|
||||
"serde 1.0.125",
|
||||
"serde_json 1.0.64",
|
||||
"smol",
|
||||
"sum_tree",
|
||||
"tempdir",
|
||||
"toml 0.5.8",
|
||||
"util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost"
|
||||
version = "0.8.0"
|
||||
@ -5975,36 +6009,6 @@ dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "worktree"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"buffer",
|
||||
"clock",
|
||||
"fsevent",
|
||||
"futures",
|
||||
"fuzzy",
|
||||
"gpui",
|
||||
"ignore",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"postage",
|
||||
"rand 0.8.3",
|
||||
"rpc",
|
||||
"rpc_client",
|
||||
"serde 1.0.125",
|
||||
"serde_json 1.0.64",
|
||||
"smol",
|
||||
"sum_tree",
|
||||
"tempdir",
|
||||
"toml 0.5.8",
|
||||
"util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.2.0"
|
||||
@ -6070,6 +6074,7 @@ dependencies = [
|
||||
"num_cpus",
|
||||
"parking_lot",
|
||||
"postage",
|
||||
"project",
|
||||
"rand 0.8.3",
|
||||
"rpc",
|
||||
"rpc_client",
|
||||
@ -6093,7 +6098,6 @@ dependencies = [
|
||||
"unindent",
|
||||
"url",
|
||||
"util",
|
||||
"worktree",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2,3 +2,7 @@
|
||||
name = "fuzzy"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
gpui = { path = "../gpui" }
|
||||
util = { path = "../util" }
|
||||
|
@ -1,8 +1,9 @@
|
||||
mod char_bag;
|
||||
|
||||
use gpui::executor;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
cmp::Ordering,
|
||||
cmp::{self, Ordering},
|
||||
path::Path,
|
||||
sync::atomic::{self, AtomicBool},
|
||||
sync::Arc,
|
||||
@ -58,6 +59,14 @@ pub struct StringMatchCandidate {
|
||||
pub char_bag: CharBag,
|
||||
}
|
||||
|
||||
pub trait PathMatchCandidateSet<'a>: Send + Sync {
|
||||
type Candidates: Iterator<Item = PathMatchCandidate<'a>>;
|
||||
fn id(&self) -> usize;
|
||||
fn len(&self) -> usize;
|
||||
fn prefix(&self) -> Arc<str>;
|
||||
fn candidates(&'a self, start: usize) -> Self::Candidates;
|
||||
}
|
||||
|
||||
impl Match for PathMatch {
|
||||
fn score(&self) -> f64 {
|
||||
self.score
|
||||
@ -152,6 +161,140 @@ impl Ord for PathMatch {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn match_strings(
|
||||
candidates: &[StringMatchCandidate],
|
||||
query: &str,
|
||||
smart_case: bool,
|
||||
max_results: usize,
|
||||
cancel_flag: &AtomicBool,
|
||||
background: Arc<executor::Background>,
|
||||
) -> Vec<StringMatch> {
|
||||
let lowercase_query = query.to_lowercase().chars().collect::<Vec<_>>();
|
||||
let query = query.chars().collect::<Vec<_>>();
|
||||
|
||||
let lowercase_query = &lowercase_query;
|
||||
let query = &query;
|
||||
let query_char_bag = CharBag::from(&lowercase_query[..]);
|
||||
|
||||
let num_cpus = background.num_cpus().min(candidates.len());
|
||||
let segment_size = (candidates.len() + num_cpus - 1) / num_cpus;
|
||||
let mut segment_results = (0..num_cpus)
|
||||
.map(|_| Vec::with_capacity(max_results))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
background
|
||||
.scoped(|scope| {
|
||||
for (segment_idx, results) in segment_results.iter_mut().enumerate() {
|
||||
let cancel_flag = &cancel_flag;
|
||||
scope.spawn(async move {
|
||||
let segment_start = segment_idx * segment_size;
|
||||
let segment_end = segment_start + segment_size;
|
||||
let mut matcher = Matcher::new(
|
||||
query,
|
||||
lowercase_query,
|
||||
query_char_bag,
|
||||
smart_case,
|
||||
max_results,
|
||||
);
|
||||
matcher.match_strings(
|
||||
&candidates[segment_start..segment_end],
|
||||
results,
|
||||
cancel_flag,
|
||||
);
|
||||
});
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
let mut results = Vec::new();
|
||||
for segment_result in segment_results {
|
||||
if results.is_empty() {
|
||||
results = segment_result;
|
||||
} else {
|
||||
util::extend_sorted(&mut results, segment_result, max_results, |a, b| b.cmp(&a));
|
||||
}
|
||||
}
|
||||
results
|
||||
}
|
||||
|
||||
pub async fn match_paths<'a, Set: PathMatchCandidateSet<'a>>(
|
||||
candidate_sets: &'a [Set],
|
||||
query: &str,
|
||||
smart_case: bool,
|
||||
max_results: usize,
|
||||
cancel_flag: &AtomicBool,
|
||||
background: Arc<executor::Background>,
|
||||
) -> Vec<PathMatch> {
|
||||
let path_count: usize = candidate_sets.iter().map(|s| s.len()).sum();
|
||||
if path_count == 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let lowercase_query = query.to_lowercase().chars().collect::<Vec<_>>();
|
||||
let query = query.chars().collect::<Vec<_>>();
|
||||
|
||||
let lowercase_query = &lowercase_query;
|
||||
let query = &query;
|
||||
let query_char_bag = CharBag::from(&lowercase_query[..]);
|
||||
|
||||
let num_cpus = background.num_cpus().min(path_count);
|
||||
let segment_size = (path_count + num_cpus - 1) / num_cpus;
|
||||
let mut segment_results = (0..num_cpus)
|
||||
.map(|_| Vec::with_capacity(max_results))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
background
|
||||
.scoped(|scope| {
|
||||
for (segment_idx, results) in segment_results.iter_mut().enumerate() {
|
||||
scope.spawn(async move {
|
||||
let segment_start = segment_idx * segment_size;
|
||||
let segment_end = segment_start + segment_size;
|
||||
let mut matcher = Matcher::new(
|
||||
query,
|
||||
lowercase_query,
|
||||
query_char_bag,
|
||||
smart_case,
|
||||
max_results,
|
||||
);
|
||||
|
||||
let mut tree_start = 0;
|
||||
for candidate_set in candidate_sets {
|
||||
let tree_end = tree_start + candidate_set.len();
|
||||
|
||||
if tree_start < segment_end && segment_start < tree_end {
|
||||
let start = cmp::max(tree_start, segment_start) - tree_start;
|
||||
let end = cmp::min(tree_end, segment_end) - tree_start;
|
||||
let candidates = candidate_set.candidates(start).take(end - start);
|
||||
|
||||
matcher.match_paths(
|
||||
candidate_set.id(),
|
||||
candidate_set.prefix(),
|
||||
candidates,
|
||||
results,
|
||||
&cancel_flag,
|
||||
);
|
||||
}
|
||||
if tree_end >= segment_end {
|
||||
break;
|
||||
}
|
||||
tree_start = tree_end;
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
let mut results = Vec::new();
|
||||
for segment_result in segment_results {
|
||||
if results.is_empty() {
|
||||
results = segment_result;
|
||||
} else {
|
||||
util::extend_sorted(&mut results, segment_result, max_results, |a, b| b.cmp(&a));
|
||||
}
|
||||
}
|
||||
results
|
||||
}
|
||||
|
||||
impl<'a> Matcher<'a> {
|
||||
pub fn new(
|
||||
query: &'a [char],
|
||||
@ -194,11 +337,11 @@ impl<'a> Matcher<'a> {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn match_paths(
|
||||
pub fn match_paths<'c: 'a>(
|
||||
&mut self,
|
||||
tree_id: usize,
|
||||
path_prefix: Arc<str>,
|
||||
path_entries: impl Iterator<Item = PathMatchCandidate<'a>>,
|
||||
path_entries: impl Iterator<Item = PathMatchCandidate<'c>>,
|
||||
results: &mut Vec<PathMatch>,
|
||||
cancel_flag: &AtomicBool,
|
||||
) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "worktree"
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
@ -1,10 +1,11 @@
|
||||
use crate::{
|
||||
fuzzy::{self, PathMatch},
|
||||
AppState,
|
||||
};
|
||||
pub mod fs;
|
||||
mod ignore;
|
||||
mod worktree;
|
||||
|
||||
use anyhow::Result;
|
||||
use buffer::LanguageRegistry;
|
||||
use futures::Future;
|
||||
use fuzzy::{self, PathMatch, PathMatchCandidate, PathMatchCandidateSet};
|
||||
use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task};
|
||||
use rpc_client as rpc;
|
||||
use std::{
|
||||
@ -12,7 +13,9 @@ use std::{
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
use util::TryFutureExt as _;
|
||||
use worktree::{fs::Fs, Worktree};
|
||||
|
||||
pub use fs::*;
|
||||
pub use worktree::*;
|
||||
|
||||
pub struct Project {
|
||||
worktrees: Vec<ModelHandle<Worktree>>,
|
||||
@ -40,13 +43,13 @@ pub struct ProjectEntry {
|
||||
}
|
||||
|
||||
impl Project {
|
||||
pub fn new(app_state: &AppState) -> Self {
|
||||
pub fn new(languages: Arc<LanguageRegistry>, rpc: Arc<rpc::Client>, fs: Arc<dyn Fs>) -> Self {
|
||||
Self {
|
||||
worktrees: Default::default(),
|
||||
active_entry: None,
|
||||
languages: app_state.languages.clone(),
|
||||
rpc: app_state.rpc.clone(),
|
||||
fs: app_state.fs.clone(),
|
||||
languages,
|
||||
rpc,
|
||||
fs,
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,18 +210,22 @@ impl Project {
|
||||
cancel_flag: &'a AtomicBool,
|
||||
cx: &AppContext,
|
||||
) -> impl 'a + Future<Output = Vec<PathMatch>> {
|
||||
let snapshots = self
|
||||
let include_root_name = self.worktrees.len() > 1;
|
||||
let candidate_sets = self
|
||||
.worktrees
|
||||
.iter()
|
||||
.map(|worktree| worktree.read(cx).snapshot())
|
||||
.map(|worktree| CandidateSet {
|
||||
snapshot: worktree.read(cx).snapshot(),
|
||||
include_ignored,
|
||||
include_root_name,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let background = cx.background().clone();
|
||||
|
||||
let background = cx.background().clone();
|
||||
async move {
|
||||
fuzzy::match_paths(
|
||||
snapshots.as_slice(),
|
||||
candidate_sets.as_slice(),
|
||||
query,
|
||||
include_ignored,
|
||||
smart_case,
|
||||
max_results,
|
||||
cancel_flag,
|
||||
@ -229,6 +236,65 @@ impl Project {
|
||||
}
|
||||
}
|
||||
|
||||
struct CandidateSet {
|
||||
snapshot: Snapshot,
|
||||
include_ignored: bool,
|
||||
include_root_name: bool,
|
||||
}
|
||||
|
||||
impl<'a> PathMatchCandidateSet<'a> for CandidateSet {
|
||||
type Candidates = CandidateSetIter<'a>;
|
||||
|
||||
fn id(&self) -> usize {
|
||||
self.snapshot.id()
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
if self.include_ignored {
|
||||
self.snapshot.file_count()
|
||||
} else {
|
||||
self.snapshot.visible_file_count()
|
||||
}
|
||||
}
|
||||
|
||||
fn prefix(&self) -> Arc<str> {
|
||||
if self.snapshot.root_entry().map_or(false, |e| e.is_file()) {
|
||||
self.snapshot.root_name().into()
|
||||
} else if self.include_root_name {
|
||||
format!("{}/", self.snapshot.root_name()).into()
|
||||
} else {
|
||||
"".into()
|
||||
}
|
||||
}
|
||||
|
||||
fn candidates(&'a self, start: usize) -> Self::Candidates {
|
||||
CandidateSetIter {
|
||||
traversal: self.snapshot.files(self.include_ignored, start),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CandidateSetIter<'a> {
|
||||
traversal: Traversal<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for CandidateSetIter<'a> {
|
||||
type Item = PathMatchCandidate<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.traversal.next().map(|entry| {
|
||||
if let EntryKind::File(char_bag) = entry.kind {
|
||||
PathMatchCandidate {
|
||||
path: &entry.path,
|
||||
char_bag,
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for Project {
|
||||
type Event = Event;
|
||||
}
|
||||
@ -236,16 +302,15 @@ impl Entity for Project {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test::test_app_state;
|
||||
use buffer::LanguageRegistry;
|
||||
use fs::RealFs;
|
||||
use gpui::TestAppContext;
|
||||
use serde_json::json;
|
||||
use std::{os::unix, path::PathBuf};
|
||||
use util::test::temp_tree;
|
||||
use worktree::fs::RealFs;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_populate_and_search(mut cx: gpui::TestAppContext) {
|
||||
let mut app_state = cx.update(test_app_state);
|
||||
Arc::get_mut(&mut app_state).unwrap().fs = Arc::new(RealFs);
|
||||
let dir = temp_tree(json!({
|
||||
"root": {
|
||||
"apple": "",
|
||||
@ -269,7 +334,8 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let project = cx.add_model(|_| Project::new(app_state.as_ref()));
|
||||
let project = build_project(&mut cx);
|
||||
|
||||
let tree = project
|
||||
.update(&mut cx, |project, cx| {
|
||||
project.add_local_worktree(&root_link_path, cx)
|
||||
@ -308,8 +374,6 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_search_worktree_without_files(mut cx: gpui::TestAppContext) {
|
||||
let mut app_state = cx.update(test_app_state);
|
||||
Arc::get_mut(&mut app_state).unwrap().fs = Arc::new(RealFs);
|
||||
let dir = temp_tree(json!({
|
||||
"root": {
|
||||
"dir1": {},
|
||||
@ -319,7 +383,7 @@ mod tests {
|
||||
}
|
||||
}));
|
||||
|
||||
let project = cx.add_model(|_| Project::new(app_state.as_ref()));
|
||||
let project = build_project(&mut cx);
|
||||
let tree = project
|
||||
.update(&mut cx, |project, cx| {
|
||||
project.add_local_worktree(&dir.path(), cx)
|
||||
@ -339,4 +403,11 @@ mod tests {
|
||||
|
||||
assert!(results.is_empty());
|
||||
}
|
||||
|
||||
fn build_project(cx: &mut TestAppContext) -> ModelHandle<Project> {
|
||||
let languages = Arc::new(LanguageRegistry::new());
|
||||
let fs = Arc::new(RealFs);
|
||||
let rpc = rpc::Client::new();
|
||||
cx.add_model(|_| Project::new(languages, rpc, fs))
|
||||
}
|
||||
}
|
@ -1,12 +1,11 @@
|
||||
pub mod fs;
|
||||
mod ignore;
|
||||
|
||||
use self::ignore::IgnoreStack;
|
||||
use super::{
|
||||
fs::{self, Fs},
|
||||
ignore::IgnoreStack,
|
||||
};
|
||||
use ::ignore::gitignore::{Gitignore, GitignoreBuilder};
|
||||
use anyhow::{anyhow, Result};
|
||||
use buffer::{self, Buffer, History, LanguageRegistry, Operation, Rope};
|
||||
use clock::ReplicaId;
|
||||
pub use fs::*;
|
||||
use futures::{Stream, StreamExt};
|
||||
use fuzzy::CharBag;
|
||||
use gpui::{
|
@ -981,12 +981,11 @@ mod tests {
|
||||
editor::{Editor, EditorSettings, Insert},
|
||||
fs::{FakeFs, Fs as _},
|
||||
people_panel::JoinWorktree,
|
||||
project::ProjectPath,
|
||||
project::{ProjectPath, Worktree},
|
||||
rpc::{self, Client, Credentials, EstablishConnectionError},
|
||||
test::FakeHttpClient,
|
||||
user::UserStore,
|
||||
workspace::Workspace,
|
||||
worktree::Worktree,
|
||||
};
|
||||
|
||||
#[gpui::test]
|
||||
|
@ -17,10 +17,10 @@ path = "src/main.rs"
|
||||
test-support = [
|
||||
"buffer/test-support",
|
||||
"gpui/test-support",
|
||||
"project/test-support",
|
||||
"rpc/test-support",
|
||||
"rpc_client/test-support",
|
||||
"tempdir",
|
||||
"worktree/test-support",
|
||||
"rpc/test-support",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
@ -30,11 +30,11 @@ fsevent = { path = "../fsevent" }
|
||||
fuzzy = { path = "../fuzzy" }
|
||||
editor = { path = "../editor" }
|
||||
gpui = { path = "../gpui" }
|
||||
project = { path = "../project" }
|
||||
rpc = { path = "../rpc" }
|
||||
rpc_client = { path = "../rpc_client" }
|
||||
sum_tree = { path = "../sum_tree" }
|
||||
util = { path = "../util" }
|
||||
worktree = { path = "../worktree" }
|
||||
|
||||
anyhow = "1.0.38"
|
||||
async-recursion = "0.3"
|
||||
@ -79,10 +79,10 @@ url = "2.2"
|
||||
buffer = { path = "../buffer", features = ["test-support"] }
|
||||
editor = { path = "../editor", features = ["test-support"] }
|
||||
gpui = { path = "../gpui", features = ["test-support"] }
|
||||
project = { path = "../project", features = ["test-support"] }
|
||||
rpc = { path = "../rpc", features = ["test-support"] }
|
||||
rpc_client = { path = "../rpc_client", features = ["test-support"] }
|
||||
util = { path = "../util", features = ["test-support"] }
|
||||
worktree = { path = "../worktree", features = ["test-support"] }
|
||||
|
||||
cargo-bundle = "0.5.0"
|
||||
env_logger = "0.8"
|
||||
|
@ -1,10 +1,6 @@
|
||||
use crate::{
|
||||
fuzzy::PathMatch,
|
||||
project::{Project, ProjectPath},
|
||||
settings::Settings,
|
||||
workspace::Workspace,
|
||||
};
|
||||
use crate::{settings::Settings, workspace::Workspace};
|
||||
use editor::{self, Editor, EditorSettings};
|
||||
use fuzzy::PathMatch;
|
||||
use gpui::{
|
||||
action,
|
||||
elements::*,
|
||||
@ -17,6 +13,7 @@ use gpui::{
|
||||
ViewContext, ViewHandle, WeakViewHandle,
|
||||
};
|
||||
use postage::watch;
|
||||
use project::{Project, ProjectPath};
|
||||
use std::{
|
||||
cmp,
|
||||
path::Path,
|
||||
@ -427,9 +424,9 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::{test::test_app_state, workspace::Workspace};
|
||||
use editor::{self, Insert};
|
||||
use project::fs::FakeFs;
|
||||
use serde_json::json;
|
||||
use std::path::PathBuf;
|
||||
use worktree::fs::FakeFs;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_matching_paths(mut cx: gpui::TestAppContext) {
|
||||
|
@ -1,173 +0,0 @@
|
||||
use gpui::executor;
|
||||
use std::{
|
||||
cmp,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
use util;
|
||||
use worktree::{EntryKind, Snapshot};
|
||||
|
||||
pub use fuzzy::*;
|
||||
|
||||
pub async fn match_strings(
|
||||
candidates: &[StringMatchCandidate],
|
||||
query: &str,
|
||||
smart_case: bool,
|
||||
max_results: usize,
|
||||
cancel_flag: &AtomicBool,
|
||||
background: Arc<executor::Background>,
|
||||
) -> Vec<StringMatch> {
|
||||
let lowercase_query = query.to_lowercase().chars().collect::<Vec<_>>();
|
||||
let query = query.chars().collect::<Vec<_>>();
|
||||
|
||||
let lowercase_query = &lowercase_query;
|
||||
let query = &query;
|
||||
let query_char_bag = CharBag::from(&lowercase_query[..]);
|
||||
|
||||
let num_cpus = background.num_cpus().min(candidates.len());
|
||||
let segment_size = (candidates.len() + num_cpus - 1) / num_cpus;
|
||||
let mut segment_results = (0..num_cpus)
|
||||
.map(|_| Vec::with_capacity(max_results))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
background
|
||||
.scoped(|scope| {
|
||||
for (segment_idx, results) in segment_results.iter_mut().enumerate() {
|
||||
let cancel_flag = &cancel_flag;
|
||||
scope.spawn(async move {
|
||||
let segment_start = segment_idx * segment_size;
|
||||
let segment_end = segment_start + segment_size;
|
||||
let mut matcher = Matcher::new(
|
||||
query,
|
||||
lowercase_query,
|
||||
query_char_bag,
|
||||
smart_case,
|
||||
max_results,
|
||||
);
|
||||
matcher.match_strings(
|
||||
&candidates[segment_start..segment_end],
|
||||
results,
|
||||
cancel_flag,
|
||||
);
|
||||
});
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
let mut results = Vec::new();
|
||||
for segment_result in segment_results {
|
||||
if results.is_empty() {
|
||||
results = segment_result;
|
||||
} else {
|
||||
util::extend_sorted(&mut results, segment_result, max_results, |a, b| b.cmp(&a));
|
||||
}
|
||||
}
|
||||
results
|
||||
}
|
||||
|
||||
pub async fn match_paths(
|
||||
snapshots: &[Snapshot],
|
||||
query: &str,
|
||||
include_ignored: bool,
|
||||
smart_case: bool,
|
||||
max_results: usize,
|
||||
cancel_flag: &AtomicBool,
|
||||
background: Arc<executor::Background>,
|
||||
) -> Vec<PathMatch> {
|
||||
let path_count: usize = if include_ignored {
|
||||
snapshots.iter().map(Snapshot::file_count).sum()
|
||||
} else {
|
||||
snapshots.iter().map(Snapshot::visible_file_count).sum()
|
||||
};
|
||||
if path_count == 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let lowercase_query = query.to_lowercase().chars().collect::<Vec<_>>();
|
||||
let query = query.chars().collect::<Vec<_>>();
|
||||
|
||||
let lowercase_query = &lowercase_query;
|
||||
let query = &query;
|
||||
let query_char_bag = CharBag::from(&lowercase_query[..]);
|
||||
|
||||
let num_cpus = background.num_cpus().min(path_count);
|
||||
let segment_size = (path_count + num_cpus - 1) / num_cpus;
|
||||
let mut segment_results = (0..num_cpus)
|
||||
.map(|_| Vec::with_capacity(max_results))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
background
|
||||
.scoped(|scope| {
|
||||
for (segment_idx, results) in segment_results.iter_mut().enumerate() {
|
||||
scope.spawn(async move {
|
||||
let segment_start = segment_idx * segment_size;
|
||||
let segment_end = segment_start + segment_size;
|
||||
let mut matcher = Matcher::new(
|
||||
query,
|
||||
lowercase_query,
|
||||
query_char_bag,
|
||||
smart_case,
|
||||
max_results,
|
||||
);
|
||||
|
||||
let mut tree_start = 0;
|
||||
for snapshot in snapshots {
|
||||
let tree_end = if include_ignored {
|
||||
tree_start + snapshot.file_count()
|
||||
} else {
|
||||
tree_start + snapshot.visible_file_count()
|
||||
};
|
||||
|
||||
if tree_start < segment_end && segment_start < tree_end {
|
||||
let path_prefix: Arc<str> =
|
||||
if snapshot.root_entry().map_or(false, |e| e.is_file()) {
|
||||
snapshot.root_name().into()
|
||||
} else if snapshots.len() > 1 {
|
||||
format!("{}/", snapshot.root_name()).into()
|
||||
} else {
|
||||
"".into()
|
||||
};
|
||||
|
||||
let start = cmp::max(tree_start, segment_start) - tree_start;
|
||||
let end = cmp::min(tree_end, segment_end) - tree_start;
|
||||
let paths = snapshot
|
||||
.files(include_ignored, start)
|
||||
.take(end - start)
|
||||
.map(|entry| {
|
||||
if let EntryKind::File(char_bag) = entry.kind {
|
||||
PathMatchCandidate {
|
||||
path: &entry.path,
|
||||
char_bag,
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
});
|
||||
|
||||
matcher.match_paths(
|
||||
snapshot.id(),
|
||||
path_prefix,
|
||||
paths,
|
||||
results,
|
||||
&cancel_flag,
|
||||
);
|
||||
}
|
||||
if tree_end >= segment_end {
|
||||
break;
|
||||
}
|
||||
tree_start = tree_end;
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
let mut results = Vec::new();
|
||||
for segment_result in segment_results {
|
||||
if results.is_empty() {
|
||||
results = segment_result;
|
||||
} else {
|
||||
util::extend_sorted(&mut results, segment_result, max_results, |a, b| b.cmp(&a));
|
||||
}
|
||||
}
|
||||
results
|
||||
}
|
@ -2,12 +2,10 @@ pub mod assets;
|
||||
pub mod channel;
|
||||
pub mod chat_panel;
|
||||
pub mod file_finder;
|
||||
mod fuzzy;
|
||||
pub mod http;
|
||||
pub mod language;
|
||||
pub mod menus;
|
||||
pub mod people_panel;
|
||||
pub mod project;
|
||||
pub mod project_panel;
|
||||
pub mod settings;
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
@ -24,11 +22,11 @@ pub use editor;
|
||||
use gpui::{action, keymap::Binding, ModelHandle};
|
||||
use parking_lot::Mutex;
|
||||
use postage::watch;
|
||||
pub use project::{self, fs};
|
||||
pub use rpc_client as rpc;
|
||||
pub use settings::Settings;
|
||||
use std::sync::Arc;
|
||||
use util::TryFutureExt;
|
||||
pub use worktree::{self, fs};
|
||||
|
||||
action!(About);
|
||||
action!(Quit);
|
||||
|
@ -20,12 +20,12 @@ use gpui::{
|
||||
ViewContext, ViewHandle, WeakViewHandle,
|
||||
};
|
||||
use postage::watch;
|
||||
use project::Worktree;
|
||||
use std::{
|
||||
collections::{hash_map, HashMap},
|
||||
ffi::OsStr,
|
||||
ops::Range,
|
||||
};
|
||||
use worktree::Worktree;
|
||||
|
||||
pub struct ProjectPanel {
|
||||
project: ModelHandle<Project>,
|
||||
@ -288,7 +288,7 @@ impl ProjectPanel {
|
||||
&self,
|
||||
target_ix: usize,
|
||||
cx: &'a AppContext,
|
||||
) -> Option<(&'a Worktree, &'a worktree::Entry)> {
|
||||
) -> Option<(&'a Worktree, &'a project::Entry)> {
|
||||
let project = self.project.read(cx);
|
||||
let mut offset = None;
|
||||
let mut ix = 0;
|
||||
@ -309,10 +309,7 @@ impl ProjectPanel {
|
||||
})
|
||||
}
|
||||
|
||||
fn selected_entry<'a>(
|
||||
&self,
|
||||
cx: &'a AppContext,
|
||||
) -> Option<(&'a Worktree, &'a worktree::Entry)> {
|
||||
fn selected_entry<'a>(&self, cx: &'a AppContext) -> Option<(&'a Worktree, &'a project::Entry)> {
|
||||
let selection = self.selection?;
|
||||
let project = self.project.read(cx);
|
||||
let worktree = project.worktree_for_id(selection.worktree_id)?.read(cx);
|
||||
@ -626,7 +623,13 @@ mod tests {
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = cx.add_model(|_| Project::new(&app_state));
|
||||
let project = cx.add_model(|_| {
|
||||
Project::new(
|
||||
app_state.languages.clone(),
|
||||
app_state.rpc.clone(),
|
||||
app_state.fs.clone(),
|
||||
)
|
||||
});
|
||||
let root1 = project
|
||||
.update(&mut cx, |project, cx| {
|
||||
project.add_local_worktree("/root1".as_ref(), cx)
|
||||
|
@ -12,9 +12,9 @@ use buffer::LanguageRegistry;
|
||||
use futures::{future::BoxFuture, Future};
|
||||
use gpui::MutableAppContext;
|
||||
use parking_lot::Mutex;
|
||||
use project::fs::FakeFs;
|
||||
use rpc_client as rpc;
|
||||
use std::{fmt, sync::Arc};
|
||||
use worktree::fs::FakeFs;
|
||||
|
||||
#[cfg(test)]
|
||||
#[ctor::ctor]
|
||||
|
@ -1,12 +1,6 @@
|
||||
use std::{cmp, sync::Arc};
|
||||
|
||||
use crate::{
|
||||
fuzzy::{match_strings, StringMatch, StringMatchCandidate},
|
||||
settings::ThemeRegistry,
|
||||
workspace::Workspace,
|
||||
AppState, Settings,
|
||||
};
|
||||
use crate::{settings::ThemeRegistry, workspace::Workspace, AppState, Settings};
|
||||
use editor::{self, Editor, EditorSettings};
|
||||
use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
|
||||
use gpui::{
|
||||
action,
|
||||
elements::*,
|
||||
@ -16,6 +10,7 @@ use gpui::{
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use postage::watch;
|
||||
use std::{cmp, sync::Arc};
|
||||
|
||||
pub struct ThemeSelector {
|
||||
settings_tx: Arc<Mutex<watch::Sender<Settings>>>,
|
||||
|
@ -32,13 +32,13 @@ use log::error;
|
||||
pub use pane::*;
|
||||
pub use pane_group::*;
|
||||
use postage::{prelude::Stream, watch};
|
||||
use project::Worktree;
|
||||
use std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
future::Future,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use worktree::Worktree;
|
||||
|
||||
action!(Open, Arc<AppState>);
|
||||
action!(OpenPaths, OpenParams);
|
||||
@ -376,7 +376,13 @@ pub struct Workspace {
|
||||
|
||||
impl Workspace {
|
||||
pub fn new(app_state: &AppState, cx: &mut ViewContext<Self>) -> Self {
|
||||
let project = cx.add_model(|_| Project::new(app_state));
|
||||
let project = cx.add_model(|_| {
|
||||
Project::new(
|
||||
app_state.languages.clone(),
|
||||
app_state.rpc.clone(),
|
||||
app_state.fs.clone(),
|
||||
)
|
||||
});
|
||||
cx.observe(&project, |_, _, cx| cx.notify()).detach();
|
||||
|
||||
let pane = cx.add_view(|_| Pane::new(app_state.settings.clone()));
|
||||
|
@ -5,8 +5,8 @@ use buffer::{Buffer, File as _};
|
||||
use editor::{Editor, EditorSettings, Event};
|
||||
use gpui::{fonts::TextStyle, AppContext, ModelHandle, Task, ViewContext};
|
||||
use postage::watch;
|
||||
use project::Worktree;
|
||||
use std::path::Path;
|
||||
use worktree::Worktree;
|
||||
|
||||
impl Item for Buffer {
|
||||
type View = Editor;
|
||||
|
Loading…
Reference in New Issue
Block a user