Detailed changes
@@ -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]]
@@ -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"
@@ -387,7 +387,8 @@
}
},
{
- "context": "Workspace",
+ // todo!() fix context
+ // "context": "Workspace",
"bindings": {
"cmd-1": ["workspace::ActivatePane", 0],
"cmd-2": ["workspace::ActivatePane", 1],
@@ -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
@@ -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<Workspace>| {
+ 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<Picker<CommandPaletteDelegate>>,
+}
+
+impl CommandPalette {
+ fn new(delegate: CommandPaletteDelegate, cx: &mut ViewContext<Self>) -> Self {
+ let picker = cx.build_view(|cx| Picker::new(delegate, cx));
+ Self { picker }
+ }
+}
+impl EventEmitter<ModalEvent> for CommandPalette {}
+
+impl Render for CommandPalette {
+ type Element = Div<Self>;
+
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+ dbg!("Rendering");
+ modal(cx).w_96().child(self.picker.clone())
+ }
+}
+
+pub type CommandPaletteInterceptor =
+ Box<dyn Fn(&str, &AppContext) -> Option<CommandInterceptResult>>;
+
+pub struct CommandInterceptResult {
+ pub action: Box<dyn Action>,
+ pub string: String,
+ pub positions: Vec<usize>,
+}
+
+pub struct CommandPaletteDelegate {
+ command_palette: WeakView<CommandPalette>,
+ actions: Vec<Command>,
+ matches: Vec<StringMatch>,
+ selected_ix: usize,
+ focus_handle: FocusHandle,
+}
+
+pub enum Event {
+ Dismissed,
+ Confirmed {
+ window: AnyWindowHandle,
+ focused_view_id: usize,
+ action: Box<dyn Action>,
+ },
+}
+
+struct Command {
+ name: String,
+ action: Box<dyn Action>,
+ keystrokes: Vec<Keystroke>,
+}
+
+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<String, usize>);
+
+impl CommandPaletteDelegate {
+ pub fn new(command_palette: WeakView<CommandPalette>, 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<Picker<Self>>;
+
+ 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<Picker<Self>>) {
+ self.selected_ix = ix;
+ }
+
+ fn update_matches(
+ &mut self,
+ query: String,
+ cx: &mut ViewContext<Picker<Self>>,
+ ) -> 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::<CommandPaletteFilter>() {
+ // let filter = cx.global::<CommandPaletteFilter>();
+ // 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::<Vec<_>>();
+
+ cx.read_global::<HitCounts, _>(|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::<Vec<_>>();
+ 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::<CommandPaletteInterceptor>() {
+ // cx.global::<CommandPaletteInterceptor>()(&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<Picker<Self>>) {
+ dbg!("dismissed");
+ self.command_palette
+ .update(cx, |command_palette, cx| cx.emit(ModalEvent::Dismissed))
+ .log_err();
+ }
+
+ fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
+ // 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<Picker<Self>>,
+ ) -> Self::ListItem {
+ div().child("ooh yeah")
+ }
+
+ // fn render_match(
+ // &self,
+ // ix: usize,
+ // mouse_state: &mut MouseState,
+ // selected: bool,
+ // cx: &gpui::AppContext,
+ // ) -> AnyElement<Picker<Self>> {
+ // 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<Deterministic>, 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::<CommandPalette>().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::<CommandPaletteFilter, _, _>(|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::<CommandPalette>().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<AppState> {
+ 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
+ })
+ }
+}
@@ -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<serde_json::Value>) -> anyhow::Result<Box<dyn Action>>;
lazy_static! {
@@ -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<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> 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::<Workspace>::new("Toggle User Settings").on_click(
- // Arc::new(|workspace, cx| workspace.debug_toggle_user_settings(cx)),
- // ))
- // .child(
- // Button::<Workspace>::new("Toggle Toasts").on_click(Arc::new(
- // |workspace, cx| workspace.debug_toggle_toast(cx),
- // )),
- // )
- // .child(
- // Button::<Workspace>::new("Toggle Livestream").on_click(Arc::new(
- // |workspace, cx| workspace.debug_toggle_livestream(cx),
- // )),
- // )
- // })
- // .child(
- // Button::<Workspace>::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::<Workspace>::new("Toggle User Settings").on_click(
+ // Arc::new(|workspace, cx| workspace.debug_toggle_user_settings(cx)),
+ // ))
+ // .child(
+ // Button::<Workspace>::new("Toggle Toasts").on_click(Arc::new(
+ // |workspace, cx| workspace.debug_toggle_toast(cx),
+ // )),
+ // )
+ // .child(
+ // Button::<Workspace>::new("Toggle Livestream").on_click(Arc::new(
+ // |workspace, cx| workspace.debug_toggle_livestream(cx),
+ // )),
+ // )
+ // })
+ // .child(
+ // Button::<Workspace>::new("Toggle Debug")
+ // .on_click(Arc::new(|workspace, cx| workspace.toggle_debug(cx))),
+ // ),
+ )
+ })
}
}
-
// todo!()
// impl Entity for Workspace {
// type Event = Event;
@@ -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"
@@ -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<dyn Fs>, _: Arc<LanguageRegistry>) -> Option<()> {
// None
-// }
+//
// #[cfg(not(debug_assertions))]
// fn watch_file_types(_fs: Arc<dyn Fs>, _cx: &mut AppContext) {}
@@ -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
@@ -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,
+}