Merge branch 'main' of github.com:zed-industries/zed

Mikayla Maki created

Change summary

Cargo.lock                              |  3 
assets/keymaps/default.json             |  4 
assets/settings/default.json            |  3 
crates/collab/src/integration_tests.rs  | 36 +++++++++++++-
crates/collab/src/rpc.rs                |  1 
crates/context_menu/src/context_menu.rs |  7 ++
crates/project/src/project.rs           | 35 +++++++++++++
crates/search/src/project_search.rs     |  8 ++
crates/settings/src/settings.rs         | 12 ++++
crates/terminal/Cargo.toml              |  1 
crates/terminal/src/connected_el.rs     | 10 +++
crates/terminal/src/connected_view.rs   | 41 ++++++++++++++-
crates/terminal/src/terminal.rs         |  3 
crates/terminal/src/terminal_view.rs    | 20 +++++--
crates/theme/src/theme.rs               |  2 
crates/workspace/src/pane.rs            | 68 +++++++++++++++++++++++---
crates/workspace/src/workspace.rs       |  2 
crates/zed/Cargo.toml                   |  2 
crates/zed/src/menus.rs                 |  2 
styles/src/styleTree/contextMenu.ts     |  2 
20 files changed, 227 insertions(+), 35 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -5356,6 +5356,7 @@ dependencies = [
  "alacritty_terminal",
  "anyhow",
  "client",
+ "context_menu",
  "dirs 4.0.0",
  "editor",
  "futures",
@@ -6995,7 +6996,7 @@ dependencies = [
 
 [[package]]
 name = "zed"
-version = "0.49.1"
+version = "0.50.0"
 dependencies = [
  "activity_indicator",
  "anyhow",

assets/keymaps/default.json 🔗

@@ -31,7 +31,7 @@
             "cmd-n": "workspace::NewFile",
             "cmd-shift-n": "workspace::NewWindow",
             "cmd-o": "workspace::Open",
-            "ctrl-`": "terminal::Deploy"
+            "ctrl-`": "workspace::NewTerminal"
         }
     },
     {
@@ -300,7 +300,7 @@
                 8
             ],
             "cmd-b": "workspace::ToggleLeftSidebar",
-            "cmd-shift-f": "project_search::Deploy",
+            "cmd-shift-f": "workspace::NewSearch",
             "cmd-k cmd-t": "theme_selector::Toggle",
             "cmd-k cmd-s": "zed::OpenKeymap",
             "cmd-t": "project_symbols::Toggle",

crates/collab/src/integration_tests.rs 🔗

@@ -2057,7 +2057,8 @@ async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
                     "a.rs": "const ONE: usize = b::TWO + b::THREE;",
                 },
                 "dir-2": {
-                    "b.rs": "const TWO: usize = 2;\nconst THREE: usize = 3;",
+                    "b.rs": "const TWO: c::T2 = 2;\nconst THREE: usize = 3;",
+                    "c.rs": "type T2 = usize;",
                 }
             }),
         )
