Merge branch 'main' into collab-panel2

Conrad Irwin created

Change summary

Cargo.lock                                           |  13 
Cargo.toml                                           |   4 
crates/auto_update2/src/auto_update.rs               |  11 
crates/auto_update2/src/update_notification.rs       | 102 ++-----
crates/collab_ui2/src/collab_panel/contact_finder.rs |  16 
crates/command_palette2/src/command_palette.rs       |  31 -
crates/editor2/src/editor.rs                         |  22 +
crates/file_finder2/src/file_finder.rs               |  28 -
crates/gpui2/src/elements/overlay.rs                 |   1 
crates/language/src/highlight_map.rs                 |   8 
crates/language/src/language.rs                      |   4 
crates/language/src/syntax_map/syntax_map_tests.rs   |   2 
crates/language2/src/highlight_map.rs                |   8 
crates/language2/src/language2.rs                    |   4 
crates/language2/src/syntax_map/syntax_map_tests.rs  |   2 
crates/node_runtime/src/node_runtime.rs              |  14 
crates/picker2/src/picker2.rs                        |   4 
crates/project_panel2/src/project_panel.rs           |  66 +---
crates/semantic_index/src/semantic_index_tests.rs    |   4 
crates/storybook2/src/stories/picker.rs              |  24 -
crates/ui2/src/components/context_menu.rs            |  13 
crates/ui2/src/components/list.rs                    | 171 ++++---------
crates/ui2/src/components/popover.rs                 |  21 -
crates/ui2/src/components/stories/list_item.rs       |   8 
crates/ui2/src/styled_ext.rs                         |   4 
crates/workspace/src/workspace.rs                    |  20 -
crates/zed/Cargo.toml                                |   1 
crates/zed/src/languages.rs                          |  13 
crates/zed/src/languages/elixir/embedding.scm        |   4 
crates/zed/src/languages/nu.rs                       |  81 ++++++
crates/zed/src/languages/uiua.rs                     |  55 ++++
crates/zed/src/languages/uiua/config.toml            |  10 
crates/zed/src/languages/uiua/highlights.scm         |  50 ++++
crates/zed/src/languages/uiua/indents.scm            |   3 
crates/zed2/Cargo.toml                               |   1 
crates/zed2/src/languages.rs                         |  13 
crates/zed2/src/languages/nu.rs                      |  55 ++++
crates/zed2/src/languages/uiua.rs                    |  55 ++++
crates/zed2/src/languages/uiua/config.toml           |  10 
crates/zed2/src/languages/uiua/highlights.scm        |  50 ++++
crates/zed2/src/languages/uiua/indents.scm           |   3 
41 files changed, 647 insertions(+), 362 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -9930,7 +9930,7 @@ dependencies = [
 [[package]]
 name = "tree-sitter"
 version = "0.20.10"
-source = "git+https://github.com/tree-sitter/tree-sitter?rev=35a6052fbcafc5e5fc0f9415b8652be7dcaf7222#35a6052fbcafc5e5fc0f9415b8652be7dcaf7222"
+source = "git+https://github.com/tree-sitter/tree-sitter?rev=3b0159d25559b603af566ade3c83d930bf466db1#3b0159d25559b603af566ade3c83d930bf466db1"
 dependencies = [
  "cc",
  "regex",
@@ -10178,6 +10178,15 @@ dependencies = [
  "tree-sitter",
 ]
 
+[[package]]
+name = "tree-sitter-uiua"
+version = "0.3.3"
+source = "git+https://github.com/shnarazk/tree-sitter-uiua?rev=9260f11be5900beda4ee6d1a24ab8ddfaf5a19b2#9260f11be5900beda4ee6d1a24ab8ddfaf5a19b2"
+dependencies = [
+ "cc",
+ "tree-sitter",
+]
+
 [[package]]
 name = "tree-sitter-vue"
 version = "0.0.1"
@@ -11616,6 +11625,7 @@ dependencies = [
  "tree-sitter-svelte",
  "tree-sitter-toml",
  "tree-sitter-typescript",
+ "tree-sitter-uiua",
  "tree-sitter-vue",
  "tree-sitter-yaml",
  "unindent",
@@ -11739,6 +11749,7 @@ dependencies = [
  "tree-sitter-svelte",
  "tree-sitter-toml",
  "tree-sitter-typescript",
+ "tree-sitter-uiua",
  "tree-sitter-vue",
  "tree-sitter-yaml",
  "unindent",

Cargo.toml 🔗

@@ -195,8 +195,10 @@ tree-sitter-lua = "0.0.14"
 tree-sitter-nix = { git = "https://github.com/nix-community/tree-sitter-nix", rev = "66e3e9ce9180ae08fc57372061006ef83f0abde7" }
 tree-sitter-nu = { git = "https://github.com/nushell/tree-sitter-nu", rev = "786689b0562b9799ce53e824cb45a1a2a04dc673"}
 tree-sitter-vue = {git = "https://github.com/zed-industries/tree-sitter-vue", rev = "9b6cb221ccb8d0b956fcb17e9a1efac2feefeb58"}
+tree-sitter-uiua = {git = "https://github.com/shnarazk/tree-sitter-uiua", rev = "9260f11be5900beda4ee6d1a24ab8ddfaf5a19b2"}
+
 [patch.crates-io]
-tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "35a6052fbcafc5e5fc0f9415b8652be7dcaf7222" }
+tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "3b0159d25559b603af566ade3c83d930bf466db1" }
 async-task = { git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e" }
 
 # TODO - Remove when a version is released with this PR: https://github.com/servo/core-foundation-rs/pull/457

crates/auto_update2/src/auto_update.rs 🔗

@@ -84,8 +84,8 @@ impl Settings for AutoUpdateSetting {
 pub fn init(http_client: Arc<dyn HttpClient>, server_url: String, cx: &mut AppContext) {
     AutoUpdateSetting::register(cx);
 
-    cx.observe_new_views(|wokrspace: &mut Workspace, _cx| {
-        wokrspace
+    cx.observe_new_views(|workspace: &mut Workspace, _cx| {
+        workspace
             .register_action(|_, action: &Check, cx| check(action, cx))
             .register_action(|_, _action: &CheckThatAutoUpdaterWorks, cx| {
                 let prompt = cx.prompt(gpui::PromptLevel::Info, "It does!", &["Ok"]);
@@ -94,6 +94,11 @@ pub fn init(http_client: Arc<dyn HttpClient>, server_url: String, cx: &mut AppCo
                 })
                 .detach();
             });
+
+        // @nate - code to trigger update notification on launch
+        // workspace.show_notification(0, _cx, |cx| {
+        //     cx.build_view(|_| UpdateNotification::new(SemanticVersion::from_str("1.1.1").unwrap()))
+        // });
     })
     .detach();
 
@@ -131,7 +136,7 @@ pub fn check(_: &Check, cx: &mut AppContext) {
     }
 }
 
-fn _view_release_notes(_: &ViewReleaseNotes, cx: &mut AppContext) {
+pub fn view_release_notes(_: &ViewReleaseNotes, cx: &mut AppContext) {
     if let Some(auto_updater) = AutoUpdater::get(cx) {
         let auto_updater = auto_updater.read(cx);
         let server_url = &auto_updater.server_url;

crates/auto_update2/src/update_notification.rs 🔗

@@ -1,10 +1,12 @@
 use gpui::{
-    div, DismissEvent, Div, EventEmitter, ParentElement, Render, SemanticVersion, ViewContext,
+    div, DismissEvent, Div, EventEmitter, InteractiveElement, ParentElement, Render,
+    SemanticVersion, StatefulInteractiveElement, Styled, ViewContext,
 };
-use menu::Cancel;
+use util::channel::ReleaseChannel;
+use workspace::ui::{h_stack, v_stack, Icon, IconElement, Label, StyledExt};
 
 pub struct UpdateNotification {
-    _version: SemanticVersion,
+    version: SemanticVersion,
 }
 
 impl EventEmitter<DismissEvent> for UpdateNotification {}
@@ -12,77 +14,43 @@ impl EventEmitter<DismissEvent> for UpdateNotification {}
 impl Render for UpdateNotification {
     type Element = Div;
 
-    fn render(&mut self, _cx: &mut gpui::ViewContext<Self>) -> Self::Element {
-        div().child("Updated zed!")
-        // let theme = theme::current(cx).clone();
-        // let theme = &theme.update_notification;
-
-        // let app_name = cx.global::<ReleaseChannel>().display_name();
-
-        // MouseEventHandler::new::<ViewReleaseNotes, _>(0, cx, |state, cx| {
-        //     Flex::column()
-        //         .with_child(
-        //             Flex::row()
-        //                 .with_child(
-        //                     Text::new(
-        //                         format!("Updated to {app_name} {}", self.version),
-        //                         theme.message.text.clone(),
-        //                     )
-        //                     .contained()
-        //                     .with_style(theme.message.container)
-        //                     .aligned()
-        //                     .top()
-        //                     .left()
-        //                     .flex(1., true),
-        //                 )
-        //                 .with_child(
-        //                     MouseEventHandler::new::<Cancel, _>(0, cx, |state, _| {
-        //                         let style = theme.dismiss_button.style_for(state);
-        //                         Svg::new("icons/x.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)
-        //                     })
-        //                     .with_padding(Padding::uniform(5.))
-        //                     .on_click(MouseButton::Left, move |_, this, cx| {
-        //                         this.dismiss(&Default::default(), cx)
-        //                     })
-        //                     .aligned()
-        //                     .constrained()
-        //                     .with_height(cx.font_cache().line_height(theme.message.text.font_size))
-        //                     .aligned()
-        //                     .top()
-        //                     .flex_float(),
-        //                 ),
-        //         )
-        //         .with_child({
-        //             let style = theme.action_message.style_for(state);
-        //             Text::new("View the release notes", style.text.clone())
-        //                 .contained()
-        //                 .with_style(style.container)
-        //         })
-        //         .contained()
-        // })
-        // .with_cursor_style(CursorStyle::PointingHand)
-        // .on_click(MouseButton::Left, |_, _, cx| {
-        //     crate::view_release_notes(&Default::default(), cx)
-        // })
-        // .into_any_named("update notification")
+    fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
+        let app_name = cx.global::<ReleaseChannel>().display_name();
+
+        v_stack()
+            .elevation_3(cx)
+            .p_4()
+            .child(
+                h_stack()
+                    .justify_between()
+                    .child(Label::new(format!(
+                        "Updated to {app_name} {}",
+                        self.version
+                    )))
+                    .child(
+                        div()
+                            .id("cancel")
+                            .child(IconElement::new(Icon::Close))
+                            .cursor_pointer()
+                            .on_click(cx.listener(|this, _, cx| this.dismiss(cx))),
+                    ),
+            )
+            .child(
+                div()
+                    .id("notes")
+                    .child(Label::new("View the release notes"))
+                    .cursor_pointer()
+                    .on_click(|_, cx| crate::view_release_notes(&Default::default(), cx)),
+            )
     }
 }
 
 impl UpdateNotification {
     pub fn new(version: SemanticVersion) -> Self {
-        Self { _version: version }
+        Self { version }
     }
 
-    pub fn _dismiss(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
+    pub fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
         cx.emit(DismissEvent::Dismiss);
     }
 }

crates/collab_ui2/src/collab_panel/contact_finder.rs 🔗

@@ -170,7 +170,7 @@ impl PickerDelegate for ContactFinderDelegate {
         ix: usize,
         selected: bool,
         cx: &mut ViewContext<Picker<Self>>,
-    ) -> Self::ListItem {
+    ) -> Option<Self::ListItem> {
         let user = &self.potential_contacts[ix];
         let request_status = self.user_store.read(cx).contact_request_status(user);
 
@@ -182,12 +182,14 @@ impl PickerDelegate for ContactFinderDelegate {
             ContactRequestStatus::RequestAccepted => None,
         };
         dbg!(icon_path);
-        div()
-            .flex_1()
-            .justify_between()
-            .children(user.avatar.clone().map(|avatar| img().data(avatar)))
-            .child(Label::new(user.github_login.clone()))
-            .children(icon_path.map(|icon_path| svg().path(icon_path)))
+        Some(
+            div()
+                .flex_1()
+                .justify_between()
+                .children(user.avatar.clone().map(|avatar| img().data(avatar)))
+                .child(Label::new(user.github_login.clone()))
+                .children(icon_path.map(|icon_path| svg().path(icon_path))),
+        )
         // Flex::row()
         //     .with_children(user.avatar.clone().map(|avatar| {
         //         Image::from_data(avatar)

crates/command_palette2/src/command_palette.rs 🔗

@@ -1,17 +1,15 @@
 use collections::{CommandPaletteFilter, HashMap};
 use fuzzy::{StringMatch, StringMatchCandidate};
 use gpui::{
-    actions, div, prelude::*, Action, AppContext, DismissEvent, Div, EventEmitter, FocusHandle,
-    FocusableView, Keystroke, ParentElement, Render, Styled, View, ViewContext, VisualContext,
-    WeakView,
+    actions, Action, AppContext, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView,
+    Keystroke, ParentElement, Render, Styled, View, ViewContext, VisualContext, WeakView,
 };
 use picker::{Picker, PickerDelegate};
 use std::{
     cmp::{self, Reverse},
     sync::Arc,
 };
-use theme::ActiveTheme;
-use ui::{h_stack, v_stack, HighlightedLabel, KeyBinding, StyledExt};
+use ui::{h_stack, v_stack, HighlightedLabel, KeyBinding, ListItem};
 use util::{
     channel::{parse_zed_link, ReleaseChannel, RELEASE_CHANNEL},
     ResultExt,
@@ -141,7 +139,7 @@ impl CommandPaletteDelegate {
 }
 
 impl PickerDelegate for CommandPaletteDelegate {
-    type ListItem = Div;
+    type ListItem = ListItem;
 
     fn placeholder_text(&self) -> Arc<str> {
         "Execute a command...".into()
@@ -294,24 +292,16 @@ impl PickerDelegate for CommandPaletteDelegate {
         ix: usize,
         selected: bool,
         cx: &mut ViewContext<Picker<Self>>,
-    ) -> Self::ListItem {
-        let colors = cx.theme().colors();
+    ) -> Option<Self::ListItem> {
         let Some(r#match) = self.matches.get(ix) else {
-            return div();
+            return None;
         };
         let Some(command) = self.commands.get(r#match.candidate_id) else {
-            return div();
+            return None;
         };
 
-        div()
-            .px_1()
-            .text_color(colors.text)
-            .text_ui()
-            .bg(colors.ghost_element_background)
-            .rounded_md()
-            .when(selected, |this| this.bg(colors.ghost_element_selected))
-            .hover(|this| this.bg(colors.ghost_element_hover))
-            .child(
+        Some(
+            ListItem::new(ix).selected(selected).child(
                 h_stack()
                     .justify_between()
                     .child(HighlightedLabel::new(
@@ -319,7 +309,8 @@ impl PickerDelegate for CommandPaletteDelegate {
                         r#match.positions.clone(),
                     ))
                     .children(KeyBinding::for_action(&*command.action, cx)),
-            )
+            ),
+        )
     }
 }
 

crates/editor2/src/editor.rs 🔗

@@ -1273,6 +1273,13 @@ impl CompletionsMenu {
             multiline_docs.map(|div| {
                 div.id("multiline_docs")
                     .max_h(max_height)
+                    .flex_1()
+                    .px_1p5()
+                    .py_1()
+                    .min_w(px(260.))
+                    .max_w(px(640.))
+                    .w(px(500.))
+                    .text_ui()
                     .overflow_y_scroll()
                     // Prevent a mouse down on documentation from being propagated to the editor,
                     // because that would move the cursor.
@@ -1327,13 +1334,18 @@ impl CompletionsMenu {
 
                         div()
                             .id(mat.candidate_id)
-                            .min_w(px(300.))
-                            .max_w(px(700.))
+                            .min_w(px(220.))
+                            .max_w(px(540.))
                             .whitespace_nowrap()
                             .overflow_hidden()
-                            .bg(gpui::green())
-                            .hover(|style| style.bg(gpui::blue()))
-                            .when(item_ix == selected_item, |div| div.bg(gpui::red()))
+                            .text_ui()
+                            .px_1()
+                            .rounded(px(4.))
+                            .bg(cx.theme().colors().ghost_element_background)
+                            .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
+                            .when(item_ix == selected_item, |div| {
+                                div.bg(cx.theme().colors().ghost_element_selected)
+                            })
                             .on_mouse_down(
                                 MouseButton::Left,
                                 cx.listener(move |editor, event, cx| {

crates/file_finder2/src/file_finder.rs 🔗

@@ -2,9 +2,8 @@ use collections::HashMap;
 use editor::{scroll::autoscroll::Autoscroll, Bias, Editor};
 use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
 use gpui::{
-    actions, div, AppContext, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView,
-    InteractiveElement, IntoElement, Model, ParentElement, Render, Styled, Task, View, ViewContext,
-    VisualContext, WeakView,
+    actions, AppContext, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView, Model,
+    ParentElement, Render, Styled, Task, View, ViewContext, VisualContext, WeakView,
 };
 use picker::{Picker, PickerDelegate};
 use project::{PathMatchCandidateSet, Project, ProjectPath, WorktreeId};
@@ -16,8 +15,7 @@ use std::{
     },
 };
 use text::Point;
-use theme::ActiveTheme;
-use ui::{v_stack, HighlightedLabel, StyledExt};
+use ui::{v_stack, HighlightedLabel, ListItem};
 use util::{paths::PathLikeWithPosition, post_inc, ResultExt};
 use workspace::Workspace;
 
@@ -530,7 +528,7 @@ impl FileFinderDelegate {
 }
 
 impl PickerDelegate for FileFinderDelegate {
-    type ListItem = Div;
+    type ListItem = ListItem;
 
     fn placeholder_text(&self) -> Arc<str> {
         "Search project files...".into()
@@ -711,30 +709,22 @@ impl PickerDelegate for FileFinderDelegate {
         ix: usize,
         selected: bool,
         cx: &mut ViewContext<Picker<Self>>,
-    ) -> Self::ListItem {
+    ) -> Option<Self::ListItem> {
         let path_match = self
             .matches
             .get(ix)
             .expect("Invalid matches state: no element for index {ix}");
-        let theme = cx.theme();
-        let colors = theme.colors();
 
         let (file_name, file_name_positions, full_path, full_path_positions) =
             self.labels_for_match(path_match, cx, ix);
 
-        div()
-            .px_1()
-            .text_color(colors.text)
-            .text_ui()
-            .bg(colors.ghost_element_background)
-            .rounded_md()
-            .when(selected, |this| this.bg(colors.ghost_element_selected))
-            .hover(|this| this.bg(colors.ghost_element_hover))
-            .child(
+        Some(
+            ListItem::new(ix).selected(selected).child(
                 v_stack()
                     .child(HighlightedLabel::new(file_name, file_name_positions))
                     .child(HighlightedLabel::new(full_path, full_path_positions)),
-            )
+            ),
+        )
     }
 }
 

crates/gpui2/src/elements/overlay.rs 🔗

@@ -105,6 +105,7 @@ impl Element for Overlay {
             origin: Point::zero(),
             size: cx.viewport_size(),
         };
+        dbg!(limits);
 
         match self.fit_mode {
             OverlayFitMode::SnapToWindow => {

crates/language/src/highlight_map.rs 🔗

@@ -11,7 +11,7 @@ pub struct HighlightId(pub u32);
 const DEFAULT_SYNTAX_HIGHLIGHT_ID: HighlightId = HighlightId(u32::MAX);
 
 impl HighlightMap {
-    pub fn new(capture_names: &[String], theme: &SyntaxTheme) -> Self {
+    pub fn new(capture_names: &[&str], theme: &SyntaxTheme) -> Self {
         // For each capture name in the highlight query, find the longest
         // key in the theme's syntax styles that matches all of the
         // dot-separated components of the capture name.
@@ -98,9 +98,9 @@ mod tests {
         );
 
         let capture_names = &[
-            "function.special".to_string(),
-            "function.async.rust".to_string(),
-            "variable.builtin.self".to_string(),
+            "function.special",
+            "function.async.rust",
+            "variable.builtin.self",
         ];
 
         let map = HighlightMap::new(capture_names, &theme);

crates/language/src/language.rs 🔗

@@ -1383,7 +1383,7 @@ impl Language {
         let query = Query::new(self.grammar_mut().ts_language, source)?;
 
         let mut override_configs_by_id = HashMap::default();
-        for (ix, name) in query.capture_names().iter().enumerate() {
+        for (ix, name) in query.capture_names().iter().copied().enumerate() {
             if !name.starts_with('_') {
                 let value = self.config.overrides.remove(name).unwrap_or_default();
                 for server_name in &value.opt_into_language_servers {
@@ -1396,7 +1396,7 @@ impl Language {
                     }
                 }
 
-                override_configs_by_id.insert(ix as u32, (name.clone(), value));
+                override_configs_by_id.insert(ix as u32, (name.into(), value));
             }
         }
 

crates/language/src/syntax_map/syntax_map_tests.rs 🔗

@@ -1300,7 +1300,7 @@ fn assert_capture_ranges(
         .collect::<Vec<_>>();
     for capture in captures {
         let name = &queries[capture.grammar_index].capture_names()[capture.index as usize];
-        if highlight_query_capture_names.contains(&name.as_str()) {
+        if highlight_query_capture_names.contains(&name) {
             actual_ranges.push(capture.node.byte_range());
         }
     }

crates/language2/src/highlight_map.rs 🔗

@@ -11,7 +11,7 @@ pub struct HighlightId(pub u32);
 const DEFAULT_SYNTAX_HIGHLIGHT_ID: HighlightId = HighlightId(u32::MAX);
 
 impl HighlightMap {
-    pub fn new(capture_names: &[String], theme: &SyntaxTheme) -> Self {
+    pub fn new(capture_names: &[&str], theme: &SyntaxTheme) -> Self {
         // For each capture name in the highlight query, find the longest
         // key in the theme's syntax styles that matches all of the
         // dot-separated components of the capture name.
@@ -100,9 +100,9 @@ mod tests {
         };
 
         let capture_names = &[
-            "function.special".to_string(),
-            "function.async.rust".to_string(),
-            "variable.builtin.self".to_string(),
+            "function.special",
+            "function.async.rust",
+            "variable.builtin.self",
         ];
 
         let map = HighlightMap::new(capture_names, &theme);

crates/language2/src/language2.rs 🔗

@@ -1391,7 +1391,7 @@ impl Language {
         let mut override_configs_by_id = HashMap::default();
         for (ix, name) in query.capture_names().iter().enumerate() {
             if !name.starts_with('_') {
-                let value = self.config.overrides.remove(name).unwrap_or_default();
+                let value = self.config.overrides.remove(*name).unwrap_or_default();
                 for server_name in &value.opt_into_language_servers {
                     if !self
                         .config
@@ -1402,7 +1402,7 @@ impl Language {
                     }
                 }
 
-                override_configs_by_id.insert(ix as u32, (name.clone(), value));
+                override_configs_by_id.insert(ix as u32, (name.to_string(), value));
             }
         }
 

crates/language2/src/syntax_map/syntax_map_tests.rs 🔗

@@ -1300,7 +1300,7 @@ fn assert_capture_ranges(
         .collect::<Vec<_>>();
     for capture in captures {
         let name = &queries[capture.grammar_index].capture_names()[capture.index as usize];
-        if highlight_query_capture_names.contains(&name.as_str()) {
+        if highlight_query_capture_names.contains(&name) {
             actual_ranges.push(capture.node.byte_range());
         }
     }

crates/node_runtime/src/node_runtime.rs 🔗

@@ -73,6 +73,7 @@ impl RealNodeRuntime {
         let npm_file = node_dir.join("bin/npm");
 
         let result = Command::new(&node_binary)
+            .env_clear()
             .arg(npm_file)
             .arg("--version")
             .stdin(Stdio::null())
@@ -149,6 +150,7 @@ impl NodeRuntime for RealNodeRuntime {
             }
 
             let mut command = Command::new(node_binary);
+            command.env_clear();
             command.env("PATH", env_path);
             command.arg(npm_file).arg(subcommand);
             command.args(["--cache".into(), installation_path.join("cache")]);
@@ -200,11 +202,11 @@ impl NodeRuntime for RealNodeRuntime {
                 &[
                     name,
                     "--json",
-                    "-fetch-retry-mintimeout",
+                    "--fetch-retry-mintimeout",
                     "2000",
-                    "-fetch-retry-maxtimeout",
+                    "--fetch-retry-maxtimeout",
                     "5000",
-                    "-fetch-timeout",
+                    "--fetch-timeout",
                     "5000",
                 ],
             )
@@ -229,11 +231,11 @@ impl NodeRuntime for RealNodeRuntime {
 
         let mut arguments: Vec<_> = packages.iter().map(|p| p.as_str()).collect();
         arguments.extend_from_slice(&[
-            "-fetch-retry-mintimeout",
+            "--fetch-retry-mintimeout",
             "2000",
-            "-fetch-retry-maxtimeout",
+            "--fetch-retry-maxtimeout",
             "5000",
-            "-fetch-timeout",
+            "--fetch-timeout",
             "5000",
         ]);
 

crates/picker2/src/picker2.rs 🔗

@@ -32,7 +32,7 @@ pub trait PickerDelegate: Sized + 'static {
         ix: usize,
         selected: bool,
         cx: &mut ViewContext<Picker<Self>>,
-    ) -> Self::ListItem;
+    ) -> Option<Self::ListItem>;
 }
 
 impl<D: PickerDelegate> FocusableView for Picker<D> {
@@ -230,7 +230,7 @@ impl<D: PickerDelegate> Render for Picker<D> {
                                                             )
                                                         }),
                                                     )
-                                                    .child(picker.delegate.render_match(
+                                                    .children(picker.delegate.render_match(
                                                         ix,
                                                         ix == selected_index,
                                                         cx,

crates/project_panel2/src/project_panel.rs 🔗

@@ -10,9 +10,8 @@ use anyhow::{anyhow, Result};
 use gpui::{
     actions, div, px, uniform_list, Action, AppContext, AssetSource, AsyncWindowContext,
     ClipboardItem, Div, EventEmitter, FocusHandle, Focusable, FocusableView, InteractiveElement,
-    IntoElement, Model, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel,
-    Render, Stateful, StatefulInteractiveElement, Styled, Task, UniformListScrollHandle, View,
-    ViewContext, VisualContext as _, WeakView, WindowContext,
+    Model, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render, Stateful, Styled,
+    Task, UniformListScrollHandle, View, ViewContext, VisualContext as _, WeakView, WindowContext,
 };
 use menu::{Confirm, SelectNext, SelectPrev};
 use project::{
@@ -30,7 +29,7 @@ use std::{
     sync::Arc,
 };
 use theme::ActiveTheme as _;
-use ui::{h_stack, v_stack, IconElement, Label};
+use ui::{v_stack, IconElement, Label, ListItem};
 use unicase::UniCase;
 use util::{maybe, ResultExt, TryFutureExt};
 use workspace::{
@@ -1335,13 +1334,19 @@ impl ProjectPanel {
         }
     }
 
-    fn render_entry_visual_element(
-        details: &EntryDetails,
-        editor: Option<&View<Editor>>,
-        padding: Pixels,
+    fn render_entry(
+        &self,
+        entry_id: ProjectEntryId,
+        details: EntryDetails,
+        // dragged_entry_destination: &mut Option<Arc<Path>>,
         cx: &mut ViewContext<Self>,
-    ) -> Div {
+    ) -> ListItem {
+        let kind = details.kind;
+        let settings = ProjectPanelSettings::get_global(cx);
         let show_editor = details.is_editing && !details.is_processing;
+        let is_selected = self
+            .selection
+            .map_or(false, |selection| selection.entry_id == entry_id);
 
         let theme = cx.theme();
         let filename_text_color = details
@@ -1354,14 +1359,17 @@ impl ProjectPanel {
             })
             .unwrap_or(theme.status().info);
 
-        h_stack()
+        ListItem::new(entry_id.to_proto() as usize)
+            .indent_level(details.depth)
+            .indent_step_size(px(settings.indent_size))
+            .selected(is_selected)
             .child(if let Some(icon) = &details.icon {
                 div().child(IconElement::from_path(icon.to_string()))
             } else {
                 div()
             })
             .child(
-                if let (Some(editor), true) = (editor, show_editor) {
+                if let (Some(editor), true) = (Some(&self.filename_editor), show_editor) {
                     div().w_full().child(editor.clone())
                 } else {
                     div()
@@ -1370,33 +1378,6 @@ impl ProjectPanel {
                 }
                 .ml_1(),
             )
-            .pl(padding)
-    }
-
-    fn render_entry(
-        &self,
-        entry_id: ProjectEntryId,
-        details: EntryDetails,
-        // dragged_entry_destination: &mut Option<Arc<Path>>,
-        cx: &mut ViewContext<Self>,
-    ) -> Stateful<Div> {
-        let kind = details.kind;
-        let settings = ProjectPanelSettings::get_global(cx);
-        const INDENT_SIZE: Pixels = px(16.0);
-        let padding = INDENT_SIZE + details.depth as f32 * px(settings.indent_size);
-        let show_editor = details.is_editing && !details.is_processing;
-        let is_selected = self
-            .selection
-            .map_or(false, |selection| selection.entry_id == entry_id);
-
-        Self::render_entry_visual_element(&details, Some(&self.filename_editor), padding, cx)
-            .id(entry_id.to_proto() as usize)
-            .w_full()
-            .cursor_pointer()
-            .when(is_selected, |this| {
-                this.bg(cx.theme().colors().element_selected)
-            })
-            .hover(|style| style.bg(cx.theme().colors().element_hover))
             .on_click(cx.listener(move |this, event: &gpui::ClickEvent, cx| {
                 if !show_editor {
                     if kind.is_dir() {
@@ -1410,12 +1391,9 @@ impl ProjectPanel {
                     }
                 }
             }))
-            .on_mouse_down(
-                MouseButton::Right,
-                cx.listener(move |this, event: &MouseDownEvent, cx| {
-                    this.deploy_context_menu(event.position, entry_id, cx);
-                }),
-            )
+            .on_secondary_mouse_down(cx.listener(move |this, event: &MouseDownEvent, cx| {
+                this.deploy_context_menu(event.position, entry_id, cx);
+            }))
         // .on_drop::<ProjectEntryId>(|this, event, cx| {
         //     this.move_entry(
         //         *dragged_entry,

crates/semantic_index/src/semantic_index_tests.rs 🔗

@@ -1659,13 +1659,13 @@ fn elixir_lang() -> Arc<Language> {
                 target: (identifier) @name)
                 operator: "when")
                 ])
-                (#match? @name "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp)$")) @item
+                (#any-match? @name "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp)$")) @item
                 )
 
             (call
                 target: (identifier) @name
                 (arguments (alias) @name)
-                (#match? @name "^(defmodule|defprotocol)$")) @item
+                (#any-match? @name "^(defmodule|defprotocol)$")) @item
             "#,
         )
         .unwrap(),

crates/storybook2/src/stories/picker.rs 🔗

@@ -5,6 +5,7 @@ use gpui::{
 use picker::{Picker, PickerDelegate};
 use std::sync::Arc;
 use theme2::ActiveTheme;
+use ui::{Label, ListItem};
 
 pub struct PickerStory {
     picker: View<Picker<Delegate>>,
@@ -36,7 +37,7 @@ impl Delegate {
 }
 
 impl PickerDelegate for Delegate {
-    type ListItem = Div;
+    type ListItem = ListItem;
 
     fn match_count(&self) -> usize {
         self.candidates.len()
@@ -51,25 +52,18 @@ impl PickerDelegate for Delegate {
         ix: usize,
         selected: bool,
         cx: &mut gpui::ViewContext<Picker<Self>>,
-    ) -> Self::ListItem {
-        let colors = cx.theme().colors();
+    ) -> Option<Self::ListItem> {
         let Some(candidate_ix) = self.matches.get(ix) else {
-            return div();
+            return None;
         };
         // TASK: Make StringMatchCandidate::string a SharedString
         let candidate = SharedString::from(self.candidates[*candidate_ix].string.clone());
 
-        div()
-            .text_color(colors.text)
-            .when(selected, |s| {
-                s.border_l_10().border_color(colors.terminal_ansi_yellow)
-            })
-            .hover(|style| {
-                style
-                    .bg(colors.element_active)
-                    .text_color(colors.text_accent)
-            })
-            .child(candidate)
+        Some(
+            ListItem::new(ix)
+                .selected(selected)
+                .child(Label::new(candidate)),
+        )
     }
 
     fn selected_index(&self) -> usize {

crates/ui2/src/components/context_menu.rs 🔗

@@ -4,18 +4,15 @@ use std::rc::Rc;
 use crate::{prelude::*, v_stack, Label, List};
 use crate::{ListItem, ListSeparator, ListSubHeader};
 use gpui::{
-    overlay, px, Action, AnchorCorner, AnyElement, AppContext, Bounds, DismissEvent, DispatchPhase,
-    Div, EventEmitter, FocusHandle, FocusableView, IntoElement, LayoutId, ManagedView, MouseButton,
-    MouseDownEvent, Pixels, Point, Render, View, VisualContext,
+    overlay, px, Action, AnchorCorner, AnyElement, AppContext, Bounds, ClickEvent, DismissEvent,
+    DispatchPhase, Div, EventEmitter, FocusHandle, FocusableView, IntoElement, LayoutId,
+    ManagedView, MouseButton, MouseDownEvent, Pixels, Point, Render, View, VisualContext,
 };
 
 pub enum ContextMenuItem {
     Separator,
     Header(SharedString),
-    Entry(
-        SharedString,
-        Rc<dyn Fn(&MouseDownEvent, &mut WindowContext)>,
-    ),
+    Entry(SharedString, Rc<dyn Fn(&ClickEvent, &mut WindowContext)>),
 }
 
 pub struct ContextMenu {
@@ -61,7 +58,7 @@ impl ContextMenu {
     pub fn entry(
         mut self,
         label: impl Into<SharedString>,
-        on_click: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
+        on_click: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
     ) -> Self {
         self.items
             .push(ContextMenuItem::Entry(label.into(), Rc::new(on_click)));

crates/ui2/src/components/list.rs 🔗

@@ -1,8 +1,10 @@
+use std::rc::Rc;
+
 use gpui::{
-    div, px, AnyElement, Div, ImageSource, IntoElement, MouseButton, MouseDownEvent, Stateful,
+    div, px, AnyElement, ClickEvent, Div, ImageSource, IntoElement, MouseButton, MouseDownEvent,
+    Pixels, Stateful, StatefulInteractiveElement,
 };
 use smallvec::SmallVec;
-use std::rc::Rc;
 
 use crate::{
     disclosure_control, h_stack, v_stack, Avatar, Icon, IconButton, IconElement, IconSize, Label,
@@ -117,66 +119,6 @@ impl ListHeader {
         self.meta = meta;
         self
     }
-
-    // before_ship!("delete")
-    // fn render<V: 'static>(self,  cx: &mut WindowContext) -> impl Element<V> {
-    //     let disclosure_control = disclosure_control(self.toggle);
-
-    //     let meta = match self.meta {
-    //         Some(ListHeaderMeta::Tools(icons)) => div().child(
-    //             h_stack()
-    //                 .gap_2()
-    //                 .items_center()
-    //                 .children(icons.into_iter().map(|i| {
-    //                     IconElement::new(i)
-    //                         .color(TextColor::Muted)
-    //                         .size(IconSize::Small)
-    //                 })),
-    //         ),
-    //         Some(ListHeaderMeta::Button(label)) => div().child(label),
-    //         Some(ListHeaderMeta::Text(label)) => div().child(label),
-    //         None => div(),
-    //     };
-
-    //     h_stack()
-    //         .w_full()
-    //         .bg(cx.theme().colors().surface_background)
-    //         // TODO: Add focus state
-    //         // .when(self.state == InteractionState::Focused, |this| {
-    //         //     this.border()
-    //         //         .border_color(cx.theme().colors().border_focused)
-    //         // })
-    //         .relative()
-    //         .child(
-    //             div()
-    //                 .h_5()
-    //                 .when(self.variant == ListItemVariant::Inset, |this| this.px_2())
-    //                 .flex()
-    //                 .flex_1()
-    //                 .items_center()
-    //                 .justify_between()
-    //                 .w_full()
-    //                 .gap_1()
-    //                 .child(
-    //                     h_stack()
-    //                         .gap_1()
-    //                         .child(
-    //                             div()
-    //                                 .flex()
-    //                                 .gap_1()
-    //                                 .items_center()
-    //                                 .children(self.left_icon.map(|i| {
-    //                                     IconElement::new(i)
-    //                                         .color(TextColor::Muted)
-    //                                         .size(IconSize::Small)
-    //                                 }))
-    //                                 .child(Label::new(self.label.clone()).color(TextColor::Muted)),
-    //                         )
-    //                         .child(disclosure_control),
-    //                 )
-    //                 .child(meta),
-    //         )
-    // }
 }
 
 #[derive(IntoElement, Clone)]
@@ -231,26 +173,21 @@ impl RenderOnce for ListSubHeader {
     }
 }
 
-#[derive(Default, PartialEq, Copy, Clone)]
-pub enum ListEntrySize {
-    #[default]
-    Small,
-    Medium,
-}
-
 #[derive(IntoElement)]
 pub struct ListItem {
     id: ElementId,
     disabled: bool,
+    selected: bool,
     // TODO: Reintroduce this
     // disclosure_control_style: DisclosureControlVisibility,
-    indent_level: u32,
+    indent_level: usize,
+    indent_step_size: Pixels,
     left_slot: Option<GraphicSlot>,
     overflow: OverflowStyle,
-    size: ListEntrySize,
     toggle: Toggle,
     variant: ListItemVariant,
-    on_click: Option<Rc<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
+    on_click: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
+    on_secondary_mouse_down: Option<Rc<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
     children: SmallVec<[AnyElement; 2]>,
 }
 
@@ -259,22 +196,29 @@ impl ListItem {
         Self {
             id: id.into(),
             disabled: false,
+            selected: false,
             indent_level: 0,
+            indent_step_size: px(12.),
             left_slot: None,
             overflow: OverflowStyle::Hidden,
-            size: ListEntrySize::default(),
             toggle: Toggle::NotToggleable,
             variant: ListItemVariant::default(),
-            on_click: Default::default(),
+            on_click: None,
+            on_secondary_mouse_down: None,
             children: SmallVec::new(),
         }
     }
 
-    pub fn on_click(
+    pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
+        self.on_click = Some(Rc::new(handler));
+        self
+    }
+
+    pub fn on_secondary_mouse_down(
         mut self,
         handler: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
     ) -> Self {
-        self.on_click = Some(Rc::new(handler));
+        self.on_secondary_mouse_down = Some(Rc::new(handler));
         self
     }
 
@@ -283,16 +227,26 @@ impl ListItem {
         self
     }
 
-    pub fn indent_level(mut self, indent_level: u32) -> Self {
+    pub fn indent_level(mut self, indent_level: usize) -> Self {
         self.indent_level = indent_level;
         self
     }
 
+    pub fn indent_step_size(mut self, indent_step_size: Pixels) -> Self {
+        self.indent_step_size = indent_step_size;
+        self
+    }
+
     pub fn toggle(mut self, toggle: Toggle) -> Self {
         self.toggle = toggle;
         self
     }
 
+    pub fn selected(mut self, selected: bool) -> Self {
+        self.selected = selected;
+        self
+    }
+
     pub fn left_content(mut self, left_content: GraphicSlot) -> Self {
         self.left_slot = Some(left_content);
         self
@@ -307,11 +261,6 @@ impl ListItem {
         self.left_slot = Some(GraphicSlot::Avatar(left_avatar.into()));
         self
     }
-
-    pub fn size(mut self, size: ListEntrySize) -> Self {
-        self.size = size;
-        self
-    }
 }
 
 impl RenderOnce for ListItem {
@@ -331,42 +280,32 @@ impl RenderOnce for ListItem {
             None => None,
         };
 
-        let sized_item = match self.size {
-            ListEntrySize::Small => div().h_6(),
-            ListEntrySize::Medium => div().h_7(),
-        };
         div()
             .id(self.id)
             .relative()
-            .bg(cx.theme().colors().editor_background.clone())
-            // .hover(|mut style| {
-            //     style.background = Some(cx.theme().colors().editor_background.into());
-            //     style
-            // })
+            .hover(|mut style| {
+                style.background = Some(cx.theme().colors().editor_background.into());
+                style
+            })
             // TODO: Add focus state
             // .when(self.state == InteractionState::Focused, |this| {
             //     this.border()
             //         .border_color(cx.theme().colors().border_focused)
             // })
-            //.hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
-            //.active(|style| style.bg(cx.theme().colors().ghost_element_active))
+            .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
+            .active(|style| style.bg(cx.theme().colors().ghost_element_active))
+            .when(self.selected, |this| {
+                this.bg(cx.theme().colors().ghost_element_selected)
+            })
+            .when_some(self.on_secondary_mouse_down, |this, on_mouse_down| {
+                this.on_mouse_down(MouseButton::Right, move |event, cx| {
+                    (on_mouse_down)(event, cx)
+                })
+            })
             .child(
-                sized_item
+                div()
                     .when(self.variant == ListItemVariant::Inset, |this| this.px_2())
-                    // .ml(rems(0.75 * self.indent_level as f32))
-                    .children((0..self.indent_level).map(|_| {
-                        div()
-                            .w(px(4.))
-                            .h_full()
-                            .flex()
-                            .justify_center()
-                            .group_hover("", |style| style.bg(cx.theme().colors().border_focused))
-                            .child(
-                                h_stack()
-                                    .child(div().w_px().h_full())
-                                    .child(div().w_px().h_full().bg(cx.theme().colors().border)),
-                            )
-                    }))
+                    .ml(self.indent_level as f32 * self.indent_step_size)
                     .flex()
                     .gap_1()
                     .items_center()
@@ -374,13 +313,19 @@ impl RenderOnce for ListItem {
                     .child(disclosure_control(self.toggle))
                     .children(left_content)
                     .children(self.children)
-                    .on_mouse_down(MouseButton::Left, {
-                        let on_click = self.on_click.clone();
-                        move |event, cx| {
-                            if let Some(on_click) = &on_click {
+                    // HACK: We need to attach the `on_click` handler to the child element in order to have the click
+                    // event actually fire.
+                    // Once this is fixed in GPUI we can remove this and rely on the `on_click` handler set above on the
+                    // outer `div`.
+                    .id("on_click_hack")
+                    .when_some(self.on_click, |this, on_click| {
+                        this.on_click(move |event, cx| {
+                            // HACK: GPUI currently fires `on_click` with any mouse button,
+                            // but we only care about the left button.
+                            if event.down.button == MouseButton::Left {
                                 (on_click)(event, cx)
                             }
-                        }
+                        })
                     }),
             )
     }

crates/ui2/src/components/popover.rs 🔗

@@ -1,8 +1,9 @@
 use gpui::{
-    AnyElement, Div, Element, ElementId, IntoElement, ParentElement, RenderOnce, Styled,
+    div, AnyElement, Div, Element, ElementId, IntoElement, ParentElement, RenderOnce, Styled,
     WindowContext,
 };
 use smallvec::SmallVec;
+use theme2::ActiveTheme;
 
 use crate::{v_stack, StyledExt};
 
@@ -43,22 +44,16 @@ impl RenderOnce for Popover {
     type Rendered = Div;
 
     fn render(self, cx: &mut WindowContext) -> Self::Rendered {
-        v_stack()
-            .relative()
-            .elevation_2(cx)
-            .p_1()
-            .children(self.children)
+        div()
+            .flex()
+            .gap_1()
+            .child(v_stack().elevation_2(cx).px_1().children(self.children))
             .when_some(self.aside, |this, aside| {
-                // TODO: This will statically position the aside to the top right of the popover.
-                // We should update this to use gpui2::overlay avoid collisions with the window edges.
                 this.child(
                     v_stack()
-                        .top_0()
-                        .left_full()
-                        .ml_1()
-                        .absolute()
                         .elevation_2(cx)
-                        .p_1()
+                        .bg(cx.theme().colors().surface_background)
+                        .px_1()
                         .child(aside),
                 )
             })

crates/ui2/src/components/stories/list_item.rs 🔗

@@ -22,5 +22,13 @@ impl Render for ListItemStory {
                         println!("Clicked!");
                     }),
             )
+            .child(Story::label("With `on_secondary_mouse_down`"))
+            .child(
+                ListItem::new("with_on_secondary_mouse_down").on_secondary_mouse_down(
+                    |_event, _cx| {
+                        println!("Right mouse down!");
+                    },
+                ),
+            )
     }
 }

crates/ui2/src/styled_ext.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{Styled, WindowContext};
+use gpui::{px, Styled, WindowContext};
 use theme2::ActiveTheme;
 
 use crate::{ElevationIndex, UITextSize};
@@ -6,7 +6,7 @@ use crate::{ElevationIndex, UITextSize};
 fn elevated<E: Styled>(this: E, cx: &mut WindowContext, index: ElevationIndex) -> E {
     this.bg(cx.theme().colors().elevated_surface_background)
         .z_index(index.z_index())
-        .rounded_lg()
+        .rounded(px(8.))
         .border()
         .border_color(cx.theme().colors().border_variant)
         .shadow(index.shadow())

crates/workspace/src/workspace.rs 🔗

@@ -56,14 +56,16 @@ use std::{
 };
 
 use crate::{
-    notifications::{simple_message_notification::MessageNotification, NotificationTracker},
+    notifications::NotificationTracker,
     persistence::model::{
         DockData, DockStructure, SerializedPane, SerializedPaneGroup, SerializedWorkspace,
     },
 };
 use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle};
 use lazy_static::lazy_static;
-use notifications::{simple_message_notification, NotificationHandle, NotifyResultExt};
+use notifications::{
+    simple_message_notification::MessageNotification, NotificationHandle, NotifyResultExt,
+};
 pub use pane::*;
 pub use pane_group::*;
 use persistence::{model::SerializedItem, DB};
@@ -778,20 +780,6 @@ impl Workspace {
 
         cx.defer(|this, cx| {
             this.update_window_title(cx);
-
-            this.show_notification(0, cx, |cx| {
-                cx.add_view(|_cx| {
-                    simple_message_notification::MessageNotification::new(format!(
-                        "Error: what happens if this message is very very very very very long "
-                    ))
-                    .with_click_message("Click here because!")
-                })
-            });
-            this.show_notification(1, cx, |cx| {
-                cx.add_view(|_cx| {
-                    simple_message_notification::MessageNotification::new(format!("Nope"))
-                })
-            });
         });
         Workspace {
             weak_self: weak_handle.clone(),

crates/zed/Cargo.toml 🔗

@@ -140,6 +140,7 @@ tree-sitter-lua.workspace = true
 tree-sitter-nix.workspace = true
 tree-sitter-nu.workspace = true
 tree-sitter-vue.workspace = true
+tree-sitter-uiua.workspace = true
 
 url = "2.2"
 urlencoding = "2.1.2"

crates/zed/src/languages.rs 🔗

@@ -17,6 +17,7 @@ mod json;
 #[cfg(feature = "plugin_runtime")]
 mod language_plugin;
 mod lua;
+mod nu;
 mod php;
 mod python;
 mod ruby;
@@ -24,6 +25,7 @@ mod rust;
 mod svelte;
 mod tailwind;
 mod typescript;
+mod uiua;
 mod vue;
 mod yaml;
 
@@ -210,12 +212,21 @@ pub fn init(
     language("elm", tree_sitter_elm::language(), vec![]);
     language("glsl", tree_sitter_glsl::language(), vec![]);
     language("nix", tree_sitter_nix::language(), vec![]);
-    language("nu", tree_sitter_nu::language(), vec![]);
+    language(
+        "nu",
+        tree_sitter_nu::language(),
+        vec![Arc::new(nu::NuLanguageServer {})],
+    );
     language(
         "vue",
         tree_sitter_vue::language(),
         vec![Arc::new(vue::VueLspAdapter::new(node_runtime))],
     );
+    language(
+        "uiua",
+        tree_sitter_uiua::language(),
+        vec![Arc::new(uiua::UiuaLanguageServer {})],
+    );
 }
 
 #[cfg(any(test, feature = "test-support"))]

crates/zed/src/languages/elixir/embedding.scm 🔗

@@ -18,10 +18,10 @@
                     target: (identifier) @name)
                     operator: "when")
             ])
-        (#match? @name "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp)$")) @item
+        (#any-match? @name "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp)$")) @item
         )
 
     (call
         target: (identifier) @name
         (arguments (alias) @name)
-        (#match? @name "^(defmodule|defprotocol)$")) @item
+        (#any-match? @name "^(defmodule|defprotocol)$")) @item

crates/zed/src/languages/nu.rs 🔗

@@ -0,0 +1,81 @@
+use anyhow::{anyhow, Result};
+use async_trait::async_trait;
+use language::{CodeLabel, Language, LanguageServerName, LspAdapter, LspAdapterDelegate};
+use lsp::LanguageServerBinary;
+use std::{any::Any, path::PathBuf, sync::Arc};
+
+pub struct NuLanguageServer;
+
+#[async_trait]
+impl LspAdapter for NuLanguageServer {
+    async fn name(&self) -> LanguageServerName {
+        LanguageServerName("nu".into())
+    }
+
+    fn short_name(&self) -> &'static str {
+        "nu"
+    }
+
+    async fn fetch_latest_server_version(
+        &self,
+        _: &dyn LspAdapterDelegate,
+    ) -> Result<Box<dyn 'static + Any + Send>> {
+        Ok(Box::new(()))
+    }
+
+    async fn fetch_server_binary(
+        &self,
+        _version: Box<dyn 'static + Send + Any>,
+        _container_dir: PathBuf,
+        _: &dyn LspAdapterDelegate,
+    ) -> Result<LanguageServerBinary> {
+        Err(anyhow!(
+            "nu v0.87.0 or greater must be installed and available in your $PATH"
+        ))
+    }
+
+    async fn cached_server_binary(
+        &self,
+        _: PathBuf,
+        _: &dyn LspAdapterDelegate,
+    ) -> Option<LanguageServerBinary> {
+        Some(LanguageServerBinary {
+            path: "nu".into(),
+            arguments: vec!["--lsp".into()],
+        })
+    }
+
+    fn can_be_reinstalled(&self) -> bool {
+        false
+    }
+
+    async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
+        None
+    }
+
+    async fn label_for_completion(
+        &self,
+        completion: &lsp::CompletionItem,
+        language: &Arc<Language>,
+    ) -> Option<CodeLabel> {
+        return Some(CodeLabel {
+            runs: language
+                .highlight_text(&completion.label.clone().into(), 0..completion.label.len()),
+            text: completion.label.clone(),
+            filter_range: 0..completion.label.len(),
+        });
+    }
+
+    async fn label_for_symbol(
+        &self,
+        name: &str,
+        _: lsp::SymbolKind,
+        language: &Arc<Language>,
+    ) -> Option<CodeLabel> {
+        Some(CodeLabel {
+            runs: language.highlight_text(&name.into(), 0..name.len()),
+            text: name.to_string(),
+            filter_range: 0..name.len(),
+        })
+    }
+}

crates/zed/src/languages/uiua.rs 🔗

@@ -0,0 +1,55 @@
+use anyhow::{anyhow, Result};
+use async_trait::async_trait;
+use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
+use lsp::LanguageServerBinary;
+use std::{any::Any, path::PathBuf};
+
+pub struct UiuaLanguageServer;
+
+#[async_trait]
+impl LspAdapter for UiuaLanguageServer {
+    async fn name(&self) -> LanguageServerName {
+        LanguageServerName("uiua".into())
+    }
+
+    fn short_name(&self) -> &'static str {
+        "uiua"
+    }
+
+    async fn fetch_latest_server_version(
+        &self,
+        _: &dyn LspAdapterDelegate,
+    ) -> Result<Box<dyn 'static + Any + Send>> {
+        Ok(Box::new(()))
+    }
+
+    async fn fetch_server_binary(
+        &self,
+        _version: Box<dyn 'static + Send + Any>,
+        _container_dir: PathBuf,
+        _: &dyn LspAdapterDelegate,
+    ) -> Result<LanguageServerBinary> {
+        Err(anyhow!(
+            "uiua must be installed and available in your $PATH"
+        ))
+    }
+
+    async fn cached_server_binary(
+        &self,
+        _: PathBuf,
+        _: &dyn LspAdapterDelegate,
+    ) -> Option<LanguageServerBinary> {
+        Some(LanguageServerBinary {
+            path: "uiua".into(),
+            arguments: vec!["lsp".into()],
+        })
+    }
+
+    fn can_be_reinstalled(&self) -> bool {
+        false
+    }
+
+    async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
+        None
+    }
+}

crates/zed/src/languages/uiua/config.toml 🔗

@@ -0,0 +1,10 @@
+name = "Uiua"
+path_suffixes = ["ua"]
+line_comment = "# "
+autoclose_before = ")]}\""
+brackets = [
+    { start = "{", end = "}", close = true, newline = false },
+    { start = "[", end = "]", close = true, newline = false },
+    { start = "(", end = ")", close = true, newline = false },
+    { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
+]

crates/zed/src/languages/uiua/highlights.scm 🔗

@@ -0,0 +1,50 @@
+[
+  (openParen)
+  (closeParen)
+  (openCurly)
+  (closeCurly)
+  (openBracket)
+  (closeBracket)
+] @punctuation.bracket
+
+[
+  (branchSeparator)
+  (underscore)
+] @constructor
+; ] @punctuation.delimiter
+
+[ (character) ] @constant.character
+[ (comment) ] @comment
+[ (constant) ] @constant.numeric
+[ (identifier) ] @variable
+[ (leftArrow) ] @keyword
+[ (function) ] @function
+[ (modifier1) ] @operator
+[ (modifier2) ] @operator
+[ (number) ] @constant.numeric
+[ (placeHolder) ] @special
+[ (otherConstant) ] @string.special
+[ (signature) ] @type
+[ (system) ] @function.builtin
+[ (tripleMinus) ] @module
+
+; planet
+[
+  "id"
+  "identity"
+  "∘"
+  "dip"
+  "⊙"
+  "gap"
+  "⋅"
+] @tag
+
+[
+  (string)
+  (multiLineString)
+] @string
+
+; [
+;   (deprecated)
+;   (identifierDeprecated)
+; ] @warning

crates/zed2/Cargo.toml 🔗

@@ -136,6 +136,7 @@ tree-sitter-lua.workspace = true
 tree-sitter-nix.workspace = true
 tree-sitter-nu.workspace = true
 tree-sitter-vue.workspace = true
+tree-sitter-uiua.workspace = true
 
 url = "2.2"
 urlencoding = "2.1.2"

crates/zed2/src/languages.rs 🔗

@@ -18,6 +18,7 @@ mod json;
 #[cfg(feature = "plugin_runtime")]
 mod language_plugin;
 mod lua;
+mod nu;
 mod php;
 mod python;
 mod ruby;
@@ -25,6 +26,7 @@ mod rust;
 mod svelte;
 mod tailwind;
 mod typescript;
+mod uiua;
 mod vue;
 mod yaml;
 
@@ -211,12 +213,21 @@ pub fn init(
     language("elm", tree_sitter_elm::language(), vec![]);
     language("glsl", tree_sitter_glsl::language(), vec![]);
     language("nix", tree_sitter_nix::language(), vec![]);
-    language("nu", tree_sitter_nu::language(), vec![]);
+    language(
+        "nu",
+        tree_sitter_nu::language(),
+        vec![Arc::new(nu::NuLanguageServer {})],
+    );
     language(
         "vue",
         tree_sitter_vue::language(),
         vec![Arc::new(vue::VueLspAdapter::new(node_runtime))],
     );
+    language(
+        "uiua",
+        tree_sitter_uiua::language(),
+        vec![Arc::new(uiua::UiuaLanguageServer {})],
+    );
 }
 
 #[cfg(any(test, feature = "test-support"))]

crates/zed2/src/languages/nu.rs 🔗

@@ -0,0 +1,55 @@
+use anyhow::{anyhow, Result};
+use async_trait::async_trait;
+use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
+use lsp::LanguageServerBinary;
+use std::{any::Any, path::PathBuf};
+
+pub struct NuLanguageServer;
+
+#[async_trait]
+impl LspAdapter for NuLanguageServer {
+    async fn name(&self) -> LanguageServerName {
+        LanguageServerName("nu".into())
+    }
+
+    fn short_name(&self) -> &'static str {
+        "nu"
+    }
+
+    async fn fetch_latest_server_version(
+        &self,
+        _: &dyn LspAdapterDelegate,
+    ) -> Result<Box<dyn 'static + Any + Send>> {
+        Ok(Box::new(()))
+    }
+
+    async fn fetch_server_binary(
+        &self,
+        _version: Box<dyn 'static + Send + Any>,
+        _container_dir: PathBuf,
+        _: &dyn LspAdapterDelegate,
+    ) -> Result<LanguageServerBinary> {
+        Err(anyhow!(
+            "nu v0.87.0 or greater must be installed and available in your $PATH"
+        ))
+    }
+
+    async fn cached_server_binary(
+        &self,
+        _: PathBuf,
+        _: &dyn LspAdapterDelegate,
+    ) -> Option<LanguageServerBinary> {
+        Some(LanguageServerBinary {
+            path: "nu".into(),
+            arguments: vec!["--lsp".into()],
+        })
+    }
+
+    fn can_be_reinstalled(&self) -> bool {
+        false
+    }
+
+    async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
+        None
+    }
+}

crates/zed2/src/languages/uiua.rs 🔗

@@ -0,0 +1,55 @@
+use anyhow::{anyhow, Result};
+use async_trait::async_trait;
+use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
+use lsp::LanguageServerBinary;
+use std::{any::Any, path::PathBuf};
+
+pub struct UiuaLanguageServer;
+
+#[async_trait]
+impl LspAdapter for UiuaLanguageServer {
+    async fn name(&self) -> LanguageServerName {
+        LanguageServerName("uiua".into())
+    }
+
+    fn short_name(&self) -> &'static str {
+        "uiua"
+    }
+
+    async fn fetch_latest_server_version(
+        &self,
+        _: &dyn LspAdapterDelegate,
+    ) -> Result<Box<dyn 'static + Any + Send>> {
+        Ok(Box::new(()))
+    }
+
+    async fn fetch_server_binary(
+        &self,
+        _version: Box<dyn 'static + Send + Any>,
+        _container_dir: PathBuf,
+        _: &dyn LspAdapterDelegate,
+    ) -> Result<LanguageServerBinary> {
+        Err(anyhow!(
+            "uiua must be installed and available in your $PATH"
+        ))
+    }
+
+    async fn cached_server_binary(
+        &self,
+        _: PathBuf,
+        _: &dyn LspAdapterDelegate,
+    ) -> Option<LanguageServerBinary> {
+        Some(LanguageServerBinary {
+            path: "uiua".into(),
+            arguments: vec!["lsp".into()],
+        })
+    }
+
+    fn can_be_reinstalled(&self) -> bool {
+        false
+    }
+
+    async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
+        None
+    }
+}

crates/zed2/src/languages/uiua/config.toml 🔗

@@ -0,0 +1,10 @@
+name = "Uiua"
+path_suffixes = ["ua"]
+line_comment = "# "
+autoclose_before = ")]}\""
+brackets = [
+    { start = "{", end = "}", close = true, newline = false},
+    { start = "[", end = "]", close = true, newline = false },
+    { start = "(", end = ")", close = true, newline = false },
+    { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
+]

crates/zed2/src/languages/uiua/highlights.scm 🔗

@@ -0,0 +1,50 @@
+[
+  (openParen)
+  (closeParen)
+  (openCurly)
+  (closeCurly)
+  (openBracket)
+  (closeBracket)
+] @punctuation.bracket
+
+[
+  (branchSeparator)
+  (underscore)
+] @constructor
+; ] @punctuation.delimiter
+
+[ (character) ] @constant.character
+[ (comment) ] @comment
+[ (constant) ] @constant.numeric
+[ (identifier) ] @variable
+[ (leftArrow) ] @keyword
+[ (function) ] @function
+[ (modifier1) ] @operator
+[ (modifier2) ] @operator
+[ (number) ] @constant.numeric
+[ (placeHolder) ] @special
+[ (otherConstant) ] @string.special
+[ (signature) ] @type
+[ (system) ] @function.builtin
+[ (tripleMinus) ] @module
+
+; planet
+[
+  "id"
+  "identity"
+  "∘"
+  "dip"
+  "⊙"
+  "gap"
+  "⋅"
+] @tag
+
+[
+  (string)
+  (multiLineString)
+] @string
+
+; [
+;   (deprecated)
+;   (identifierDeprecated)
+; ] @warning