mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-11 05:00:16 +00:00
Start work on opening files
This commit is contained in:
parent
3d6336b12a
commit
88b88a8067
6 changed files with 132 additions and 23 deletions
|
@ -18,11 +18,12 @@ use crate::{
|
|||
worktree::FileHandle,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use gpui::{Entity, ModelContext};
|
||||
use gpui::{AppContext, Entity, ModelContext};
|
||||
use lazy_static::lazy_static;
|
||||
use rand::prelude::*;
|
||||
use std::{
|
||||
cmp,
|
||||
ffi::OsString,
|
||||
hash::BuildHasher,
|
||||
iter::{self, Iterator},
|
||||
ops::{AddAssign, Range},
|
||||
|
@ -447,6 +448,10 @@ impl Buffer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn file_name(&self, ctx: &AppContext) -> Option<OsString> {
|
||||
self.file.as_ref().and_then(|file| file.file_name(ctx))
|
||||
}
|
||||
|
||||
pub fn path(&self) -> Option<Arc<Path>> {
|
||||
self.file.as_ref().map(|file| file.path())
|
||||
}
|
||||
|
|
|
@ -1362,11 +1362,8 @@ impl workspace::ItemView for BufferView {
|
|||
}
|
||||
|
||||
fn title(&self, app: &AppContext) -> std::string::String {
|
||||
if let Some(path) = self.buffer.read(app).path() {
|
||||
path.file_name()
|
||||
.expect("buffer's path is always to a file")
|
||||
.to_string_lossy()
|
||||
.into()
|
||||
if let Some(name) = self.buffer.read(app).file_name(app) {
|
||||
name.to_string_lossy().into()
|
||||
} else {
|
||||
"untitled".into()
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ fn open_paths(params: &OpenParams, app: &mut MutableAppContext) {
|
|||
if let Some(handle) = app.root_view::<WorkspaceView>(window_id) {
|
||||
if handle.update(app, |view, ctx| {
|
||||
if view.contains_paths(¶ms.paths, ctx.as_ref()) {
|
||||
view.open_paths(¶ms.paths, ctx.as_mut());
|
||||
view.open_paths(¶ms.paths, ctx);
|
||||
log::info!("open paths on existing workspace");
|
||||
true
|
||||
} else {
|
||||
|
@ -67,8 +67,12 @@ fn open_paths(params: &OpenParams, app: &mut MutableAppContext) {
|
|||
log::info!("open new workspace");
|
||||
|
||||
// Add a new workspace if necessary
|
||||
let workspace = app.add_model(|ctx| Workspace::new(params.paths.clone(), ctx));
|
||||
app.add_window(|ctx| WorkspaceView::new(workspace, params.settings.clone(), ctx));
|
||||
let workspace = app.add_model(|ctx| Workspace::new(vec![], ctx));
|
||||
app.add_window(|ctx| {
|
||||
let view = WorkspaceView::new(workspace, params.settings.clone(), ctx);
|
||||
view.open_paths(¶ms.paths, ctx);
|
||||
view
|
||||
});
|
||||
}
|
||||
|
||||
fn quit(_: &(), app: &mut MutableAppContext) {
|
||||
|
|
|
@ -117,23 +117,31 @@ impl Workspace {
|
|||
.any(|worktree| worktree.read(app).contains_abs_path(path))
|
||||
}
|
||||
|
||||
pub fn open_paths(&mut self, paths: &[PathBuf], ctx: &mut ModelContext<Self>) {
|
||||
for path in paths.iter().cloned() {
|
||||
self.open_path(path, ctx);
|
||||
}
|
||||
pub fn open_paths(
|
||||
&mut self,
|
||||
paths: &[PathBuf],
|
||||
ctx: &mut ModelContext<Self>,
|
||||
) -> Vec<(usize, Arc<Path>)> {
|
||||
paths
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(move |path| self.open_path(path, ctx))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn open_path<'a>(&'a mut self, path: PathBuf, ctx: &mut ModelContext<Self>) {
|
||||
fn open_path(&mut self, path: PathBuf, ctx: &mut ModelContext<Self>) -> (usize, Arc<Path>) {
|
||||
for tree in self.worktrees.iter() {
|
||||
if tree.read(ctx).contains_abs_path(&path) {
|
||||
return;
|
||||
if let Ok(relative_path) = path.strip_prefix(tree.read(ctx).abs_path()) {
|
||||
return (tree.id(), relative_path.into());
|
||||
}
|
||||
}
|
||||
|
||||
let worktree = ctx.add_model(|ctx| Worktree::new(path, ctx));
|
||||
let worktree = ctx.add_model(|ctx| Worktree::new(path.clone(), ctx));
|
||||
let worktree_id = worktree.id();
|
||||
ctx.observe(&worktree, Self::on_worktree_updated);
|
||||
self.worktrees.insert(worktree);
|
||||
ctx.notify();
|
||||
(worktree_id, Path::new("").into())
|
||||
}
|
||||
|
||||
pub fn open_entry(
|
||||
|
@ -174,7 +182,6 @@ impl Workspace {
|
|||
let replica_id = self.replica_id;
|
||||
let file = worktree.file(path.clone(), ctx.as_ref())?;
|
||||
let history = file.load_history(ctx.as_ref());
|
||||
// let buffer = async move { Ok(Buffer::from_history(replica_id, file, history.await?)) };
|
||||
|
||||
let (mut tx, rx) = watch::channel(None);
|
||||
self.items.insert(item_key, OpenedItem::Loading(rx));
|
||||
|
|
|
@ -161,9 +161,23 @@ impl WorkspaceView {
|
|||
self.workspace.read(app).contains_paths(paths, app)
|
||||
}
|
||||
|
||||
pub fn open_paths(&self, paths: &[PathBuf], app: &mut MutableAppContext) {
|
||||
self.workspace
|
||||
.update(app, |workspace, ctx| workspace.open_paths(paths, ctx));
|
||||
pub fn open_paths(&self, paths: &[PathBuf], ctx: &mut ViewContext<Self>) {
|
||||
let entries = self
|
||||
.workspace
|
||||
.update(ctx, |workspace, ctx| workspace.open_paths(paths, ctx));
|
||||
for (i, entry) in entries.into_iter().enumerate() {
|
||||
let path = paths[i].clone();
|
||||
ctx.spawn(
|
||||
ctx.background_executor()
|
||||
.spawn(async move { path.is_file() }),
|
||||
|me, is_file, ctx| {
|
||||
if is_file {
|
||||
me.open_entry(entry, ctx)
|
||||
}
|
||||
},
|
||||
)
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle_modal<V, F>(&mut self, ctx: &mut ViewContext<Self>, add_view: F)
|
||||
|
@ -382,6 +396,7 @@ mod tests {
|
|||
use crate::{settings, test::temp_tree, workspace::WorkspaceHandle as _};
|
||||
use gpui::App;
|
||||
use serde_json::json;
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[test]
|
||||
fn test_open_entry() {
|
||||
|
@ -444,6 +459,67 @@ mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_open_paths() {
|
||||
App::test_async((), |mut app| async move {
|
||||
let dir1 = temp_tree(json!({
|
||||
"a.txt": "",
|
||||
}));
|
||||
let dir2 = temp_tree(json!({
|
||||
"b.txt": "",
|
||||
}));
|
||||
|
||||
let workspace = app.add_model(|ctx| Workspace::new(vec![dir1.path().into()], ctx));
|
||||
let settings = settings::channel(&app.font_cache()).unwrap().1;
|
||||
let (_, workspace_view) =
|
||||
app.add_window(|ctx| WorkspaceView::new(workspace.clone(), settings, ctx));
|
||||
app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx))
|
||||
.await;
|
||||
|
||||
// Open a file within an existing worktree.
|
||||
app.update(|ctx| {
|
||||
workspace_view.update(ctx, |view, ctx| {
|
||||
view.open_paths(&[dir1.path().join("a.txt")], ctx);
|
||||
});
|
||||
});
|
||||
workspace_view
|
||||
.condition(&app, |view, ctx| {
|
||||
view.active_pane()
|
||||
.read(ctx)
|
||||
.active_item()
|
||||
.map_or(false, |item| item.title(&ctx) == "a.txt")
|
||||
})
|
||||
.await;
|
||||
|
||||
// Open a file outside of any existing worktree.
|
||||
app.update(|ctx| {
|
||||
workspace_view.update(ctx, |view, ctx| {
|
||||
view.open_paths(&[dir2.path().join("b.txt")], ctx);
|
||||
});
|
||||
let worktree_roots = workspace
|
||||
.read(ctx)
|
||||
.worktrees()
|
||||
.iter()
|
||||
.map(|w| w.read(ctx).abs_path())
|
||||
.collect::<HashSet<_>>();
|
||||
assert_eq!(
|
||||
worktree_roots,
|
||||
vec![dir1.path(), &dir2.path().join("b.txt")]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
);
|
||||
});
|
||||
workspace_view
|
||||
.condition(&app, |view, ctx| {
|
||||
view.active_pane()
|
||||
.read(ctx)
|
||||
.active_item()
|
||||
.map_or(false, |item| item.title(&ctx) == "b.txt")
|
||||
})
|
||||
.await;
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pane_actions() {
|
||||
App::test_async((), |mut app| async move {
|
||||
|
|
|
@ -20,7 +20,7 @@ use smol::{channel::Sender, Timer};
|
|||
use std::{
|
||||
cmp,
|
||||
collections::{HashMap, HashSet},
|
||||
ffi::{CStr, OsStr},
|
||||
ffi::{CStr, OsStr, OsString},
|
||||
fmt, fs,
|
||||
future::Future,
|
||||
io::{self, Read, Write},
|
||||
|
@ -163,6 +163,10 @@ impl Worktree {
|
|||
self.snapshot.clone()
|
||||
}
|
||||
|
||||
pub fn abs_path(&self) -> &Path {
|
||||
self.snapshot.abs_path.as_ref()
|
||||
}
|
||||
|
||||
pub fn contains_abs_path(&self, path: &Path) -> bool {
|
||||
path.starts_with(&self.snapshot.abs_path)
|
||||
}
|
||||
|
@ -172,7 +176,11 @@ impl Worktree {
|
|||
path: &Path,
|
||||
ctx: &AppContext,
|
||||
) -> impl Future<Output = Result<History>> {
|
||||
let abs_path = self.snapshot.abs_path.join(path);
|
||||
let abs_path = if path.file_name().is_some() {
|
||||
self.snapshot.abs_path.join(path)
|
||||
} else {
|
||||
self.snapshot.abs_path.to_path_buf()
|
||||
};
|
||||
ctx.background_executor().spawn(async move {
|
||||
let mut file = std::fs::File::open(&abs_path)?;
|
||||
let mut base_text = String::new();
|
||||
|
@ -381,10 +389,22 @@ impl fmt::Debug for Snapshot {
|
|||
}
|
||||
|
||||
impl FileHandle {
|
||||
/// Returns this file's path relative to the root of its worktree.
|
||||
pub fn path(&self) -> Arc<Path> {
|
||||
self.state.lock().path.clone()
|
||||
}
|
||||
|
||||
/// Returns the last component of this handle's absolute path. If this handle refers to the root
|
||||
/// of its worktree, then this method will return the name of the worktree itself.
|
||||
pub fn file_name<'a>(&'a self, ctx: &'a AppContext) -> Option<OsString> {
|
||||
self.state
|
||||
.lock()
|
||||
.path
|
||||
.file_name()
|
||||
.or_else(|| self.worktree.read(ctx).abs_path().file_name())
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
pub fn is_deleted(&self) -> bool {
|
||||
self.state.lock().is_deleted
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue