diff --git a/Cargo.lock b/Cargo.lock index 7d8fb758bf..8838fdb130 100644 --- a/Cargo.lock +++ b/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", diff --git a/Cargo.toml b/Cargo.toml index 874b9e8de5..1f6a291a26 100644 --- a/Cargo.toml +++ b/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 diff --git a/crates/auto_update2/src/auto_update.rs b/crates/auto_update2/src/auto_update.rs index aeff68965f..d2eab15d09 100644 --- a/crates/auto_update2/src/auto_update.rs +++ b/crates/auto_update2/src/auto_update.rs @@ -84,8 +84,8 @@ impl Settings for AutoUpdateSetting { pub fn init(http_client: Arc, 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, 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; diff --git a/crates/auto_update2/src/update_notification.rs b/crates/auto_update2/src/update_notification.rs index 9cb1550bd4..d15d82e112 100644 --- a/crates/auto_update2/src/update_notification.rs +++ b/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 for UpdateNotification {} @@ -12,77 +14,43 @@ impl EventEmitter for UpdateNotification {} impl Render for UpdateNotification { type Element = Div; - fn render(&mut self, _cx: &mut gpui::ViewContext) -> Self::Element { - div().child("Updated zed!") - // let theme = theme::current(cx).clone(); - // let theme = &theme.update_notification; + fn render(&mut self, cx: &mut gpui::ViewContext) -> Self::Element { + let app_name = cx.global::().display_name(); - // let app_name = cx.global::().display_name(); - - // MouseEventHandler::new::(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::(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") + 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) { + pub fn dismiss(&mut self, cx: &mut ViewContext) { cx.emit(DismissEvent::Dismiss); } } diff --git a/crates/collab_ui2/src/collab_panel/contact_finder.rs b/crates/collab_ui2/src/collab_panel/contact_finder.rs index 80872db729..48453ada72 100644 --- a/crates/collab_ui2/src/collab_panel/contact_finder.rs +++ b/crates/collab_ui2/src/collab_panel/contact_finder.rs @@ -170,7 +170,7 @@ impl PickerDelegate for ContactFinderDelegate { ix: usize, selected: bool, cx: &mut ViewContext>, - ) -> Self::ListItem { + ) -> Option { 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) diff --git a/crates/command_palette2/src/command_palette.rs b/crates/command_palette2/src/command_palette.rs index 07b819d3a1..5f3fb4a985 100644 --- a/crates/command_palette2/src/command_palette.rs +++ b/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 { "Execute a command...".into() @@ -294,24 +292,16 @@ impl PickerDelegate for CommandPaletteDelegate { ix: usize, selected: bool, cx: &mut ViewContext>, - ) -> Self::ListItem { - let colors = cx.theme().colors(); + ) -> Option { 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)), - ) + ), + ) } } diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 39d7d9ed09..c8ce37d7e2 100644 --- a/crates/editor2/src/editor.rs +++ b/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| { diff --git a/crates/file_finder2/src/file_finder.rs b/crates/file_finder2/src/file_finder.rs index ea578fbb0e..b54d28a400 100644 --- a/crates/file_finder2/src/file_finder.rs +++ b/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 { "Search project files...".into() @@ -711,30 +709,22 @@ impl PickerDelegate for FileFinderDelegate { ix: usize, selected: bool, cx: &mut ViewContext>, - ) -> Self::ListItem { + ) -> Option { 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)), - ) + ), + ) } } diff --git a/crates/gpui2/src/elements/overlay.rs b/crates/gpui2/src/elements/overlay.rs index 764bdfabcd..1a8084ec0a 100644 --- a/crates/gpui2/src/elements/overlay.rs +++ b/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 => { diff --git a/crates/language/src/highlight_map.rs b/crates/language/src/highlight_map.rs index 109d79cf70..cf790e803e 100644 --- a/crates/language/src/highlight_map.rs +++ b/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); diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 1d22d7773b..af7504529c 100644 --- a/crates/language/src/language.rs +++ b/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)); } } diff --git a/crates/language/src/syntax_map/syntax_map_tests.rs b/crates/language/src/syntax_map/syntax_map_tests.rs index bd50608122..f20f481613 100644 --- a/crates/language/src/syntax_map/syntax_map_tests.rs +++ b/crates/language/src/syntax_map/syntax_map_tests.rs @@ -1300,7 +1300,7 @@ fn assert_capture_ranges( .collect::>(); 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()); } } diff --git a/crates/language2/src/highlight_map.rs b/crates/language2/src/highlight_map.rs index 1421ef672d..8e7a35233c 100644 --- a/crates/language2/src/highlight_map.rs +++ b/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); diff --git a/crates/language2/src/language2.rs b/crates/language2/src/language2.rs index 311049f032..5c17592f0c 100644 --- a/crates/language2/src/language2.rs +++ b/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)); } } diff --git a/crates/language2/src/syntax_map/syntax_map_tests.rs b/crates/language2/src/syntax_map/syntax_map_tests.rs index bd50608122..f20f481613 100644 --- a/crates/language2/src/syntax_map/syntax_map_tests.rs +++ b/crates/language2/src/syntax_map/syntax_map_tests.rs @@ -1300,7 +1300,7 @@ fn assert_capture_ranges( .collect::>(); 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()); } } diff --git a/crates/node_runtime/src/node_runtime.rs b/crates/node_runtime/src/node_runtime.rs index 2621c58120..ecabdeb718 100644 --- a/crates/node_runtime/src/node_runtime.rs +++ b/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", ]); diff --git a/crates/picker2/src/picker2.rs b/crates/picker2/src/picker2.rs index dc6b77c7c7..ea31419201 100644 --- a/crates/picker2/src/picker2.rs +++ b/crates/picker2/src/picker2.rs @@ -32,7 +32,7 @@ pub trait PickerDelegate: Sized + 'static { ix: usize, selected: bool, cx: &mut ViewContext>, - ) -> Self::ListItem; + ) -> Option; } impl FocusableView for Picker { @@ -230,7 +230,7 @@ impl Render for Picker { ) }), ) - .child(picker.delegate.render_match( + .children(picker.delegate.render_match( ix, ix == selected_index, cx, diff --git a/crates/project_panel2/src/project_panel.rs b/crates/project_panel2/src/project_panel.rs index b027209870..d4c63e75bf 100644 --- a/crates/project_panel2/src/project_panel.rs +++ b/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>, - padding: Pixels, + fn render_entry( + &self, + entry_id: ProjectEntryId, + details: EntryDetails, + // dragged_entry_destination: &mut Option>, cx: &mut ViewContext, - ) -> 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>, - cx: &mut ViewContext, - ) -> Stateful
{ - 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::(|this, event, cx| { // this.move_entry( // *dragged_entry, diff --git a/crates/semantic_index/src/semantic_index_tests.rs b/crates/semantic_index/src/semantic_index_tests.rs index 2145d1f9e0..f4e2c5ea13 100644 --- a/crates/semantic_index/src/semantic_index_tests.rs +++ b/crates/semantic_index/src/semantic_index_tests.rs @@ -1659,13 +1659,13 @@ fn elixir_lang() -> Arc { 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(), diff --git a/crates/storybook2/src/stories/picker.rs b/crates/storybook2/src/stories/picker.rs index ae6a26161b..8bcfb8923d 100644 --- a/crates/storybook2/src/stories/picker.rs +++ b/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>, @@ -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>, - ) -> Self::ListItem { - let colors = cx.theme().colors(); + ) -> Option { 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 { diff --git a/crates/ui2/src/components/context_menu.rs b/crates/ui2/src/components/context_menu.rs index fda446a78b..a92c08d82f 100644 --- a/crates/ui2/src/components/context_menu.rs +++ b/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, - ), + Entry(SharedString, Rc), } pub struct ContextMenu { @@ -61,7 +58,7 @@ impl ContextMenu { pub fn entry( mut self, label: impl Into, - 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))); diff --git a/crates/ui2/src/components/list.rs b/crates/ui2/src/components/list.rs index ecdd32d168..d17c77ea44 100644 --- a/crates/ui2/src/components/list.rs +++ b/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(self, cx: &mut WindowContext) -> impl Element { - // 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, overflow: OverflowStyle, - size: ListEntrySize, toggle: Toggle, variant: ListItemVariant, - on_click: Option>, + on_click: Option>, + on_secondary_mouse_down: Option>, 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) } - } + }) }), ) } diff --git a/crates/ui2/src/components/popover.rs b/crates/ui2/src/components/popover.rs index ac407d2335..d9269b0ac4 100644 --- a/crates/ui2/src/components/popover.rs +++ b/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), ) }) diff --git a/crates/ui2/src/components/stories/list_item.rs b/crates/ui2/src/components/stories/list_item.rs index ee1b4d0be6..ec0da7f07e 100644 --- a/crates/ui2/src/components/stories/list_item.rs +++ b/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!"); + }, + ), + ) } } diff --git a/crates/ui2/src/styled_ext.rs b/crates/ui2/src/styled_ext.rs index 9e81ab19ee..d064312c32 100644 --- a/crates/ui2/src/styled_ext.rs +++ b/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(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()) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 268c4f2ca0..bea26e402e 100644 --- a/crates/workspace/src/workspace.rs +++ b/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(), diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 3603f1c7fd..297785fe9f 100644 --- a/crates/zed/Cargo.toml +++ b/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" diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index 2398f81c78..5ade8cb302 100644 --- a/crates/zed/src/languages.rs +++ b/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"))] diff --git a/crates/zed/src/languages/elixir/embedding.scm b/crates/zed/src/languages/elixir/embedding.scm index 16ad20746d..743ebe4d2f 100644 --- a/crates/zed/src/languages/elixir/embedding.scm +++ b/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 diff --git a/crates/zed/src/languages/nu.rs b/crates/zed/src/languages/nu.rs new file mode 100644 index 0000000000..16a3b0e4c0 --- /dev/null +++ b/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> { + Ok(Box::new(())) + } + + async fn fetch_server_binary( + &self, + _version: Box, + _container_dir: PathBuf, + _: &dyn LspAdapterDelegate, + ) -> Result { + 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 { + Some(LanguageServerBinary { + path: "nu".into(), + arguments: vec!["--lsp".into()], + }) + } + + fn can_be_reinstalled(&self) -> bool { + false + } + + async fn installation_test_binary(&self, _: PathBuf) -> Option { + None + } + + async fn label_for_completion( + &self, + completion: &lsp::CompletionItem, + language: &Arc, + ) -> Option { + 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, + ) -> Option { + Some(CodeLabel { + runs: language.highlight_text(&name.into(), 0..name.len()), + text: name.to_string(), + filter_range: 0..name.len(), + }) + } +} diff --git a/crates/zed/src/languages/uiua.rs b/crates/zed/src/languages/uiua.rs new file mode 100644 index 0000000000..0efdfdd70d --- /dev/null +++ b/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> { + Ok(Box::new(())) + } + + async fn fetch_server_binary( + &self, + _version: Box, + _container_dir: PathBuf, + _: &dyn LspAdapterDelegate, + ) -> Result { + Err(anyhow!( + "uiua must be installed and available in your $PATH" + )) + } + + async fn cached_server_binary( + &self, + _: PathBuf, + _: &dyn LspAdapterDelegate, + ) -> Option { + Some(LanguageServerBinary { + path: "uiua".into(), + arguments: vec!["lsp".into()], + }) + } + + fn can_be_reinstalled(&self) -> bool { + false + } + + async fn installation_test_binary(&self, _: PathBuf) -> Option { + None + } +} diff --git a/crates/zed/src/languages/uiua/config.toml b/crates/zed/src/languages/uiua/config.toml new file mode 100644 index 0000000000..87c0d8a9db --- /dev/null +++ b/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"] }, +] diff --git a/crates/zed/src/languages/uiua/highlights.scm b/crates/zed/src/languages/uiua/highlights.scm new file mode 100644 index 0000000000..2c37f404e6 --- /dev/null +++ b/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 diff --git a/crates/zed/src/languages/uiua/indents.scm b/crates/zed/src/languages/uiua/indents.scm new file mode 100644 index 0000000000..add68c723c --- /dev/null +++ b/crates/zed/src/languages/uiua/indents.scm @@ -0,0 +1,3 @@ +[ + (array) +] @indent diff --git a/crates/zed2/Cargo.toml b/crates/zed2/Cargo.toml index 9deec31d21..dd08f7bd2f 100644 --- a/crates/zed2/Cargo.toml +++ b/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" diff --git a/crates/zed2/src/languages.rs b/crates/zed2/src/languages.rs index 555f12dd0f..129dad8f48 100644 --- a/crates/zed2/src/languages.rs +++ b/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"))] diff --git a/crates/zed2/src/languages/nu.rs b/crates/zed2/src/languages/nu.rs new file mode 100644 index 0000000000..a3631b8471 --- /dev/null +++ b/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> { + Ok(Box::new(())) + } + + async fn fetch_server_binary( + &self, + _version: Box, + _container_dir: PathBuf, + _: &dyn LspAdapterDelegate, + ) -> Result { + 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 { + Some(LanguageServerBinary { + path: "nu".into(), + arguments: vec!["--lsp".into()], + }) + } + + fn can_be_reinstalled(&self) -> bool { + false + } + + async fn installation_test_binary(&self, _: PathBuf) -> Option { + None + } +} diff --git a/crates/zed2/src/languages/uiua.rs b/crates/zed2/src/languages/uiua.rs new file mode 100644 index 0000000000..0efdfdd70d --- /dev/null +++ b/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> { + Ok(Box::new(())) + } + + async fn fetch_server_binary( + &self, + _version: Box, + _container_dir: PathBuf, + _: &dyn LspAdapterDelegate, + ) -> Result { + Err(anyhow!( + "uiua must be installed and available in your $PATH" + )) + } + + async fn cached_server_binary( + &self, + _: PathBuf, + _: &dyn LspAdapterDelegate, + ) -> Option { + Some(LanguageServerBinary { + path: "uiua".into(), + arguments: vec!["lsp".into()], + }) + } + + fn can_be_reinstalled(&self) -> bool { + false + } + + async fn installation_test_binary(&self, _: PathBuf) -> Option { + None + } +} diff --git a/crates/zed2/src/languages/uiua/config.toml b/crates/zed2/src/languages/uiua/config.toml new file mode 100644 index 0000000000..72fdc91040 --- /dev/null +++ b/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"] }, +] diff --git a/crates/zed2/src/languages/uiua/highlights.scm b/crates/zed2/src/languages/uiua/highlights.scm new file mode 100644 index 0000000000..2c37f404e6 --- /dev/null +++ b/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 diff --git a/crates/zed2/src/languages/uiua/indents.scm b/crates/zed2/src/languages/uiua/indents.scm new file mode 100644 index 0000000000..add68c723c --- /dev/null +++ b/crates/zed2/src/languages/uiua/indents.scm @@ -0,0 +1,3 @@ +[ + (array) +] @indent