@@ -2093,7 +2094,7 @@ async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
         let target_buffer = definitions_1[0].target.buffer.read(cx);
         assert_eq!(
             target_buffer.text(),
-            "const TWO: usize = 2;\nconst THREE: usize = 3;"
+            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
         );
         assert_eq!(
             definitions_1[0].target.range.to_point(target_buffer),
@@ -2122,7 +2123,7 @@ async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
         let target_buffer = definitions_2[0].target.buffer.read(cx);
         assert_eq!(
             target_buffer.text(),
-            "const TWO: usize = 2;\nconst THREE: usize = 3;"
+            "const TWO: c::T2 = 2;\nconst THREE: usize = 3;"
         );
         assert_eq!(
             definitions_2[0].target.range.to_point(target_buffer),
@@ -2133,6 +2134,35 @@ async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
         definitions_1[0].target.buffer,
         definitions_2[0].target.buffer
     );
+
+    fake_language_server.handle_request::<lsp::request::GotoTypeDefinition, _, _>(
+        |req, _| async move {
+            assert_eq!(
+                req.text_document_position_params.position,
+                lsp::Position::new(0, 7)
+            );
+            Ok(Some(lsp::GotoDefinitionResponse::Scalar(
+                lsp::Location::new(
+                    lsp::Url::from_file_path("/root/dir-2/c.rs").unwrap(),
+                    lsp::Range::new(lsp::Position::new(0, 5), lsp::Position::new(0, 7)),
+                ),
+            )))
+        },
+    );
+
+    let type_definitions = project_b
+        .update(cx_b, |p, cx| p.type_definition(&buffer_b, 7, cx))
+        .await
+        .unwrap();
+    cx_b.read(|cx| {
+        assert_eq!(type_definitions.len(), 1);
+        let target_buffer = type_definitions[0].target.buffer.read(cx);
+        assert_eq!(target_buffer.text(), "type T2 = usize;");
+        assert_eq!(
+            type_definitions[0].target.range.to_point(target_buffer),
+            Point::new(0, 5)..Point::new(0, 7)
+        );
+    });
 }
 
 #[gpui::test(iterations = 10)]

crates/collab/src/rpc.rs 🔗

