Merge branch 'main' into collab-panel2

This commit is contained in:
Conrad Irwin 2023-11-28 11:33:29 -07:00
commit 01d3432a40
41 changed files with 646 additions and 361 deletions

13
Cargo.lock generated
View file

@ -9930,7 +9930,7 @@ dependencies = [
[[package]] [[package]]
name = "tree-sitter" name = "tree-sitter"
version = "0.20.10" 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 = [ dependencies = [
"cc", "cc",
"regex", "regex",
@ -10178,6 +10178,15 @@ dependencies = [
"tree-sitter", "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]] [[package]]
name = "tree-sitter-vue" name = "tree-sitter-vue"
version = "0.0.1" version = "0.0.1"
@ -11616,6 +11625,7 @@ dependencies = [
"tree-sitter-svelte", "tree-sitter-svelte",
"tree-sitter-toml", "tree-sitter-toml",
"tree-sitter-typescript", "tree-sitter-typescript",
"tree-sitter-uiua",
"tree-sitter-vue", "tree-sitter-vue",
"tree-sitter-yaml", "tree-sitter-yaml",
"unindent", "unindent",
@ -11739,6 +11749,7 @@ dependencies = [
"tree-sitter-svelte", "tree-sitter-svelte",
"tree-sitter-toml", "tree-sitter-toml",
"tree-sitter-typescript", "tree-sitter-typescript",
"tree-sitter-uiua",
"tree-sitter-vue", "tree-sitter-vue",
"tree-sitter-yaml", "tree-sitter-yaml",
"unindent", "unindent",

View file

@ -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-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-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-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] [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" } 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 # TODO - Remove when a version is released with this PR: https://github.com/servo/core-foundation-rs/pull/457

View file

@ -84,8 +84,8 @@ impl Settings for AutoUpdateSetting {
pub fn init(http_client: Arc<dyn HttpClient>, server_url: String, cx: &mut AppContext) { pub fn init(http_client: Arc<dyn HttpClient>, server_url: String, cx: &mut AppContext) {
AutoUpdateSetting::register(cx); AutoUpdateSetting::register(cx);
cx.observe_new_views(|wokrspace: &mut Workspace, _cx| { cx.observe_new_views(|workspace: &mut Workspace, _cx| {
wokrspace workspace
.register_action(|_, action: &Check, cx| check(action, cx)) .register_action(|_, action: &Check, cx| check(action, cx))
.register_action(|_, _action: &CheckThatAutoUpdaterWorks, cx| { .register_action(|_, _action: &CheckThatAutoUpdaterWorks, cx| {
let prompt = cx.prompt(gpui::PromptLevel::Info, "It does!", &["Ok"]); 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(); .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(); .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) { if let Some(auto_updater) = AutoUpdater::get(cx) {
let auto_updater = auto_updater.read(cx); let auto_updater = auto_updater.read(cx);
let server_url = &auto_updater.server_url; let server_url = &auto_updater.server_url;

View file

@ -1,10 +1,12 @@
use gpui::{ 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 { pub struct UpdateNotification {
_version: SemanticVersion, version: SemanticVersion,
} }
impl EventEmitter<DismissEvent> for UpdateNotification {} impl EventEmitter<DismissEvent> for UpdateNotification {}
@ -12,77 +14,43 @@ impl EventEmitter<DismissEvent> for UpdateNotification {}
impl Render for UpdateNotification { impl Render for UpdateNotification {
type Element = Div; type Element = Div;
fn render(&mut self, _cx: &mut gpui::ViewContext<Self>) -> Self::Element { fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
div().child("Updated zed!") let app_name = cx.global::<ReleaseChannel>().display_name();
// let theme = theme::current(cx).clone();
// let theme = &theme.update_notification;
// let app_name = cx.global::<ReleaseChannel>().display_name(); v_stack()
.elevation_3(cx)
// MouseEventHandler::new::<ViewReleaseNotes, _>(0, cx, |state, cx| { .p_4()
// Flex::column() .child(
// .with_child( h_stack()
// Flex::row() .justify_between()
// .with_child( .child(Label::new(format!(
// Text::new( "Updated to {app_name} {}",
// format!("Updated to {app_name} {}", self.version), self.version
// theme.message.text.clone(), )))
// ) .child(
// .contained() div()
// .with_style(theme.message.container) .id("cancel")
// .aligned() .child(IconElement::new(Icon::Close))
// .top() .cursor_pointer()
// .left() .on_click(cx.listener(|this, _, cx| this.dismiss(cx))),
// .flex(1., true), ),
// ) )
// .with_child( .child(
// MouseEventHandler::new::<Cancel, _>(0, cx, |state, _| { div()
// let style = theme.dismiss_button.style_for(state); .id("notes")
// Svg::new("icons/x.svg") .child(Label::new("View the release notes"))
// .with_color(style.color) .cursor_pointer()
// .constrained() .on_click(|_, cx| crate::view_release_notes(&Default::default(), cx)),
// .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")
} }
} }
impl UpdateNotification { impl UpdateNotification {
pub fn new(version: SemanticVersion) -> Self { 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); cx.emit(DismissEvent::Dismiss);
} }
} }

View file

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

View file

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

View file

@ -1273,6 +1273,13 @@ impl CompletionsMenu {
multiline_docs.map(|div| { multiline_docs.map(|div| {
div.id("multiline_docs") div.id("multiline_docs")
.max_h(max_height) .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() .overflow_y_scroll()
// Prevent a mouse down on documentation from being propagated to the editor, // Prevent a mouse down on documentation from being propagated to the editor,
// because that would move the cursor. // because that would move the cursor.
@ -1327,13 +1334,18 @@ impl CompletionsMenu {
div() div()
.id(mat.candidate_id) .id(mat.candidate_id)
.min_w(px(300.)) .min_w(px(220.))
.max_w(px(700.)) .max_w(px(540.))
.whitespace_nowrap() .whitespace_nowrap()
.overflow_hidden() .overflow_hidden()
.bg(gpui::green()) .text_ui()
.hover(|style| style.bg(gpui::blue())) .px_1()
.when(item_ix == selected_item, |div| div.bg(gpui::red())) .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( .on_mouse_down(
MouseButton::Left, MouseButton::Left,
cx.listener(move |editor, event, cx| { cx.listener(move |editor, event, cx| {

View file

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

View file

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

View file

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

View file

@ -1383,7 +1383,7 @@ impl Language {
let query = Query::new(self.grammar_mut().ts_language, source)?; let query = Query::new(self.grammar_mut().ts_language, source)?;
let mut override_configs_by_id = HashMap::default(); 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('_') { 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 { 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));
} }
} }

View file

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

View file

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

View file

@ -1391,7 +1391,7 @@ impl Language {
let mut override_configs_by_id = HashMap::default(); 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().enumerate() {
if !name.starts_with('_') { 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 { for server_name in &value.opt_into_language_servers {
if !self if !self
.config .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));
} }
} }

View file

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

View file

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

View file

@ -32,7 +32,7 @@ pub trait PickerDelegate: Sized + 'static {
ix: usize, ix: usize,
selected: bool, selected: bool,
cx: &mut ViewContext<Picker<Self>>, cx: &mut ViewContext<Picker<Self>>,
) -> Self::ListItem; ) -> Option<Self::ListItem>;
} }
impl<D: PickerDelegate> FocusableView for Picker<D> { 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,
ix == selected_index, ix == selected_index,
cx, cx,

View file

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

View file

@ -1659,13 +1659,13 @@ fn elixir_lang() -> Arc<Language> {
target: (identifier) @name) target: (identifier) @name)
operator: "when") 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 (call
target: (identifier) @name target: (identifier) @name
(arguments (alias) @name) (arguments (alias) @name)
(#match? @name "^(defmodule|defprotocol)$")) @item (#any-match? @name "^(defmodule|defprotocol)$")) @item
"#, "#,
) )
.unwrap(), .unwrap(),

View file

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

View file

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

View file

@ -1,8 +1,10 @@
use std::rc::Rc;
use gpui::{ 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 smallvec::SmallVec;
use std::rc::Rc;
use crate::{ use crate::{
disclosure_control, h_stack, v_stack, Avatar, Icon, IconButton, IconElement, IconSize, Label, disclosure_control, h_stack, v_stack, Avatar, Icon, IconButton, IconElement, IconSize, Label,
@ -117,66 +119,6 @@ impl ListHeader {
self.meta = meta; self.meta = meta;
self 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)] #[derive(IntoElement, Clone)]
@ -231,26 +173,21 @@ impl RenderOnce for ListSubHeader {
} }
} }
#[derive(Default, PartialEq, Copy, Clone)]
pub enum ListEntrySize {
#[default]
Small,
Medium,
}
#[derive(IntoElement)] #[derive(IntoElement)]
pub struct ListItem { pub struct ListItem {
id: ElementId, id: ElementId,
disabled: bool, disabled: bool,
selected: bool,
// TODO: Reintroduce this // TODO: Reintroduce this
// disclosure_control_style: DisclosureControlVisibility, // disclosure_control_style: DisclosureControlVisibility,
indent_level: u32, indent_level: usize,
indent_step_size: Pixels,
left_slot: Option<GraphicSlot>, left_slot: Option<GraphicSlot>,
overflow: OverflowStyle, overflow: OverflowStyle,
size: ListEntrySize,
toggle: Toggle, toggle: Toggle,
variant: ListItemVariant, 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]>, children: SmallVec<[AnyElement; 2]>,
} }
@ -259,22 +196,29 @@ impl ListItem {
Self { Self {
id: id.into(), id: id.into(),
disabled: false, disabled: false,
selected: false,
indent_level: 0, indent_level: 0,
indent_step_size: px(12.),
left_slot: None, left_slot: None,
overflow: OverflowStyle::Hidden, overflow: OverflowStyle::Hidden,
size: ListEntrySize::default(),
toggle: Toggle::NotToggleable, toggle: Toggle::NotToggleable,
variant: ListItemVariant::default(), variant: ListItemVariant::default(),
on_click: Default::default(), on_click: None,
on_secondary_mouse_down: None,
children: SmallVec::new(), 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, mut self,
handler: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static, handler: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
) -> Self { ) -> Self {
self.on_click = Some(Rc::new(handler)); self.on_secondary_mouse_down = Some(Rc::new(handler));
self self
} }
@ -283,16 +227,26 @@ impl ListItem {
self 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.indent_level = indent_level;
self 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 { pub fn toggle(mut self, toggle: Toggle) -> Self {
self.toggle = toggle; self.toggle = toggle;
self self
} }
pub fn selected(mut self, selected: bool) -> Self {
self.selected = selected;
self
}
pub fn left_content(mut self, left_content: GraphicSlot) -> Self { pub fn left_content(mut self, left_content: GraphicSlot) -> Self {
self.left_slot = Some(left_content); self.left_slot = Some(left_content);
self self
@ -307,11 +261,6 @@ impl ListItem {
self.left_slot = Some(GraphicSlot::Avatar(left_avatar.into())); self.left_slot = Some(GraphicSlot::Avatar(left_avatar.into()));
self self
} }
pub fn size(mut self, size: ListEntrySize) -> Self {
self.size = size;
self
}
} }
impl RenderOnce for ListItem { impl RenderOnce for ListItem {
@ -331,42 +280,32 @@ impl RenderOnce for ListItem {
None => None, None => None,
}; };
let sized_item = match self.size {
ListEntrySize::Small => div().h_6(),
ListEntrySize::Medium => div().h_7(),
};
div() div()
.id(self.id) .id(self.id)
.relative() .relative()
.bg(cx.theme().colors().editor_background.clone()) .hover(|mut style| {
// .hover(|mut style| { style.background = Some(cx.theme().colors().editor_background.into());
// style.background = Some(cx.theme().colors().editor_background.into()); style
// style })
// })
// TODO: Add focus state // TODO: Add focus state
// .when(self.state == InteractionState::Focused, |this| { // .when(self.state == InteractionState::Focused, |this| {
// this.border() // this.border()
// .border_color(cx.theme().colors().border_focused) // .border_color(cx.theme().colors().border_focused)
// }) // })
//.hover(|style| style.bg(cx.theme().colors().ghost_element_hover)) .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
//.active(|style| style.bg(cx.theme().colors().ghost_element_active)) .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( .child(
sized_item div()
.when(self.variant == ListItemVariant::Inset, |this| this.px_2()) .when(self.variant == ListItemVariant::Inset, |this| this.px_2())
// .ml(rems(0.75 * self.indent_level as f32)) .ml(self.indent_level as f32 * self.indent_step_size)
.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)),
)
}))
.flex() .flex()
.gap_1() .gap_1()
.items_center() .items_center()
@ -374,13 +313,19 @@ impl RenderOnce for ListItem {
.child(disclosure_control(self.toggle)) .child(disclosure_control(self.toggle))
.children(left_content) .children(left_content)
.children(self.children) .children(self.children)
.on_mouse_down(MouseButton::Left, { // HACK: We need to attach the `on_click` handler to the child element in order to have the click
let on_click = self.on_click.clone(); // event actually fire.
move |event, cx| { // Once this is fixed in GPUI we can remove this and rely on the `on_click` handler set above on the
if let Some(on_click) = &on_click { // 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) (on_click)(event, cx)
} }
} })
}), }),
) )
} }

View file

@ -1,8 +1,9 @@
use gpui::{ use gpui::{
AnyElement, Div, Element, ElementId, IntoElement, ParentElement, RenderOnce, Styled, div, AnyElement, Div, Element, ElementId, IntoElement, ParentElement, RenderOnce, Styled,
WindowContext, WindowContext,
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
use theme2::ActiveTheme;
use crate::{v_stack, StyledExt}; use crate::{v_stack, StyledExt};
@ -43,22 +44,16 @@ impl RenderOnce for Popover {
type Rendered = Div; type Rendered = Div;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {
v_stack() div()
.relative() .flex()
.elevation_2(cx) .gap_1()
.p_1() .child(v_stack().elevation_2(cx).px_1().children(self.children))
.children(self.children)
.when_some(self.aside, |this, aside| { .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( this.child(
v_stack() v_stack()
.top_0()
.left_full()
.ml_1()
.absolute()
.elevation_2(cx) .elevation_2(cx)
.p_1() .bg(cx.theme().colors().surface_background)
.px_1()
.child(aside), .child(aside),
) )
}) })

View file

@ -22,5 +22,13 @@ impl Render for ListItemStory {
println!("Clicked!"); 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!");
},
),
)
} }
} }

View file

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

View file

@ -56,14 +56,16 @@ use std::{
}; };
use crate::{ use crate::{
notifications::{simple_message_notification::MessageNotification, NotificationTracker}, notifications::NotificationTracker,
persistence::model::{ persistence::model::{
DockData, DockStructure, SerializedPane, SerializedPaneGroup, SerializedWorkspace, DockData, DockStructure, SerializedPane, SerializedPaneGroup, SerializedWorkspace,
}, },
}; };
use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle}; use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle};
use lazy_static::lazy_static; 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::*;
pub use pane_group::*; pub use pane_group::*;
use persistence::{model::SerializedItem, DB}; use persistence::{model::SerializedItem, DB};
@ -778,20 +780,6 @@ impl Workspace {
cx.defer(|this, cx| { cx.defer(|this, cx| {
this.update_window_title(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 { Workspace {
weak_self: weak_handle.clone(), weak_self: weak_handle.clone(),

View file

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

View file

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

View file

@ -18,10 +18,10 @@
target: (identifier) @name) target: (identifier) @name)
operator: "when") 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 (call
target: (identifier) @name target: (identifier) @name
(arguments (alias) @name) (arguments (alias) @name)
(#match? @name "^(defmodule|defprotocol)$")) @item (#any-match? @name "^(defmodule|defprotocol)$")) @item

View file

@ -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(),
})
}
}

View file

@ -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
}
}

View file

@ -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"] },
]

View file

@ -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

View file

@ -0,0 +1,3 @@
[
(array)
] @indent

View file

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

View file

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

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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"] },
]

View file

@ -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

View file

@ -0,0 +1,3 @@
[
(array)
] @indent