diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 281ad99145..093f5c7d4d 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -132,7 +132,7 @@ impl AnyAction for () { #[macro_export] macro_rules! action { ($name:ident, $arg:ty) => { - #[derive(Clone, Debug)] + #[derive(Clone)] pub struct $name(pub $arg); impl $crate::Action for $name { diff --git a/gpui/src/keymap.rs b/gpui/src/keymap.rs index da4bfa1ced..4093664193 100644 --- a/gpui/src/keymap.rs +++ b/gpui/src/keymap.rs @@ -2,6 +2,7 @@ use anyhow::anyhow; use std::{ any::Any, collections::{HashMap, HashSet}, + fmt::Debug, }; use tree_sitter::{Language, Node, Parser}; @@ -148,7 +149,7 @@ impl Keymap { } } -mod menu { +pub mod menu { use crate::action; action!(SelectPrev); @@ -425,6 +426,11 @@ mod tests { } } impl Eq for A {} + impl Debug for A { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "A({:?})", &self.0) + } + } #[derive(Clone, Debug, Eq, PartialEq)] struct ActionArg { @@ -472,12 +478,10 @@ mod tests { } impl Matcher { - fn test_keystroke( - &mut self, - keystroke: &str, - view_id: usize, - cx: &Context, - ) -> Option { + fn test_keystroke(&mut self, keystroke: &str, view_id: usize, cx: &Context) -> Option + where + A: Action + Debug + Eq, + { if let MatchResult::Action(action) = self.push_keystroke(Keystroke::parse(keystroke).unwrap(), view_id, cx) { diff --git a/gpui/src/platform/mac/platform.rs b/gpui/src/platform/mac/platform.rs index 1b4331bedc..6593f9e3ef 100644 --- a/gpui/src/platform/mac/platform.rs +++ b/gpui/src/platform/mac/platform.rs @@ -29,7 +29,6 @@ use objc::{ }; use ptr::null_mut; use std::{ - any::Any, cell::{Cell, RefCell}, convert::TryInto, ffi::{c_void, CStr}, diff --git a/server/src/rpc.rs b/server/src/rpc.rs index 9742ec015f..5b907df01e 100644 --- a/server/src/rpc.rs +++ b/server/src/rpc.rs @@ -934,7 +934,7 @@ mod tests { use std::{path::Path, sync::Arc, time::Duration}; use zed::{ channel::{Channel, ChannelDetails, ChannelList}, - editor::Editor, + editor::{Editor, Insert}, fs::{FakeFs, Fs as _}, language::LanguageRegistry, rpc::Client, @@ -1023,7 +1023,7 @@ mod tests { // Edit the buffer as client B and see that edit as client A. editor_b.update(&mut cx_b, |editor, cx| { - editor.insert(&"ok, ".to_string(), cx) + editor.insert(&Insert("ok, ".into()), cx) }); buffer_a .condition(&cx_a, |buffer, _| buffer.text() == "ok, b-contents") diff --git a/zed/src/file_finder.rs b/zed/src/file_finder.rs index d370f85a08..4d4cf13013 100644 --- a/zed/src/file_finder.rs +++ b/zed/src/file_finder.rs @@ -6,8 +6,13 @@ use crate::{ worktree::{match_paths, PathMatch}, }; use gpui::{ + action, elements::*, - keymap::{self, Binding}, + keymap::{ + self, + menu::{SelectNext, SelectPrev}, + Binding, + }, AppContext, Axis, Entity, MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle, WeakViewHandle, }; @@ -36,17 +41,27 @@ pub struct FileFinder { list_state: UniformListState, } +action!(Toggle); +action!(Confirm); +action!(Select, Entry); + +#[derive(Clone)] +pub struct Entry { + worktree_id: usize, + path: Arc, +} + pub fn init(cx: &mut MutableAppContext) { - cx.add_action("file_finder:toggle", FileFinder::toggle); - cx.add_action("file_finder:confirm", FileFinder::confirm); - cx.add_action("file_finder:select", FileFinder::select); - cx.add_action("menu:select_prev", FileFinder::select_prev); - cx.add_action("menu:select_next", FileFinder::select_next); + cx.add_action(FileFinder::toggle); + cx.add_action(FileFinder::confirm); + cx.add_action(FileFinder::select); + cx.add_action(FileFinder::select_prev); + cx.add_action(FileFinder::select_next); cx.add_bindings(vec![ - Binding::new("cmd-p", "file_finder:toggle", None), - Binding::new("escape", "file_finder:toggle", Some("FileFinder")), - Binding::new("enter", "file_finder:confirm", Some("FileFinder")), + Binding::new("cmd-p", Toggle, None), + Binding::new("escape", Toggle, Some("FileFinder")), + Binding::new("enter", Confirm, Some("FileFinder")), ]); } @@ -196,10 +211,13 @@ impl FileFinder { ) .with_style(&style.container); - let entry = (path_match.tree_id, path_match.path.clone()); + let action = Select(Entry { + worktree_id: path_match.tree_id, + path: path_match.path.clone(), + }); EventHandler::new(container.boxed()) .on_mouse_down(move |cx| { - cx.dispatch_action("file_finder:select", entry.clone()); + cx.dispatch_action(action.clone()); true }) .named("match") @@ -230,7 +248,7 @@ impl FileFinder { (file_name, file_name_positions, full_path, path_positions) } - fn toggle(workspace: &mut Workspace, _: &(), cx: &mut ViewContext) { + fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext) { workspace.toggle_modal(cx, |cx, workspace| { let handle = cx.handle(); let finder = cx.add_view(|cx| Self::new(workspace.settings.clone(), handle, cx)); @@ -328,7 +346,7 @@ impl FileFinder { 0 } - fn select_prev(&mut self, _: &(), cx: &mut ViewContext) { + fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext) { let mut selected_index = self.selected_index(); if selected_index > 0 { selected_index -= 1; @@ -339,7 +357,7 @@ impl FileFinder { cx.notify(); } - fn select_next(&mut self, _: &(), cx: &mut ViewContext) { + fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext) { let mut selected_index = self.selected_index(); if selected_index + 1 < self.matches.len() { selected_index += 1; @@ -350,14 +368,14 @@ impl FileFinder { cx.notify(); } - fn confirm(&mut self, _: &(), cx: &mut ViewContext) { + fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { if let Some(m) = self.matches.get(self.selected_index()) { cx.emit(Event::Selected(m.tree_id, m.path.clone())); } } - fn select(&mut self, (tree_id, path): &(usize, Arc), cx: &mut ViewContext) { - cx.emit(Event::Selected(*tree_id, path.clone())); + fn select(&mut self, Select(entry): &Select, cx: &mut ViewContext) { + cx.emit(Event::Selected(entry.worktree_id, entry.path.clone())); } #[must_use] @@ -417,7 +435,7 @@ impl FileFinder { mod tests { use super::*; use crate::{ - editor, + editor::{self, Insert}, fs::FakeFs, test::{build_app_state, temp_tree}, workspace::Workspace, @@ -447,12 +465,7 @@ mod tests { .unwrap(); cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx)) .await; - cx.dispatch_action( - window_id, - vec![workspace.id()], - "file_finder:toggle".into(), - (), - ); + cx.dispatch_action(window_id, vec![workspace.id()], Toggle); let finder = cx.read(|cx| { workspace @@ -466,26 +479,16 @@ mod tests { let query_buffer = cx.read(|cx| finder.read(cx).query_buffer.clone()); let chain = vec![finder.id(), query_buffer.id()]; - cx.dispatch_action(window_id, chain.clone(), "buffer:insert", "b".to_string()); - cx.dispatch_action(window_id, chain.clone(), "buffer:insert", "n".to_string()); - cx.dispatch_action(window_id, chain.clone(), "buffer:insert", "a".to_string()); + cx.dispatch_action(window_id, chain.clone(), Insert("b".into())); + cx.dispatch_action(window_id, chain.clone(), Insert("n".into())); + cx.dispatch_action(window_id, chain.clone(), Insert("a".into())); finder .condition(&cx, |finder, _| finder.matches.len() == 2) .await; let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); - cx.dispatch_action( - window_id, - vec![workspace.id(), finder.id()], - "menu:select_next", - (), - ); - cx.dispatch_action( - window_id, - vec![workspace.id(), finder.id()], - "file_finder:confirm", - (), - ); + cx.dispatch_action(window_id, vec![workspace.id(), finder.id()], SelectNext); + cx.dispatch_action(window_id, vec![workspace.id(), finder.id()], Confirm); active_pane .condition(&cx, |pane, _| pane.active_item().is_some()) .await; @@ -648,9 +651,9 @@ mod tests { finder.update(&mut cx, |f, cx| { assert_eq!(f.matches.len(), 2); assert_eq!(f.selected_index(), 0); - f.select_next(&(), cx); + f.select_next(&SelectNext, cx); assert_eq!(f.selected_index(), 1); - f.select_prev(&(), cx); + f.select_prev(&SelectPrev, cx); assert_eq!(f.selected_index(), 0); }); } diff --git a/zed/src/lib.rs b/zed/src/lib.rs index 45af7e355c..d76a308e38 100644 --- a/zed/src/lib.rs +++ b/zed/src/lib.rs @@ -19,12 +19,16 @@ mod util; pub mod workspace; pub mod worktree; +use gpui::action; pub use settings::Settings; use parking_lot::Mutex; use postage::watch; use std::sync::Arc; +action!(About); +action!(Quit); + pub struct AppState { pub settings_tx: Arc>>, pub settings: watch::Receiver, @@ -35,9 +39,9 @@ pub struct AppState { } pub fn init(cx: &mut gpui::MutableAppContext) { - cx.add_global_action("app:quit", quit); + cx.add_global_action(quit); } -fn quit(_: &(), cx: &mut gpui::MutableAppContext) { +fn quit(_: &Quit, cx: &mut gpui::MutableAppContext) { cx.platform().quit(); } diff --git a/zed/src/main.rs b/zed/src/main.rs index f087109c99..c35b2bf915 100644 --- a/zed/src/main.rs +++ b/zed/src/main.rs @@ -10,7 +10,7 @@ use zed::{ self, assets, editor, file_finder, fs::RealFs, language, menus, rpc, settings, theme_selector, - workspace::{self, OpenParams}, + workspace::{self, OpenParams, OpenPaths}, AppState, }; @@ -51,13 +51,10 @@ fn main() { let paths = collect_path_args(); if !paths.is_empty() { - cx.dispatch_global_action( - "workspace:open_paths", - OpenParams { - paths, - app_state: app_state.clone(), - }, - ); + cx.dispatch_global_action(OpenPaths(OpenParams { + paths, + app_state: app_state.clone(), + })); } }); } diff --git a/zed/src/menus.rs b/zed/src/menus.rs index 227f0b9efc..c43d72a87d 100644 --- a/zed/src/menus.rs +++ b/zed/src/menus.rs @@ -1,9 +1,11 @@ -use crate::AppState; +use crate::{workspace, AppState}; use gpui::{Menu, MenuItem}; use std::sync::Arc; #[cfg(target_os = "macos")] pub fn menus(state: &Arc) -> Vec> { + use crate::editor; + vec![ Menu { name: "Zed", @@ -11,27 +13,23 @@ pub fn menus(state: &Arc) -> Vec> { MenuItem::Action { name: "About Zed…", keystroke: None, - action: "app:about-zed", - arg: None, + action: Box::new(super::About), }, MenuItem::Separator, MenuItem::Action { name: "Share", keystroke: None, - action: "workspace:share_worktree", - arg: None, + action: Box::new(workspace::ShareWorktree), }, MenuItem::Action { name: "Join", keystroke: None, - action: "workspace:join_worktree", - arg: None, + action: Box::new(workspace::JoinWorktree(state.clone())), }, MenuItem::Action { name: "Quit", keystroke: Some("cmd-q"), - action: "app:quit", - arg: None, + action: Box::new(super::Quit), }, ], }, @@ -41,15 +39,13 @@ pub fn menus(state: &Arc) -> Vec> { MenuItem::Action { name: "New", keystroke: Some("cmd-n"), - action: "workspace:new_file", - arg: Some(Box::new(state.clone())), + action: Box::new(workspace::OpenNew(state.clone())), }, MenuItem::Separator, MenuItem::Action { name: "Open…", keystroke: Some("cmd-o"), - action: "workspace:open", - arg: Some(Box::new(state.clone())), + action: Box::new(workspace::Open(state.clone())), }, ], }, @@ -59,33 +55,28 @@ pub fn menus(state: &Arc) -> Vec> { MenuItem::Action { name: "Undo", keystroke: Some("cmd-z"), - action: "buffer:undo", - arg: None, + action: Box::new(editor::Undo), }, MenuItem::Action { name: "Redo", keystroke: Some("cmd-Z"), - action: "buffer:redo", - arg: None, + action: Box::new(editor::Redo), }, MenuItem::Separator, MenuItem::Action { name: "Cut", keystroke: Some("cmd-x"), - action: "buffer:cut", - arg: None, + action: Box::new(editor::Cut), }, MenuItem::Action { name: "Copy", keystroke: Some("cmd-c"), - action: "buffer:copy", - arg: None, + action: Box::new(editor::Copy), }, MenuItem::Action { name: "Paste", keystroke: Some("cmd-v"), - action: "buffer:paste", - arg: None, + action: Box::new(editor::Paste), }, ], }, diff --git a/zed/src/theme_selector.rs b/zed/src/theme_selector.rs index 2853f938fa..506d11a278 100644 --- a/zed/src/theme_selector.rs +++ b/zed/src/theme_selector.rs @@ -8,11 +8,12 @@ use crate::{ AppState, Settings, }; use gpui::{ + action, elements::{ Align, ChildView, ConstrainedBox, Container, Expanded, Flex, Label, ParentElement, UniformList, UniformListState, }, - keymap::{self, Binding}, + keymap::{self, menu, Binding}, AppContext, Axis, Element, ElementBox, Entity, MutableAppContext, RenderContext, View, ViewContext, ViewHandle, }; @@ -29,19 +30,22 @@ pub struct ThemeSelector { selected_index: usize, } +action!(Confirm); +action!(Toggle, Arc); +action!(Reload, Arc); + pub fn init(cx: &mut MutableAppContext, app_state: &Arc) { - cx.add_action("theme_selector:confirm", ThemeSelector::confirm); - cx.add_action("menu:select_prev", ThemeSelector::select_prev); - cx.add_action("menu:select_next", ThemeSelector::select_next); - cx.add_action("theme_selector:toggle", ThemeSelector::toggle); - cx.add_action("theme_selector:reload", ThemeSelector::reload); + cx.add_action(ThemeSelector::confirm); + cx.add_action(ThemeSelector::select_prev); + cx.add_action(ThemeSelector::select_next); + cx.add_action(ThemeSelector::toggle); + cx.add_action(ThemeSelector::reload); cx.add_bindings(vec![ - Binding::new("cmd-k cmd-t", "theme_selector:toggle", None).with_arg(app_state.clone()), - Binding::new("cmd-k t", "theme_selector:reload", None).with_arg(app_state.clone()), - Binding::new("escape", "theme_selector:toggle", Some("ThemeSelector")) - .with_arg(app_state.clone()), - Binding::new("enter", "theme_selector:confirm", Some("ThemeSelector")), + Binding::new("cmd-k cmd-t", Toggle(app_state.clone()), None), + Binding::new("cmd-k t", Reload(app_state.clone()), None), + Binding::new("escape", Toggle(app_state.clone()), Some("ThemeSelector")), + Binding::new("enter", Confirm, Some("ThemeSelector")), ]); } @@ -72,17 +76,13 @@ impl ThemeSelector { this } - fn toggle( - workspace: &mut Workspace, - app_state: &Arc, - cx: &mut ViewContext, - ) { + fn toggle(workspace: &mut Workspace, action: &Toggle, cx: &mut ViewContext) { workspace.toggle_modal(cx, |cx, _| { let selector = cx.add_view(|cx| { Self::new( - app_state.settings_tx.clone(), - app_state.settings.clone(), - app_state.themes.clone(), + action.0.settings_tx.clone(), + action.0.settings.clone(), + action.0.themes.clone(), cx, ) }); @@ -91,13 +91,13 @@ impl ThemeSelector { }); } - fn reload(_: &mut Workspace, app_state: &Arc, cx: &mut ViewContext) { - let current_theme_name = app_state.settings.borrow().theme.name.clone(); - app_state.themes.clear(); - match app_state.themes.get(¤t_theme_name) { + fn reload(_: &mut Workspace, action: &Reload, cx: &mut ViewContext) { + let current_theme_name = action.0.settings.borrow().theme.name.clone(); + action.0.themes.clear(); + match action.0.themes.get(¤t_theme_name) { Ok(theme) => { cx.notify_all(); - app_state.settings_tx.lock().borrow_mut().theme = theme; + action.0.settings_tx.lock().borrow_mut().theme = theme; } Err(error) => { log::error!("failed to load theme {}: {:?}", current_theme_name, error) @@ -105,7 +105,7 @@ impl ThemeSelector { } } - fn confirm(&mut self, _: &(), cx: &mut ViewContext) { + fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { if let Some(mat) = self.matches.get(self.selected_index) { match self.registry.get(&mat.string) { Ok(theme) => { @@ -118,7 +118,7 @@ impl ThemeSelector { } } - fn select_prev(&mut self, _: &(), cx: &mut ViewContext) { + fn select_prev(&mut self, _: &menu::SelectPrev, cx: &mut ViewContext) { if self.selected_index > 0 { self.selected_index -= 1; } @@ -126,7 +126,7 @@ impl ThemeSelector { cx.notify(); } - fn select_next(&mut self, _: &(), cx: &mut ViewContext) { + fn select_next(&mut self, _: &menu::SelectNext, cx: &mut ViewContext) { if self.selected_index + 1 < self.matches.len() { self.selected_index += 1; } diff --git a/zed/src/workspace.rs b/zed/src/workspace.rs index 7c133944f1..e5f4c95495 100644 --- a/zed/src/workspace.rs +++ b/zed/src/workspace.rs @@ -14,6 +14,7 @@ use crate::{ }; use anyhow::{anyhow, Result}; use gpui::{ + action, elements::*, geometry::{rect::RectF, vector::vec2f}, json::to_string_pretty, @@ -36,37 +37,43 @@ use std::{ sync::Arc, }; +action!(Open, Arc); +action!(OpenPaths, OpenParams); +action!(OpenNew, Arc); +action!(ShareWorktree); +action!(JoinWorktree, Arc); +action!(Save); +action!(DebugElements); +action!(ToggleSidebarItem, (Side, usize)); + pub fn init(cx: &mut MutableAppContext) { - cx.add_global_action("workspace:open", open); - cx.add_global_action( - "workspace:open_paths", - |params: &OpenParams, cx: &mut MutableAppContext| open_paths(params, cx).detach(), - ); - cx.add_global_action("workspace:new_file", open_new); - cx.add_global_action("workspace:join_worktree", join_worktree); - cx.add_action("workspace:save", Workspace::save_active_item); - cx.add_action("workspace:debug_elements", Workspace::debug_elements); - cx.add_action("workspace:new_file", Workspace::open_new_file); - cx.add_action("workspace:share_worktree", Workspace::share_worktree); - cx.add_action("workspace:join_worktree", Workspace::join_worktree); - cx.add_action( - "workspace:toggle_sidebar_item", - Workspace::toggle_sidebar_item, - ); + 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(join_worktree); + cx.add_action(Workspace::save_active_item); + cx.add_action(Workspace::debug_elements); + cx.add_action(Workspace::open_new_file); + cx.add_action(Workspace::share_worktree); + cx.add_action(Workspace::join_worktree); + cx.add_action(Workspace::toggle_sidebar_item); cx.add_bindings(vec![ - Binding::new("cmd-s", "workspace:save", None), - Binding::new("cmd-alt-i", "workspace:debug_elements", None), + Binding::new("cmd-s", Save, None), + Binding::new("cmd-alt-i", DebugElements, None), ]); pane::init(cx); } +#[derive(Clone)] pub struct OpenParams { pub paths: Vec, pub app_state: Arc, } -fn open(app_state: &Arc, cx: &mut MutableAppContext) { - let app_state = app_state.clone(); +fn open(action: &Open, cx: &mut MutableAppContext) { + let app_state = action.0.clone(); cx.prompt_for_paths( PathPromptOptions { files: true, @@ -75,22 +82,22 @@ fn open(app_state: &Arc, cx: &mut MutableAppContext) { }, move |paths, cx| { if let Some(paths) = paths { - cx.dispatch_global_action("workspace:open_paths", OpenParams { paths, app_state }); + cx.dispatch_global_action(OpenPaths(OpenParams { paths, app_state })); } }, ); } -fn open_paths(params: &OpenParams, cx: &mut MutableAppContext) -> Task<()> { - log::info!("open paths {:?}", params.paths); +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(¶ms.paths, cx.as_ref()) { + if view.contains_paths(&action.0.paths, cx.as_ref()) { log::info!("open paths on existing workspace"); - Some(view.open_paths(¶ms.paths, cx)) + Some(view.open_paths(&action.0.paths, cx)) } else { None } @@ -106,23 +113,26 @@ fn open_paths(params: &OpenParams, cx: &mut MutableAppContext) -> Task<()> { // Add a new workspace if necessary - let (_, workspace) = - cx.add_window(window_options(), |cx| Workspace::new(¶ms.app_state, cx)); - workspace.update(cx, |workspace, cx| workspace.open_paths(¶ms.paths, cx)) + let (_, workspace) = cx.add_window(window_options(), |cx| { + Workspace::new(&action.0.app_state, cx) + }); + workspace.update(cx, |workspace, cx| { + workspace.open_paths(&action.0.paths, cx) + }) } -fn open_new(app_state: &Arc, cx: &mut MutableAppContext) { +fn open_new(action: &OpenNew, cx: &mut MutableAppContext) { cx.add_window(window_options(), |cx| { - let mut view = Workspace::new(app_state.as_ref(), cx); - view.open_new_file(&app_state, cx); + let mut view = Workspace::new(action.0.as_ref(), cx); + view.open_new_file(&action, cx); view }); } -fn join_worktree(app_state: &Arc, cx: &mut MutableAppContext) { +fn join_worktree(action: &JoinWorktree, cx: &mut MutableAppContext) { cx.add_window(window_options(), |cx| { - let mut view = Workspace::new(app_state.as_ref(), cx); - view.join_worktree(&(), cx); + let mut view = Workspace::new(action.0.as_ref(), cx); + view.join_worktree(action, cx); view }); } @@ -544,7 +554,7 @@ impl Workspace { } } - pub fn open_new_file(&mut self, _: &Arc, cx: &mut ViewContext) { + pub fn open_new_file(&mut self, _: &OpenNew, cx: &mut ViewContext) { let buffer = cx.add_model(|cx| Buffer::new(0, "", cx)); let buffer_view = cx.add_view(|cx| Editor::for_buffer(buffer.clone(), self.settings.clone(), cx)); @@ -677,7 +687,7 @@ impl Workspace { self.active_pane().read(cx).active_item() } - pub fn save_active_item(&mut self, _: &(), cx: &mut ViewContext) { + pub fn save_active_item(&mut self, _: &Save, cx: &mut ViewContext) { if let Some(item) = self.active_item(cx) { let handle = cx.handle(); if item.entry_id(cx.as_ref()).is_none() { @@ -744,7 +754,7 @@ impl Workspace { pub fn toggle_sidebar_item( &mut self, - (side, item_ix): &(Side, usize), + ToggleSidebarItem((side, item_ix)): &ToggleSidebarItem, cx: &mut ViewContext, ) { let sidebar = match side { @@ -755,7 +765,7 @@ impl Workspace { cx.notify(); } - pub fn debug_elements(&mut self, _: &(), cx: &mut ViewContext) { + pub fn debug_elements(&mut self, _: &DebugElements, cx: &mut ViewContext) { match to_string_pretty(&cx.debug_elements()) { Ok(json) => { let kib = json.len() as f32 / 1024.; @@ -771,7 +781,7 @@ impl Workspace { }; } - fn share_worktree(&mut self, _: &(), cx: &mut ViewContext) { + fn share_worktree(&mut self, _: &ShareWorktree, cx: &mut ViewContext) { let rpc = self.rpc.clone(); let platform = cx.platform(); @@ -803,7 +813,7 @@ impl Workspace { .detach(); } - fn join_worktree(&mut self, _: &(), cx: &mut ViewContext) { + fn join_worktree(&mut self, _: &JoinWorktree, cx: &mut ViewContext) { let rpc = self.rpc.clone(); let languages = self.languages.clone(); @@ -1000,7 +1010,7 @@ impl WorkspaceHandle for ViewHandle { mod tests { use super::*; use crate::{ - editor::Editor, + editor::{Editor, Insert}, fs::FakeFs, test::{build_app_state, temp_tree}, worktree::WorktreeHandle, @@ -1029,13 +1039,13 @@ mod tests { cx.update(|cx| { open_paths( - &OpenParams { + &OpenPaths(OpenParams { paths: vec![ dir.path().join("a").to_path_buf(), dir.path().join("b").to_path_buf(), ], app_state: app_state.clone(), - }, + }), cx, ) }) @@ -1044,10 +1054,10 @@ mod tests { cx.update(|cx| { open_paths( - &OpenParams { + &OpenPaths(OpenParams { paths: vec![dir.path().join("a").to_path_buf()], app_state: app_state.clone(), - }, + }), cx, ) }) @@ -1060,13 +1070,13 @@ mod tests { cx.update(|cx| { open_paths( - &OpenParams { + &OpenPaths(OpenParams { paths: vec![ dir.path().join("b").to_path_buf(), dir.path().join("c").to_path_buf(), ], app_state: app_state.clone(), - }, + }), cx, ) }) @@ -1284,14 +1294,14 @@ mod tests { item.to_any().downcast::().unwrap() }); - cx.update(|cx| editor.update(cx, |editor, cx| editor.insert(&"x".to_string(), cx))); + cx.update(|cx| editor.update(cx, |editor, cx| editor.insert(&Insert("x".into()), cx))); fs::write(dir.path().join("a.txt"), "changed").unwrap(); editor .condition(&cx, |editor, cx| editor.has_conflict(cx)) .await; cx.read(|cx| assert!(editor.is_dirty(cx))); - cx.update(|cx| workspace.update(cx, |w, cx| w.save_active_item(&(), cx))); + cx.update(|cx| workspace.update(cx, |w, cx| w.save_active_item(&Save, cx))); cx.simulate_prompt_answer(window_id, 0); editor .condition(&cx, |editor, cx| !editor.is_dirty(cx)) @@ -1323,7 +1333,7 @@ mod tests { // Create a new untitled buffer let editor = workspace.update(&mut cx, |workspace, cx| { - workspace.open_new_file(&app_state, cx); + workspace.open_new_file(&OpenNew(app_state.clone()), cx); workspace .active_item(cx) .unwrap() @@ -1335,12 +1345,14 @@ mod tests { editor.update(&mut cx, |editor, cx| { assert!(!editor.is_dirty(cx.as_ref())); assert_eq!(editor.title(cx.as_ref()), "untitled"); - editor.insert(&"hi".to_string(), cx); + editor.insert(&Insert("hi".into()), cx); assert!(editor.is_dirty(cx.as_ref())); }); // Save the buffer. This prompts for a filename. - workspace.update(&mut cx, |workspace, cx| workspace.save_active_item(&(), cx)); + workspace.update(&mut cx, |workspace, cx| { + workspace.save_active_item(&Save, cx) + }); cx.simulate_new_path_selection(|parent_dir| { assert_eq!(parent_dir, dir.path()); Some(parent_dir.join("the-new-name")) @@ -1361,10 +1373,12 @@ mod tests { // Edit the file and save it again. This time, there is no filename prompt. editor.update(&mut cx, |editor, cx| { - editor.insert(&" there".to_string(), cx); + editor.insert(&Insert(" there".into()), cx); assert_eq!(editor.is_dirty(cx.as_ref()), true); }); - workspace.update(&mut cx, |workspace, cx| workspace.save_active_item(&(), cx)); + workspace.update(&mut cx, |workspace, cx| { + workspace.save_active_item(&Save, cx) + }); assert!(!cx.did_prompt_for_new_path()); editor .condition(&cx, |editor, cx| !editor.is_dirty(cx)) @@ -1374,7 +1388,7 @@ mod tests { // Open the same newly-created file in another pane item. The new editor should reuse // the same buffer. workspace.update(&mut cx, |workspace, cx| { - workspace.open_new_file(&app_state, cx); + workspace.open_new_file(&OpenNew(app_state.clone()), cx); workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx); assert!(workspace .open_entry((tree.id(), Path::new("the-new-name").into()), cx) @@ -1398,7 +1412,7 @@ mod tests { cx.update(init); let app_state = cx.read(build_app_state); - cx.dispatch_global_action("workspace:new_file", app_state); + cx.dispatch_global_action(OpenNew(app_state)); let window_id = *cx.window_ids().first().unwrap(); let workspace = cx.root_view::(window_id).unwrap(); let editor = workspace.update(&mut cx, |workspace, cx| { @@ -1414,7 +1428,9 @@ mod tests { assert!(editor.text(cx).is_empty()); }); - workspace.update(&mut cx, |workspace, cx| workspace.save_active_item(&(), cx)); + workspace.update(&mut cx, |workspace, cx| { + workspace.save_active_item(&Save, cx) + }); let dir = TempDir::new("test-new-empty-workspace").unwrap(); cx.simulate_new_path_selection(|_| { @@ -1467,7 +1483,11 @@ mod tests { ); }); - cx.dispatch_action(window_id, vec![pane_1.id()], "pane:split_right", ()); + cx.dispatch_action( + window_id, + vec![pane_1.id()], + pane::Split(SplitDirection::Right), + ); cx.update(|cx| { let pane_2 = workspace.read(cx).active_pane().clone(); assert_ne!(pane_1, pane_2); @@ -1475,7 +1495,7 @@ mod tests { let pane2_item = pane_2.read(cx).active_item().unwrap(); assert_eq!(pane2_item.entry_id(cx.as_ref()), Some(file1.clone())); - cx.dispatch_action(window_id, vec![pane_2.id()], "pane:close_active_item", ()); + cx.dispatch_action(window_id, vec![pane_2.id()], &CloseActiveItem); let workspace = workspace.read(cx); assert_eq!(workspace.panes.len(), 1); assert_eq!(workspace.active_pane(), &pane_1); diff --git a/zed/src/workspace/pane.rs b/zed/src/workspace/pane.rs index f99e2a3fd6..845f6e2521 100644 --- a/zed/src/workspace/pane.rs +++ b/zed/src/workspace/pane.rs @@ -1,6 +1,7 @@ use super::{ItemViewHandle, SplitDirection}; use crate::{settings::Settings, theme}; use gpui::{ + action, color::Color, elements::*, geometry::{rect::RectF, vector::vec2f}, @@ -11,46 +12,41 @@ use gpui::{ use postage::watch; use std::{cmp, path::Path, sync::Arc}; +action!(Split, SplitDirection); +action!(ActivateItem, usize); +action!(ActivatePrevItem); +action!(ActivateNextItem); +action!(CloseActiveItem); +action!(CloseItem, usize); + pub fn init(cx: &mut MutableAppContext) { - cx.add_action( - "pane:activate_item", - |pane: &mut Pane, index: &usize, cx| { - pane.activate_item(*index, cx); - }, - ); - cx.add_action("pane:activate_prev_item", |pane: &mut Pane, _: &(), cx| { + cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| { + pane.activate_item(action.0, cx); + }); + cx.add_action(|pane: &mut Pane, _: &ActivatePrevItem, cx| { pane.activate_prev_item(cx); }); - cx.add_action("pane:activate_next_item", |pane: &mut Pane, _: &(), cx| { + cx.add_action(|pane: &mut Pane, _: &ActivateNextItem, cx| { pane.activate_next_item(cx); }); - cx.add_action("pane:close_active_item", |pane: &mut Pane, _: &(), cx| { + cx.add_action(|pane: &mut Pane, _: &CloseActiveItem, cx| { pane.close_active_item(cx); }); - cx.add_action("pane:close_item", |pane: &mut Pane, item_id: &usize, cx| { - pane.close_item(*item_id, cx); + cx.add_action(|pane: &mut Pane, action: &CloseItem, cx| { + pane.close_item(action.0, cx); }); - cx.add_action("pane:split_up", |pane: &mut Pane, _: &(), cx| { - pane.split(SplitDirection::Up, cx); - }); - cx.add_action("pane:split_down", |pane: &mut Pane, _: &(), cx| { - pane.split(SplitDirection::Down, cx); - }); - cx.add_action("pane:split_left", |pane: &mut Pane, _: &(), cx| { - pane.split(SplitDirection::Left, cx); - }); - cx.add_action("pane:split_right", |pane: &mut Pane, _: &(), cx| { - pane.split(SplitDirection::Right, cx); + cx.add_action(|pane: &mut Pane, action: &Split, cx| { + pane.split(action.0, cx); }); cx.add_bindings(vec![ - Binding::new("shift-cmd-{", "pane:activate_prev_item", Some("Pane")), - Binding::new("shift-cmd-}", "pane:activate_next_item", Some("Pane")), - Binding::new("cmd-w", "pane:close_active_item", Some("Pane")), - Binding::new("cmd-k up", "pane:split_up", Some("Pane")), - Binding::new("cmd-k down", "pane:split_down", Some("Pane")), - Binding::new("cmd-k left", "pane:split_left", Some("Pane")), - Binding::new("cmd-k right", "pane:split_right", Some("Pane")), + Binding::new("shift-cmd-{", ActivatePrevItem, Some("Pane")), + Binding::new("shift-cmd-}", ActivateNextItem, Some("Pane")), + Binding::new("cmd-w", CloseActiveItem, Some("Pane")), + Binding::new("cmd-k up", Split(SplitDirection::Up), Some("Pane")), + Binding::new("cmd-k down", Split(SplitDirection::Down), Some("Pane")), + Binding::new("cmd-k left", Split(SplitDirection::Left), Some("Pane")), + Binding::new("cmd-k right", Split(SplitDirection::Right), Some("Pane")), ]); } @@ -253,7 +249,7 @@ impl Pane { ConstrainedBox::new( EventHandler::new(container.boxed()) .on_mouse_down(move |cx| { - cx.dispatch_action("pane:activate_item", ix); + cx.dispatch_action(ActivateItem(ix)); true }) .boxed(), @@ -338,7 +334,7 @@ impl Pane { icon.boxed() } }) - .on_click(move |cx| cx.dispatch_action("pane:close_item", item_id)) + .on_click(move |cx| cx.dispatch_action(CloseItem(item_id))) .named("close-tab-icon") } else { let diameter = 8.; diff --git a/zed/src/workspace/pane_group.rs b/zed/src/workspace/pane_group.rs index d5d0040009..d6bf992776 100644 --- a/zed/src/workspace/pane_group.rs +++ b/zed/src/workspace/pane_group.rs @@ -184,7 +184,7 @@ impl PaneAxis { } } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub enum SplitDirection { Up, Down, diff --git a/zed/src/workspace/sidebar.rs b/zed/src/workspace/sidebar.rs index 7d7e1f7c6e..1cd63bb2fb 100644 --- a/zed/src/workspace/sidebar.rs +++ b/zed/src/workspace/sidebar.rs @@ -1,5 +1,6 @@ use crate::Settings; use gpui::{ + action, elements::{ Align, ConstrainedBox, Container, Flex, MouseEventHandler, ParentElement as _, Svg, }, @@ -23,6 +24,14 @@ struct Item { view: AnyViewHandle, } +action!(ToggleSidebarItem, ToggleArg); + +#[derive(Clone)] +pub struct ToggleArg { + side: Side, + item_index: usize, +} + impl Sidebar { pub fn new(side: Side) -> Self { Self { @@ -59,8 +68,8 @@ impl Sidebar { Container::new( Flex::column() - .with_children(self.items.iter().enumerate().map(|(item_ix, item)| { - let theme = if Some(item_ix) == self.active_item_ix { + .with_children(self.items.iter().enumerate().map(|(item_index, item)| { + let theme = if Some(item_index) == self.active_item_ix { &settings.theme.active_sidebar_icon } else { &settings.theme.sidebar_icon @@ -81,7 +90,7 @@ impl Sidebar { .boxed() }) .on_click(move |cx| { - cx.dispatch_action("workspace:toggle_sidebar_item", (side, item_ix)) + cx.dispatch_action(ToggleSidebarItem(ToggleArg { side, item_index })) }) .boxed() }))