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
18 changed files with 316 additions and 270 deletions
|
@ -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);
|
||||
|
|
|
@ -1,342 +0,0 @@
|
|||
use crate::{
|
||||
fuzzy::{self, PathMatch},
|
||||
AppState,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use buffer::LanguageRegistry;
|
||||
use futures::Future;
|
||||
use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task};
|
||||
use rpc_client as rpc;
|
||||
use std::{
|
||||
path::Path,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
use util::TryFutureExt as _;
|
||||
use worktree::{fs::Fs, Worktree};
|
||||
|
||||
pub struct Project {
|
||||
worktrees: Vec<ModelHandle<Worktree>>,
|
||||
active_entry: Option<ProjectEntry>,
|
||||
languages: Arc<LanguageRegistry>,
|
||||
rpc: Arc<rpc::Client>,
|
||||
fs: Arc<dyn Fs>,
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
ActiveEntryChanged(Option<ProjectEntry>),
|
||||
WorktreeRemoved(usize),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct ProjectPath {
|
||||
pub worktree_id: usize,
|
||||
pub path: Arc<Path>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ProjectEntry {
|
||||
pub worktree_id: usize,
|
||||
pub entry_id: usize,
|
||||
}
|
||||
|
||||
impl Project {
|
||||
pub fn new(app_state: &AppState) -> Self {
|
||||
Self {
|
||||
worktrees: Default::default(),
|
||||
active_entry: None,
|
||||
languages: app_state.languages.clone(),
|
||||
rpc: app_state.rpc.clone(),
|
||||
fs: app_state.fs.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn worktrees(&self) -> &[ModelHandle<Worktree>] {
|
||||
&self.worktrees
|
||||
}
|
||||
|
||||
pub fn worktree_for_id(&self, id: usize) -> Option<ModelHandle<Worktree>> {
|
||||
self.worktrees
|
||||
.iter()
|
||||
.find(|worktree| worktree.id() == id)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
pub fn add_local_worktree(
|
||||
&mut self,
|
||||
abs_path: &Path,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<ModelHandle<Worktree>>> {
|
||||
let fs = self.fs.clone();
|
||||
let rpc = self.rpc.clone();
|
||||
let languages = self.languages.clone();
|
||||
let path = Arc::from(abs_path);
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let worktree = Worktree::open_local(rpc, path, fs, languages, &mut cx).await?;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.add_worktree(worktree.clone(), cx);
|
||||
});
|
||||
Ok(worktree)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_remote_worktree(
|
||||
&mut self,
|
||||
remote_id: u64,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<ModelHandle<Worktree>>> {
|
||||
let rpc = self.rpc.clone();
|
||||
let languages = self.languages.clone();
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
rpc.authenticate_and_connect(&cx).await?;
|
||||
let worktree =
|
||||
Worktree::open_remote(rpc.clone(), remote_id, languages, &mut cx).await?;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
cx.subscribe(&worktree, move |this, _, event, cx| match event {
|
||||
worktree::Event::Closed => {
|
||||
this.close_remote_worktree(remote_id, cx);
|
||||
cx.notify();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
this.add_worktree(worktree.clone(), cx);
|
||||
});
|
||||
Ok(worktree)
|
||||
})
|
||||
}
|
||||
|
||||
fn add_worktree(&mut self, worktree: ModelHandle<Worktree>, cx: &mut ModelContext<Self>) {
|
||||
cx.observe(&worktree, |_, _, cx| cx.notify()).detach();
|
||||
self.worktrees.push(worktree);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn set_active_path(&mut self, entry: Option<ProjectPath>, cx: &mut ModelContext<Self>) {
|
||||
let new_active_entry = entry.and_then(|project_path| {
|
||||
let worktree = self.worktree_for_id(project_path.worktree_id)?;
|
||||
let entry = worktree.read(cx).entry_for_path(project_path.path)?;
|
||||
Some(ProjectEntry {
|
||||
worktree_id: project_path.worktree_id,
|
||||
entry_id: entry.id,
|
||||
})
|
||||
});
|
||||
if new_active_entry != self.active_entry {
|
||||
self.active_entry = new_active_entry;
|
||||
cx.emit(Event::ActiveEntryChanged(new_active_entry));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn active_entry(&self) -> Option<ProjectEntry> {
|
||||
self.active_entry
|
||||
}
|
||||
|
||||
pub fn share_worktree(&self, remote_id: u64, cx: &mut ModelContext<Self>) {
|
||||
let rpc = self.rpc.clone();
|
||||
cx.spawn(|this, mut cx| {
|
||||
async move {
|
||||
rpc.authenticate_and_connect(&cx).await?;
|
||||
|
||||
let task = this.update(&mut cx, |this, cx| {
|
||||
for worktree in &this.worktrees {
|
||||
let task = worktree.update(cx, |worktree, cx| {
|
||||
worktree.as_local_mut().and_then(|worktree| {
|
||||
if worktree.remote_id() == Some(remote_id) {
|
||||
Some(worktree.share(cx))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
});
|
||||
if task.is_some() {
|
||||
return task;
|
||||
}
|
||||
}
|
||||
None
|
||||
});
|
||||
|
||||
if let Some(task) = task {
|
||||
task.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
.log_err()
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
pub fn unshare_worktree(&mut self, remote_id: u64, cx: &mut ModelContext<Self>) {
|
||||
for worktree in &self.worktrees {
|
||||
if worktree.update(cx, |worktree, cx| {
|
||||
if let Some(worktree) = worktree.as_local_mut() {
|
||||
if worktree.remote_id() == Some(remote_id) {
|
||||
worktree.unshare(cx);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn close_remote_worktree(&mut self, id: u64, cx: &mut ModelContext<Self>) {
|
||||
self.worktrees.retain(|worktree| {
|
||||
let keep = worktree.update(cx, |worktree, cx| {
|
||||
if let Some(worktree) = worktree.as_remote_mut() {
|
||||
if worktree.remote_id() == id {
|
||||
worktree.close_all_buffers(cx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
});
|
||||
if !keep {
|
||||
cx.emit(Event::WorktreeRemoved(worktree.id()));
|
||||
}
|
||||
keep
|
||||
});
|
||||
}
|
||||
|
||||
pub fn match_paths<'a>(
|
||||
&self,
|
||||
query: &'a str,
|
||||
include_ignored: bool,
|
||||
smart_case: bool,
|
||||
max_results: usize,
|
||||
cancel_flag: &'a AtomicBool,
|
||||
cx: &AppContext,
|
||||
) -> impl 'a + Future<Output = Vec<PathMatch>> {
|
||||
let snapshots = self
|
||||
.worktrees
|
||||
.iter()
|
||||
.map(|worktree| worktree.read(cx).snapshot())
|
||||
.collect::<Vec<_>>();
|
||||
let background = cx.background().clone();
|
||||
|
||||
async move {
|
||||
fuzzy::match_paths(
|
||||
snapshots.as_slice(),
|
||||
query,
|
||||
include_ignored,
|
||||
smart_case,
|
||||
max_results,
|
||||
cancel_flag,
|
||||
background,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for Project {
|
||||
type Event = Event;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test::test_app_state;
|
||||
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": "",
|
||||
"banana": {
|
||||
"carrot": {
|
||||
"date": "",
|
||||
"endive": "",
|
||||
}
|
||||
},
|
||||
"fennel": {
|
||||
"grape": "",
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
let root_link_path = dir.path().join("root_link");
|
||||
unix::fs::symlink(&dir.path().join("root"), &root_link_path).unwrap();
|
||||
unix::fs::symlink(
|
||||
&dir.path().join("root/fennel"),
|
||||
&dir.path().join("root/finnochio"),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let project = cx.add_model(|_| Project::new(app_state.as_ref()));
|
||||
let tree = project
|
||||
.update(&mut cx, |project, cx| {
|
||||
project.add_local_worktree(&root_link_path, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
|
||||
.await;
|
||||
cx.read(|cx| {
|
||||
let tree = tree.read(cx);
|
||||
assert_eq!(tree.file_count(), 5);
|
||||
assert_eq!(
|
||||
tree.inode_for_path("fennel/grape"),
|
||||
tree.inode_for_path("finnochio/grape")
|
||||
);
|
||||
});
|
||||
|
||||
let cancel_flag = Default::default();
|
||||
let results = project
|
||||
.read_with(&cx, |project, cx| {
|
||||
project.match_paths("bna", false, false, 10, &cancel_flag, cx)
|
||||
})
|
||||
.await;
|
||||
assert_eq!(
|
||||
results
|
||||
.into_iter()
|
||||
.map(|result| result.path)
|
||||
.collect::<Vec<Arc<Path>>>(),
|
||||
vec![
|
||||
PathBuf::from("banana/carrot/date").into(),
|
||||
PathBuf::from("banana/carrot/endive").into(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[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": {},
|
||||
"dir2": {
|
||||
"dir3": {}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
let project = cx.add_model(|_| Project::new(app_state.as_ref()));
|
||||
let tree = project
|
||||
.update(&mut cx, |project, cx| {
|
||||
project.add_local_worktree(&dir.path(), cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
|
||||
.await;
|
||||
|
||||
let cancel_flag = Default::default();
|
||||
let results = project
|
||||
.read_with(&cx, |project, cx| {
|
||||
project.match_paths("dir", false, false, 10, &cancel_flag, cx)
|
||||
})
|
||||
.await;
|
||||
|
||||
assert!(results.is_empty());
|
||||
}
|
||||
}
|
|
@ -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…
Add table
Add a link
Reference in a new issue