From d184e0d4269fd8932ccff0ae2a77f315d8986ab0 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 9 Nov 2023 17:54:05 +0100 Subject: [PATCH] Start working on command_palette2 --- Cargo.lock | 34 ++ Cargo.toml | 4 +- assets/keymaps/default.json | 3 +- crates/command_palette2/Cargo.toml | 34 ++ .../command_palette2/src/command_palette.rs | 542 ++++++++++++++++++ crates/gpui2/src/action.rs | 7 +- crates/workspace2/src/workspace2.rs | 301 +++++----- crates/zed2/Cargo.toml | 4 +- crates/zed2/src/main.rs | 4 +- crates/zed_actions2/Cargo.toml | 11 + crates/zed_actions2/src/lib.rs | 34 ++ 11 files changed, 822 insertions(+), 156 deletions(-) create mode 100644 crates/command_palette2/Cargo.toml create mode 100644 crates/command_palette2/src/command_palette.rs create mode 100644 crates/zed_actions2/Cargo.toml create mode 100644 crates/zed_actions2/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index ded64052c8..4143cf8fa7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1880,6 +1880,30 @@ dependencies = [ "zed-actions", ] +[[package]] +name = "command_palette2" +version = "0.1.0" +dependencies = [ + "anyhow", + "collections", + "ctor", + "editor2", + "env_logger 0.9.3", + "fuzzy2", + "gpui2", + "language2", + "picker2", + "project2", + "serde", + "serde_json", + "settings2", + "theme2", + "ui2", + "util", + "workspace2", + "zed_actions2", +] + [[package]] name = "component_test" version = "0.1.0" @@ -11362,6 +11386,7 @@ dependencies = [ "cli", "client2", "collections", + "command_palette2", "copilot2", "ctor", "db2", @@ -11448,6 +11473,15 @@ dependencies = [ "util", "uuid 1.4.1", "workspace2", + "zed_actions2", +] + +[[package]] +name = "zed_actions2" +version = "0.1.0" +dependencies = [ + "gpui2", + "serde", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 1b8081d066..905750f835 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ members = [ "crates/collab_ui", "crates/collections", "crates/command_palette", + "crates/command_palette2", "crates/component_test", "crates/context_menu", "crates/copilot", @@ -110,7 +111,8 @@ members = [ "crates/xtask", "crates/zed", "crates/zed2", - "crates/zed-actions" + "crates/zed-actions", + "crates/zed_actions2" ] default-members = ["crates/zed"] resolver = "2" diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index ef6a655bdc..b18cb4a7ae 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -387,7 +387,8 @@ } }, { - "context": "Workspace", + // todo!() fix context + // "context": "Workspace", "bindings": { "cmd-1": ["workspace::ActivatePane", 0], "cmd-2": ["workspace::ActivatePane", 1], diff --git a/crates/command_palette2/Cargo.toml b/crates/command_palette2/Cargo.toml new file mode 100644 index 0000000000..bcc0099c20 --- /dev/null +++ b/crates/command_palette2/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "command_palette2" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +path = "src/command_palette.rs" +doctest = false + +[dependencies] +collections = { path = "../collections" } +editor = { package = "editor2", path = "../editor2" } +fuzzy = { package = "fuzzy2", path = "../fuzzy2" } +gpui = { package = "gpui2", path = "../gpui2" } +picker = { package = "picker2", path = "../picker2" } +project = { package = "project2", path = "../project2" } +settings = { package = "settings2", path = "../settings2" } +ui = { package = "ui2", path = "../ui2" } +util = { path = "../util" } +theme = { package = "theme2", path = "../theme2" } +workspace = { package="workspace2", path = "../workspace2" } +zed_actions = { package = "zed_actions2", path = "../zed_actions2" } +anyhow.workspace = true +serde.workspace = true +[dev-dependencies] +gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] } +editor = { package = "editor2", path = "../editor2", features = ["test-support"] } +language = { package="language2", path = "../language2", features = ["test-support"] } +project = { package="project2", path = "../project2", features = ["test-support"] } +serde_json.workspace = true +workspace = { package="workspace2", path = "../workspace2", features = ["test-support"] } +ctor.workspace = true +env_logger.workspace = true diff --git a/crates/command_palette2/src/command_palette.rs b/crates/command_palette2/src/command_palette.rs new file mode 100644 index 0000000000..dd0490082e --- /dev/null +++ b/crates/command_palette2/src/command_palette.rs @@ -0,0 +1,542 @@ +use anyhow::anyhow; +use collections::{CommandPaletteFilter, HashMap}; +use fuzzy::{StringMatch, StringMatchCandidate}; +use gpui::{ + actions, div, Action, AnyElement, AnyWindowHandle, AppContext, BorrowWindow, Div, Element, + EventEmitter, FocusHandle, Keystroke, ParentElement, Render, View, ViewContext, VisualContext, + WeakView, +}; +use picker::{Picker, PickerDelegate}; +use std::cmp::{self, Reverse}; +use ui::modal; +use util::{ + channel::{parse_zed_link, ReleaseChannel, RELEASE_CHANNEL}, + ResultExt, +}; +use workspace::{ModalEvent, Workspace}; +use zed_actions::OpenZedURL; + +actions!(Toggle); + +pub fn init(cx: &mut AppContext) { + dbg!("init"); + cx.set_global(HitCounts::default()); + + cx.observe_new_views( + |workspace: &mut Workspace, _: &mut ViewContext| { + dbg!("new workspace found"); + workspace + .modal_layer() + .register_modal(Toggle, |workspace, cx| { + dbg!("hitting cmd-shift-p"); + let Some(focus_handle) = cx.focused() else { + return None; + }; + + Some(cx.build_view(|cx| { + let delegate = + CommandPaletteDelegate::new(cx.view().downgrade(), focus_handle); + CommandPalette::new(delegate, cx) + })) + }); + }, + ) + .detach(); +} + +pub struct CommandPalette { + picker: View>, +} + +impl CommandPalette { + fn new(delegate: CommandPaletteDelegate, cx: &mut ViewContext) -> Self { + let picker = cx.build_view(|cx| Picker::new(delegate, cx)); + Self { picker } + } +} +impl EventEmitter for CommandPalette {} + +impl Render for CommandPalette { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + dbg!("Rendering"); + modal(cx).w_96().child(self.picker.clone()) + } +} + +pub type CommandPaletteInterceptor = + Box Option>; + +pub struct CommandInterceptResult { + pub action: Box, + pub string: String, + pub positions: Vec, +} + +pub struct CommandPaletteDelegate { + command_palette: WeakView, + actions: Vec, + matches: Vec, + selected_ix: usize, + focus_handle: FocusHandle, +} + +pub enum Event { + Dismissed, + Confirmed { + window: AnyWindowHandle, + focused_view_id: usize, + action: Box, + }, +} + +struct Command { + name: String, + action: Box, + keystrokes: Vec, +} + +impl Clone for Command { + fn clone(&self) -> Self { + Self { + name: self.name.clone(), + action: self.action.boxed_clone(), + keystrokes: self.keystrokes.clone(), + } + } +} +/// Hit count for each command in the palette. +/// We only account for commands triggered directly via command palette and not by e.g. keystrokes because +/// if an user already knows a keystroke for a command, they are unlikely to use a command palette to look for it. +#[derive(Default)] +struct HitCounts(HashMap); + +impl CommandPaletteDelegate { + pub fn new(command_palette: WeakView, focus_handle: FocusHandle) -> Self { + Self { + command_palette, + actions: Default::default(), + matches: vec![StringMatch { + candidate_id: 0, + score: 0., + positions: vec![], + string: "Foo my bar".into(), + }], + selected_ix: 0, + focus_handle, + } + } +} + +impl PickerDelegate for CommandPaletteDelegate { + type ListItem = Div>; + + fn match_count(&self) -> usize { + self.matches.len() + } + + fn selected_index(&self) -> usize { + self.selected_ix + } + + fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext>) { + self.selected_ix = ix; + } + + fn update_matches( + &mut self, + query: String, + cx: &mut ViewContext>, + ) -> gpui::Task<()> { + let view_id = &self.focus_handle; + let window = cx.window(); + cx.spawn(move |picker, mut cx| async move { + let mut actions = picker + .update(&mut cx, |this, _| this.delegate.actions.clone()) + .expect("todo: handle picker no longer being around"); + // _ = window + // .available_actions(view_id, &cx) + // .into_iter() + // .flatten() + // .filter_map(|(name, action, bindings)| { + // let filtered = cx.read(|cx| { + // if cx.has_global::() { + // let filter = cx.global::(); + // filter.filtered_namespaces.contains(action.namespace()) + // } else { + // false + // } + // }); + + // if filtered { + // None + // } else { + // Some(Command { + // name: humanize_action_name(name), + // action, + // keystrokes: bindings + // .iter() + // .map(|binding| binding.keystrokes()) + // .last() + // .map_or(Vec::new(), |keystrokes| keystrokes.to_vec()), + // }) + // } + // }) + // .collect::>(); + + cx.read_global::(|hit_counts, _| { + actions.sort_by_key(|action| { + ( + Reverse(hit_counts.0.get(&action.name).cloned()), + action.name.clone(), + ) + }); + }) + .ok(); + + let candidates = actions + .iter() + .enumerate() + .map(|(ix, command)| StringMatchCandidate { + id: ix, + string: command.name.to_string(), + char_bag: command.name.chars().collect(), + }) + .collect::>(); + let mut matches = if query.is_empty() { + candidates + .into_iter() + .enumerate() + .map(|(index, candidate)| StringMatch { + candidate_id: index, + string: candidate.string, + positions: Vec::new(), + score: 0.0, + }) + .collect() + } else { + fuzzy::match_strings( + &candidates, + &query, + true, + 10000, + &Default::default(), + cx.background_executor().clone(), + ) + .await + }; + let mut intercept_result = None; + // todo!() for vim mode + // cx.read(|cx| { + // if cx.has_global::() { + // cx.global::()(&query, cx) + // } else { + // None + // } + // }); + if *RELEASE_CHANNEL == ReleaseChannel::Dev { + if parse_zed_link(&query).is_some() { + intercept_result = Some(CommandInterceptResult { + action: OpenZedURL { url: query.clone() }.boxed_clone(), + string: query.clone(), + positions: vec![], + }) + } + } + if let Some(CommandInterceptResult { + action, + string, + positions, + }) = intercept_result + { + if let Some(idx) = matches + .iter() + .position(|m| actions[m.candidate_id].action.type_id() == action.type_id()) + { + matches.remove(idx); + } + actions.push(Command { + name: string.clone(), + action, + keystrokes: vec![], + }); + matches.insert( + 0, + StringMatch { + candidate_id: actions.len() - 1, + string, + positions, + score: 0.0, + }, + ) + } + picker + .update(&mut cx, |picker, _| { + let delegate = &mut picker.delegate; + delegate.actions = actions; + delegate.matches = matches; + if delegate.matches.is_empty() { + delegate.selected_ix = 0; + } else { + delegate.selected_ix = + cmp::min(delegate.selected_ix, delegate.matches.len() - 1); + } + }) + .log_err(); + }) + } + + fn dismissed(&mut self, cx: &mut ViewContext>) { + dbg!("dismissed"); + self.command_palette + .update(cx, |command_palette, cx| cx.emit(ModalEvent::Dismissed)) + .log_err(); + } + + fn confirm(&mut self, _: bool, cx: &mut ViewContext>) { + // if !self.matches.is_empty() { + // let window = cx.window(); + // let focused_view_id = self.focused_view_id; + // let action_ix = self.matches[self.selected_ix].candidate_id; + // let command = self.actions.remove(action_ix); + // cx.update_default_global(|hit_counts: &mut HitCounts, _| { + // *hit_counts.0.entry(command.name).or_default() += 1; + // }); + // let action = command.action; + + // cx.app_context() + // .spawn(move |mut cx| async move { + // window + // .dispatch_action(focused_view_id, action.as_ref(), &mut cx) + // .ok_or_else(|| anyhow!("window was closed")) + // }) + // .detach_and_log_err(cx); + // } + self.dismissed(cx) + } + + fn render_match( + &self, + ix: usize, + selected: bool, + cx: &mut ViewContext>, + ) -> Self::ListItem { + div().child("ooh yeah") + } + + // fn render_match( + // &self, + // ix: usize, + // mouse_state: &mut MouseState, + // selected: bool, + // cx: &gpui::AppContext, + // ) -> AnyElement> { + // let mat = &self.matches[ix]; + // let command = &self.actions[mat.candidate_id]; + // let theme = theme::current(cx); + // let style = theme.picker.item.in_state(selected).style_for(mouse_state); + // let key_style = &theme.command_palette.key.in_state(selected); + // let keystroke_spacing = theme.command_palette.keystroke_spacing; + + // Flex::row() + // .with_child( + // Label::new(mat.string.clone(), style.label.clone()) + // .with_highlights(mat.positions.clone()), + // ) + // .with_children(command.keystrokes.iter().map(|keystroke| { + // Flex::row() + // .with_children( + // [ + // (keystroke.ctrl, "^"), + // (keystroke.alt, "⌥"), + // (keystroke.cmd, "⌘"), + // (keystroke.shift, "⇧"), + // ] + // .into_iter() + // .filter_map(|(modifier, label)| { + // if modifier { + // Some( + // Label::new(label, key_style.label.clone()) + // .contained() + // .with_style(key_style.container), + // ) + // } else { + // None + // } + // }), + // ) + // .with_child( + // Label::new(keystroke.key.clone(), key_style.label.clone()) + // .contained() + // .with_style(key_style.container), + // ) + // .contained() + // .with_margin_left(keystroke_spacing) + // .flex_float() + // })) + // .contained() + // .with_style(style.container) + // .into_any() + // } +} + +fn humanize_action_name(name: &str) -> String { + let capacity = name.len() + name.chars().filter(|c| c.is_uppercase()).count(); + let mut result = String::with_capacity(capacity); + for char in name.chars() { + if char == ':' { + if result.ends_with(':') { + result.push(' '); + } else { + result.push(':'); + } + } else if char == '_' { + result.push(' '); + } else if char.is_uppercase() { + if !result.ends_with(' ') { + result.push(' '); + } + result.extend(char.to_lowercase()); + } else { + result.push(char); + } + } + result +} + +impl std::fmt::Debug for Command { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Command") + .field("name", &self.name) + .field("keystrokes", &self.keystrokes) + .finish() + } +} + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use super::*; + use editor::Editor; + use gpui::{executor::Deterministic, TestAppContext}; + use project::Project; + use workspace::{AppState, Workspace}; + + #[test] + fn test_humanize_action_name() { + assert_eq!( + humanize_action_name("editor::GoToDefinition"), + "editor: go to definition" + ); + assert_eq!( + humanize_action_name("editor::Backspace"), + "editor: backspace" + ); + assert_eq!( + humanize_action_name("go_to_line::Deploy"), + "go to line: deploy" + ); + } + + #[gpui::test] + async fn test_command_palette(deterministic: Arc, cx: &mut TestAppContext) { + let app_state = init_test(cx); + + let project = Project::test(app_state.fs.clone(), [], cx).await; + let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); + let workspace = window.root(cx); + let editor = window.add_view(cx, |cx| { + let mut editor = Editor::single_line(None, cx); + editor.set_text("abc", cx); + editor + }); + + workspace.update(cx, |workspace, cx| { + cx.focus(&editor); + workspace.add_item(Box::new(editor.clone()), cx) + }); + + workspace.update(cx, |workspace, cx| { + toggle_command_palette(workspace, &Toggle, cx); + }); + + let palette = workspace.read_with(cx, |workspace, _| { + workspace.modal::().unwrap() + }); + + palette + .update(cx, |palette, cx| { + // Fill up palette's command list by running an empty query; + // we only need it to subsequently assert that the palette is initially + // sorted by command's name. + palette.delegate_mut().update_matches("".to_string(), cx) + }) + .await; + + palette.update(cx, |palette, _| { + let is_sorted = + |actions: &[Command]| actions.windows(2).all(|pair| pair[0].name <= pair[1].name); + assert!(is_sorted(&palette.delegate().actions)); + }); + + palette + .update(cx, |palette, cx| { + palette + .delegate_mut() + .update_matches("bcksp".to_string(), cx) + }) + .await; + + palette.update(cx, |palette, cx| { + assert_eq!(palette.delegate().matches[0].string, "editor: backspace"); + palette.confirm(&Default::default(), cx); + }); + deterministic.run_until_parked(); + editor.read_with(cx, |editor, cx| { + assert_eq!(editor.text(cx), "ab"); + }); + + // Add namespace filter, and redeploy the palette + cx.update(|cx| { + cx.update_default_global::(|filter, _| { + filter.filtered_namespaces.insert("editor"); + }) + }); + + workspace.update(cx, |workspace, cx| { + toggle_command_palette(workspace, &Toggle, cx); + }); + + // Assert editor command not present + let palette = workspace.read_with(cx, |workspace, _| { + workspace.modal::().unwrap() + }); + + palette + .update(cx, |palette, cx| { + palette + .delegate_mut() + .update_matches("bcksp".to_string(), cx) + }) + .await; + + palette.update(cx, |palette, _| { + assert!(palette.delegate().matches.is_empty()) + }); + } + + fn init_test(cx: &mut TestAppContext) -> Arc { + cx.update(|cx| { + let app_state = AppState::test(cx); + theme::init(cx); + language::init(cx); + editor::init(cx); + workspace::init(app_state.clone(), cx); + init(cx); + Project::init_settings(cx); + app_state + }) + } +} diff --git a/crates/gpui2/src/action.rs b/crates/gpui2/src/action.rs index 85149f5d55..5cd5eb4cdb 100644 --- a/crates/gpui2/src/action.rs +++ b/crates/gpui2/src/action.rs @@ -4,7 +4,7 @@ use collections::{HashMap, HashSet}; use lazy_static::lazy_static; use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard}; use serde::Deserialize; -use std::any::{type_name, Any}; +use std::any::{type_name, Any, TypeId}; /// Actions are used to implement keyboard-driven UI. /// When you declare an action, you can bind keys to the action in the keymap and @@ -100,6 +100,11 @@ where } } +impl dyn Action { + pub fn type_id(&self) -> TypeId { + self.as_any().type_id() + } +} type ActionBuilder = fn(json: Option) -> anyhow::Result>; lazy_static! { diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index 24ec810ac5..c91c388f2a 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -37,10 +37,10 @@ use futures::{ }; use gpui::{ actions, div, point, rems, size, AnyModel, AnyView, AnyWeakView, AppContext, AsyncAppContext, - AsyncWindowContext, Bounds, Component, Div, Entity, EntityId, EventEmitter, FocusHandle, - GlobalPixels, Model, ModelContext, ParentElement, Point, Render, Size, StatefulInteractive, - Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowBounds, - WindowContext, WindowHandle, WindowOptions, + AsyncWindowContext, Bounds, Component, DispatchContext, Div, Entity, EntityId, EventEmitter, + FocusHandle, GlobalPixels, Model, ModelContext, ParentElement, Point, Render, Size, + StatefulInteractive, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, + WindowBounds, WindowContext, WindowHandle, WindowOptions, }; use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem}; use itertools::Itertools; @@ -3709,157 +3709,160 @@ impl Render for Workspace { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - div() - .relative() - .size_full() - .flex() - .flex_col() - .font("Zed Sans") - .gap_0() - .justify_start() - .items_start() - .text_color(cx.theme().colors().text) - .bg(cx.theme().colors().background) - .child(self.render_titlebar(cx)) - .child( - // todo! should this be a component a view? - self.modal_layer - .wrapper_element(cx) - .relative() - .flex_1() - .w_full() - .flex() - .overflow_hidden() - .border_t() - .border_b() - .border_color(cx.theme().colors().border) - // .children( - // Some( - // Panel::new("project-panel-outer", cx) - // .side(PanelSide::Left) - // .child(ProjectPanel::new("project-panel-inner")), - // ) - // .filter(|_| self.is_project_panel_open()), - // ) - // .children( - // Some( - // Panel::new("collab-panel-outer", cx) - // .child(CollabPanel::new("collab-panel-inner")) - // .side(PanelSide::Left), - // ) - // .filter(|_| self.is_collab_panel_open()), - // ) - // .child(NotificationToast::new( - // "maxbrunsfeld has requested to add you as a contact.".into(), - // )) - .child( - div().flex().flex_col().flex_1().h_full().child( - div().flex().flex_1().child(self.center.render( - &self.project, - &self.follower_states, - self.active_call(), - &self.active_pane, - self.zoomed.as_ref(), - &self.app_state, - cx, - )), + let mut context = DispatchContext::default(); + context.insert("Workspace"); + cx.with_key_dispatch_context(context, |cx| { + div() + .relative() + .size_full() + .flex() + .flex_col() + .font("Zed Sans") + .gap_0() + .justify_start() + .items_start() + .text_color(cx.theme().colors().text) + .bg(cx.theme().colors().background) + .child(self.render_titlebar(cx)) + .child( + // todo! should this be a component a view? + self.modal_layer + .wrapper_element(cx) + .relative() + .flex_1() + .w_full() + .flex() + .overflow_hidden() + .border_t() + .border_b() + .border_color(cx.theme().colors().border) + // .children( + // Some( + // Panel::new("project-panel-outer", cx) + // .side(PanelSide::Left) + // .child(ProjectPanel::new("project-panel-inner")), + // ) + // .filter(|_| self.is_project_panel_open()), + // ) + // .children( + // Some( + // Panel::new("collab-panel-outer", cx) + // .child(CollabPanel::new("collab-panel-inner")) + // .side(PanelSide::Left), + // ) + // .filter(|_| self.is_collab_panel_open()), + // ) + // .child(NotificationToast::new( + // "maxbrunsfeld has requested to add you as a contact.".into(), + // )) + .child( + div().flex().flex_col().flex_1().h_full().child( + div().flex().flex_1().child(self.center.render( + &self.project, + &self.follower_states, + self.active_call(), + &self.active_pane, + self.zoomed.as_ref(), + &self.app_state, + cx, + )), + ), // .children( + // Some( + // Panel::new("terminal-panel", cx) + // .child(Terminal::new()) + // .allowed_sides(PanelAllowedSides::BottomOnly) + // .side(PanelSide::Bottom), + // ) + // .filter(|_| self.is_terminal_open()), + // ), ), // .children( // Some( - // Panel::new("terminal-panel", cx) - // .child(Terminal::new()) - // .allowed_sides(PanelAllowedSides::BottomOnly) - // .side(PanelSide::Bottom), + // Panel::new("chat-panel-outer", cx) + // .side(PanelSide::Right) + // .child(ChatPanel::new("chat-panel-inner").messages(vec![ + // ChatMessage::new( + // "osiewicz".to_string(), + // "is this thing on?".to_string(), + // DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z") + // .unwrap() + // .naive_local(), + // ), + // ChatMessage::new( + // "maxdeviant".to_string(), + // "Reading you loud and clear!".to_string(), + // DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z") + // .unwrap() + // .naive_local(), + // ), + // ])), // ) - // .filter(|_| self.is_terminal_open()), + // .filter(|_| self.is_chat_panel_open()), + // ) + // .children( + // Some( + // Panel::new("notifications-panel-outer", cx) + // .side(PanelSide::Right) + // .child(NotificationsPanel::new("notifications-panel-inner")), + // ) + // .filter(|_| self.is_notifications_panel_open()), + // ) + // .children( + // Some( + // Panel::new("assistant-panel-outer", cx) + // .child(AssistantPanel::new("assistant-panel-inner")), + // ) + // .filter(|_| self.is_assistant_panel_open()), // ), - ), // .children( - // Some( - // Panel::new("chat-panel-outer", cx) - // .side(PanelSide::Right) - // .child(ChatPanel::new("chat-panel-inner").messages(vec![ - // ChatMessage::new( - // "osiewicz".to_string(), - // "is this thing on?".to_string(), - // DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z") - // .unwrap() - // .naive_local(), - // ), - // ChatMessage::new( - // "maxdeviant".to_string(), - // "Reading you loud and clear!".to_string(), - // DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z") - // .unwrap() - // .naive_local(), - // ), - // ])), - // ) - // .filter(|_| self.is_chat_panel_open()), - // ) - // .children( - // Some( - // Panel::new("notifications-panel-outer", cx) - // .side(PanelSide::Right) - // .child(NotificationsPanel::new("notifications-panel-inner")), - // ) - // .filter(|_| self.is_notifications_panel_open()), - // ) - // .children( - // Some( - // Panel::new("assistant-panel-outer", cx) - // .child(AssistantPanel::new("assistant-panel-inner")), - // ) - // .filter(|_| self.is_assistant_panel_open()), - // ), - ) - .child(self.status_bar.clone()) - // .when(self.debug.show_toast, |this| { - // this.child(Toast::new(ToastOrigin::Bottom).child(Label::new("A toast"))) - // }) - // .children( - // Some( - // div() - // .absolute() - // .top(px(50.)) - // .left(px(640.)) - // .z_index(8) - // .child(LanguageSelector::new("language-selector")), - // ) - // .filter(|_| self.is_language_selector_open()), - // ) - .z_index(8) - // Debug - .child( - div() - .flex() - .flex_col() - .z_index(9) - .absolute() - .top_20() - .left_1_4() - .w_40() - .gap_2(), // .when(self.show_debug, |this| { - // this.child(Button::::new("Toggle User Settings").on_click( - // Arc::new(|workspace, cx| workspace.debug_toggle_user_settings(cx)), - // )) - // .child( - // Button::::new("Toggle Toasts").on_click(Arc::new( - // |workspace, cx| workspace.debug_toggle_toast(cx), - // )), - // ) - // .child( - // Button::::new("Toggle Livestream").on_click(Arc::new( - // |workspace, cx| workspace.debug_toggle_livestream(cx), - // )), - // ) - // }) - // .child( - // Button::::new("Toggle Debug") - // .on_click(Arc::new(|workspace, cx| workspace.toggle_debug(cx))), - // ), - ) + ) + .child(self.status_bar.clone()) + // .when(self.debug.show_toast, |this| { + // this.child(Toast::new(ToastOrigin::Bottom).child(Label::new("A toast"))) + // }) + // .children( + // Some( + // div() + // .absolute() + // .top(px(50.)) + // .left(px(640.)) + // .z_index(8) + // .child(LanguageSelector::new("language-selector")), + // ) + // .filter(|_| self.is_language_selector_open()), + // ) + .z_index(8) + // Debug + .child( + div() + .flex() + .flex_col() + .z_index(9) + .absolute() + .top_20() + .left_1_4() + .w_40() + .gap_2(), // .when(self.show_debug, |this| { + // this.child(Button::::new("Toggle User Settings").on_click( + // Arc::new(|workspace, cx| workspace.debug_toggle_user_settings(cx)), + // )) + // .child( + // Button::::new("Toggle Toasts").on_click(Arc::new( + // |workspace, cx| workspace.debug_toggle_toast(cx), + // )), + // ) + // .child( + // Button::::new("Toggle Livestream").on_click(Arc::new( + // |workspace, cx| workspace.debug_toggle_livestream(cx), + // )), + // ) + // }) + // .child( + // Button::::new("Toggle Debug") + // .on_click(Arc::new(|workspace, cx| workspace.toggle_debug(cx))), + // ), + ) + }) } } - // todo!() // impl Entity for Workspace { // type Event = Event; diff --git a/crates/zed2/Cargo.toml b/crates/zed2/Cargo.toml index 661ab0c293..570912abc5 100644 --- a/crates/zed2/Cargo.toml +++ b/crates/zed2/Cargo.toml @@ -25,7 +25,7 @@ call = { package = "call2", path = "../call2" } cli = { path = "../cli" } # collab_ui = { path = "../collab_ui" } collections = { path = "../collections" } -# command_palette = { path = "../command_palette" } +command_palette = { package="command_palette2", path = "../command_palette2" } # component_test = { path = "../component_test" } # context_menu = { path = "../context_menu" } client = { package = "client2", path = "../client2" } @@ -74,7 +74,7 @@ util = { path = "../util" } # vim = { path = "../vim" } workspace = { package = "workspace2", path = "../workspace2" } # welcome = { path = "../welcome" } -# zed-actions = {path = "../zed-actions"} +zed_actions = {package = "zed_actions2", path = "../zed_actions2"} anyhow.workspace = true async-compression = { version = "0.3", features = ["gzip", "futures-bufread"] } async-tar = "0.4.2" diff --git a/crates/zed2/src/main.rs b/crates/zed2/src/main.rs index cd0f8e5fbf..c9e7ee8c58 100644 --- a/crates/zed2/src/main.rs +++ b/crates/zed2/src/main.rs @@ -142,7 +142,7 @@ fn main() { // context_menu::init(cx); project::Project::init(&client, cx); client::init(&client, cx); - // command_palette::init(cx); + command_palette::init(cx); language::init(cx); editor::init(cx); copilot::init( @@ -761,7 +761,7 @@ fn load_embedded_fonts(cx: &AppContext) { // #[cfg(not(debug_assertions))] // async fn watch_languages(_: Arc, _: Arc) -> Option<()> { // None -// } +// // #[cfg(not(debug_assertions))] // fn watch_file_types(_fs: Arc, _cx: &mut AppContext) {} diff --git a/crates/zed_actions2/Cargo.toml b/crates/zed_actions2/Cargo.toml new file mode 100644 index 0000000000..b3b5b4ce57 --- /dev/null +++ b/crates/zed_actions2/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "zed_actions2" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +gpui = { package = "gpui2", path = "../gpui2" } +serde.workspace = true diff --git a/crates/zed_actions2/src/lib.rs b/crates/zed_actions2/src/lib.rs new file mode 100644 index 0000000000..090352b2cc --- /dev/null +++ b/crates/zed_actions2/src/lib.rs @@ -0,0 +1,34 @@ +use gpui::{action, actions}; + +actions!( + About, + DebugElements, + DecreaseBufferFontSize, + Hide, + HideOthers, + IncreaseBufferFontSize, + Minimize, + OpenDefaultKeymap, + OpenDefaultSettings, + OpenKeymap, + OpenLicenses, + OpenLocalSettings, + OpenLog, + OpenSettings, + OpenTelemetryLog, + Quit, + ResetBufferFontSize, + ResetDatabase, + ShowAll, + ToggleFullScreen, + Zoom, +); + +#[action] +pub struct OpenBrowser { + pub url: String, +} +#[action] +pub struct OpenZedURL { + pub url: String, +}