From cd65031cda46d859db1b23faf3d32aaaaed13c9c Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 18 Dec 2021 08:12:08 -0700 Subject: [PATCH 1/7] Halt keystroke dispatch immediately when we call a global action handler Someday, we may want to define a global action context that allows us to propagate the action, but this isn't currently supported. Previous to this commit, we were invoking the same global action handler multiple times, once for each view in the responder chain. --- crates/gpui/src/app.rs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 38c3aba045c4771e8476dd0231f44eaa6a278301..f5c947019fe59f22a56a41b41b9b1b0de8548441 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -667,7 +667,7 @@ pub struct MutableAppContext { assets: Arc, cx: AppContext, actions: HashMap>>>, - global_actions: HashMap>>, + global_actions: HashMap>, keystroke_matcher: keymap::Matcher, next_entity_id: usize, next_window_id: usize, @@ -838,10 +838,13 @@ impl MutableAppContext { handler(action, cx); }); - self.global_actions - .entry(TypeId::of::()) - .or_default() - .push(handler); + if self + .global_actions + .insert(TypeId::of::(), handler) + .is_some() + { + panic!("registered multiple global handlers for the same action type"); + } } pub fn window_ids(&self) -> impl Iterator + '_ { @@ -1125,7 +1128,7 @@ impl MutableAppContext { } if !halted_dispatch { - this.dispatch_global_action_any(action); + halted_dispatch = this.dispatch_global_action_any(action); } halted_dispatch }) @@ -1135,13 +1138,14 @@ impl MutableAppContext { self.dispatch_global_action_any(&action); } - fn dispatch_global_action_any(&mut self, action: &dyn AnyAction) { + fn dispatch_global_action_any(&mut self, action: &dyn AnyAction) -> bool { self.update(|this| { - if let Some((name, mut handlers)) = this.global_actions.remove_entry(&action.id()) { - for handler in handlers.iter_mut().rev() { - handler(action, this); - } - this.global_actions.insert(name, handlers); + if let Some((name, mut handler)) = this.global_actions.remove_entry(&action.id()) { + handler(action, this); + this.global_actions.insert(name, handler); + true + } else { + false } }) } From ca0d7e5e1f52c5b8d1815109aa7438cc3434d2bf Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 18 Dec 2021 08:12:59 -0700 Subject: [PATCH 2/7] Add journal crate and move supporting logic into workspace crate I needed to interact with the workspace to open a file from the journal crate, so I moved a bunch of logic out of main related to opening new workspaces and paths. --- Cargo.lock | 82 ++++----- crates/journal/Cargo.toml | 16 ++ crates/journal/src/journal.rs | 65 ++++++++ crates/theme_selector/src/theme_selector.rs | 12 +- crates/util/src/lib.rs | 5 +- crates/workspace/Cargo.toml | 1 + crates/workspace/src/workspace.rs | 126 +++++++++++++- crates/zed/Cargo.toml | 9 +- crates/zed/src/main.rs | 11 +- crates/zed/src/menus.rs | 16 +- crates/zed/src/test.rs | 4 +- crates/zed/src/zed.rs | 174 ++++---------------- 12 files changed, 299 insertions(+), 222 deletions(-) create mode 100644 crates/journal/Cargo.toml create mode 100644 crates/journal/src/journal.rs diff --git a/Cargo.lock b/Cargo.lock index 0aadd18f662e46f0702390405eb815a324f4caa8..09e08a510b783578c86aab5186cf3363bc3c0dd7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -633,17 +633,6 @@ dependencies = [ "wyz", ] -[[package]] -name = "blake2b_simd" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" -dependencies = [ - "arrayref", - "arrayvec 0.5.2", - "constant_time_eq", -] - [[package]] name = "blake3" version = "0.3.8" @@ -1437,6 +1426,15 @@ dependencies = [ "dirs-sys", ] +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -1449,12 +1447,12 @@ dependencies = [ [[package]] name = "dirs-sys" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" +checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" dependencies = [ "libc", - "redox_users 0.3.5", + "redox_users", "winapi", ] @@ -1465,7 +1463,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", - "redox_users 0.4.0", + "redox_users", "winapi", ] @@ -2499,6 +2497,19 @@ dependencies = [ "libc", ] +[[package]] +name = "journal" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "dirs 4.0.0", + "gpui", + "log", + "util", + "workspace", +] + [[package]] name = "jpeg-decoder" version = "0.1.22" @@ -3130,7 +3141,7 @@ dependencies = [ "cfg-if 1.0.0", "instant", "libc", - "redox_syscall 0.2.5", + "redox_syscall", "smallvec", "winapi", ] @@ -3697,12 +3708,6 @@ dependencies = [ "rand_core 0.3.1", ] -[[package]] -name = "redox_syscall" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" - [[package]] name = "redox_syscall" version = "0.2.5" @@ -3712,17 +3717,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "redox_users" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" -dependencies = [ - "getrandom 0.1.16", - "redox_syscall 0.1.57", - "rust-argon2", -] - [[package]] name = "redox_users" version = "0.4.0" @@ -3730,7 +3724,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" dependencies = [ "getrandom 0.2.2", - "redox_syscall 0.2.5", + "redox_syscall", ] [[package]] @@ -3858,18 +3852,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rust-argon2" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" -dependencies = [ - "base64 0.13.0", - "blake2b_simd", - "constant_time_eq", - "crossbeam-utils", -] - [[package]] name = "rust-embed" version = "6.2.0" @@ -4522,7 +4504,7 @@ dependencies = [ "crossbeam-channel", "crossbeam-queue", "crossbeam-utils", - "dirs", + "dirs 3.0.1", "either", "futures-channel", "futures-core", @@ -4827,7 +4809,7 @@ dependencies = [ "cfg-if 1.0.0", "libc", "rand 0.8.3", - "redox_syscall 0.2.5", + "redox_syscall", "remove_dir_all", "winapi", ] @@ -5649,6 +5631,7 @@ dependencies = [ "gpui", "language", "log", + "parking_lot", "postage", "project", "serde_json", @@ -5693,7 +5676,7 @@ dependencies = [ "contacts_panel", "crossbeam-channel", "ctor", - "dirs", + "dirs 3.0.1", "easy-parallel", "editor", "env_logger", @@ -5707,6 +5690,7 @@ dependencies = [ "ignore", "image", "indexmap", + "journal", "language", "lazy_static", "libc", diff --git a/crates/journal/Cargo.toml b/crates/journal/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..64786f34aeee71f675de1397046d08db2f82492e --- /dev/null +++ b/crates/journal/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "journal" +version = "0.1.0" +edition = "2021" + +[lib] +path = "src/journal.rs" + +[dependencies] +gpui = { path = "../gpui" } +util = { path = "../util" } +workspace = { path = "../workspace" } +anyhow = "1.0" +chrono = "0.4" +dirs = "4.0" +log = "0.4" \ No newline at end of file diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs new file mode 100644 index 0000000000000000000000000000000000000000..68a77295ce95b14a97a33527de96fc9dbd3778d8 --- /dev/null +++ b/crates/journal/src/journal.rs @@ -0,0 +1,65 @@ +use std::{fs::OpenOptions, sync::Arc}; + +use anyhow::anyhow; +use chrono::{Datelike, Local}; +use gpui::{action, keymap::Binding, MutableAppContext}; +use util::TryFutureExt as _; +use workspace::AppState; + +action!(NewJournalEntry); + +pub fn init(app_state: Arc, cx: &mut MutableAppContext) { + log::info!("JOURNAL INIT"); + cx.add_bindings(vec![Binding::new("ctrl-alt-cmd-j", NewJournalEntry, None)]); + + let mut counter = 0; + cx.add_global_action(move |_: &NewJournalEntry, cx| { + log::info!("NEW JOURNAL ENTRY ACTION"); + counter += 1; + if counter == 2 { + log::info!("called twice?"); + } + new_journal_entry(app_state.clone(), cx) + }); +} + +pub fn new_journal_entry(app_state: Arc, cx: &mut MutableAppContext) { + log::info!("NEW JOURNAL ENTRY"); + let paths = cx.background().spawn(async move { + let now = Local::now(); + let home_dir = dirs::home_dir().ok_or_else(|| anyhow!("can't determine home directory"))?; + let journal_dir = home_dir.join("journal"); + let month_dir = journal_dir + .join(now.year().to_string()) + .join(now.month().to_string()); + let entry_path = month_dir.join(format!("{}.md", now.day())); + + std::fs::create_dir_all(dbg!(month_dir))?; + OpenOptions::new() + .create(true) + .write(true) + .open(dbg!(&entry_path))?; + + Ok::<_, anyhow::Error>((journal_dir, entry_path)) + }); + + cx.spawn(|mut cx| { + async move { + let (journal_dir, entry_path) = paths.await?; + let workspace = cx + .update(|cx| workspace::open_paths(&[journal_dir], &app_state, cx)) + .await; + + workspace + .update(&mut cx, |workspace, cx| { + workspace.open_paths(&[entry_path], cx) + }) + .await; + + dbg!(workspace); + Ok(()) + } + .log_err() + }) + .detach(); +} diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index 545b512a8bfd4ec5a6ba9d3c83e0113e4e9441bd..ef5512a3eae2089900b8e146b635e4038c9b27ce 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -11,7 +11,7 @@ use parking_lot::Mutex; use postage::watch; use std::{cmp, sync::Arc}; use theme::ThemeRegistry; -use workspace::{Settings, Workspace}; +use workspace::{Settings, Workspace, AppState}; #[derive(Clone)] pub struct ThemeSelectorParams { @@ -317,3 +317,13 @@ impl View for ThemeSelector { cx } } + +impl<'a> From<&'a AppState> for ThemeSelectorParams { + fn from(state: &'a AppState) -> Self { + Self { + settings_tx: state.settings_tx.clone(), + settings: state.settings.clone(), + themes: state.themes.clone(), + } + } +} diff --git a/crates/util/src/lib.rs b/crates/util/src/lib.rs index 66a36889f5aea95b9883c1a8586f1f9b632e2afd..b0c66b005bd0e0deb39962ac37eb5292d63d21bf 100644 --- a/crates/util/src/lib.rs +++ b/crates/util/src/lib.rs @@ -43,7 +43,10 @@ pub trait ResultExt { fn warn_on_err(self) -> Option; } -impl ResultExt for anyhow::Result { +impl ResultExt for Result +where + E: std::fmt::Debug, +{ type Ok = T; fn log_err(self) -> Option { diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index a5ca3c91e911b07601064e82c8a3f39dc1c6c96c..497254074eb4b4c7eb4f62751fab6a002676d8e9 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -17,6 +17,7 @@ project = { path = "../project" } theme = { path = "../theme" } anyhow = "1.0.38" log = "0.4" +parking_lot = "0.11.1" postage = { version = "0.4.1", features = ["futures-traits"] } [dev-dependencies] diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 1f11f116936717acc9f5c17eb11c791b97b0b7c8..87f792054b1dcba1159437bc61ac3e3c9ff95374 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -13,16 +13,18 @@ use gpui::{ geometry::{vector::vec2f, PathBuilder}, json::{self, to_string_pretty, ToJson}, keymap::Binding, - platform::CursorStyle, + platform::{CursorStyle, WindowOptions}, AnyViewHandle, AppContext, ClipboardItem, Entity, ModelContext, ModelHandle, MutableAppContext, - PromptLevel, RenderContext, Task, View, ViewContext, ViewHandle, WeakModelHandle, + PathPromptOptions, PromptLevel, RenderContext, Task, View, ViewContext, ViewHandle, + WeakModelHandle, }; use language::LanguageRegistry; use log::error; pub use pane::*; pub use pane_group::*; +use parking_lot::Mutex; use postage::{prelude::Stream, watch}; -use project::{Fs, Project, ProjectPath, Worktree}; +use project::{fs, Fs, Project, ProjectPath, Worktree}; pub use settings::Settings; use sidebar::{Side, Sidebar, SidebarItemId, ToggleSidebarItem, ToggleSidebarItemFocus}; use status_bar::StatusBar; @@ -33,13 +35,23 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use theme::Theme; +use theme::{Theme, ThemeRegistry}; -action!(OpenNew, WorkspaceParams); +action!(Open, Arc); +action!(OpenNew, Arc); +action!(OpenPaths, OpenParams); action!(Save); action!(DebugElements); pub fn init(cx: &mut MutableAppContext) { + cx.add_global_action(open); + cx.add_global_action(move |action: &OpenPaths, cx: &mut MutableAppContext| { + open_paths(&action.0.paths, &action.0.app_state, cx).detach() + }); + cx.add_global_action(move |action: &OpenNew, cx: &mut MutableAppContext| { + open_new(&action.0, cx) + }); + cx.add_action(Workspace::save_active_item); cx.add_action(Workspace::debug_elements); cx.add_action(Workspace::toggle_sidebar_item); @@ -67,6 +79,27 @@ pub fn init(cx: &mut MutableAppContext) { pane::init(cx); } +pub struct AppState { + pub settings_tx: Arc>>, + pub settings: watch::Receiver, + pub languages: Arc, + pub themes: Arc, + pub client: Arc, + pub user_store: ModelHandle, + pub fs: Arc, + pub channel_list: ModelHandle, + pub entry_openers: Arc<[Box]>, + pub build_window_options: &'static dyn Fn() -> WindowOptions<'static>, + pub build_workspace: + &'static dyn Fn(&WorkspaceParams, &mut ViewContext) -> Workspace, +} + +#[derive(Clone)] +pub struct OpenParams { + pub paths: Vec, + pub app_state: Arc, +} + pub trait EntryOpener { fn open( &self, @@ -1226,3 +1259,86 @@ impl Element for AvatarRibbon { }) } } + +impl std::fmt::Debug for OpenParams { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("OpenParams") + .field("paths", &self.paths) + .finish() + } +} + +impl<'a> From<&'a AppState> for WorkspaceParams { + fn from(state: &'a AppState) -> Self { + Self { + client: state.client.clone(), + fs: state.fs.clone(), + languages: state.languages.clone(), + settings: state.settings.clone(), + user_store: state.user_store.clone(), + channel_list: state.channel_list.clone(), + entry_openers: state.entry_openers.clone(), + } + } +} + +fn open(action: &Open, cx: &mut MutableAppContext) { + let app_state = action.0.clone(); + cx.prompt_for_paths( + PathPromptOptions { + files: true, + directories: true, + multiple: true, + }, + move |paths, cx| { + if let Some(paths) = paths { + cx.dispatch_global_action(OpenPaths(OpenParams { paths, app_state })); + } + }, + ); +} + +pub fn open_paths( + abs_paths: &[PathBuf], + app_state: &Arc, + cx: &mut MutableAppContext, +) -> Task> { + log::info!("open paths {:?}", abs_paths); + + // Open paths in existing workspace if possible + let mut existing = None; + for window_id in cx.window_ids().collect::>() { + if let Some(workspace) = cx.root_view::(window_id) { + if workspace.update(cx, |view, cx| { + if view.contains_paths(abs_paths, cx.as_ref()) { + existing = Some(workspace.clone()); + true + } else { + false + } + }) { + break; + } + } + } + + let workspace = existing.unwrap_or_else(|| { + cx.add_window((app_state.build_window_options)(), |cx| { + (app_state.build_workspace)(&WorkspaceParams::from(app_state.as_ref()), cx) + }) + .1 + }); + + let task = workspace.update(cx, |workspace, cx| workspace.open_paths(abs_paths, cx)); + cx.spawn(|_| async move { + task.await; + workspace + }) +} + +fn open_new(app_state: &Arc, cx: &mut MutableAppContext) { + let (window_id, workspace) = cx.add_window((app_state.build_window_options)(), |cx| { + (app_state.build_workspace)(&app_state.as_ref().into(), cx) + }); + cx.dispatch_action(window_id, vec![workspace.id()], &OpenNew(app_state.clone())); +} diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index e03ef6dcf9ccd02989c294f94cc495f2f97986be..adcd7365ae669953ab164852899e24b1f3008473 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -28,23 +28,24 @@ test-support = [ ] [dependencies] -text = { path = "../text" } chat_panel = { path = "../chat_panel" } client = { path = "../client" } clock = { path = "../clock" } -fsevent = { path = "../fsevent" } -fuzzy = { path = "../fuzzy" } +contacts_panel = { path = "../contacts_panel" } editor = { path = "../editor" } file_finder = { path = "../file_finder" } +fsevent = { path = "../fsevent" } +fuzzy = { path = "../fuzzy" } go_to_line = { path = "../go_to_line" } gpui = { path = "../gpui" } +journal = { path = "../journal" } language = { path = "../language" } lsp = { path = "../lsp" } -contacts_panel = { path = "../contacts_panel" } project = { path = "../project" } project_panel = { path = "../project_panel" } rpc = { path = "../rpc" } sum_tree = { path = "../sum_tree" } +text = { path = "../text" } theme = { path = "../theme" } theme_selector = { path = "../theme_selector" } util = { path = "../util" } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index c6374c66e71f25dd5fc8015504086eb0dff916d0..34828b7fa7b6c059842d5e1338cc7de7044d3c0b 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -9,8 +9,10 @@ use parking_lot::Mutex; use simplelog::SimpleLogger; use std::{fs, path::PathBuf, sync::Arc}; use theme::{ThemeRegistry, DEFAULT_THEME_NAME}; -use workspace::{self, settings, OpenNew, Settings}; -use zed::{self, assets::Assets, fs::RealFs, language, menus, AppState, OpenParams, OpenPaths}; +use workspace::{self, settings, AppState, OpenNew, OpenParams, OpenPaths, Settings}; +use zed::{ + self, assets::Assets, build_window_options, build_workspace, fs::RealFs, language, menus, +}; fn main() { init_logger(); @@ -71,7 +73,10 @@ fn main() { user_store, fs: Arc::new(RealFs), entry_openers: Arc::from(entry_openers), + build_window_options: &build_window_options, + build_workspace: &build_workspace, }); + journal::init(app_state.clone(), cx); zed::init(&app_state, cx); theme_selector::init(app_state.as_ref().into(), cx); @@ -83,7 +88,7 @@ fn main() { let paths = collect_path_args(); if paths.is_empty() { - cx.dispatch_global_action(OpenNew(app_state.as_ref().into())); + cx.dispatch_global_action(OpenNew(app_state.clone())); } else { cx.dispatch_global_action(OpenPaths(OpenParams { paths, app_state })); } diff --git a/crates/zed/src/menus.rs b/crates/zed/src/menus.rs index 25a4b5e6f96d85683b05dbd93819866e683c4d86..33ac76e63cc3d7ff75fb3df1c9f6f9523854acf9 100644 --- a/crates/zed/src/menus.rs +++ b/crates/zed/src/menus.rs @@ -1,19 +1,9 @@ -use crate::{AppState, WorkspaceParams}; +use crate::AppState; use gpui::{Menu, MenuItem}; use std::sync::Arc; #[cfg(target_os = "macos")] pub fn menus(state: &Arc) -> Vec> { - let workspace_params = WorkspaceParams { - client: state.client.clone(), - fs: state.fs.clone(), - languages: state.languages.clone(), - settings: state.settings.clone(), - user_store: state.user_store.clone(), - channel_list: state.channel_list.clone(), - entry_openers: state.entry_openers.clone(), - }; - vec![ Menu { name: "Zed", @@ -37,13 +27,13 @@ pub fn menus(state: &Arc) -> Vec> { MenuItem::Action { name: "New", keystroke: Some("cmd-n"), - action: Box::new(workspace::OpenNew(workspace_params)), + action: Box::new(workspace::OpenNew(state.clone())), }, MenuItem::Separator, MenuItem::Action { name: "Open…", keystroke: Some("cmd-o"), - action: Box::new(crate::Open(state.clone())), + action: Box::new(workspace::Open(state.clone())), }, ], }, diff --git a/crates/zed/src/test.rs b/crates/zed/src/test.rs index 91f1c165ee825d8a31a0a2681bf58bc6cb10d24c..a495b2689d49ed71045f90eb286ad90b9efe4f43 100644 --- a/crates/zed/src/test.rs +++ b/crates/zed/src/test.rs @@ -1,4 +1,4 @@ -use crate::{assets::Assets, AppState}; +use crate::{assets::Assets, build_window_options, AppState, build_workspace}; use client::{http::ServerResponse, test::FakeHttpClient, ChannelList, Client, UserStore}; use gpui::{AssetSource, MutableAppContext}; use language::LanguageRegistry; @@ -42,6 +42,8 @@ pub fn test_app_state(cx: &mut MutableAppContext) -> Arc { user_store, fs: Arc::new(FakeFs::new()), entry_openers: Arc::from(entry_openers), + build_window_options: &build_window_options, + build_workspace: &build_workspace, }) } diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 16f67fa17180e77fd7ad4e7a6794c423454a0667..3c3f4f7f45930b31ce4419c9dc6250f1a7ad1803 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -4,7 +4,6 @@ pub mod menus; #[cfg(any(test, feature = "test-support"))] pub mod test; -use self::language::LanguageRegistry; use chat_panel::ChatPanel; pub use client; pub use contacts_panel; @@ -15,51 +14,22 @@ use gpui::{ geometry::vector::vec2f, keymap::Binding, platform::{WindowBounds, WindowOptions}, - ModelHandle, MutableAppContext, PathPromptOptions, Task, ViewContext, + ViewContext, }; pub use lsp; -use parking_lot::Mutex; -use postage::watch; pub use project::{self, fs}; use project_panel::ProjectPanel; -use std::{path::PathBuf, sync::Arc}; -use theme::ThemeRegistry; -use theme_selector::ThemeSelectorParams; +use std::sync::Arc; pub use workspace; -use workspace::{OpenNew, Settings, Workspace, WorkspaceParams}; +use workspace::{AppState, Workspace, WorkspaceParams}; action!(About); -action!(Open, Arc); -action!(OpenPaths, OpenParams); action!(Quit); action!(AdjustBufferFontSize, f32); const MIN_FONT_SIZE: f32 = 6.0; -pub struct AppState { - pub settings_tx: Arc>>, - pub settings: watch::Receiver, - pub languages: Arc, - pub themes: Arc, - pub client: Arc, - pub user_store: ModelHandle, - pub fs: Arc, - pub channel_list: ModelHandle, - pub entry_openers: Arc<[Box]>, -} - -#[derive(Clone)] -pub struct OpenParams { - pub paths: Vec, - pub app_state: Arc, -} - pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { - cx.add_global_action(open); - cx.add_global_action(|action: &OpenPaths, cx: &mut MutableAppContext| { - open_paths(action, cx).detach() - }); - cx.add_global_action(open_new); cx.add_global_action(quit); cx.add_global_action({ @@ -79,63 +49,7 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { ]) } -fn open(action: &Open, cx: &mut MutableAppContext) { - let app_state = action.0.clone(); - cx.prompt_for_paths( - PathPromptOptions { - files: true, - directories: true, - multiple: true, - }, - move |paths, cx| { - if let Some(paths) = paths { - cx.dispatch_global_action(OpenPaths(OpenParams { paths, app_state })); - } - }, - ); -} - -fn open_paths(action: &OpenPaths, cx: &mut MutableAppContext) -> Task<()> { - log::info!("open paths {:?}", action.0.paths); - - // Open paths in existing workspace if possible - for window_id in cx.window_ids().collect::>() { - if let Some(handle) = cx.root_view::(window_id) { - let task = handle.update(cx, |view, cx| { - if view.contains_paths(&action.0.paths, cx.as_ref()) { - log::info!("open paths on existing workspace"); - Some(view.open_paths(&action.0.paths, cx)) - } else { - None - } - }); - - if let Some(task) = task { - return task; - } - } - } - - log::info!("open new workspace"); - - // Add a new workspace if necessary - let app_state = &action.0.app_state; - let (_, workspace) = cx.add_window(window_options(), |cx| { - build_workspace(&WorkspaceParams::from(app_state.as_ref()), cx) - }); - // cx.resize_window(window_id); - workspace.update(cx, |workspace, cx| { - workspace.open_paths(&action.0.paths, cx) - }) -} - -fn open_new(action: &OpenNew, cx: &mut MutableAppContext) { - let (window_id, workspace) = - cx.add_window(window_options(), |cx| build_workspace(&action.0, cx)); - cx.dispatch_action(window_id, vec![workspace.id()], action); -} - -fn build_workspace(params: &WorkspaceParams, cx: &mut ViewContext) -> Workspace { +pub fn build_workspace(params: &WorkspaceParams, cx: &mut ViewContext) -> Workspace { let mut workspace = Workspace::new(params, cx); let project = workspace.project().clone(); workspace.left_sidebar_mut().add_item( @@ -174,7 +88,7 @@ fn build_workspace(params: &WorkspaceParams, cx: &mut ViewContext) -> workspace } -fn window_options() -> WindowOptions<'static> { +pub fn build_window_options() -> WindowOptions<'static> { WindowOptions { bounds: WindowBounds::Maximized, title: None, @@ -187,41 +101,23 @@ fn quit(_: &Quit, cx: &mut gpui::MutableAppContext) { cx.platform().quit(); } -impl<'a> From<&'a AppState> for WorkspaceParams { - fn from(state: &'a AppState) -> Self { - Self { - client: state.client.clone(), - fs: state.fs.clone(), - languages: state.languages.clone(), - settings: state.settings.clone(), - user_store: state.user_store.clone(), - channel_list: state.channel_list.clone(), - entry_openers: state.entry_openers.clone(), - } - } -} - -impl<'a> From<&'a AppState> for ThemeSelectorParams { - fn from(state: &'a AppState) -> Self { - Self { - settings_tx: state.settings_tx.clone(), - settings: state.settings.clone(), - themes: state.themes.clone(), - } - } -} - #[cfg(test)] mod tests { use super::*; use editor::Editor; + use gpui::MutableAppContext; use project::ProjectPath; use serde_json::json; - use std::{collections::HashSet, path::Path}; + use std::{ + collections::HashSet, + path::{Path, PathBuf}, + }; use test::test_app_state; use theme::DEFAULT_THEME_NAME; use util::test::temp_tree; - use workspace::{pane, ItemView, ItemViewHandle, SplitDirection, WorkspaceHandle}; + use workspace::{ + open_paths, pane, ItemView, ItemViewHandle, OpenNew, SplitDirection, WorkspaceHandle, + }; #[gpui::test] async fn test_open_paths_action(mut cx: gpui::TestAppContext) { @@ -243,29 +139,19 @@ mod tests { cx.update(|cx| { open_paths( - &OpenPaths(OpenParams { - paths: vec![ - dir.path().join("a").to_path_buf(), - dir.path().join("b").to_path_buf(), - ], - app_state: app_state.clone(), - }), + &[ + dir.path().join("a").to_path_buf(), + dir.path().join("b").to_path_buf(), + ], + &app_state, cx, ) }) .await; assert_eq!(cx.window_ids().len(), 1); - cx.update(|cx| { - open_paths( - &OpenPaths(OpenParams { - paths: vec![dir.path().join("a").to_path_buf()], - app_state: app_state.clone(), - }), - cx, - ) - }) - .await; + cx.update(|cx| open_paths(&[dir.path().join("a").to_path_buf()], &app_state, cx)) + .await; assert_eq!(cx.window_ids().len(), 1); let workspace_1 = cx.root_view::(cx.window_ids()[0]).unwrap(); workspace_1.read_with(&cx, |workspace, cx| { @@ -274,13 +160,11 @@ mod tests { cx.update(|cx| { open_paths( - &OpenPaths(OpenParams { - paths: vec![ - dir.path().join("b").to_path_buf(), - dir.path().join("c").to_path_buf(), - ], - app_state: app_state.clone(), - }), + &[ + dir.path().join("b").to_path_buf(), + dir.path().join("c").to_path_buf(), + ], + &app_state, cx, ) }) @@ -292,7 +176,7 @@ mod tests { async fn test_new_empty_workspace(mut cx: gpui::TestAppContext) { let app_state = cx.update(test_app_state); cx.update(|cx| init(&app_state, cx)); - cx.dispatch_global_action(workspace::OpenNew(app_state.as_ref().into())); + cx.dispatch_global_action(workspace::OpenNew(app_state.clone())); let window_id = *cx.window_ids().first().unwrap(); let workspace = cx.root_view::(window_id).unwrap(); let editor = workspace.update(&mut cx, |workspace, cx| { @@ -576,7 +460,7 @@ mod tests { }); // Create a new untitled buffer - cx.dispatch_action(window_id, vec![workspace.id()], OpenNew(params.clone())); + cx.dispatch_action(window_id, vec![workspace.id()], OpenNew(app_state.clone())); let editor = workspace.read_with(&cx, |workspace, cx| { workspace .active_item(cx) @@ -639,7 +523,7 @@ mod tests { // Open the same newly-created file in another pane item. The new editor should reuse // the same buffer. - cx.dispatch_action(window_id, vec![workspace.id()], OpenNew(params.clone())); + cx.dispatch_action(window_id, vec![workspace.id()], OpenNew(app_state.clone())); workspace.update(&mut cx, |workspace, cx| { workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx); assert!(workspace @@ -675,7 +559,7 @@ mod tests { let (window_id, workspace) = cx.add_window(|cx| Workspace::new(¶ms, cx)); // Create a new untitled buffer - cx.dispatch_action(window_id, vec![workspace.id()], OpenNew(params.clone())); + cx.dispatch_action(window_id, vec![workspace.id()], OpenNew(app_state.clone())); let editor = workspace.read_with(&cx, |workspace, cx| { workspace .active_item(cx) From 9e8ef314521fddda647931f44a80a1a74dcd5c0c Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 18 Dec 2021 08:26:57 -0700 Subject: [PATCH 3/7] Return item handles when opening items This will support interacting with the opened item. Although I think I should probably return the ItemView rather than the Item. Next commit. --- crates/journal/src/journal.rs | 20 ++++---------- crates/workspace/src/workspace.rs | 46 +++++++++++++++---------------- 2 files changed, 29 insertions(+), 37 deletions(-) diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index 68a77295ce95b14a97a33527de96fc9dbd3778d8..a62fc0c5625cbe658938961e1aaec47ee843b5f8 100644 --- a/crates/journal/src/journal.rs +++ b/crates/journal/src/journal.rs @@ -9,22 +9,11 @@ use workspace::AppState; action!(NewJournalEntry); pub fn init(app_state: Arc, cx: &mut MutableAppContext) { - log::info!("JOURNAL INIT"); cx.add_bindings(vec![Binding::new("ctrl-alt-cmd-j", NewJournalEntry, None)]); - - let mut counter = 0; - cx.add_global_action(move |_: &NewJournalEntry, cx| { - log::info!("NEW JOURNAL ENTRY ACTION"); - counter += 1; - if counter == 2 { - log::info!("called twice?"); - } - new_journal_entry(app_state.clone(), cx) - }); + cx.add_global_action(move |_: &NewJournalEntry, cx| new_journal_entry(app_state.clone(), cx)); } pub fn new_journal_entry(app_state: Arc, cx: &mut MutableAppContext) { - log::info!("NEW JOURNAL ENTRY"); let paths = cx.background().spawn(async move { let now = Local::now(); let home_dir = dirs::home_dir().ok_or_else(|| anyhow!("can't determine home directory"))?; @@ -50,13 +39,16 @@ pub fn new_journal_entry(app_state: Arc, cx: &mut MutableAppContext) { .update(|cx| workspace::open_paths(&[journal_dir], &app_state, cx)) .await; - workspace + let opened = workspace .update(&mut cx, |workspace, cx| { workspace.open_paths(&[entry_path], cx) }) .await; - dbg!(workspace); + if let Some(Some(Ok(item))) = opened.first() { + log::info!("opened an item!"); + } + Ok(()) } .log_err() diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 87f792054b1dcba1159437bc61ac3e3c9ff95374..44d5d288b017aceaa014c403ea0c18b83953fd79 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -494,7 +494,11 @@ impl Workspace { } } - pub fn open_paths(&mut self, abs_paths: &[PathBuf], cx: &mut ViewContext) -> Task<()> { + pub fn open_paths( + &mut self, + abs_paths: &[PathBuf], + cx: &mut ViewContext, + ) -> Task, Arc>>>> { let entries = abs_paths .iter() .cloned() @@ -510,26 +514,26 @@ impl Workspace { cx.spawn(|this, mut cx| { let fs = fs.clone(); async move { - let project_path = project_path.await?; + let project_path = project_path.await.ok()?; if fs.is_file(&abs_path).await { if let Some(entry) = this.update(&mut cx, |this, cx| this.open_entry(project_path, cx)) { - entry.await; + return Some(entry.await); } } - Ok(()) + None } }) }) - .collect::>>>(); + .collect::>(); cx.foreground().spawn(async move { + let mut items = Vec::new(); for task in tasks { - if let Err(error) = task.await { - log::error!("error opening paths {}", error); - } + items.push(task.await); } + items }) } @@ -621,7 +625,7 @@ impl Workspace { &mut self, project_path: ProjectPath, cx: &mut ViewContext, - ) -> Option> { + ) -> Option, Arc>>> { let pane = self.active_pane().clone(); if self.activate_or_open_existing_entry(project_path.clone(), &pane, cx) { return None; @@ -676,21 +680,17 @@ impl Workspace { this.update(&mut cx, |this, cx| { this.loading_items.remove(&project_path); - if let Some(pane) = pane.upgrade(&cx) { - match load_result { - Ok(item) => { - // By the time loading finishes, the entry could have been already added - // to the pane. If it was, we activate it, otherwise we'll store the - // item and add a new view for it. - if !this.activate_or_open_existing_entry(project_path, &pane, cx) { - this.add_item(item, cx); - } - } - Err(error) => { - log::error!("error opening item: {}", error); - } - } + let pane = pane + .upgrade(&cx) + .ok_or_else(|| anyhow!("could not upgrade pane reference"))?; + let item = load_result?; + // By the time loading finishes, the entry could have been already added + // to the pane. If it was, we activate it, otherwise we'll store the + // item and add a new view for it. + if !this.activate_or_open_existing_entry(project_path, &pane, cx) { + this.add_item(item.boxed_clone(), cx); } + Ok(item) }) })) } From e4f18947de99e90b1f34328bfb0df2b8d0bbb0cc Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 18 Dec 2021 10:38:54 -0700 Subject: [PATCH 4/7] Insert a time heading when creating a journal entry --- Cargo.lock | 2 +- crates/editor/src/editor.rs | 2 +- crates/journal/Cargo.toml | 2 +- crates/journal/src/journal.rs | 56 ++++++++++++++++++++----------- crates/workspace/src/pane.rs | 6 ++-- crates/workspace/src/workspace.rs | 43 +++++++++++++++--------- 6 files changed, 71 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 09e08a510b783578c86aab5186cf3363bc3c0dd7..0b5450b8f434508179d37cd9a7a57a9a8ca02f3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2501,9 +2501,9 @@ dependencies = [ name = "journal" version = "0.1.0" dependencies = [ - "anyhow", "chrono", "dirs 4.0.0", + "editor", "gpui", "log", "util", diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 539736aca254e803fe9ac2ab29c51c63e0f014cb..968856493d890728a7b59e3823737282a3c91c26 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1251,7 +1251,7 @@ impl Editor { } } - fn insert(&mut self, text: &str, cx: &mut ViewContext) { + pub fn insert(&mut self, text: &str, cx: &mut ViewContext) { self.start_transaction(cx); let old_selections = self.selections::(cx).collect::>(); let mut new_selections = Vec::new(); diff --git a/crates/journal/Cargo.toml b/crates/journal/Cargo.toml index 64786f34aeee71f675de1397046d08db2f82492e..b2c470f4c5508f52da506ba8504016c2170250e3 100644 --- a/crates/journal/Cargo.toml +++ b/crates/journal/Cargo.toml @@ -7,10 +7,10 @@ edition = "2021" path = "src/journal.rs" [dependencies] +editor = { path = "../editor" } gpui = { path = "../gpui" } util = { path = "../util" } workspace = { path = "../workspace" } -anyhow = "1.0" chrono = "0.4" dirs = "4.0" log = "0.4" \ No newline at end of file diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index a62fc0c5625cbe658938961e1aaec47ee843b5f8..11895426d3c7fc3237050c33ca547cf5840f815c 100644 --- a/crates/journal/src/journal.rs +++ b/crates/journal/src/journal.rs @@ -1,8 +1,7 @@ -use std::{fs::OpenOptions, sync::Arc}; - -use anyhow::anyhow; -use chrono::{Datelike, Local}; +use chrono::{Datelike, Local, Timelike}; +use editor::{Autoscroll, Editor}; use gpui::{action, keymap::Binding, MutableAppContext}; +use std::{fs::OpenOptions, sync::Arc}; use util::TryFutureExt as _; use workspace::AppState; @@ -14,27 +13,37 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { } pub fn new_journal_entry(app_state: Arc, cx: &mut MutableAppContext) { - let paths = cx.background().spawn(async move { - let now = Local::now(); - let home_dir = dirs::home_dir().ok_or_else(|| anyhow!("can't determine home directory"))?; - let journal_dir = home_dir.join("journal"); - let month_dir = journal_dir - .join(now.year().to_string()) - .join(now.month().to_string()); - let entry_path = month_dir.join(format!("{}.md", now.day())); - - std::fs::create_dir_all(dbg!(month_dir))?; + let now = Local::now(); + let home_dir = match dirs::home_dir() { + Some(home_dir) => home_dir, + None => { + log::error!("can't determine home directory"); + return; + } + }; + + let journal_dir = home_dir.join("journal"); + let month_dir = journal_dir + .join(now.year().to_string()) + .join(now.month().to_string()); + let entry_path = month_dir.join(format!("{}.md", now.day())); + let now = now.time(); + let (pm, hour) = now.hour12(); + let am_or_pm = if pm { "PM" } else { "AM" }; + let entry_heading = format!("# {}:{} {}\n\n", hour, now.minute(), am_or_pm); + + let create_entry = cx.background().spawn(async move { + std::fs::create_dir_all(month_dir)?; OpenOptions::new() .create(true) .write(true) - .open(dbg!(&entry_path))?; - - Ok::<_, anyhow::Error>((journal_dir, entry_path)) + .open(&entry_path)?; + Ok::<_, std::io::Error>((journal_dir, entry_path)) }); cx.spawn(|mut cx| { async move { - let (journal_dir, entry_path) = paths.await?; + let (journal_dir, entry_path) = create_entry.await?; let workspace = cx .update(|cx| workspace::open_paths(&[journal_dir], &app_state, cx)) .await; @@ -46,7 +55,16 @@ pub fn new_journal_entry(app_state: Arc, cx: &mut MutableAppContext) { .await; if let Some(Some(Ok(item))) = opened.first() { - log::info!("opened an item!"); + if let Some(editor) = item.to_any().downcast::() { + editor.update(&mut cx, |editor, cx| { + let len = editor.buffer().read(cx).len(); + editor.select_ranges([len..len], Some(Autoscroll::Center), cx); + if len > 0 { + editor.insert("\n\n", cx); + } + editor.insert(&entry_heading, cx); + }); + } } Ok(()) diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index a13602016abbede33230ee18d791f936606fc314..5a3411c9268c254f811075cf11397f9e73079a8b 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -107,15 +107,15 @@ impl Pane { &mut self, project_path: ProjectPath, cx: &mut ViewContext, - ) -> bool { + ) -> Option> { if let Some(index) = self.items.iter().position(|item| { item.project_path(cx.as_ref()) .map_or(false, |item_path| item_path == project_path) }) { self.activate_item(index, cx); - true + self.items.get(index).map(|handle| handle.boxed_clone()) } else { - false + None } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 44d5d288b017aceaa014c403ea0c18b83953fd79..bd074f89f2fab51c6662acdd3bec029ba44c208e 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -498,7 +498,7 @@ impl Workspace { &mut self, abs_paths: &[PathBuf], cx: &mut ViewContext, - ) -> Task, Arc>>>> { + ) -> Task, Arc>>>> { let entries = abs_paths .iter() .cloned() @@ -625,10 +625,12 @@ impl Workspace { &mut self, project_path: ProjectPath, cx: &mut ViewContext, - ) -> Option, Arc>>> { + ) -> Option, Arc>>> { let pane = self.active_pane().clone(); - if self.activate_or_open_existing_entry(project_path.clone(), &pane, cx) { - return None; + if let Some(existing_item) = + self.activate_or_open_existing_entry(project_path.clone(), &pane, cx) + { + return Some(cx.foreground().spawn(async move { Ok(existing_item) })); } let worktree = match self @@ -687,10 +689,13 @@ impl Workspace { // By the time loading finishes, the entry could have been already added // to the pane. If it was, we activate it, otherwise we'll store the // item and add a new view for it. - if !this.activate_or_open_existing_entry(project_path, &pane, cx) { - this.add_item(item.boxed_clone(), cx); + if let Some(existing) = + this.activate_or_open_existing_entry(project_path, &pane, cx) + { + Ok(existing) + } else { + Ok(this.add_item(item.boxed_clone(), cx)) } - Ok(item) }) })) } @@ -700,11 +705,13 @@ impl Workspace { project_path: ProjectPath, pane: &ViewHandle, cx: &mut ViewContext, - ) -> bool { + ) -> Option> { // If the pane contains a view for this file, then activate // that item view. - if pane.update(cx, |pane, cx| pane.activate_entry(project_path.clone(), cx)) { - return true; + if let Some(existing_item_view) = + pane.update(cx, |pane, cx| pane.activate_entry(project_path.clone(), cx)) + { + return Some(existing_item_view); } // Otherwise, if this file is already open somewhere in the workspace, @@ -727,10 +734,10 @@ impl Workspace { } }); if let Some(view) = view_for_existing_item { - pane.add_item_view(view, cx.as_mut()); - true + pane.add_item_view(view.boxed_clone(), cx.as_mut()); + Some(view) } else { - false + None } } @@ -875,13 +882,19 @@ impl Workspace { pane } - pub fn add_item(&mut self, item_handle: T, cx: &mut ViewContext) + pub fn add_item( + &mut self, + item_handle: T, + cx: &mut ViewContext, + ) -> Box where T: ItemHandle, { let view = item_handle.add_view(cx.window_id(), self.settings.clone(), cx); self.items.push(item_handle.downgrade()); - self.active_pane().add_item_view(view, cx.as_mut()); + self.active_pane() + .add_item_view(view.boxed_clone(), cx.as_mut()); + view } fn activate_pane(&mut self, pane: ViewHandle, cx: &mut ViewContext) { From ab5db0bc1e5b6b54f523db9e240961fc9725d97b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 18 Dec 2021 11:06:07 -0700 Subject: [PATCH 5/7] Fix warnings and tests --- crates/gpui/src/app.rs | 9 ++---- crates/server/src/rpc.rs | 3 +- crates/zed/src/test.rs | 2 +- crates/zed/src/zed.rs | 68 +++++++++++++++++++++++++--------------- 4 files changed, 47 insertions(+), 35 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index f5c947019fe59f22a56a41b41b9b1b0de8548441..dd9e09f663fb21cf562e8b60469a0a5b72fec207 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -3992,12 +3992,7 @@ mod tests { let actions_clone = actions.clone(); cx.add_global_action(move |_: &Action, _: &mut MutableAppContext| { - actions_clone.borrow_mut().push("global a".to_string()); - }); - - let actions_clone = actions.clone(); - cx.add_global_action(move |_: &Action, _: &mut MutableAppContext| { - actions_clone.borrow_mut().push("global b".to_string()); + actions_clone.borrow_mut().push("global".to_string()); }); let actions_clone = actions.clone(); @@ -4053,7 +4048,7 @@ mod tests { assert_eq!( *actions.borrow(), - vec!["4 d", "4 c", "3 b", "3 a", "2 d", "2 c", "global b", "global a"] + vec!["4 d", "4 c", "3 b", "3 a", "2 d", "2 c", "global"] ); } diff --git a/crates/server/src/rpc.rs b/crates/server/src/rpc.rs index 96949d05ff57192341f14995d1fd50fdd875cd4a..5febe696c66fb2f73618a41fdb7a3802e963e491 100644 --- a/crates/server/src/rpc.rs +++ b/crates/server/src/rpc.rs @@ -1153,7 +1153,8 @@ mod tests { ) }) .unwrap() - .await; + .await + .unwrap(); workspace_b.read_with(&cx_b, |workspace, cx| { let active_pane = workspace.active_pane().read(cx); assert!(active_pane.active_item().is_some()); diff --git a/crates/zed/src/test.rs b/crates/zed/src/test.rs index a495b2689d49ed71045f90eb286ad90b9efe4f43..fa540ae72ae5011709f77e003f2e5bc5db4a9faa 100644 --- a/crates/zed/src/test.rs +++ b/crates/zed/src/test.rs @@ -1,4 +1,4 @@ -use crate::{assets::Assets, build_window_options, AppState, build_workspace}; +use crate::{assets::Assets, build_window_options, build_workspace, AppState}; use client::{http::ServerResponse, test::FakeHttpClient, ChannelList, Client, UserStore}; use gpui::{AssetSource, MutableAppContext}; use language::LanguageRegistry; diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 3c3f4f7f45930b31ce4419c9dc6250f1a7ad1803..ad543ee166e35bf1f27b771e0d963ccd7932bd0f 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -31,7 +31,6 @@ const MIN_FONT_SIZE: f32 = 6.0; pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { cx.add_global_action(quit); - cx.add_global_action({ let settings_tx = app_state.settings_tx.clone(); @@ -175,7 +174,9 @@ mod tests { #[gpui::test] async fn test_new_empty_workspace(mut cx: gpui::TestAppContext) { let app_state = cx.update(test_app_state); - cx.update(|cx| init(&app_state, cx)); + cx.update(|cx| { + workspace::init(cx); + }); cx.dispatch_global_action(workspace::OpenNew(app_state.clone())); let window_id = *cx.window_ids().first().unwrap(); let workspace = cx.root_view::(window_id).unwrap(); @@ -241,10 +242,11 @@ mod tests { let file3 = entries[2].clone(); // Open the first entry - workspace + let entry_1 = workspace .update(&mut cx, |w, cx| w.open_entry(file1.clone(), cx)) .unwrap() - .await; + .await + .unwrap(); cx.read(|cx| { let pane = workspace.read(cx).active_pane().read(cx); assert_eq!( @@ -258,7 +260,8 @@ mod tests { workspace .update(&mut cx, |w, cx| w.open_entry(file2.clone(), cx)) .unwrap() - .await; + .await + .unwrap(); cx.read(|cx| { let pane = workspace.read(cx).active_pane().read(cx); assert_eq!( @@ -269,9 +272,12 @@ mod tests { }); // Open the first entry again. The existing pane item is activated. - workspace.update(&mut cx, |w, cx| { - assert!(w.open_entry(file1.clone(), cx).is_none()) - }); + let entry_1b = workspace + .update(&mut cx, |w, cx| w.open_entry(file1.clone(), cx).unwrap()) + .await + .unwrap(); + assert_eq!(entry_1.id(), entry_1b.id()); + cx.read(|cx| { let pane = workspace.read(cx).active_pane().read(cx); assert_eq!( @@ -282,9 +288,15 @@ mod tests { }); // Split the pane with the first entry, then open the second entry again. - workspace.update(&mut cx, |w, cx| { - w.split_pane(w.active_pane().clone(), SplitDirection::Right, cx); - assert!(w.open_entry(file2.clone(), cx).is_none()); + workspace + .update(&mut cx, |w, cx| { + w.split_pane(w.active_pane().clone(), SplitDirection::Right, cx); + w.open_entry(file2.clone(), cx).unwrap() + }) + .await + .unwrap(); + + workspace.read_with(&cx, |w, cx| { assert_eq!( w.active_pane() .read(cx) @@ -302,8 +314,8 @@ mod tests { w.open_entry(file3.clone(), cx).unwrap(), ) }); - t1.await; - t2.await; + t1.await.unwrap(); + t2.await.unwrap(); cx.read(|cx| { let pane = workspace.read(cx).active_pane().read(cx); assert_eq!( @@ -524,18 +536,21 @@ mod tests { // Open the same newly-created file in another pane item. The new editor should reuse // the same buffer. cx.dispatch_action(window_id, vec![workspace.id()], OpenNew(app_state.clone())); - workspace.update(&mut cx, |workspace, cx| { - workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx); - assert!(workspace - .open_entry( - ProjectPath { - worktree_id: worktree.id(), - path: Path::new("the-new-name.rs").into() - }, - cx - ) - .is_none()); - }); + workspace + .update(&mut cx, |workspace, cx| { + workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx); + workspace + .open_entry( + ProjectPath { + worktree_id: worktree.id(), + path: Path::new("the-new-name.rs").into(), + }, + cx, + ) + .unwrap() + }) + .await + .unwrap(); let editor2 = workspace.update(&mut cx, |workspace, cx| { workspace .active_item(cx) @@ -631,7 +646,8 @@ mod tests { workspace .update(&mut cx, |w, cx| w.open_entry(file1.clone(), cx)) .unwrap() - .await; + .await + .unwrap(); cx.read(|cx| { assert_eq!( pane_1.read(cx).active_item().unwrap().project_path(cx), From 0a37d40fad237a715f147709db4b67247069a468 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 18 Dec 2021 11:11:04 -0700 Subject: [PATCH 6/7] Pad single-digit months, days, and minutes with a leading zero --- crates/journal/src/journal.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index 11895426d3c7fc3237050c33ca547cf5840f815c..6f5241d1654fcc88cc4415ba12470e706ccb130e 100644 --- a/crates/journal/src/journal.rs +++ b/crates/journal/src/journal.rs @@ -24,13 +24,13 @@ pub fn new_journal_entry(app_state: Arc, cx: &mut MutableAppContext) { let journal_dir = home_dir.join("journal"); let month_dir = journal_dir - .join(now.year().to_string()) - .join(now.month().to_string()); - let entry_path = month_dir.join(format!("{}.md", now.day())); + .join(format!("{:2}", now.year())) + .join(format!("{:2}", now.month())); + let entry_path = month_dir.join(format!("{:2}.md", now.day())); let now = now.time(); let (pm, hour) = now.hour12(); let am_or_pm = if pm { "PM" } else { "AM" }; - let entry_heading = format!("# {}:{} {}\n\n", hour, now.minute(), am_or_pm); + let entry_heading = format!("# {}:{:2} {}\n\n", hour, now.minute(), am_or_pm); let create_entry = cx.background().spawn(async move { std::fs::create_dir_all(month_dir)?; From 29bc2db6e85030e5ce7ecd8d8740edad1b0bbcf2 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 18 Dec 2021 12:15:07 -0700 Subject: [PATCH 7/7] Fix journal format strings --- crates/journal/src/journal.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index 6f5241d1654fcc88cc4415ba12470e706ccb130e..71d6af83663cf450cea59fb90ae6607f98241f9d 100644 --- a/crates/journal/src/journal.rs +++ b/crates/journal/src/journal.rs @@ -24,13 +24,13 @@ pub fn new_journal_entry(app_state: Arc, cx: &mut MutableAppContext) { let journal_dir = home_dir.join("journal"); let month_dir = journal_dir - .join(format!("{:2}", now.year())) - .join(format!("{:2}", now.month())); - let entry_path = month_dir.join(format!("{:2}.md", now.day())); + .join(format!("{:02}", now.year())) + .join(format!("{:02}", now.month())); + let entry_path = month_dir.join(format!("{:02}.md", now.day())); let now = now.time(); let (pm, hour) = now.hour12(); let am_or_pm = if pm { "PM" } else { "AM" }; - let entry_heading = format!("# {}:{:2} {}\n\n", hour, now.minute(), am_or_pm); + let entry_heading = format!("# {}:{:02} {}\n\n", hour, now.minute(), am_or_pm); let create_entry = cx.background().spawn(async move { std::fs::create_dir_all(month_dir)?;