@@ -165,6 +165,7 @@ impl Server {
             .add_message_handler(Server::update_diagnostic_summary)
             .add_request_handler(Server::forward_project_request::<proto::GetHover>)
             .add_request_handler(Server::forward_project_request::<proto::GetDefinition>)
+            .add_request_handler(Server::forward_project_request::<proto::GetTypeDefinition>)
             .add_request_handler(Server::forward_project_request::<proto::GetReferences>)
             .add_request_handler(Server::forward_project_request::<proto::SearchProject>)
             .add_request_handler(Server::forward_project_request::<proto::GetDocumentHighlights>)

crates/context_menu/src/context_menu.rs 🔗

@@ -22,6 +22,7 @@ pub fn init(cx: &mut MutableAppContext) {
     cx.add_action(ContextMenu::cancel);
 }
 
+//
 pub enum ContextMenuItem {
     Item {
         label: String,
@@ -258,6 +259,7 @@ impl ContextMenu {
                                 let style = style
                                     .item
                                     .style_for(Default::default(), Some(ix) == self.selected_index);
+
                                 Label::new(label.to_string(), style.label.clone())
                                     .contained()
                                     .with_style(style.container)
@@ -319,9 +321,12 @@ impl ContextMenu {
                             MouseEventHandler::new::<MenuItem, _, _>(ix, cx, |state, _| {
                                 let style =
                                     style.item.style_for(state, Some(ix) == self.selected_index);
+
                                 Flex::row()
                                     .with_child(
-                                        Label::new(label.to_string(), style.label.clone()).boxed(),
+                                        Label::new(label.to_string(), style.label.clone())
+                                            .contained()
+                                            .boxed(),
                                     )
                                     .with_child({
                                         KeystrokeLabel::new(

crates/project/src/project.rs 🔗

@@ -389,6 +389,7 @@ impl Project {
         client.add_model_request_handler(Self::handle_get_completions);
         client.add_model_request_handler(Self::handle_lsp_command::<GetHover>);
         client.add_model_request_handler(Self::handle_lsp_command::<GetDefinition>);
+        client.add_model_request_handler(Self::handle_lsp_command::<GetTypeDefinition>);
         client.add_model_request_handler(Self::handle_lsp_command::<GetDocumentHighlights>);
         client.add_model_request_handler(Self::handle_lsp_command::<GetReferences>);
         client.add_model_request_handler(Self::handle_lsp_command::<PrepareRename>);
@@ -2071,6 +2072,24 @@ impl Project {
         None
     }
 
+    fn merge_json_value_into(source: serde_json::Value, target: &mut serde_json::Value) {
+        use serde_json::Value;
+
+        match (source, target) {
+            (Value::Object(source), Value::Object(target)) => {
+                for (key, value) in source {
+                    if let Some(target) = target.get_mut(&key) {
+                        Self::merge_json_value_into(value, target);
+                    } else {
+                        target.insert(key.clone(), value);
+                    }
+                }
+            }
+
+            (source, target) => *target = source,
+        }
+    }
+
     fn start_language_server(
         &mut self,
         worktree_id: WorktreeId,
@@ -2092,6 +2111,20 @@ impl Project {
         };
         let key = (worktree_id, adapter.name.clone());
 
+        let mut initialization_options = adapter.initialization_options.clone();
+
+        let lsp = &cx.global::<Settings>().lsp.get(&adapter.name.0);
+        let override_options = lsp.map(|s| s.initialization_options.clone()).flatten();
+        match (&mut initialization_options, override_options) {
+            (Some(initialization_options), Some(override_options)) => {
+                Self::merge_json_value_into(override_options, initialization_options);
+            }
+
+            (None, override_options) => initialization_options = override_options,
+
+            _ => {}
+        }
+
         self.language_server_ids
             .entry(key.clone())
             .or_insert_with(|| {
@@ -2108,7 +2141,7 @@ impl Project {
                     LanguageServerState::Starting(cx.spawn_weak(|this, mut cx| async move {
                         let language_server = language_server?.await.log_err()?;
                         let language_server = language_server
-                            .initialize(adapter.initialization_options.clone())
+                            .initialize(initialization_options)
                             .await
                             .log_err()?;
                         let this = this.upgrade(&cx)?;

crates/search/src/project_search.rs 🔗

@@ -24,7 +24,7 @@ use workspace::{
     Item, ItemHandle, ItemNavHistory, Pane, ToolbarItemLocation, ToolbarItemView, Workspace,
 };
 
-actions!(project_search, [Deploy, SearchInNew, ToggleFocus]);
+actions!(project_search, [SearchInNew, ToggleFocus]);
 
 #[derive(Default)]
 struct ActiveSearches(HashMap<WeakModelHandle<Project>, WeakViewHandle<ProjectSearchView>>);
@@ -431,7 +431,11 @@ impl ProjectSearchView {
 
     // Re-activate the most recently activated search or the most recent if it has been closed.
     // If no search exists in the workspace, create a new one.
-    fn deploy(workspace: &mut Workspace, _: &Deploy, cx: &mut ViewContext<Workspace>) {
+    fn deploy(
+        workspace: &mut Workspace,
+        _: &workspace::NewSearch,
+        cx: &mut ViewContext<Workspace>,
+    ) {
         // Clean up entries for dropped projects
         cx.update_global(|state: &mut ActiveSearches, cx| {
             state.0.retain(|project, _| project.is_upgradable(cx))

crates/settings/src/settings.rs 🔗

@@ -34,6 +34,7 @@ pub struct Settings {
     pub terminal_overrides: TerminalSettings,
     pub language_defaults: HashMap<Arc<str>, EditorSettings>,
     pub language_overrides: HashMap<Arc<str>, EditorSettings>,
+    pub lsp: HashMap<Arc<str>, LspSettings>,
     pub theme: Arc<Theme>,
 }
 
@@ -131,9 +132,17 @@ pub struct SettingsFileContent {
     #[serde(alias = "language_overrides")]
     pub languages: HashMap<Arc<str>, EditorSettings>,
     #[serde(default)]
+    pub lsp: HashMap<Arc<str>, LspSettings>,
+    #[serde(default)]
     pub theme: Option<String>,
 }
 
+#[derive(Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub struct LspSettings {
+    pub initialization_options: Option<Value>,
+}
+
 impl Settings {
     pub fn defaults(
         assets: impl AssetSource,
@@ -174,6 +183,7 @@ impl Settings {
             terminal_overrides: Default::default(),
             language_defaults: defaults.languages,
             language_overrides: Default::default(),
+            lsp: defaults.lsp.clone(),
             theme: themes.get(&defaults.theme.unwrap()).unwrap(),
         }
     }
@@ -218,6 +228,7 @@ impl Settings {
         self.terminal_defaults.font_size = data.terminal.font_size;
         self.terminal_overrides = data.terminal;
         self.language_overrides = data.languages;
+        self.lsp = data.lsp;
     }
 
     pub fn with_language_defaults(
@@ -288,6 +299,7 @@ impl Settings {
             terminal_overrides: Default::default(),
             language_defaults: Default::default(),
             language_overrides: Default::default(),
+            lsp: Default::default(),
             projects_online_by_default: true,
             theme: gpui::fonts::with_font_cache(cx.font_cache().clone(), || Default::default()),
         }

crates/terminal/Cargo.toml 🔗

@@ -16,6 +16,7 @@ theme = { path = "../theme" }
 settings = { path = "../settings" }
 workspace = { path = "../workspace" }
 project = { path = "../project" }
+context_menu = { path = "../context_menu" }
 smallvec = { version = "1.6", features = ["union"] }
 smol = "1.2.5"
 mio-extras = "2.0.6"

crates/terminal/src/connected_el.rs 🔗

@@ -33,7 +33,9 @@ use std::{
 use std::{fmt::Debug, ops::Sub};
 
 use crate::{
-    connected_view::ConnectedView, mappings::colors::convert_color, Terminal, TerminalSize,
+    connected_view::{ConnectedView, DeployContextMenu},
+    mappings::colors::convert_color,
+    Terminal, TerminalSize,
 };
 
 ///Scrolling is unbearably sluggish by default. Alacritty supports a configurable
@@ -463,6 +465,12 @@ impl TerminalEl {
                         }
                     },
                 )
+                .on_click(
+                    MouseButton::Right,
+                    move |MouseButtonEvent { position, .. }, cx| {
+                        cx.dispatch_action(DeployContextMenu { position });
+                    },
+                )
                 .on_drag(
                     MouseButton::Left,
                     move |_, MouseMovedEvent { position, .. }, cx| {

crates/terminal/src/connected_view.rs 🔗

@@ -1,8 +1,14 @@
 use alacritty_terminal::term::TermMode;
+use context_menu::{ContextMenu, ContextMenuItem};
 use gpui::{
-    actions, keymap::Keystroke, AppContext, Element, ElementBox, ModelHandle, MutableAppContext,
-    View, ViewContext,
+    actions,
+    elements::{ChildView, ParentElement, Stack},
+    geometry::vector::Vector2F,
+    impl_internal_actions,
+    keymap::Keystroke,
+    AppContext, Element, ElementBox, ModelHandle, MutableAppContext, View, ViewContext, ViewHandle,
 };
+use workspace::pane;
 
 use crate::{connected_el::TerminalEl, Event, Terminal};
 
@@ -10,10 +16,16 @@ use crate::{connected_el::TerminalEl, Event, Terminal};
 #[derive(Clone, Debug, PartialEq)]
 pub struct ScrollTerminal(pub i32);
 
+#[derive(Clone, PartialEq)]
+pub struct DeployContextMenu {
+    pub position: Vector2F,
+}
+
 actions!(
     terminal,
     [Up, Down, CtrlC, Escape, Enter, Clear, Copy, Paste,]
 );
+impl_internal_actions!(project_panel, [DeployContextMenu]);
 
 pub fn init(cx: &mut MutableAppContext) {
     //Global binding overrrides
@@ -23,6 +35,7 @@ pub fn init(cx: &mut MutableAppContext) {
     cx.add_action(ConnectedView::escape);
     cx.add_action(ConnectedView::enter);
     //Useful terminal views
+    cx.add_action(ConnectedView::deploy_context_menu);
     cx.add_action(ConnectedView::copy);
     cx.add_action(ConnectedView::paste);
     cx.add_action(ConnectedView::clear);
@@ -36,6 +49,7 @@ pub struct ConnectedView {
     has_bell: bool,
     // Only for styling purposes. Doesn't effect behavior
     modal: bool,
+    context_menu: ViewHandle<ContextMenu>,
 }
 
 impl ConnectedView {
@@ -67,6 +81,7 @@ impl ConnectedView {
             has_new_content: true,
             has_bell: false,
             modal,
+            context_menu: cx.add_view(|cx| ContextMenu::new(cx)),
         }
     }
 
@@ -87,6 +102,18 @@ impl ConnectedView {
         cx.emit(Event::Wakeup);
     }
 
+    pub fn deploy_context_menu(&mut self, action: &DeployContextMenu, cx: &mut ViewContext<Self>) {
+        let menu_entries = vec![
+            ContextMenuItem::item("Clear Buffer", Clear),
+            ContextMenuItem::item("Close Terminal", pane::CloseActiveItem),
+        ];
+
+        self.context_menu
+            .update(cx, |menu, cx| menu.show(action.position, menu_entries, cx));
+
+        cx.notify();
+    }
+
     fn clear(&mut self, _: &Clear, cx: &mut ViewContext<Self>) {
         self.terminal.update(cx, |term, _| term.clear());
         cx.notify();
@@ -152,8 +179,14 @@ impl View for ConnectedView {
 
     fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> ElementBox {
         let terminal_handle = self.terminal.clone().downgrade();
-        TerminalEl::new(cx.handle(), terminal_handle, self.modal)
-            .contained()
+
+        Stack::new()
+            .with_child(
+                TerminalEl::new(cx.handle(), terminal_handle, self.modal)
+                    .contained()
+                    .boxed(),
+            )
+            .with_child(ChildView::new(&self.context_menu).boxed())
             .boxed()
     }
 

crates/terminal/src/terminal.rs 🔗

@@ -27,7 +27,6 @@ use futures::{
 use modal::deploy_modal;
 use settings::{Settings, Shell};
 use std::{collections::HashMap, fmt::Display, path::PathBuf, sync::Arc, time::Duration};
-use terminal_view::TerminalView;
 use thiserror::Error;
 
 use gpui::{
@@ -43,9 +42,9 @@ use crate::mappings::{
 
 ///Initialize and register all of our action handlers
 pub fn init(cx: &mut MutableAppContext) {
-    cx.add_action(TerminalView::deploy);
     cx.add_action(deploy_modal);
 
+    terminal_view::init(cx);
     connected_view::init(cx);
 }
 

crates/terminal/src/terminal_view.rs 🔗

@@ -1,21 +1,26 @@
 use crate::connected_view::ConnectedView;
 use crate::{Event, Terminal, TerminalBuilder, TerminalError};
+
 use dirs::home_dir;
 use gpui::{
-    actions, elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, View, ViewContext,
-    ViewHandle,
+    actions, elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, MutableAppContext, View,
+    ViewContext, ViewHandle,
 };
+use workspace::{Item, Workspace};
 
 use crate::TerminalSize;
 use project::{LocalWorktree, Project, ProjectPath};
 use settings::{Settings, WorkingDirectory};
 use smallvec::SmallVec;
 use std::path::{Path, PathBuf};
-use workspace::{Item, Workspace};
 
 use crate::connected_el::TerminalEl;
 
-actions!(terminal, [Deploy, DeployModal]);
+actions!(terminal, [DeployModal]);
+
+pub fn init(cx: &mut MutableAppContext) {
+    cx.add_action(TerminalView::deploy);
+}
 
 //Make terminal view an enum, that can give you views for the error and non-error states
 //Take away all the result unwrapping in the current TerminalView by making it 'infallible'
@@ -59,7 +64,11 @@ impl Entity for ErrorView {
 
 impl TerminalView {
     ///Create a new Terminal in the current working directory or the user's home directory
-    pub fn deploy(workspace: &mut Workspace, _: &Deploy, cx: &mut ViewContext<Workspace>) {
+    pub fn deploy(
+        workspace: &mut Workspace,
+        _: &workspace::NewTerminal,
+        cx: &mut ViewContext<Workspace>,
+    ) {
         let strategy = cx
             .global::<Settings>()
             .terminal_overrides
@@ -134,7 +143,6 @@ impl View for TerminalView {
             TerminalContent::Connected(connected) => ChildView::new(connected),
             TerminalContent::Error(error) => ChildView::new(error),
         };
-
         if self.modal {
             let settings = cx.global::<Settings>();
             let container_style = settings.theme.terminal.modal_container;

crates/theme/src/theme.rs 🔗

@@ -267,6 +267,8 @@ pub struct ContextMenuItem {
     pub container: ContainerStyle,
     pub label: TextStyle,
     pub keystroke: ContainedText,
+    pub icon_width: f32,
+    pub icon_spacing: f32,
 }
 
 #[derive(Debug, Deserialize, Default)]

crates/workspace/src/pane.rs 🔗

@@ -1,5 +1,5 @@
 use super::{ItemHandle, SplitDirection};
-use crate::{toolbar::Toolbar, Item, WeakItemHandle, Workspace};
+use crate::{toolbar::Toolbar, Item, NewFile, NewSearch, NewTerminal, WeakItemHandle, Workspace};
 use anyhow::Result;
 use collections::{HashMap, HashSet, VecDeque};
 use context_menu::{ContextMenu, ContextMenuItem};
@@ -65,8 +65,13 @@ pub struct DeploySplitMenu {
     position: Vector2F,
 }
 
+#[derive(Clone, PartialEq)]
+pub struct DeployNewMenu {
+    position: Vector2F,
+}
+
 impl_actions!(pane, [GoBack, GoForward, ActivateItem]);
-impl_internal_actions!(pane, [CloseItem, DeploySplitMenu]);
+impl_internal_actions!(pane, [CloseItem, DeploySplitMenu, DeployNewMenu]);
 
 const MAX_NAVIGATION_HISTORY_LEN: usize = 1024;
 
@@ -98,6 +103,7 @@ pub fn init(cx: &mut MutableAppContext) {
     cx.add_action(|pane: &mut Pane, _: &SplitRight, cx| pane.split(SplitDirection::Right, cx));
     cx.add_action(|pane: &mut Pane, _: &SplitDown, cx| pane.split(SplitDirection::Down, cx));
     cx.add_action(Pane::deploy_split_menu);
+    cx.add_action(Pane::deploy_new_menu);
     cx.add_action(|workspace: &mut Workspace, _: &ReopenClosedItem, cx| {
         Pane::reopen_closed_item(workspace, cx).detach();
     });
@@ -141,7 +147,7 @@ pub struct Pane {
     autoscroll: bool,
     nav_history: Rc<RefCell<NavHistory>>,
     toolbar: ViewHandle<Toolbar>,
-    split_menu: ViewHandle<ContextMenu>,
+    context_menu: ViewHandle<ContextMenu>,
 }
 
 pub struct ItemNavHistory {
@@ -182,7 +188,7 @@ pub struct NavigationEntry {
 impl Pane {
     pub fn new(cx: &mut ViewContext<Self>) -> Self {
         let handle = cx.weak_handle();
-        let split_menu = cx.add_view(|cx| ContextMenu::new(cx));
+        let context_menu = cx.add_view(|cx| ContextMenu::new(cx));
         Self {
             items: Vec::new(),
             is_active: true,
@@ -197,7 +203,7 @@ impl Pane {
                 pane: handle.clone(),
             })),
             toolbar: cx.add_view(|_| Toolbar::new(handle)),
-            split_menu,
+            context_menu,
         }
     }
 
@@ -831,7 +837,7 @@ impl Pane {
     }
 
     fn deploy_split_menu(&mut self, action: &DeploySplitMenu, cx: &mut ViewContext<Self>) {
-        self.split_menu.update(cx, |menu, cx| {
+        self.context_menu.update(cx, |menu, cx| {
             menu.show(
                 action.position,
                 vec![
@@ -845,6 +851,20 @@ impl Pane {
         });
     }
 
+    fn deploy_new_menu(&mut self, action: &DeployNewMenu, cx: &mut ViewContext<Self>) {
+        self.context_menu.update(cx, |menu, cx| {
+            menu.show(
+                action.position,
+                vec![
+                    ContextMenuItem::item("New File", NewFile),
+                    ContextMenuItem::item("New Terminal", NewTerminal),
+                    ContextMenuItem::item("New Search", NewSearch),
+                ],
+                cx,
+            );
+        });
+    }
+
     pub fn toolbar(&self) -> &ViewHandle<Toolbar> {
         &self.toolbar
     }
@@ -1083,10 +1103,40 @@ impl View for Pane {
                                 .with_child(self.render_tabs(cx).flex(1., true).named("tabs"));
 
                             if self.is_active {
-                                tab_row.add_child(
+                                tab_row.add_children([
                                     MouseEventHandler::new::<SplitIcon, _, _>(
                                         0,
                                         cx,
+                                        |mouse_state, cx| {
+                                            let theme =
+                                                &cx.global::<Settings>().theme.workspace.tab_bar;
+                                            let style =
+                                                theme.pane_button.style_for(mouse_state, false);
+                                            Svg::new("icons/plus_12.svg")
+                                                .with_color(style.color)
+                                                .constrained()
+                                                .with_width(style.icon_width)
+                                                .aligned()
+                                                .contained()
+                                                .with_style(style.container)
+                                                .constrained()
+                                                .with_width(style.button_width)
+                                                .with_height(style.button_width)
+                                                .aligned()
+                                                .boxed()
+                                        },
+                                    )
+                                    .with_cursor_style(CursorStyle::PointingHand)
+                                    .on_down(
+                                        MouseButton::Left,
+                                        |MouseButtonEvent { position, .. }, cx| {
+                                            cx.dispatch_action(DeployNewMenu { position });
+                                        },
+                                    )
+                                    .boxed(),
+                                    MouseEventHandler::new::<SplitIcon, _, _>(
+                                        1,
+                                        cx,
                                         |mouse_state, cx| {
                                             let theme =
                                                 &cx.global::<Settings>().theme.workspace.tab_bar;
@@ -1114,7 +1164,7 @@ impl View for Pane {
                                         },
                                     )
                                     .boxed(),
-                                )
+                                ])
                             }
 
                             tab_row
@@ -1155,7 +1205,7 @@ impl View for Pane {
                 })
                 .boxed(),
             )
-            .with_child(ChildView::new(&self.split_menu).boxed())
+            .with_child(ChildView::new(&self.context_menu).boxed())
             .named("pane")
     }
 

crates/zed/Cargo.toml 🔗

@@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
 description = "The fast, collaborative code editor."
 edition = "2021"
 name = "zed"
-version = "0.49.1"
+version = "0.50.0"
 
 [lib]
 name = "zed"

crates/zed/src/menus.rs 🔗

@@ -136,7 +136,7 @@ pub fn menus() -> Vec<Menu<'static>> {
                 },
                 MenuItem::Action {
                     name: "Find In Project",
-                    action: Box::new(search::project_search::Deploy),
+                    action: Box::new(workspace::NewSearch),
                 },
                 MenuItem::Separator,
                 MenuItem::Action {

styles/src/styleTree/contextMenu.ts 🔗

@@ -16,6 +16,8 @@ export default function contextMenu(theme: Theme) {
     border: border(theme, "primary"),
     keystrokeMargin: 30,
     item: {
+      iconSpacing: 8,
+      iconWidth: 14,
       padding: { left: 4, right: 4, top: 2, bottom: 2 },
       cornerRadius: 6,
       label: text(theme, "sans", "primary", { size: "sm" }),