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 38c3aba045..f5c947019f 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 0aadd18f66..09e08a510b 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 0000000000..64786f34ae --- /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 0000000000..68a77295ce --- /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 545b512a8b..ef5512a3ea 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 66a36889f5..b0c66b005b 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 a5ca3c91e9..497254074e 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 1f11f11693..87f792054b 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 e03ef6dcf9..adcd7365ae 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 c6374c66e7..34828b7fa7 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 25a4b5e6f9..33ac76e63c 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 91f1c165ee..a495b2689d 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 16f67fa171..3c3f4f7f45 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 68a77295ce..a62fc0c562 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 87f792054b..44d5d288b0 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 | 54 ++++++++++++++++++++----------- crates/workspace/src/pane.rs | 6 ++-- crates/workspace/src/workspace.rs | 43 +++++++++++++++--------- 6 files changed, 70 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 09e08a510b..0b5450b8f4 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 539736aca2..968856493d 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 64786f34ae..b2c470f4c5 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 a62fc0c562..11895426d3 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())); + 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; + } + }; - std::fs::create_dir_all(dbg!(month_dir))?; + 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 a13602016a..5a3411c926 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 44d5d288b0..bd074f89f2 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 f5c947019f..dd9e09f663 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 96949d05ff..5febe696c6 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 a495b2689d..fa540ae72a 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 3c3f4f7f45..ad543ee166 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 11895426d3..6f5241d165 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 6f5241d165..71d6af8366 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)?;