mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-28 21:43:45 +03:00
Start work on creating and saving new files
This commit is contained in:
parent
2ce9f271b5
commit
5fd084ec09
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -449,7 +449,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "cocoa"
|
name = "cocoa"
|
||||||
version = "0.24.0"
|
version = "0.24.0"
|
||||||
source = "git+https://github.com/servo/core-foundation-rs?rev=e9a65bb15d591ec22649e03659db8095d4f2dd60#e9a65bb15d591ec22649e03659db8095d4f2dd60"
|
source = "git+https://github.com/zed-industries/core-foundation-rs?rev=39e1e0eeef11a17cf49aa6a500c37e665d967d2a#39e1e0eeef11a17cf49aa6a500c37e665d967d2a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.2.1",
|
"bitflags 1.2.1",
|
||||||
"block",
|
"block",
|
||||||
@ -464,7 +464,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "cocoa-foundation"
|
name = "cocoa-foundation"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/servo/core-foundation-rs?rev=e9a65bb15d591ec22649e03659db8095d4f2dd60#e9a65bb15d591ec22649e03659db8095d4f2dd60"
|
source = "git+https://github.com/zed-industries/core-foundation-rs?rev=39e1e0eeef11a17cf49aa6a500c37e665d967d2a#39e1e0eeef11a17cf49aa6a500c37e665d967d2a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.2.1",
|
"bitflags 1.2.1",
|
||||||
"block",
|
"block",
|
||||||
@ -499,7 +499,7 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.1"
|
version = "0.9.1"
|
||||||
source = "git+https://github.com/servo/core-foundation-rs?rev=e9a65bb15d591ec22649e03659db8095d4f2dd60#e9a65bb15d591ec22649e03659db8095d4f2dd60"
|
source = "git+https://github.com/zed-industries/core-foundation-rs?rev=39e1e0eeef11a17cf49aa6a500c37e665d967d2a#39e1e0eeef11a17cf49aa6a500c37e665d967d2a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"libc",
|
"libc",
|
||||||
@ -508,12 +508,12 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation-sys"
|
name = "core-foundation-sys"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
source = "git+https://github.com/servo/core-foundation-rs?rev=e9a65bb15d591ec22649e03659db8095d4f2dd60#e9a65bb15d591ec22649e03659db8095d4f2dd60"
|
source = "git+https://github.com/zed-industries/core-foundation-rs?rev=39e1e0eeef11a17cf49aa6a500c37e665d967d2a#39e1e0eeef11a17cf49aa6a500c37e665d967d2a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-graphics"
|
name = "core-graphics"
|
||||||
version = "0.22.2"
|
version = "0.22.2"
|
||||||
source = "git+https://github.com/servo/core-foundation-rs?rev=e9a65bb15d591ec22649e03659db8095d4f2dd60#e9a65bb15d591ec22649e03659db8095d4f2dd60"
|
source = "git+https://github.com/zed-industries/core-foundation-rs?rev=39e1e0eeef11a17cf49aa6a500c37e665d967d2a#39e1e0eeef11a17cf49aa6a500c37e665d967d2a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.2.1",
|
"bitflags 1.2.1",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
@ -525,7 +525,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "core-graphics-types"
|
name = "core-graphics-types"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "git+https://github.com/servo/core-foundation-rs?rev=e9a65bb15d591ec22649e03659db8095d4f2dd60#e9a65bb15d591ec22649e03659db8095d4f2dd60"
|
source = "git+https://github.com/zed-industries/core-foundation-rs?rev=39e1e0eeef11a17cf49aa6a500c37e665d967d2a#39e1e0eeef11a17cf49aa6a500c37e665d967d2a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.2.1",
|
"bitflags 1.2.1",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
|
10
Cargo.toml
10
Cargo.toml
@ -4,11 +4,11 @@ members = ["zed", "gpui", "fsevent", "scoped_pool"]
|
|||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
async-task = {git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e"}
|
async-task = {git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e"}
|
||||||
|
|
||||||
# TODO - Remove when a version is released with this PR: https://github.com/servo/core-foundation-rs/pull/454
|
# TODO - Remove when a version is released with this PR: https://github.com/servo/core-foundation-rs/pull/457
|
||||||
cocoa = {git = "https://github.com/servo/core-foundation-rs", rev = "e9a65bb15d591ec22649e03659db8095d4f2dd60"}
|
cocoa = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "39e1e0eeef11a17cf49aa6a500c37e665d967d2a"}
|
||||||
cocoa-foundation = {git = "https://github.com/servo/core-foundation-rs", rev = "e9a65bb15d591ec22649e03659db8095d4f2dd60"}
|
cocoa-foundation = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "39e1e0eeef11a17cf49aa6a500c37e665d967d2a"}
|
||||||
core-foundation = {git = "https://github.com/servo/core-foundation-rs", rev = "e9a65bb15d591ec22649e03659db8095d4f2dd60"}
|
core-foundation = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "39e1e0eeef11a17cf49aa6a500c37e665d967d2a"}
|
||||||
core-graphics = {git = "https://github.com/servo/core-foundation-rs", rev = "e9a65bb15d591ec22649e03659db8095d4f2dd60"}
|
core-graphics = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "39e1e0eeef11a17cf49aa6a500c37e665d967d2a"}
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
split-debuginfo = "unpacked"
|
split-debuginfo = "unpacked"
|
||||||
|
@ -21,7 +21,7 @@ use std::{
|
|||||||
fmt::{self, Debug},
|
fmt::{self, Debug},
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
path::PathBuf,
|
path::{Path, PathBuf},
|
||||||
rc::{self, Rc},
|
rc::{self, Rc},
|
||||||
sync::{Arc, Weak},
|
sync::{Arc, Weak},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
@ -586,6 +586,22 @@ impl MutableAppContext {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn prompt_for_new_path<F>(&self, directory: &Path, done_fn: F)
|
||||||
|
where
|
||||||
|
F: 'static + FnOnce(Option<PathBuf>, &mut MutableAppContext),
|
||||||
|
{
|
||||||
|
let app = self.weak_self.as_ref().unwrap().upgrade().unwrap();
|
||||||
|
let foreground = self.foreground.clone();
|
||||||
|
self.platform().prompt_for_new_path(
|
||||||
|
directory,
|
||||||
|
Box::new(move |path| {
|
||||||
|
foreground
|
||||||
|
.spawn(async move { (done_fn)(path, &mut *app.borrow_mut()) })
|
||||||
|
.detach();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn notify_view(&mut self, window_id: usize, view_id: usize) {
|
pub(crate) fn notify_view(&mut self, window_id: usize, view_id: usize) {
|
||||||
self.pending_effects
|
self.pending_effects
|
||||||
.push_back(Effect::ViewNotification { window_id, view_id });
|
.push_back(Effect::ViewNotification { window_id, view_id });
|
||||||
@ -1765,6 +1781,20 @@ impl<'a, T: View> ViewContext<'a, T> {
|
|||||||
&self.app.ctx.background
|
&self.app.ctx.background
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn prompt_for_paths<F>(&self, options: PathPromptOptions, done_fn: F)
|
||||||
|
where
|
||||||
|
F: 'static + FnOnce(Option<Vec<PathBuf>>, &mut MutableAppContext),
|
||||||
|
{
|
||||||
|
self.app.prompt_for_paths(options, done_fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prompt_for_new_path<F>(&self, directory: &Path, done_fn: F)
|
||||||
|
where
|
||||||
|
F: 'static + FnOnce(Option<PathBuf>, &mut MutableAppContext),
|
||||||
|
{
|
||||||
|
self.app.prompt_for_new_path(directory, done_fn)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn debug_elements(&self) -> crate::json::Value {
|
pub fn debug_elements(&self) -> crate::json::Value {
|
||||||
self.app.debug_elements(self.window_id).unwrap()
|
self.app.debug_elements(self.window_id).unwrap()
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ use cocoa::{
|
|||||||
appkit::{
|
appkit::{
|
||||||
NSApplication, NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular,
|
NSApplication, NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular,
|
||||||
NSEventModifierFlags, NSMenu, NSMenuItem, NSModalResponse, NSOpenPanel, NSPasteboard,
|
NSEventModifierFlags, NSMenu, NSMenuItem, NSModalResponse, NSOpenPanel, NSPasteboard,
|
||||||
NSPasteboardTypeString, NSWindow,
|
NSPasteboardTypeString, NSSavePanel, NSWindow,
|
||||||
},
|
},
|
||||||
base::{id, nil, selector},
|
base::{id, nil, selector},
|
||||||
foundation::{NSArray, NSAutoreleasePool, NSData, NSInteger, NSString, NSURL},
|
foundation::{NSArray, NSAutoreleasePool, NSData, NSInteger, NSString, NSURL},
|
||||||
@ -25,7 +25,7 @@ use std::{
|
|||||||
convert::TryInto,
|
convert::TryInto,
|
||||||
ffi::{c_void, CStr},
|
ffi::{c_void, CStr},
|
||||||
os::raw::c_char,
|
os::raw::c_char,
|
||||||
path::PathBuf,
|
path::{Path, PathBuf},
|
||||||
ptr,
|
ptr,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
slice, str,
|
slice, str,
|
||||||
@ -305,6 +305,43 @@ impl platform::Platform for MacPlatform {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn prompt_for_new_path(
|
||||||
|
&self,
|
||||||
|
directory: &Path,
|
||||||
|
done_fn: Box<dyn FnOnce(Option<std::path::PathBuf>)>,
|
||||||
|
) {
|
||||||
|
unsafe {
|
||||||
|
let panel = NSSavePanel::savePanel(nil);
|
||||||
|
let path = ns_string(directory.to_string_lossy().as_ref());
|
||||||
|
let url = NSURL::fileURLWithPath_isDirectory_(nil, path, true.to_objc());
|
||||||
|
panel.setDirectoryURL(url);
|
||||||
|
|
||||||
|
let done_fn = Cell::new(Some(done_fn));
|
||||||
|
let block = ConcreteBlock::new(move |response: NSModalResponse| {
|
||||||
|
let result = if response == NSModalResponse::NSModalResponseOk {
|
||||||
|
let url = panel.URL();
|
||||||
|
let string = url.absoluteString();
|
||||||
|
let string = std::ffi::CStr::from_ptr(string.UTF8String())
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string();
|
||||||
|
if let Some(path) = string.strip_prefix("file://") {
|
||||||
|
Some(PathBuf::from(path))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(done_fn) = done_fn.take() {
|
||||||
|
(done_fn)(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let block = block.copy();
|
||||||
|
let _: () = msg_send![panel, beginWithCompletionHandler: block];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn fonts(&self) -> Arc<dyn platform::FontSystem> {
|
fn fonts(&self) -> Arc<dyn platform::FontSystem> {
|
||||||
self.fonts.clone()
|
self.fonts.clone()
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,13 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use async_task::Runnable;
|
use async_task::Runnable;
|
||||||
pub use event::Event;
|
pub use event::Event;
|
||||||
use std::{any::Any, ops::Range, path::PathBuf, rc::Rc, sync::Arc};
|
use std::{
|
||||||
|
any::Any,
|
||||||
|
ops::Range,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
rc::Rc,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
pub trait Platform {
|
pub trait Platform {
|
||||||
fn on_menu_command(&self, callback: Box<dyn FnMut(&str, Option<&dyn Any>)>);
|
fn on_menu_command(&self, callback: Box<dyn FnMut(&str, Option<&dyn Any>)>);
|
||||||
@ -45,6 +51,11 @@ pub trait Platform {
|
|||||||
options: PathPromptOptions,
|
options: PathPromptOptions,
|
||||||
done_fn: Box<dyn FnOnce(Option<Vec<std::path::PathBuf>>)>,
|
done_fn: Box<dyn FnOnce(Option<Vec<std::path::PathBuf>>)>,
|
||||||
);
|
);
|
||||||
|
fn prompt_for_new_path(
|
||||||
|
&self,
|
||||||
|
directory: &Path,
|
||||||
|
done_fn: Box<dyn FnOnce(Option<std::path::PathBuf>)>,
|
||||||
|
);
|
||||||
fn quit(&self);
|
fn quit(&self);
|
||||||
fn write_to_clipboard(&self, item: ClipboardItem);
|
fn write_to_clipboard(&self, item: ClipboardItem);
|
||||||
fn read_from_clipboard(&self) -> Option<ClipboardItem>;
|
fn read_from_clipboard(&self) -> Option<ClipboardItem>;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::ClipboardItem;
|
use crate::ClipboardItem;
|
||||||
use pathfinder_geometry::vector::Vector2F;
|
use pathfinder_geometry::vector::Vector2F;
|
||||||
use std::{any::Any, cell::RefCell, rc::Rc, sync::Arc};
|
use std::{any::Any, cell::RefCell, path::Path, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
struct Platform {
|
struct Platform {
|
||||||
dispatcher: Arc<dyn super::Dispatcher>,
|
dispatcher: Arc<dyn super::Dispatcher>,
|
||||||
@ -77,6 +77,8 @@ impl super::Platform for Platform {
|
|||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn prompt_for_new_path(&self, _: &Path, _: Box<dyn FnOnce(Option<std::path::PathBuf>)>) {}
|
||||||
|
|
||||||
fn write_to_clipboard(&self, item: ClipboardItem) {
|
fn write_to_clipboard(&self, item: ClipboardItem) {
|
||||||
*self.current_clipboard_item.borrow_mut() = Some(item);
|
*self.current_clipboard_item.borrow_mut() = Some(item);
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,10 @@ use crate::{settings::Settings, watch, workspace, worktree::FileHandle};
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use futures_core::future::LocalBoxFuture;
|
use futures_core::future::LocalBoxFuture;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
fonts::Properties as FontProperties, keymap::Binding, text_layout, AppContext, ClipboardItem,
|
fonts::Properties as FontProperties, geometry::vector::Vector2F, keymap::Binding, text_layout,
|
||||||
Element, ElementBox, Entity, FontCache, ModelHandle, MutableAppContext, View, ViewContext,
|
AppContext, ClipboardItem, Element, ElementBox, Entity, FontCache, ModelHandle,
|
||||||
WeakViewHandle,
|
MutableAppContext, TextLayoutCache, View, ViewContext, WeakViewHandle,
|
||||||
};
|
};
|
||||||
use gpui::{geometry::vector::Vector2F, TextLayoutCache};
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
@ -2135,7 +2134,14 @@ impl workspace::ItemView for BufferView {
|
|||||||
Some(clone)
|
Some(clone)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save(&self, ctx: &mut ViewContext<Self>) -> LocalBoxFuture<'static, Result<()>> {
|
fn save(
|
||||||
|
&mut self,
|
||||||
|
file: Option<FileHandle>,
|
||||||
|
ctx: &mut ViewContext<Self>,
|
||||||
|
) -> LocalBoxFuture<'static, Result<()>> {
|
||||||
|
if file.is_some() {
|
||||||
|
self.file = file;
|
||||||
|
}
|
||||||
if let Some(file) = self.file.as_ref() {
|
if let Some(file) = self.file.as_ref() {
|
||||||
self.buffer
|
self.buffer
|
||||||
.update(ctx, |buffer, ctx| buffer.save(file, ctx))
|
.update(ctx, |buffer, ctx| buffer.save(file, ctx))
|
||||||
|
@ -24,12 +24,21 @@ pub fn menus(settings: Receiver<Settings>) -> Vec<Menu<'static>> {
|
|||||||
},
|
},
|
||||||
Menu {
|
Menu {
|
||||||
name: "File",
|
name: "File",
|
||||||
items: vec![MenuItem::Action {
|
items: vec![
|
||||||
name: "Open…",
|
MenuItem::Action {
|
||||||
keystroke: Some("cmd-o"),
|
name: "New",
|
||||||
action: "workspace:open",
|
keystroke: Some("cmd-n"),
|
||||||
arg: Some(Box::new(settings)),
|
action: "workspace:new_file",
|
||||||
}],
|
arg: None,
|
||||||
|
},
|
||||||
|
MenuItem::Separator,
|
||||||
|
MenuItem::Action {
|
||||||
|
name: "Open…",
|
||||||
|
keystroke: Some("cmd-o"),
|
||||||
|
action: "workspace:open",
|
||||||
|
arg: Some(Box::new(settings)),
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
Menu {
|
Menu {
|
||||||
name: "Edit",
|
name: "Edit",
|
||||||
|
@ -6,6 +6,7 @@ pub use pane_group::*;
|
|||||||
use crate::{
|
use crate::{
|
||||||
settings::Settings,
|
settings::Settings,
|
||||||
watch::{self, Receiver},
|
watch::{self, Receiver},
|
||||||
|
worktree::FileHandle,
|
||||||
};
|
};
|
||||||
use gpui::{MutableAppContext, PathPromptOptions};
|
use gpui::{MutableAppContext, PathPromptOptions};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@ -15,6 +16,7 @@ pub fn init(app: &mut MutableAppContext) {
|
|||||||
app.add_global_action("app:quit", quit);
|
app.add_global_action("app:quit", quit);
|
||||||
app.add_action("workspace:save", Workspace::save_active_item);
|
app.add_action("workspace:save", Workspace::save_active_item);
|
||||||
app.add_action("workspace:debug_elements", Workspace::debug_elements);
|
app.add_action("workspace:debug_elements", Workspace::debug_elements);
|
||||||
|
app.add_action("workspace:new_file", Workspace::open_new_file);
|
||||||
app.add_bindings(vec![
|
app.add_bindings(vec![
|
||||||
Binding::new("cmd-s", "workspace:save", None),
|
Binding::new("cmd-s", "workspace:save", None),
|
||||||
Binding::new("cmd-alt-i", "workspace:debug_elements", None),
|
Binding::new("cmd-alt-i", "workspace:debug_elements", None),
|
||||||
@ -108,7 +110,11 @@ pub trait ItemView: View {
|
|||||||
fn is_dirty(&self, _: &AppContext) -> bool {
|
fn is_dirty(&self, _: &AppContext) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
fn save(&self, _: &mut ViewContext<Self>) -> LocalBoxFuture<'static, anyhow::Result<()>> {
|
fn save(
|
||||||
|
&mut self,
|
||||||
|
_: Option<FileHandle>,
|
||||||
|
_: &mut ViewContext<Self>,
|
||||||
|
) -> LocalBoxFuture<'static, anyhow::Result<()>> {
|
||||||
Box::pin(async { Ok(()) })
|
Box::pin(async { Ok(()) })
|
||||||
}
|
}
|
||||||
fn should_activate_item_on_event(_: &Self::Event) -> bool {
|
fn should_activate_item_on_event(_: &Self::Event) -> bool {
|
||||||
@ -128,7 +134,11 @@ pub trait ItemViewHandle: Send + Sync {
|
|||||||
fn id(&self) -> usize;
|
fn id(&self) -> usize;
|
||||||
fn to_any(&self) -> AnyViewHandle;
|
fn to_any(&self) -> AnyViewHandle;
|
||||||
fn is_dirty(&self, ctx: &AppContext) -> bool;
|
fn is_dirty(&self, ctx: &AppContext) -> bool;
|
||||||
fn save(&self, ctx: &mut MutableAppContext) -> LocalBoxFuture<'static, anyhow::Result<()>>;
|
fn save(
|
||||||
|
&self,
|
||||||
|
file: Option<FileHandle>,
|
||||||
|
ctx: &mut MutableAppContext,
|
||||||
|
) -> LocalBoxFuture<'static, anyhow::Result<()>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ItemView> ItemViewHandle for ViewHandle<T> {
|
impl<T: ItemView> ItemViewHandle for ViewHandle<T> {
|
||||||
@ -167,8 +177,12 @@ impl<T: ItemView> ItemViewHandle for ViewHandle<T> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save(&self, ctx: &mut MutableAppContext) -> LocalBoxFuture<'static, anyhow::Result<()>> {
|
fn save(
|
||||||
self.update(ctx, |item, ctx| item.save(ctx))
|
&self,
|
||||||
|
file: Option<FileHandle>,
|
||||||
|
ctx: &mut MutableAppContext,
|
||||||
|
) -> LocalBoxFuture<'static, anyhow::Result<()>> {
|
||||||
|
self.update(ctx, |item, ctx| item.save(file, ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_dirty(&self, ctx: &AppContext) -> bool {
|
fn is_dirty(&self, ctx: &AppContext) -> bool {
|
||||||
@ -209,6 +223,7 @@ pub struct Workspace {
|
|||||||
(usize, u64),
|
(usize, u64),
|
||||||
postage::watch::Receiver<Option<Result<ModelHandle<Buffer>, Arc<anyhow::Error>>>>,
|
postage::watch::Receiver<Option<Result<ModelHandle<Buffer>, Arc<anyhow::Error>>>>,
|
||||||
>,
|
>,
|
||||||
|
untitled_buffers: HashSet<ModelHandle<Buffer>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Workspace {
|
impl Workspace {
|
||||||
@ -234,6 +249,7 @@ impl Workspace {
|
|||||||
replica_id,
|
replica_id,
|
||||||
worktrees: Default::default(),
|
worktrees: Default::default(),
|
||||||
buffers: Default::default(),
|
buffers: Default::default(),
|
||||||
|
untitled_buffers: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,15 +288,7 @@ impl Workspace {
|
|||||||
let entries = paths
|
let entries = paths
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|path| {
|
.map(|path| self.file_for_path(&path, ctx))
|
||||||
for tree in self.worktrees.iter() {
|
|
||||||
if let Ok(relative_path) = path.strip_prefix(tree.read(ctx).abs_path()) {
|
|
||||||
return (tree.id(), relative_path.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let worktree_id = self.add_worktree(&path, ctx);
|
|
||||||
(worktree_id, Path::new("").into())
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let bg = ctx.background_executor().clone();
|
let bg = ctx.background_executor().clone();
|
||||||
@ -288,12 +296,12 @@ impl Workspace {
|
|||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.zip(entries.into_iter())
|
.zip(entries.into_iter())
|
||||||
.map(|(path, entry)| {
|
.map(|(abs_path, file)| {
|
||||||
ctx.spawn(
|
ctx.spawn(
|
||||||
bg.spawn(async move { path.is_file() }),
|
bg.spawn(async move { abs_path.is_file() }),
|
||||||
|me, is_file, ctx| {
|
move |me, is_file, ctx| {
|
||||||
if is_file {
|
if is_file {
|
||||||
me.open_entry(entry, ctx)
|
me.open_entry(file.entry_id(), ctx)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -310,13 +318,26 @@ impl Workspace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_worktree(&mut self, path: &Path, ctx: &mut ViewContext<Self>) -> usize {
|
fn file_for_path(&mut self, abs_path: &Path, ctx: &mut ViewContext<Self>) -> FileHandle {
|
||||||
|
for tree in self.worktrees.iter() {
|
||||||
|
if let Ok(relative_path) = abs_path.strip_prefix(tree.read(ctx).abs_path()) {
|
||||||
|
return tree.file(relative_path, ctx.as_ref());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let worktree = self.add_worktree(&abs_path, ctx);
|
||||||
|
worktree.file(Path::new(""), ctx.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_worktree(
|
||||||
|
&mut self,
|
||||||
|
path: &Path,
|
||||||
|
ctx: &mut ViewContext<Self>,
|
||||||
|
) -> ModelHandle<Worktree> {
|
||||||
let worktree = ctx.add_model(|ctx| Worktree::new(path, ctx));
|
let worktree = ctx.add_model(|ctx| Worktree::new(path, ctx));
|
||||||
let worktree_id = worktree.id();
|
|
||||||
ctx.observe_model(&worktree, |_, _, ctx| ctx.notify());
|
ctx.observe_model(&worktree, |_, _, ctx| ctx.notify());
|
||||||
self.worktrees.insert(worktree);
|
self.worktrees.insert(worktree.clone());
|
||||||
ctx.notify();
|
ctx.notify();
|
||||||
worktree_id
|
worktree
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toggle_modal<V, F>(&mut self, ctx: &mut ViewContext<Self>, add_view: F)
|
pub fn toggle_modal<V, F>(&mut self, ctx: &mut ViewContext<Self>, add_view: F)
|
||||||
@ -346,6 +367,15 @@ impl Workspace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn open_new_file(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
|
||||||
|
let buffer = ctx.add_model(|_| Buffer::new(self.replica_id, ""));
|
||||||
|
let buffer_view = Box::new(ctx.add_view(|ctx| {
|
||||||
|
BufferView::for_buffer(buffer.clone(), None, self.settings.clone(), ctx)
|
||||||
|
}));
|
||||||
|
self.untitled_buffers.insert(buffer);
|
||||||
|
self.add_item(buffer_view, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn open_entry(
|
pub fn open_entry(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -381,13 +411,11 @@ impl Workspace {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let file = match worktree.file(path.clone(), ctx.as_ref()) {
|
let file = worktree.file(path.clone(), ctx.as_ref());
|
||||||
Some(file) => file,
|
if file.is_deleted() {
|
||||||
None => {
|
log::error!("path {:?} does not exist", path);
|
||||||
log::error!("path {:?} does not exist", path);
|
return None;
|
||||||
return None;
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.loading_entries.insert(entry.clone());
|
self.loading_entries.insert(entry.clone());
|
||||||
|
|
||||||
@ -441,12 +469,34 @@ impl Workspace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn save_active_item(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
|
pub fn save_active_item(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
|
||||||
self.active_pane.update(ctx, |pane, ctx| {
|
let handle = ctx.handle();
|
||||||
|
let first_worktree = self.worktrees.iter().next();
|
||||||
|
self.active_pane.update(ctx, move |pane, ctx| {
|
||||||
if let Some(item) = pane.active_item() {
|
if let Some(item) = pane.active_item() {
|
||||||
let task = item.save(ctx.as_mut());
|
if item.entry_id(ctx.as_ref()).is_none() {
|
||||||
|
let start_path = first_worktree
|
||||||
|
.map_or(Path::new(""), |h| h.read(ctx).abs_path())
|
||||||
|
.to_path_buf();
|
||||||
|
ctx.prompt_for_new_path(&start_path, move |path, ctx| {
|
||||||
|
if let Some(path) = path {
|
||||||
|
handle.update(ctx, move |this, ctx| {
|
||||||
|
let file = this.file_for_path(&path, ctx);
|
||||||
|
let task = item.save(Some(file), ctx.as_mut());
|
||||||
|
ctx.spawn(task, |_, result, _| {
|
||||||
|
if let Err(e) = result {
|
||||||
|
error!("failed to save item: {:?}, ", e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let task = item.save(None, ctx.as_mut());
|
||||||
ctx.spawn(task, |_, result, _| {
|
ctx.spawn(task, |_, result, _| {
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
// TODO - present this error to the user
|
|
||||||
error!("failed to save item: {:?}, ", e);
|
error!("failed to save item: {:?}, ", e);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1126,31 +1126,38 @@ struct UpdateIgnoreStatusJob {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait WorktreeHandle {
|
pub trait WorktreeHandle {
|
||||||
fn file(&self, path: impl AsRef<Path>, app: &AppContext) -> Option<FileHandle>;
|
fn file(&self, path: impl AsRef<Path>, app: &AppContext) -> FileHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorktreeHandle for ModelHandle<Worktree> {
|
impl WorktreeHandle for ModelHandle<Worktree> {
|
||||||
fn file(&self, path: impl AsRef<Path>, app: &AppContext) -> Option<FileHandle> {
|
fn file(&self, path: impl AsRef<Path>, app: &AppContext) -> FileHandle {
|
||||||
|
let path = path.as_ref();
|
||||||
let tree = self.read(app);
|
let tree = self.read(app);
|
||||||
let entry = tree.entry_for_path(&path)?;
|
|
||||||
|
|
||||||
let path = entry.path().clone();
|
|
||||||
let mut handles = tree.handles.lock();
|
let mut handles = tree.handles.lock();
|
||||||
let state = if let Some(state) = handles.get(&path).and_then(Weak::upgrade) {
|
let state = if let Some(state) = handles.get(path).and_then(Weak::upgrade) {
|
||||||
state
|
state
|
||||||
} else {
|
} else {
|
||||||
let state = Arc::new(Mutex::new(FileHandleState {
|
let handle_state = if let Some(entry) = tree.entry_for_path(path) {
|
||||||
path: path.clone(),
|
FileHandleState {
|
||||||
is_deleted: false,
|
path: entry.path().clone(),
|
||||||
}));
|
is_deleted: false,
|
||||||
handles.insert(path, Arc::downgrade(&state));
|
}
|
||||||
|
} else {
|
||||||
|
FileHandleState {
|
||||||
|
path: path.into(),
|
||||||
|
is_deleted: true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let state = Arc::new(Mutex::new(handle_state.clone()));
|
||||||
|
handles.insert(handle_state.path, Arc::downgrade(&state));
|
||||||
state
|
state
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(FileHandle {
|
FileHandle {
|
||||||
worktree: self.clone(),
|
worktree: self.clone(),
|
||||||
state,
|
state,
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1389,10 +1396,10 @@ mod tests {
|
|||||||
|
|
||||||
let (file2, file3, file4, file5) = app.read(|ctx| {
|
let (file2, file3, file4, file5) = app.read(|ctx| {
|
||||||
(
|
(
|
||||||
tree.file("a/file2", ctx).unwrap(),
|
tree.file("a/file2", ctx),
|
||||||
tree.file("a/file3", ctx).unwrap(),
|
tree.file("a/file3", ctx),
|
||||||
tree.file("b/c/file4", ctx).unwrap(),
|
tree.file("b/c/file4", ctx),
|
||||||
tree.file("b/c/file5", ctx).unwrap(),
|
tree.file("b/c/file5", ctx),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user