From f9dc87142247710adf959c147ffa4334d42f6833 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 11:11:08 -0500 Subject: [PATCH 001/121] Fix Clippy warnings in `theme` crate (#8715) This PR fixes a number of Clippy warnings in the `theme` crate. Release Notes: - N/A --- crates/theme/src/one_themes.rs | 4 +- crates/theme/src/registry.rs | 4 +- crates/theme/src/schema.rs | 288 +++++++++++++++-------------- crates/theme/src/settings.rs | 12 +- crates/theme/src/styles/players.rs | 8 +- 5 files changed, 159 insertions(+), 157 deletions(-) diff --git a/crates/theme/src/one_themes.rs b/crates/theme/src/one_themes.rs index 73104b07c1..c556b87de2 100644 --- a/crates/theme/src/one_themes.rs +++ b/crates/theme/src/one_themes.rs @@ -72,7 +72,7 @@ pub(crate) fn one_dark() -> Theme { icon_muted: hsla(220.0 / 360., 12.1 / 100., 66.1 / 100., 1.0), icon_disabled: hsla(220.0 / 360., 6.4 / 100., 45.7 / 100., 1.0), icon_placeholder: hsla(220.0 / 360., 6.4 / 100., 45.7 / 100., 1.0), - icon_accent: blue.into(), + icon_accent: blue, status_bar_background: bg, title_bar_background: bg, toolbar_background: editor, @@ -218,7 +218,7 @@ pub(crate) fn one_dark() -> Theme { ( "link_uri".into(), HighlightStyle { - color: Some(teal.into()), + color: Some(teal), font_style: Some(FontStyle::Italic), ..HighlightStyle::default() }, diff --git a/crates/theme/src/registry.rs b/crates/theme/src/registry.rs index 06d8626f42..830a461726 100644 --- a/crates/theme/src/registry.rs +++ b/crates/theme/src/registry.rs @@ -134,7 +134,7 @@ impl ThemeRegistry { color: highlight .color .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), font_style: highlight.font_style.map(Into::into), font_weight: highlight.font_weight.map(Into::into), ..Default::default() @@ -248,7 +248,7 @@ impl ThemeRegistry { } pub async fn read_user_theme(theme_path: &Path, fs: Arc) -> Result { - let reader = fs.open_sync(&theme_path).await?; + let reader = fs.open_sync(theme_path).await?; let theme = serde_json_lenient::from_reader(reader)?; Ok(theme) diff --git a/crates/theme/src/schema.rs b/crates/theme/src/schema.rs index 87f71cc7ee..603cf035d7 100644 --- a/crates/theme/src/schema.rs +++ b/crates/theme/src/schema.rs @@ -91,7 +91,7 @@ impl ThemeStyleContent { color: style .color .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), ..Default::default() }, ) @@ -489,351 +489,351 @@ impl ThemeColorsContent { border: self .border .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), border_variant: self .border_variant .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), border_focused: self .border_focused .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), border_selected: self .border_selected .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), border_transparent: self .border_transparent .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), border_disabled: self .border_disabled .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), elevated_surface_background: self .elevated_surface_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), surface_background: self .surface_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), background: self .background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), element_background: self .element_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), element_hover: self .element_hover .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), element_active: self .element_active .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), element_selected: self .element_selected .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), element_disabled: self .element_disabled .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), drop_target_background: self .drop_target_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), ghost_element_background: self .ghost_element_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), ghost_element_hover: self .ghost_element_hover .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), ghost_element_active: self .ghost_element_active .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), ghost_element_selected: self .ghost_element_selected .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), ghost_element_disabled: self .ghost_element_disabled .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), text: self .text .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), text_muted: self .text_muted .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), text_placeholder: self .text_placeholder .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), text_disabled: self .text_disabled .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), text_accent: self .text_accent .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), icon: self .icon .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), icon_muted: self .icon_muted .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), icon_disabled: self .icon_disabled .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), icon_placeholder: self .icon_placeholder .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), icon_accent: self .icon_accent .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), status_bar_background: self .status_bar_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), title_bar_background: self .title_bar_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), toolbar_background: self .toolbar_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), tab_bar_background: self .tab_bar_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), tab_inactive_background: self .tab_inactive_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), tab_active_background: self .tab_active_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), search_match_background: self .search_match_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), panel_background: self .panel_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), panel_focused_border: self .panel_focused_border .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), pane_focused_border: self .pane_focused_border .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), scrollbar_thumb_background: self .scrollbar_thumb_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), scrollbar_thumb_hover_background: self .scrollbar_thumb_hover_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), scrollbar_thumb_border: self .scrollbar_thumb_border .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), scrollbar_track_background: self .scrollbar_track_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), scrollbar_track_border: self .scrollbar_track_border .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), editor_foreground: self .editor_foreground .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), editor_background: self .editor_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), editor_gutter_background: self .editor_gutter_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), editor_subheader_background: self .editor_subheader_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), editor_active_line_background: self .editor_active_line_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), editor_highlighted_line_background: self .editor_highlighted_line_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), editor_line_number: self .editor_line_number .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), editor_active_line_number: self .editor_active_line_number .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), editor_invisible: self .editor_invisible .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), editor_wrap_guide: self .editor_wrap_guide .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), editor_active_wrap_guide: self .editor_active_wrap_guide .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), editor_document_highlight_read_background: self .editor_document_highlight_read_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), editor_document_highlight_write_background: self .editor_document_highlight_write_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_background: self .terminal_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_foreground: self .terminal_foreground .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_bright_foreground: self .terminal_bright_foreground .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_dim_foreground: self .terminal_dim_foreground .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_black: self .terminal_ansi_black .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_bright_black: self .terminal_ansi_bright_black .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_dim_black: self .terminal_ansi_dim_black .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_red: self .terminal_ansi_red .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_bright_red: self .terminal_ansi_bright_red .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_dim_red: self .terminal_ansi_dim_red .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_green: self .terminal_ansi_green .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_bright_green: self .terminal_ansi_bright_green .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_dim_green: self .terminal_ansi_dim_green .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_yellow: self .terminal_ansi_yellow .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_bright_yellow: self .terminal_ansi_bright_yellow .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_dim_yellow: self .terminal_ansi_dim_yellow .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_blue: self .terminal_ansi_blue .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_bright_blue: self .terminal_ansi_bright_blue .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_dim_blue: self .terminal_ansi_dim_blue .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_magenta: self .terminal_ansi_magenta .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_bright_magenta: self .terminal_ansi_bright_magenta .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_dim_magenta: self .terminal_ansi_dim_magenta .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_cyan: self .terminal_ansi_cyan .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_bright_cyan: self .terminal_ansi_bright_cyan .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_dim_cyan: self .terminal_ansi_dim_cyan .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_white: self .terminal_ansi_white .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_bright_white: self .terminal_ansi_bright_white .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), terminal_ansi_dim_white: self .terminal_ansi_dim_white .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), link_text_hover: self .link_text_hover .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), } } } @@ -990,171 +990,171 @@ impl StatusColorsContent { conflict: self .conflict .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), conflict_background: self .conflict_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), conflict_border: self .conflict_border .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), created: self .created .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), created_background: self .created_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), created_border: self .created_border .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), deleted: self .deleted .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), deleted_background: self .deleted_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), deleted_border: self .deleted_border .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), error: self .error .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), error_background: self .error_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), error_border: self .error_border .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), hidden: self .hidden .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), hidden_background: self .hidden_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), hidden_border: self .hidden_border .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), hint: self .hint .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), hint_background: self .hint_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), hint_border: self .hint_border .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), ignored: self .ignored .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), ignored_background: self .ignored_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), ignored_border: self .ignored_border .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), info: self .info .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), info_background: self .info_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), info_border: self .info_border .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), modified: self .modified .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), modified_background: self .modified_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), modified_border: self .modified_border .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), predictive: self .predictive .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), predictive_background: self .predictive_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), predictive_border: self .predictive_border .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), renamed: self .renamed .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), renamed_background: self .renamed_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), renamed_border: self .renamed_border .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), success: self .success .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), success_background: self .success_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), success_border: self .success_border .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), unreachable: self .unreachable .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), unreachable_background: self .unreachable_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), unreachable_border: self .unreachable_border .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), warning: self .warning .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), warning_background: self .warning_background .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), warning_border: self .warning_border .as_ref() - .and_then(|color| try_parse_color(&color).ok()), + .and_then(|color| try_parse_color(color).ok()), } } } @@ -1208,19 +1208,21 @@ impl JsonSchema for FontWeightContent { } fn json_schema(_: &mut SchemaGenerator) -> Schema { - let mut schema_object = SchemaObject::default(); - schema_object.enum_values = Some(vec![ - 100.into(), - 200.into(), - 300.into(), - 400.into(), - 500.into(), - 600.into(), - 700.into(), - 800.into(), - 900.into(), - ]); - schema_object.into() + SchemaObject { + enum_values: Some(vec![ + 100.into(), + 200.into(), + 300.into(), + 400.into(), + 500.into(), + 600.into(), + 700.into(), + 800.into(), + 900.into(), + ]), + ..Default::default() + } + .into() } } diff --git a/crates/theme/src/settings.rs b/crates/theme/src/settings.rs index d331b3b645..1b860dec92 100644 --- a/crates/theme/src/settings.rs +++ b/crates/theme/src/settings.rs @@ -48,14 +48,14 @@ impl ThemeSettings { // If the selected theme doesn't exist, fall back to a default theme // based on the system appearance. let theme_registry = ThemeRegistry::global(cx); - if theme_registry.get(&theme_name).ok().is_none() { + if theme_registry.get(theme_name).ok().is_none() { theme_name = match *system_appearance { Appearance::Light => "One Light", Appearance::Dark => "One Dark", }; }; - if let Some(_theme) = theme_settings.switch_theme(&theme_name, cx) { + if let Some(_theme) = theme_settings.switch_theme(theme_name, cx) { ThemeSettings::override_global(theme_settings, cx); } } @@ -220,7 +220,7 @@ impl ThemeSettings { let mut new_theme = None; - if let Some(theme) = themes.get(&theme).log_err() { + if let Some(theme) = themes.get(theme).log_err() { self.active_theme = theme.clone(); new_theme = Some(theme); } @@ -313,13 +313,13 @@ impl settings::Settings for ThemeSettings { ui_font_size: defaults.ui_font_size.unwrap().into(), ui_font: Font { family: defaults.ui_font_family.clone().unwrap().into(), - features: defaults.ui_font_features.clone().unwrap(), + features: defaults.ui_font_features.unwrap(), weight: Default::default(), style: Default::default(), }, buffer_font: Font { family: defaults.buffer_font_family.clone().unwrap().into(), - features: defaults.buffer_font_features.clone().unwrap(), + features: defaults.buffer_font_features.unwrap(), weight: FontWeight::default(), style: FontStyle::default(), }, @@ -333,7 +333,7 @@ impl settings::Settings for ThemeSettings { theme_overrides: None, }; - for value in user_values.into_iter().copied().cloned() { + for value in user_values.iter().copied().cloned() { if let Some(value) = value.buffer_font_family { this.buffer_font.family = value.into(); } diff --git a/crates/theme/src/styles/players.rs b/crates/theme/src/styles/players.rs index 98e5f16699..e80c7161b1 100644 --- a/crates/theme/src/styles/players.rs +++ b/crates/theme/src/styles/players.rs @@ -151,19 +151,19 @@ impl PlayerColors { return; } - for (idx, player) in user_player_colors.into_iter().enumerate() { + for (idx, player) in user_player_colors.iter().enumerate() { let cursor = player .cursor .as_ref() - .and_then(|color| try_parse_color(&color).ok()); + .and_then(|color| try_parse_color(color).ok()); let background = player .background .as_ref() - .and_then(|color| try_parse_color(&color).ok()); + .and_then(|color| try_parse_color(color).ok()); let selection = player .selection .as_ref() - .and_then(|color| try_parse_color(&color).ok()); + .and_then(|color| try_parse_color(color).ok()); if let Some(player_color) = self.0.get_mut(idx) { *player_color = PlayerColor { From 5de74921469e4f13ed1528ff98e8c78491686a22 Mon Sep 17 00:00:00 2001 From: Tommi Pisto Date: Sat, 2 Mar 2024 18:29:44 +0200 Subject: [PATCH 002/121] Add flex_wrap_* and content_* to GPUI (#8710) Now with GPUI you can auto-layout something like this: Screenshot 2024-03-02 at 13 56 50 --- crates/gpui/src/styled.rs | 86 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 3 deletions(-) diff --git a/crates/gpui/src/styled.rs b/crates/gpui/src/styled.rs index 3896052cc4..928f9f0a23 100644 --- a/crates/gpui/src/styled.rs +++ b/crates/gpui/src/styled.rs @@ -1,11 +1,11 @@ use crate::{ self as gpui, hsla, point, px, relative, rems, AbsoluteLength, AlignItems, CursorStyle, - DefiniteLength, Fill, FlexDirection, FontWeight, Hsla, JustifyContent, Length, Position, - SharedString, StyleRefinement, Visibility, WhiteSpace, + DefiniteLength, Fill, FlexDirection, FlexWrap, FontWeight, Hsla, JustifyContent, Length, + Position, SharedString, StyleRefinement, Visibility, WhiteSpace, }; use crate::{BoxShadow, TextStyleRefinement}; use smallvec::{smallvec, SmallVec}; -use taffy::style::{Display, Overflow}; +use taffy::style::{AlignContent, Display, Overflow}; /// A trait for elements that can be styled. /// Use this to opt-in to a CSS-like styling API. @@ -333,6 +333,27 @@ pub trait Styled: Sized { self } + /// Sets the element to allow flex items to wrap. + /// [Docs](https://tailwindcss.com/docs/flex-wrap#wrap-normally) + fn flex_wrap(mut self) -> Self { + self.style().flex_wrap = Some(FlexWrap::Wrap); + self + } + + /// Sets the element wrap flex items in the reverse direction. + /// [Docs](https://tailwindcss.com/docs/flex-wrap#wrap-reversed) + fn flex_wrap_reverse(mut self) -> Self { + self.style().flex_wrap = Some(FlexWrap::WrapReverse); + self + } + + /// Sets the element to prevent flex items from wrapping, causing inflexible items to overflow the container if necessary. + /// [Docs](https://tailwindcss.com/docs/flex-wrap#dont-wrap) + fn flex_nowrap(mut self) -> Self { + self.style().flex_wrap = Some(FlexWrap::NoWrap); + self + } + /// Sets the element to align flex items to the start of the container's cross axis. /// [Docs](https://tailwindcss.com/docs/align-items#start) fn items_start(mut self) -> Self { @@ -391,6 +412,65 @@ pub trait Styled: Sized { self } + /// Sets the element to pack content items in their default position as if no align-content value was set. + /// [Docs](https://tailwindcss.com/docs/align-content#normal) + fn content_normal(mut self) -> Self { + self.style().align_content = None; + self + } + + /// Sets the element to pack content items in the center of the container's cross axis. + /// [Docs](https://tailwindcss.com/docs/align-content#center) + fn content_center(mut self) -> Self { + self.style().align_content = Some(AlignContent::Center); + self + } + + /// Sets the element to pack content items against the start of the container's cross axis. + /// [Docs](https://tailwindcss.com/docs/align-content#start) + fn content_start(mut self) -> Self { + self.style().align_content = Some(AlignContent::FlexStart); + self + } + + /// Sets the element to pack content items against the end of the container's cross axis. + /// [Docs](https://tailwindcss.com/docs/align-content#end) + fn content_end(mut self) -> Self { + self.style().align_content = Some(AlignContent::FlexEnd); + self + } + + /// Sets the element to pack content items along the container's cross axis + /// such that there is an equal amount of space between each item. + /// [Docs](https://tailwindcss.com/docs/align-content#space-between) + fn content_between(mut self) -> Self { + self.style().align_content = Some(AlignContent::SpaceBetween); + self + } + + /// Sets the element to pack content items along the container's cross axis + /// such that there is an equal amount of space on each side of each item. + /// [Docs](https://tailwindcss.com/docs/align-content#space-around) + fn content_around(mut self) -> Self { + self.style().align_content = Some(AlignContent::SpaceAround); + self + } + + /// Sets the element to pack content items along the container's cross axis + /// such that there is an equal amount of space between each item. + /// [Docs](https://tailwindcss.com/docs/align-content#space-evenly) + fn content_evenly(mut self) -> Self { + self.style().align_content = Some(AlignContent::SpaceEvenly); + self + } + + /// Sets the element to allow content items to fill the available space along the container's cross axis. + /// [Docs](https://tailwindcss.com/docs/align-content#stretch) + fn content_stretch(mut self) -> Self { + self.style().align_content = Some(AlignContent::Stretch); + self + } + /// Sets the background color of the element. fn bg(mut self, fill: F) -> Self where From 0ac203bde0fa954ed8f130555317d12bf807cfe8 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 12:33:02 -0500 Subject: [PATCH 003/121] Fix Clippy warnings in `client` crate (#8719) This PR fixes a number of Clippy warnings in the `client` crate. Release Notes: - N/A --- crates/client/src/client.rs | 35 +++++++++++++++++----------------- crates/client/src/telemetry.rs | 18 ++++++++--------- crates/client/src/user.rs | 2 +- 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 57d59fa041..0f60f439d0 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -27,7 +27,7 @@ use release_channel::{AppVersion, ReleaseChannel}; use rpc::proto::{AnyTypedEnvelope, EntityMessage, EnvelopedMessage, PeerId, RequestMessage}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use serde_json; + use settings::{Settings, SettingsStore}; use std::{ any::TypeId, @@ -61,7 +61,7 @@ lazy_static! { pub static ref ZED_APP_PATH: Option = std::env::var("ZED_APP_PATH").ok().map(PathBuf::from); pub static ref ZED_ALWAYS_ACTIVE: bool = - std::env::var("ZED_ALWAYS_ACTIVE").map_or(false, |e| e.len() > 0); + std::env::var("ZED_ALWAYS_ACTIVE").map_or(false, |e| !e.is_empty()); } pub const INITIAL_RECONNECTION_DELAY: Duration = Duration::from_millis(100); @@ -427,7 +427,7 @@ impl Client { http: Arc, cx: &mut AppContext, ) -> Arc { - let client = Arc::new(Self { + Arc::new(Self { id: AtomicU64::new(0), peer: Peer::new(0), telemetry: Telemetry::new(clock, http.clone(), cx), @@ -438,9 +438,7 @@ impl Client { authenticate: Default::default(), #[cfg(any(test, feature = "test-support"))] establish_connection: Default::default(), - }); - - client + }) } pub fn id(&self) -> u64 { @@ -573,17 +571,18 @@ impl Client { let mut state = self.state.write(); if state.entities_by_type_and_remote_id.contains_key(&id) { return Err(anyhow!("already subscribed to entity")); - } else { - state - .entities_by_type_and_remote_id - .insert(id, WeakSubscriber::Pending(Default::default())); - Ok(PendingEntitySubscription { - client: self.clone(), - remote_id, - consumed: false, - _entity_type: PhantomData, - }) } + + state + .entities_by_type_and_remote_id + .insert(id, WeakSubscriber::Pending(Default::default())); + + Ok(PendingEntitySubscription { + client: self.clone(), + remote_id, + consumed: false, + _entity_type: PhantomData, + }) } #[track_caller] @@ -926,7 +925,7 @@ impl Client { move |cx| async move { match handle_io.await { Ok(()) => { - if this.status().borrow().clone() + if *this.status().borrow() == (Status::Connected { connection_id, peer_id, @@ -1335,7 +1334,7 @@ impl Client { pending.push(message); return; } - Some(weak_subscriber @ _) => match weak_subscriber { + Some(weak_subscriber) => match weak_subscriber { WeakSubscriber::Entity { handle } => { subscriber = handle.upgrade(); } diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index 946e5da407..475da1658c 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -84,7 +84,7 @@ impl Telemetry { TelemetrySettings::register(cx); let state = Arc::new(Mutex::new(TelemetryState { - settings: TelemetrySettings::get_global(cx).clone(), + settings: *TelemetrySettings::get_global(cx), app_metadata: cx.app_metadata(), architecture: env::consts::ARCH, release_channel, @@ -119,7 +119,7 @@ impl Telemetry { move |cx| { let mut state = state.lock(); - state.settings = TelemetrySettings::get_global(cx).clone(); + state.settings = *TelemetrySettings::get_global(cx); } }) .detach(); @@ -168,7 +168,7 @@ impl Telemetry { ) { let mut state = self.state.lock(); state.installation_id = installation_id.map(|id| id.into()); - state.session_id = Some(session_id.into()); + state.session_id = Some(session_id); drop(state); let this = self.clone(); @@ -387,11 +387,9 @@ impl Telemetry { event, }); - if state.installation_id.is_some() { - if state.events_queue.len() >= state.max_queue_size { - drop(state); - self.flush_events(); - } + if state.installation_id.is_some() && state.events_queue.len() >= state.max_queue_size { + drop(state); + self.flush_events(); } } @@ -433,7 +431,7 @@ impl Telemetry { json_bytes.clear(); serde_json::to_writer(&mut json_bytes, event)?; file.write_all(&json_bytes)?; - file.write(b"\n")?; + file.write_all(b"\n")?; } } @@ -442,7 +440,7 @@ impl Telemetry { let request_body = EventRequestBody { installation_id: state.installation_id.as_deref().map(Into::into), session_id: state.session_id.clone(), - is_staff: state.is_staff.clone(), + is_staff: state.is_staff, app_version: state .app_metadata .app_version diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 7a45ddb8f9..9b7ef1dbb1 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -653,7 +653,7 @@ impl UserStore { let users = response .users .into_iter() - .map(|user| User::new(user)) + .map(User::new) .collect::>(); this.update(&mut cx, |this, _| { From c19587d4e459d92b40ab281d076c509f2eb94008 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 13:06:35 -0500 Subject: [PATCH 004/121] Fix Clippy warnings in `util` crate (#8721) This PR fixes a number of Clippy warnings in the `util` crate. Release Notes: - N/A --- crates/util/src/http.rs | 19 +++++++++++-------- crates/util/src/paths.rs | 4 ++-- crates/util/src/test.rs | 4 ++-- crates/util/src/test/marked_text.rs | 9 +++++---- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/crates/util/src/http.rs b/crates/util/src/http.rs index 62cf2d1239..3df8886fa4 100644 --- a/crates/util/src/http.rs +++ b/crates/util/src/http.rs @@ -128,22 +128,25 @@ impl HttpClient for isahc::HttpClient { } } +#[cfg(feature = "test-support")] +type FakeHttpHandler = Box< + dyn Fn(Request) -> BoxFuture<'static, Result, Error>> + + Send + + Sync + + 'static, +>; + #[cfg(feature = "test-support")] pub struct FakeHttpClient { - handler: Box< - dyn 'static - + Send - + Sync - + Fn(Request) -> BoxFuture<'static, Result, Error>>, - >, + handler: FakeHttpHandler, } #[cfg(feature = "test-support")] impl FakeHttpClient { pub fn create(handler: F) -> Arc where - Fut: 'static + Send + futures::Future, Error>>, - F: 'static + Send + Sync + Fn(Request) -> Fut, + Fut: futures::Future, Error>> + Send + 'static, + F: Fn(Request) -> Fut + Send + Sync + 'static, { Arc::new(HttpClientWithUrl { base_url: Mutex::new("http://test.example".into()), diff --git a/crates/util/src/paths.rs b/crates/util/src/paths.rs index 9dbe64c418..27306bcfb7 100644 --- a/crates/util/src/paths.rs +++ b/crates/util/src/paths.rs @@ -459,7 +459,7 @@ mod tests { let path = Path::new("/work/node_modules"); let path_matcher = PathMatcher::new("**/node_modules/**").unwrap(); assert!( - path_matcher.is_match(&path), + path_matcher.is_match(path), "Path matcher {path_matcher} should match {path:?}" ); } @@ -469,7 +469,7 @@ mod tests { let path = Path::new("/Users/someonetoignore/work/zed/zed.dev/node_modules"); let path_matcher = PathMatcher::new("**/node_modules/**").unwrap(); assert!( - path_matcher.is_match(&path), + path_matcher.is_match(path), "Path matcher {path_matcher} should match {path:?}" ); } diff --git a/crates/util/src/test.rs b/crates/util/src/test.rs index e728281ea6..9915a6c159 100644 --- a/crates/util/src/test.rs +++ b/crates/util/src/test.rs @@ -29,8 +29,8 @@ fn write_tree(path: &Path, tree: serde_json::Value) { Value::Object(_) => { fs::create_dir(&path).unwrap(); - if path.file_name() == Some(&OsStr::new(".git")) { - git2::Repository::init(&path.parent().unwrap()).unwrap(); + if path.file_name() == Some(OsStr::new(".git")) { + git2::Repository::init(path.parent().unwrap()).unwrap(); } write_tree(&path, contents); diff --git a/crates/util/src/test/marked_text.rs b/crates/util/src/test/marked_text.rs index cf85a806e4..7ab45e9b20 100644 --- a/crates/util/src/test/marked_text.rs +++ b/crates/util/src/test/marked_text.rs @@ -12,7 +12,7 @@ pub fn marked_text_offsets_by( for char in marked_text.chars() { if markers.contains(&char) { - let char_offsets = extracted_markers.entry(char).or_insert(Vec::new()); + let char_offsets = extracted_markers.entry(char).or_default(); char_offsets.push(unmarked_text.len()); } else { unmarked_text.push(char); @@ -119,7 +119,7 @@ pub fn marked_text_ranges( let mut current_range_start = None; let mut current_range_cursor = None; - let marked_text = marked_text.replace("•", " "); + let marked_text = marked_text.replace('•', " "); for (marked_ix, marker) in marked_text.match_indices(&['«', '»', 'ˇ']) { unmarked_text.push_str(&marked_text[prev_marked_ix..marked_ix]); let unmarked_len = unmarked_text.len(); @@ -131,9 +131,9 @@ pub fn marked_text_ranges( if current_range_start.is_some() { if current_range_cursor.is_some() { panic!("duplicate point marker 'ˇ' at index {marked_ix}"); - } else { - current_range_cursor = Some(unmarked_len); } + + current_range_cursor = Some(unmarked_len); } else { ranges.push(unmarked_len..unmarked_len); } @@ -252,6 +252,7 @@ impl From<(char, char)> for TextRangeMarker { mod tests { use super::{generate_marked_text, marked_text_ranges}; + #[allow(clippy::reversed_empty_ranges)] #[test] fn test_marked_text() { let (text, ranges) = marked_text_ranges("one «ˇtwo» «threeˇ» «ˇfour» fiveˇ six", true); From c9a509c80531a346380615ab8a5f4eb9d077539f Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 14:46:02 -0500 Subject: [PATCH 005/121] Add `cargo xtask clippy` (#8722) This PR sets up a `cargo xtask clippy` command for running `cargo clippy` with our defined set of options. The intent is to make this easier to manage as we start enabling more Clippy rules. Release Notes: - N/A --- .cargo/ci-config.toml | 3 ++ .cargo/config.toml | 3 ++ Cargo.lock | 8 ++++ Cargo.toml | 2 + crates/cli/Cargo.toml | 1 + crates/storybook/Cargo.toml | 2 +- crates/theme_importer/Cargo.toml | 2 +- script/clippy | 6 +-- tooling/xtask/Cargo.toml | 10 ++++ tooling/xtask/LICENSE-GPL | 1 + tooling/xtask/src/main.rs | 80 ++++++++++++++++++++++++++++++++ 11 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 tooling/xtask/Cargo.toml create mode 120000 tooling/xtask/LICENSE-GPL create mode 100644 tooling/xtask/src/main.rs diff --git a/.cargo/ci-config.toml b/.cargo/ci-config.toml index 6dbaf4b446..664419837b 100644 --- a/.cargo/ci-config.toml +++ b/.cargo/ci-config.toml @@ -10,3 +10,6 @@ # in one spot, that's going to trigger a rebuild of all of the artifacts. Using ci-config.toml we can define these overrides for CI in one spot and not worry about it. [build] rustflags = ["-D", "warnings"] + +[alias] +xtask = "run --package xtask --" diff --git a/.cargo/config.toml b/.cargo/config.toml index 32fdb271ad..70a2137854 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,3 +1,6 @@ [build] # v0 mangling scheme provides more detailed backtraces around closures rustflags = ["-C", "symbol-mangling-version=v0"] + +[alias] +xtask = "run --package xtask --" diff --git a/Cargo.lock b/Cargo.lock index 976b888f6e..41aa25e9b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12208,6 +12208,14 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" +[[package]] +name = "xtask" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap 4.4.4", +] + [[package]] name = "yansi" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index b39dfbdad5..067c08786d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,6 +93,7 @@ members = [ "crates/zed", "crates/zed_actions", "extensions/gleam", + "tooling/xtask", ] default-members = ["crates/zed"] resolver = "2" @@ -200,6 +201,7 @@ blade-graphics = { git = "https://github.com/kvark/blade", rev = "e9d93a4d41f394 blade-macros = { git = "https://github.com/kvark/blade", rev = "e9d93a4d41f3946a03ffb76136290d6ccf7f2b80" } blade-rwh = { package = "raw-window-handle", version = "0.5" } chrono = { version = "0.4", features = ["serde"] } +clap = "4.4" clickhouse = { version = "0.11.6" } ctor = "0.2.6" core-foundation = { version = "0.9.3" } diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index f1b2875670..60285628e8 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -15,6 +15,7 @@ path = "src/main.rs" [dependencies] anyhow.workspace = true +# TODO: Use workspace version of `clap`. clap = { version = "3.1", features = ["derive"] } ipc-channel = "0.16" serde.workspace = true diff --git a/crates/storybook/Cargo.toml b/crates/storybook/Cargo.toml index a9186978b4..caf66c4c4f 100644 --- a/crates/storybook/Cargo.toml +++ b/crates/storybook/Cargo.toml @@ -11,7 +11,7 @@ path = "src/storybook.rs" [dependencies] anyhow.workspace = true -clap = { version = "4.4", features = ["derive", "string"] } +clap = { workspace = true, features = ["derive", "string"] } collab_ui = { workspace = true, features = ["stories"] } ctrlc = "3.4" dialoguer = { version = "0.11.0", features = ["fuzzy-select"] } diff --git a/crates/theme_importer/Cargo.toml b/crates/theme_importer/Cargo.toml index 75d9724342..ddff83f385 100644 --- a/crates/theme_importer/Cargo.toml +++ b/crates/theme_importer/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0-or-later" [dependencies] anyhow.workspace = true -clap = { version = "4.4", features = ["derive"] } +clap = { workspace = true, features = ["derive"] } gpui.workspace = true indexmap = { version = "1.6.2", features = ["serde"] } log.workspace = true diff --git a/script/clippy b/script/clippy index b0a1c86cc2..90a121be1c 100755 --- a/script/clippy +++ b/script/clippy @@ -2,8 +2,4 @@ set -euxo pipefail -# clippy.toml is not currently supporting specifying allowed lints -# so specify those here, and disable the rest until Zed's workspace -# will have more fixes & suppression for the standard lint set -cargo clippy --release --workspace --all-features --all-targets -- --allow clippy::all --deny clippy::dbg_macro --deny clippy::todo -cargo clippy --package gpui -- --deny warnings +cargo xtask clippy diff --git a/tooling/xtask/Cargo.toml b/tooling/xtask/Cargo.toml new file mode 100644 index 0000000000..fac3b54e5e --- /dev/null +++ b/tooling/xtask/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "xtask" +version = "0.1.0" +edition = "2021" +publish = false +license = "GPL-3.0-or-later" + +[dependencies] +anyhow.workspace = true +clap = { workspace = true, features = ["derive"] } diff --git a/tooling/xtask/LICENSE-GPL b/tooling/xtask/LICENSE-GPL new file mode 120000 index 0000000000..89e542f750 --- /dev/null +++ b/tooling/xtask/LICENSE-GPL @@ -0,0 +1 @@ +../../LICENSE-GPL \ No newline at end of file diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs new file mode 100644 index 0000000000..1fed1574b3 --- /dev/null +++ b/tooling/xtask/src/main.rs @@ -0,0 +1,80 @@ +use std::process::Command; + +use anyhow::{bail, Context, Result}; +use clap::{Parser, Subcommand}; + +#[derive(Parser)] +#[command(name = "cargo xtask")] +struct Args { + #[command(subcommand)] + command: CliCommand, +} + +#[derive(Subcommand)] +enum CliCommand { + /// Runs `cargo clippy`. + Clippy(ClippyArgs), +} + +fn main() -> Result<()> { + let args = Args::parse(); + + match args.command { + CliCommand::Clippy(args) => run_clippy(args), + } +} + +#[derive(Parser)] +struct ClippyArgs { + /// Whether to deny warnings (`clippy --deny warnings`). + #[arg(long)] + deny_warnings: bool, +} + +fn run_clippy(args: ClippyArgs) -> Result<()> { + let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); + + let mut clippy_command = Command::new(&cargo); + clippy_command + .arg("clippy") + .arg("--workspace") + .arg("--release") + .arg("--all-targets") + .arg("--all-features"); + + clippy_command.arg("--"); + + if args.deny_warnings { + clippy_command.args(["--deny", "warnings"]); + } + + // Allow all Clippy lints by default, as we have a lot of violations at the moment. + // We can tighten things up once we have a better handle on them. + clippy_command.args(["--allow", "clippy::all"]); + + // Deny `dbg!` and `todo!`s. + clippy_command + .args(["--deny", "clippy::dbg_macro"]) + .args(["--deny", "clippy::todo"]); + + eprintln!( + "running: {cargo} {}", + clippy_command + .get_args() + .map(|arg| format!("{}", arg.to_str().unwrap())) + .collect::>() + .join(" ") + ); + + let exit_status = clippy_command + .spawn() + .context("failed to spawn child process")? + .wait() + .context("failed to wait for child process")?; + + if !exit_status.success() { + bail!("clippy failed: {}", exit_status); + } + + Ok(()) +} From bf666af3a2c44f4e131d71bf75cc99c2fa9f1c30 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 15:51:01 -0500 Subject: [PATCH 006/121] Deny all Clippy warnings (#8720) This PR makes Clippy deny all warnings across the workspace. We now enumerate all of the rules that have violations and temporarily allow them, with the goal being to drive the list down over time. On Windows we don't yet use `--deny warnings`, as the Windows build still has some warnings. Release Notes: - N/A --- .github/workflows/ci.yml | 9 +- .github/workflows/deploy_collab.yml | 3 +- .github/workflows/release_nightly.yml | 3 +- crates/gpui/build.rs | 2 +- tooling/xtask/src/main.rs | 122 +++++++++++++++++++++++--- 5 files changed, 116 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f3ed4a18d..fac8924863 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -93,8 +93,7 @@ jobs: fi - name: cargo clippy - shell: bash -euxo pipefail {0} - run: script/clippy + run: cargo xtask clippy - name: Run tests uses: ./.github/actions/run_tests @@ -126,8 +125,7 @@ jobs: run: script/linux - name: cargo clippy - shell: bash -euxo pipefail {0} - run: script/clippy + run: cargo xtask clippy - name: Build Zed run: cargo build -p zed @@ -149,8 +147,7 @@ jobs: save-if: ${{ github.ref == 'refs/heads/main' }} - name: cargo clippy - shell: bash -euxo pipefail {0} - run: script/clippy + run: cargo xtask clippy - name: Build Zed run: cargo build -p zed diff --git a/.github/workflows/deploy_collab.yml b/.github/workflows/deploy_collab.yml index b866d9080c..220c4ca6f7 100644 --- a/.github/workflows/deploy_collab.yml +++ b/.github/workflows/deploy_collab.yml @@ -28,8 +28,7 @@ jobs: uses: ./.github/actions/check_style - name: Run clippy - shell: bash -euxo pipefail {0} - run: script/clippy + run: cargo xtask clippy tests: name: Run tests diff --git a/.github/workflows/release_nightly.yml b/.github/workflows/release_nightly.yml index 1a7a0f704d..6e34e859b5 100644 --- a/.github/workflows/release_nightly.yml +++ b/.github/workflows/release_nightly.yml @@ -32,8 +32,7 @@ jobs: uses: ./.github/actions/check_style - name: Run clippy - shell: bash -euxo pipefail {0} - run: script/clippy + run: cargo xtask clippy tests: name: Run tests if: github.repository_owner == 'zed-industries' diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index 6d6f384bc5..8f63787c8b 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -125,7 +125,7 @@ fn emit_stitched_shaders(header_path: &Path) { let shader_contents = std::fs::read_to_string(shader_path)?; let stitched_contents = format!("{header_contents}\n{shader_contents}"); let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("stitched_shaders.metal"); - let _ = std::fs::write(&out_path, stitched_contents)?; + std::fs::write(&out_path, stitched_contents)?; Ok(out_path) } let shader_source_path = "./src/platform/mac/shaders.metal"; diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 1fed1574b3..6126978e57 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -26,31 +26,129 @@ fn main() -> Result<()> { #[derive(Parser)] struct ClippyArgs { - /// Whether to deny warnings (`clippy --deny warnings`). + /// Automatically apply lint suggestions (`clippy --fix`). #[arg(long)] - deny_warnings: bool, + fix: bool, + + /// The package to run Clippy against (`cargo -p clippy`). + #[arg(long, short)] + package: Option, } fn run_clippy(args: ClippyArgs) -> Result<()> { let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); let mut clippy_command = Command::new(&cargo); + clippy_command.arg("clippy"); + + if let Some(package) = args.package { + clippy_command.args(["--package", &package]); + } else { + clippy_command.arg("--workspace"); + } + clippy_command - .arg("clippy") - .arg("--workspace") .arg("--release") .arg("--all-targets") .arg("--all-features"); - clippy_command.arg("--"); - - if args.deny_warnings { - clippy_command.args(["--deny", "warnings"]); + if args.fix { + clippy_command.arg("--fix"); } - // Allow all Clippy lints by default, as we have a lot of violations at the moment. - // We can tighten things up once we have a better handle on them. - clippy_command.args(["--allow", "clippy::all"]); + clippy_command.arg("--"); + + // Deny all warnings. + // We don't do this yet on Windows, as it still has some warnings present. + #[cfg(not(target_os = "windows"))] + clippy_command.args(["--deny", "warnings"]); + + /// These are all of the rules that currently have violations in the Zed + /// codebase. + /// + /// We'll want to drive this list down by either: + /// 1. fixing violations of the rule and begin enforcing it + /// 2. deciding we want to allow the rule permanently, at which point + /// we should codify that separately in this script. + const MIGRATORY_RULES_TO_ALLOW: &[&str] = &[ + // There's a bunch of rules currently failing in the `style` group, so + // allow all of those, for now. + "clippy::style", + // Individual rules that have violations in the codebase: + "clippy::almost_complete_range", + "clippy::arc_with_non_send_sync", + "clippy::await_holding_lock", + "clippy::bind_instead_of_map", + "clippy::bool_comparison", + "clippy::borrow_deref_ref", + "clippy::borrowed_box", + "clippy::cast_abs_to_unsigned", + "clippy::clone_on_copy", + "clippy::cmp_owned", + "clippy::crate_in_macro_def", + "clippy::default_constructed_unit_structs", + "clippy::derivable_impls", + "clippy::derive_ord_xor_partial_ord", + "clippy::drain_collect", + "clippy::eq_op", + "clippy::expect_fun_call", + "clippy::explicit_auto_deref", + "clippy::explicit_counter_loop", + "clippy::extra_unused_lifetimes", + "clippy::filter_map_identity", + "clippy::identity_op", + "clippy::implied_bounds_in_impls", + "clippy::iter_kv_map", + "clippy::iter_overeager_cloned", + "clippy::let_underscore_future", + "clippy::manual_find", + "clippy::manual_flatten", + "clippy::map_entry", + "clippy::map_flatten", + "clippy::map_identity", + "clippy::needless_arbitrary_self_type", + "clippy::needless_borrowed_reference", + "clippy::needless_lifetimes", + "clippy::needless_option_as_deref", + "clippy::needless_question_mark", + "clippy::needless_update", + "clippy::never_loop", + "clippy::non_canonical_clone_impl", + "clippy::non_canonical_partial_ord_impl", + "clippy::nonminimal_bool", + "clippy::option_as_ref_deref", + "clippy::option_map_unit_fn", + "clippy::redundant_closure_call", + "clippy::redundant_guards", + "clippy::redundant_locals", + "clippy::reversed_empty_ranges", + "clippy::search_is_some", + "clippy::single_char_pattern", + "clippy::single_range_in_vec_init", + "clippy::suspicious_to_owned", + "clippy::to_string_in_format_args", + "clippy::too_many_arguments", + "clippy::type_complexity", + "clippy::unit_arg", + "clippy::unnecessary_cast", + "clippy::unnecessary_filter_map", + "clippy::unnecessary_find_map", + "clippy::unnecessary_operation", + "clippy::unnecessary_to_owned", + "clippy::unnecessary_unwrap", + "clippy::useless_conversion", + "clippy::useless_format", + "clippy::vec_init_then_push", + ]; + + // When fixing violations automatically we don't care about the + // rules we're already violating, since it may be possible to + // have them fixed automatically. + if !args.fix { + for rule in MIGRATORY_RULES_TO_ALLOW { + clippy_command.args(["--allow", rule]); + } + } // Deny `dbg!` and `todo!`s. clippy_command @@ -61,7 +159,7 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "running: {cargo} {}", clippy_command .get_args() - .map(|arg| format!("{}", arg.to_str().unwrap())) + .map(|arg| arg.to_str().unwrap()) .collect::>() .join(" ") ); From 87efb75e5302a48e94f742649fc54cd2a4666427 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 16:10:34 -0500 Subject: [PATCH 007/121] Enable `clippy::bind_instead_of_map` (#8723) This PR enables the [`clippy::bind_instead_of_map`](https://rust-lang.github.io/rust-clippy/master/index.html#/bind_instead_of_map) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/ai/src/prompts/base.rs | 9 +++++---- crates/ai/src/prompts/repository_context.rs | 8 +++----- crates/language_tools/src/lsp_log.rs | 6 +++--- crates/workspace/src/workspace.rs | 6 +++--- tooling/xtask/src/main.rs | 1 - 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/crates/ai/src/prompts/base.rs b/crates/ai/src/prompts/base.rs index 5e624f23ac..529f775ae8 100644 --- a/crates/ai/src/prompts/base.rs +++ b/crates/ai/src/prompts/base.rs @@ -30,7 +30,7 @@ impl PromptArguments { if self .language_name .as_ref() - .and_then(|name| Some(!["Markdown", "Plain Text"].contains(&name.as_str()))) + .map(|name| !["Markdown", "Plain Text"].contains(&name.as_str())) .unwrap_or(true) { PromptFileType::Code @@ -51,8 +51,10 @@ pub trait PromptTemplate { #[repr(i8)] #[derive(PartialEq, Eq, Ord)] pub enum PromptPriority { - Mandatory, // Ignores truncation - Ordered { order: usize }, // Truncates based on priority + /// Ignores truncation. + Mandatory, + /// Truncates based on priority. + Ordered { order: usize }, } impl PartialOrd for PromptPriority { @@ -86,7 +88,6 @@ impl PromptChain { let mut sorted_indices = (0..self.templates.len()).collect::>(); sorted_indices.sort_by_key(|&i| Reverse(&self.templates[i].0)); - // If Truncate let mut tokens_outstanding = if truncate { Some(self.args.model.capacity()? - self.args.reserved_tokens) } else { diff --git a/crates/ai/src/prompts/repository_context.rs b/crates/ai/src/prompts/repository_context.rs index 89869c53a0..f74b06a5ce 100644 --- a/crates/ai/src/prompts/repository_context.rs +++ b/crates/ai/src/prompts/repository_context.rs @@ -24,11 +24,9 @@ impl PromptCodeSnippet { let language_name = buffer .language() - .and_then(|language| Some(language.name().to_string().to_lowercase())); + .map(|language| language.name().to_string().to_lowercase()); - let file_path = buffer - .file() - .and_then(|file| Some(file.path().to_path_buf())); + let file_path = buffer.file().map(|file| file.path().to_path_buf()); (content, language_name, file_path) })?; @@ -46,7 +44,7 @@ impl ToString for PromptCodeSnippet { let path = self .path .as_ref() - .and_then(|path| Some(path.to_string_lossy().to_string())) + .map(|path| path.to_string_lossy().to_string()) .unwrap_or("".to_string()); let language_name = self.language_name.clone().unwrap_or("".to_string()); let content = self.content.clone(); diff --git a/crates/language_tools/src/lsp_log.rs b/crates/language_tools/src/lsp_log.rs index d2bdd95d7d..f1590b6cb8 100644 --- a/crates/language_tools/src/lsp_log.rs +++ b/crates/language_tools/src/lsp_log.rs @@ -756,8 +756,8 @@ impl Render for LspLogToolbarItemView { .trigger(Button::new( "language_server_menu_header", current_server - .and_then(|row| { - Some(Cow::Owned(format!( + .map(|row| { + Cow::Owned(format!( "{} ({}) - {}", row.server_name.0, row.worktree_root_name, @@ -766,7 +766,7 @@ impl Render for LspLogToolbarItemView { } else { SERVER_LOGS }, - ))) + )) }) .unwrap_or_else(|| "No server selected".into()), )) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 26d1ad3dd5..0cb3ff9912 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -3355,7 +3355,7 @@ impl Workspace { let left_visible = left_dock.is_open(); let left_active_panel = left_dock .visible_panel() - .and_then(|panel| Some(panel.persistent_name().to_string())); + .map(|panel| panel.persistent_name().to_string()); let left_dock_zoom = left_dock .visible_panel() .map(|panel| panel.is_zoomed(cx)) @@ -3365,7 +3365,7 @@ impl Workspace { let right_visible = right_dock.is_open(); let right_active_panel = right_dock .visible_panel() - .and_then(|panel| Some(panel.persistent_name().to_string())); + .map(|panel| panel.persistent_name().to_string()); let right_dock_zoom = right_dock .visible_panel() .map(|panel| panel.is_zoomed(cx)) @@ -3375,7 +3375,7 @@ impl Workspace { let bottom_visible = bottom_dock.is_open(); let bottom_active_panel = bottom_dock .visible_panel() - .and_then(|panel| Some(panel.persistent_name().to_string())); + .map(|panel| panel.persistent_name().to_string()); let bottom_dock_zoom = bottom_dock .visible_panel() .map(|panel| panel.is_zoomed(cx)) diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 6126978e57..b65efb03b1 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -78,7 +78,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::almost_complete_range", "clippy::arc_with_non_send_sync", "clippy::await_holding_lock", - "clippy::bind_instead_of_map", "clippy::bool_comparison", "clippy::borrow_deref_ref", "clippy::borrowed_box", From 4b81b15cad327b73c2f85db53152bd1d3f2675e3 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 16:31:47 -0500 Subject: [PATCH 008/121] Enable `clippy::useless_conversion` (#8724) This PR enables the [`clippy::useless_conversion`](https://rust-lang.github.io/rust-clippy/master/index.html#/useless_conversion) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/ai/src/embedding.rs | 2 +- crates/channel/src/channel_buffer.rs | 2 +- crates/channel/src/channel_chat.rs | 2 +- crates/collab/src/db.rs | 2 +- crates/editor/src/display_map/block_map.rs | 2 +- crates/editor/src/display_map/tab_map.rs | 4 ++-- crates/editor/src/editor.rs | 2 +- crates/editor/src/editor_tests.rs | 4 ++-- crates/editor/src/element.rs | 7 +++---- crates/editor/src/scroll.rs | 2 +- crates/language/src/outline.rs | 6 +++--- crates/language/src/syntax_map.rs | 2 +- crates/language_tools/src/syntax_tree_view.rs | 2 +- crates/lsp/src/lsp.rs | 2 +- crates/project/src/project.rs | 4 ++-- crates/storybook/src/stories/scroll.rs | 2 +- crates/terminal/src/mappings/mouse.rs | 2 +- crates/terminal/src/terminal.rs | 13 ++++++------- crates/terminal_view/src/terminal_element.rs | 6 +++--- crates/vim/src/motion.rs | 2 +- tooling/xtask/src/main.rs | 18 ++++++++++-------- 21 files changed, 44 insertions(+), 44 deletions(-) diff --git a/crates/ai/src/embedding.rs b/crates/ai/src/embedding.rs index 6768b7ce7b..1be99d7b06 100644 --- a/crates/ai/src/embedding.rs +++ b/crates/ai/src/embedding.rs @@ -112,7 +112,7 @@ mod tests { } fn round_to_decimals(n: OrderedFloat, decimal_places: i32) -> f32 { - let factor = (10.0 as f32).powi(decimal_places); + let factor = 10.0_f32.powi(decimal_places); (n * factor).round() / factor } diff --git a/crates/channel/src/channel_buffer.rs b/crates/channel/src/channel_buffer.rs index 8e63e347f1..c2115a7cab 100644 --- a/crates/channel/src/channel_buffer.rs +++ b/crates/channel/src/channel_buffer.rs @@ -126,7 +126,7 @@ impl ChannelBuffer { for (_, old_collaborator) in &self.collaborators { if !new_collaborators.contains_key(&old_collaborator.peer_id) { self.buffer.update(cx, |buffer, cx| { - buffer.remove_peer(old_collaborator.replica_id as u16, cx) + buffer.remove_peer(old_collaborator.replica_id, cx) }); } } diff --git a/crates/channel/src/channel_chat.rs b/crates/channel/src/channel_chat.rs index 5313b34d51..bc26a8477b 100644 --- a/crates/channel/src/channel_chat.rs +++ b/crates/channel/src/channel_chat.rs @@ -681,7 +681,7 @@ pub fn mentions_to_proto(mentions: &[(Range, UserId)]) -> Vec (u32, usize) { if row >= target { break; } - offset += line.len() as usize; + offset += line.len(); } (row, offset) } diff --git a/crates/editor/src/display_map/tab_map.rs b/crates/editor/src/display_map/tab_map.rs index c761c9ba60..018797386d 100644 --- a/crates/editor/src/display_map/tab_map.rs +++ b/crates/editor/src/display_map/tab_map.rs @@ -296,7 +296,7 @@ impl TabSnapshot { let (collapsed, expanded_char_column, to_next_stop) = self.collapse_tabs(chars, expanded, bias); ( - FoldPoint::new(output.row(), collapsed as u32), + FoldPoint::new(output.row(), collapsed), expanded_char_column, to_next_stop, ) @@ -513,7 +513,7 @@ impl<'a> Iterator for TabChunks<'a> { } else { self.chunk.text = &self.chunk.text[1..]; let tab_size = if self.input_column < self.max_expansion_column { - self.tab_size.get() as u32 + self.tab_size.get() } else { 1 }; diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index b5555c5a2f..c93df92042 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -4107,7 +4107,7 @@ impl Editor { fold_data .map(|(fold_status, buffer_row, active)| { (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| { - IconButton::new(ix as usize, ui::IconName::ChevronDown) + IconButton::new(ix, ui::IconName::ChevronDown) .on_click({ let view = editor_view.clone(); move |_e, cx| { diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 98e1ff2991..04b8c01a90 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -6752,8 +6752,8 @@ async fn test_following(cx: &mut gpui::TestAppContext) { cx.open_window( WindowOptions { bounds: WindowBounds::Fixed(Bounds::from_corners( - gpui::Point::new((0. as f64).into(), (0. as f64).into()), - gpui::Point::new((10. as f64).into(), (80. as f64).into()), + gpui::Point::new(0_f64.into(), 0_f64.into()), + gpui::Point::new(10_f64.into(), 80_f64.into()), )), ..Default::default() }, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 0082ff343c..9dae0d0ee3 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1586,7 +1586,7 @@ impl EditorElement { let new_y = event.position.y; if (track_bounds.top()..track_bounds.bottom()).contains(&y) { let mut position = editor.scroll_position(cx); - position.y += (new_y - y) * (max_row as f32) / height; + position.y += (new_y - y) * max_row / height; if position.y < 0.0 { position.y = 0.0; } @@ -1633,8 +1633,7 @@ impl EditorElement { let y = event.position.y; if y < thumb_top || thumb_bottom < y { - let center_row = - ((y - top) * max_row as f32 / height).round() as u32; + let center_row = ((y - top) * max_row / height).round() as u32; let top_row = center_row .saturating_sub((row_range.end - row_range.start) as u32 / 2); let mut position = editor.scroll_position(cx); @@ -1964,7 +1963,7 @@ impl EditorElement { chunks, &self.style.text, MAX_LINE_LEN, - rows.len() as usize, + rows.len(), line_number_layouts, snapshot.mode, cx, diff --git a/crates/editor/src/scroll.rs b/crates/editor/src/scroll.rs index c354f98150..4ac7374651 100644 --- a/crates/editor/src/scroll.rs +++ b/crates/editor/src/scroll.rs @@ -470,7 +470,7 @@ impl Editor { .buffer() .read(cx) .snapshot(cx) - .anchor_at(Point::new(top_row as u32, 0), Bias::Left); + .anchor_at(Point::new(top_row, 0), Bias::Left); let scroll_anchor = ScrollAnchor { offset: gpui::Point::new(x, y), anchor: top_anchor, diff --git a/crates/language/src/outline.rs b/crates/language/src/outline.rs index 014b32676a..685fd297fc 100644 --- a/crates/language/src/outline.rs +++ b/crates/language/src/outline.rs @@ -43,7 +43,7 @@ impl Outline { let candidate_text = item .name_ranges .iter() - .map(|range| &item.text[range.start as usize..range.end as usize]) + .map(|range| &item.text[range.start..range.end]) .collect::(); path_candidates.push(StringMatchCandidate::new(id, path_text.clone())); @@ -97,11 +97,11 @@ impl Outline { let mut name_range = name_ranges.next().unwrap(); let mut preceding_ranges_len = 0; for position in &mut string_match.positions { - while *position >= preceding_ranges_len + name_range.len() as usize { + while *position >= preceding_ranges_len + name_range.len() { preceding_ranges_len += name_range.len(); name_range = name_ranges.next().unwrap(); } - *position = name_range.start as usize + (*position - preceding_ranges_len); + *position = name_range.start + (*position - preceding_ranges_len); } } diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index 045e475f07..c13d7f4894 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -1421,7 +1421,7 @@ fn insert_newlines_between_ranges( if range_a.end_point.row < range_b.start_point.row { let end_point = start_point + Point::from_ts_point(range_a.end_point); let line_end = Point::new(end_point.row, text.line_len(end_point.row)); - if end_point.column as u32 >= line_end.column { + if end_point.column >= line_end.column { range_a.end_byte += 1; range_a.end_point.row += 1; range_a.end_point.column = 0; diff --git a/crates/language_tools/src/syntax_tree_view.rs b/crates/language_tools/src/syntax_tree_view.rs index 082e77fc36..bca193bc94 100644 --- a/crates/language_tools/src/syntax_tree_view.rs +++ b/crates/language_tools/src/syntax_tree_view.rs @@ -284,7 +284,7 @@ impl Render for SyntaxTreeView { move |this, range, cx| { let mut items = Vec::new(); let mut cursor = layer.node().walk(); - let mut descendant_ix = range.start as usize; + let mut descendant_ix = range.start; cursor.goto_descendant(descendant_ix); let mut depth = cursor.depth(); let mut visited_children = false; diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 638ed9286f..ce021abf45 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -977,7 +977,7 @@ impl LanguageServer { Self::notify_internal::( &outbound_tx, CancelParams { - id: NumberOrString::Number(id as i32), + id: NumberOrString::Number(id), }, ) .log_err(); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 45aca64a5a..07a95198a0 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -3665,7 +3665,7 @@ impl Project { proto::LspWorkStart { token, message: report.message, - percentage: report.percentage.map(|p| p as u32), + percentage: report.percentage.map(|p| p), }, ), }) @@ -3691,7 +3691,7 @@ impl Project { proto::LspWorkProgress { token, message: report.message, - percentage: report.percentage.map(|p| p as u32), + percentage: report.percentage.map(|p| p), }, ), }) diff --git a/crates/storybook/src/stories/scroll.rs b/crates/storybook/src/stories/scroll.rs index a0318dc30e..096afaccf6 100644 --- a/crates/storybook/src/stories/scroll.rs +++ b/crates/storybook/src/stories/scroll.rs @@ -38,7 +38,7 @@ impl Render for ScrollStory { .id(id) .tooltip(move |cx| Tooltip::text(format!("{}, {}", row, column), cx)) .bg(bg) - .size(px(100. as f32)) + .size(px(100_f32)) .when(row >= 5 && column >= 5, |d| { d.overflow_scroll() .child(div().size(px(50.)).bg(color_1)) diff --git a/crates/terminal/src/mappings/mouse.rs b/crates/terminal/src/mappings/mouse.rs index af3e6b640f..2d00431cad 100644 --- a/crates/terminal/src/mappings/mouse.rs +++ b/crates/terminal/src/mappings/mouse.rs @@ -183,7 +183,7 @@ pub fn grid_point_and_side( let col = min(col, cur_size.last_column()); let mut line = (pos.y / cur_size.line_height) as i32; if line > cur_size.bottommost_line() { - line = cur_size.bottommost_line().0 as i32; + line = cur_size.bottommost_line().0; side = Side::Right; } else if line < 0 { side = Side::Left; diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 68d43a430b..7916d8f947 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -1294,8 +1294,7 @@ impl Terminal { self.last_content.display_offset, ); - if let Some(scrolls) = - scroll_report(point, scroll_lines as i32, e, self.last_content.mode) + if let Some(scrolls) = scroll_report(point, scroll_lines, e, self.last_content.mode) { for scroll in scrolls { self.pty_tx.notify(scroll); @@ -1554,9 +1553,9 @@ fn rgb_for_index(i: &u8) -> (u8, u8, u8) { pub fn rgba_color(r: u8, g: u8, b: u8) -> Hsla { Rgba { - r: (r as f32 / 255.) as f32, - g: (g as f32 / 255.) as f32, - b: (b as f32 / 255.) as f32, + r: (r as f32 / 255.), + g: (g as f32 / 255.), + b: (b as f32 / 255.), a: 1., } .into() @@ -1577,9 +1576,9 @@ mod tests { #[test] fn test_rgb_for_index() { - //Test every possible value in the color cube + // Test every possible value in the color cube. for i in 16..=231 { - let (r, g, b) = rgb_for_index(&(i as u8)); + let (r, g, b) = rgb_for_index(&i); assert_eq!(i, 16 + 36 * r + 6 * g + b); } } diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index cd439fca1f..b121459d4a 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -365,7 +365,7 @@ impl TerminalElement { }; let mut result = TextRun { - len: indexed.c.len_utf8() as usize, + len: indexed.c.len_utf8(), color: fg, background_color: None, font: Font { @@ -1015,10 +1015,10 @@ fn to_highlighted_range_lines( let mut line_end = layout.dimensions.columns(); if line == clamped_start_line { - line_start = unclamped_start.column.0 as usize; + line_start = unclamped_start.column.0; } if line == clamped_end_line { - line_end = unclamped_end.column.0 as usize + 1; //+1 for inclusive + line_end = unclamped_end.column.0 + 1; // +1 for inclusive } highlighted_range_lines.push(HighlightedRangeLine { diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index 177fbe4294..b46035ce2e 100644 --- a/crates/vim/src/motion.rs +++ b/crates/vim/src/motion.rs @@ -1176,7 +1176,7 @@ fn window_middle( (visible_rows as u32).min(map.max_point().row() - first_visible_line.row()); let new_row = - (first_visible_line.row() + (max_visible_rows / 2) as u32).min(map.max_point().row()); + (first_visible_line.row() + (max_visible_rows / 2)).min(map.max_point().row()); let new_col = point.column().min(map.line_len(new_row)); let new_point = DisplayPoint::new(new_row, new_col); (map.clip_point(new_point, Bias::Left), SelectionGoal::None) diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index b65efb03b1..037071ff7e 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -41,8 +41,8 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { let mut clippy_command = Command::new(&cargo); clippy_command.arg("clippy"); - if let Some(package) = args.package { - clippy_command.args(["--package", &package]); + if let Some(package) = args.package.as_ref() { + clippy_command.args(["--package", package]); } else { clippy_command.arg("--workspace"); } @@ -69,9 +69,11 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { /// We'll want to drive this list down by either: /// 1. fixing violations of the rule and begin enforcing it /// 2. deciding we want to allow the rule permanently, at which point - /// we should codify that separately in this script. + /// we should codify that separately in this task. + /// + /// This list shouldn't be added to; it should only get shorter. const MIGRATORY_RULES_TO_ALLOW: &[&str] = &[ - // There's a bunch of rules currently failing in the `style` group, so + // There are a bunch of rules currently failing in the `style` group, so // allow all of those, for now. "clippy::style", // Individual rules that have violations in the codebase: @@ -129,7 +131,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::too_many_arguments", "clippy::type_complexity", "clippy::unit_arg", - "clippy::unnecessary_cast", "clippy::unnecessary_filter_map", "clippy::unnecessary_find_map", "clippy::unnecessary_operation", @@ -140,10 +141,11 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::vec_init_then_push", ]; - // When fixing violations automatically we don't care about the - // rules we're already violating, since it may be possible to + // When fixing violations automatically for a single package we don't care + // about the rules we're already violating, since it may be possible to // have them fixed automatically. - if !args.fix { + let ignore_suppressed_rules = args.fix && args.package.is_some(); + if !ignore_suppressed_rules { for rule in MIGRATORY_RULES_TO_ALLOW { clippy_command.args(["--allow", rule]); } From 12440d5e0d9e9c8441830a2210b2c98dc100f0a9 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 16:51:46 -0500 Subject: [PATCH 009/121] terminal: Make `rgb_for_index` take a `u8` instead of a `&u8` (#8726) This PR makes the `rgb_for_index` take a `u8` instead of a `&u8`. `u8` is `Copy` and is only 1 byte, so there really isn't any reason to pass a reference to it. Release Notes: - N/A --- crates/terminal/src/terminal.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 7916d8f947..d0a145dad0 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -1503,7 +1503,7 @@ pub fn get_color_at_index(index: usize, theme: &Theme) -> Hsla { 15 => colors.terminal_ansi_bright_white, // 16-231 are mapped to their RGB colors on a 0-5 range per channel 16..=231 => { - let (r, g, b) = rgb_for_index(&(index as u8)); // Split the index into its ANSI-RGB components + let (r, g, b) = rgb_for_index(index as u8); // Split the index into its ANSI-RGB components let step = (u8::MAX as f32 / 5.).floor() as u8; // Split the RGB range into 5 chunks, with floor so no overflow rgba_color(r * step, g * step, b * step) // Map the ANSI-RGB components to an RGB color } @@ -1542,8 +1542,8 @@ pub fn get_color_at_index(index: usize, theme: &Theme) -> Hsla { /// ``` /// /// This function does the reverse, calculating the `r`, `g`, and `b` components from a given index. -fn rgb_for_index(i: &u8) -> (u8, u8, u8) { - debug_assert!((&16..=&231).contains(&i)); +fn rgb_for_index(i: u8) -> (u8, u8, u8) { + debug_assert!((16..=231).contains(&i)); let i = i - 16; let r = (i - (i % 36)) / 36; let g = ((i % 36) - (i % 6)) / 6; @@ -1578,7 +1578,7 @@ mod tests { fn test_rgb_for_index() { // Test every possible value in the color cube. for i in 16..=231 { - let (r, g, b) = rgb_for_index(&i); + let (r, g, b) = rgb_for_index(i); assert_eq!(i, 16 + 36 * r + 6 * g + b); } } From 5935681c5c844a6e3f7f2e91a7493e763b691ca6 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 17:04:59 -0500 Subject: [PATCH 010/121] Enable `clippy::single_char_pattern` (#8727) This PR enables the [`clippy::single_char_pattern`](https://rust-lang.github.io/rust-clippy/master/index.html#/single_char_pattern) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/assistant/src/codegen.rs | 4 ++-- crates/channel/src/channel_store.rs | 2 +- crates/collab/src/db/tests.rs | 2 +- .../collab_ui/src/chat_panel/message_editor.rs | 2 +- crates/editor/src/editor.rs | 8 ++++---- crates/editor/src/git/permalink.rs | 16 ++++++++-------- crates/file_finder/src/file_finder.rs | 2 +- crates/gpui/src/app/test_context.rs | 2 +- crates/language/src/buffer_tests.rs | 6 +++--- crates/languages/src/ocaml.rs | 4 ++-- crates/languages/src/terraform.rs | 2 +- crates/lsp/src/lsp.rs | 2 +- crates/project_panel/src/project_panel.rs | 2 +- .../semantic_index/src/semantic_index_tests.rs | 4 ++-- crates/vim/src/command.rs | 8 ++++---- crates/vim/src/normal/paste.rs | 6 +++--- .../vim/src/test/neovim_backed_test_context.rs | 6 +++--- crates/vim/src/test/neovim_connection.rs | 2 +- crates/workspace/src/pane.rs | 4 ++-- crates/workspace/src/workspace.rs | 2 +- crates/zed/src/open_listener.rs | 4 ++-- tooling/xtask/src/main.rs | 1 - 22 files changed, 45 insertions(+), 46 deletions(-) diff --git a/crates/assistant/src/codegen.rs b/crates/assistant/src/codegen.rs index c1a663d7ef..04d08a3315 100644 --- a/crates/assistant/src/codegen.rs +++ b/crates/assistant/src/codegen.rs @@ -297,7 +297,7 @@ fn strip_invalid_spans_from_codeblock( } else if buffer.starts_with("<|") || buffer.starts_with("<|S") || buffer.starts_with("<|S|") - || buffer.ends_with("|") + || buffer.ends_with('|') || buffer.ends_with("|E") || buffer.ends_with("|E|") { @@ -335,7 +335,7 @@ fn strip_invalid_spans_from_codeblock( .strip_suffix("|E|>") .or_else(|| text.strip_suffix("E|>")) .or_else(|| text.strip_prefix("|>")) - .or_else(|| text.strip_prefix(">")) + .or_else(|| text.strip_prefix('>')) .unwrap_or(&text) .to_string(); }; diff --git a/crates/channel/src/channel_store.rs b/crates/channel/src/channel_store.rs index 63b4fba109..ecda9481f2 100644 --- a/crates/channel/src/channel_store.rs +++ b/crates/channel/src/channel_store.rs @@ -592,7 +592,7 @@ impl ChannelStore { cx: &mut ModelContext, ) -> Task> { let client = self.client.clone(); - let name = name.trim_start_matches("#").to_owned(); + let name = name.trim_start_matches('#').to_owned(); cx.spawn(move |this, mut cx| async move { let response = client .request(proto::CreateChannel { diff --git a/crates/collab/src/db/tests.rs b/crates/collab/src/db/tests.rs index 7790b951b2..b6116c2e77 100644 --- a/crates/collab/src/db/tests.rs +++ b/crates/collab/src/db/tests.rs @@ -168,7 +168,7 @@ async fn new_test_user(db: &Arc, email: &str) -> UserId { email, false, NewUserParams { - github_login: email[0..email.find("@").unwrap()].to_string(), + github_login: email[0..email.find('@').unwrap()].to_string(), github_user_id: GITHUB_USER_ID.fetch_add(1, SeqCst), }, ) diff --git a/crates/collab_ui/src/chat_panel/message_editor.rs b/crates/collab_ui/src/chat_panel/message_editor.rs index 76c355f3f8..5e95cdc450 100644 --- a/crates/collab_ui/src/chat_panel/message_editor.rs +++ b/crates/collab_ui/src/chat_panel/message_editor.rs @@ -310,7 +310,7 @@ impl MessageEditor { for range in ranges { text.clear(); text.extend(buffer.text_for_range(range.clone())); - if let Some(username) = text.strip_prefix("@") { + if let Some(username) = text.strip_prefix('@') { if let Some(user_id) = this.channel_members.get(username) { let start = multi_buffer.anchor_after(range.start); let end = multi_buffer.anchor_after(range.end); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index c93df92042..730e4181cd 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -4845,7 +4845,7 @@ impl Editor { .text_for_range(start_point..end_point) .collect::(); - let mut lines = text.split("\n").collect_vec(); + let mut lines = text.split('\n').collect_vec(); let lines_before = lines.len(); callback(&mut lines); @@ -4913,7 +4913,7 @@ impl Editor { self.manipulate_text(cx, |text| { // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary // https://github.com/rutrum/convert-case/issues/16 - text.split("\n") + text.split('\n') .map(|line| line.to_case(Case::Title)) .join("\n") }) @@ -4935,7 +4935,7 @@ impl Editor { self.manipulate_text(cx, |text| { // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary // https://github.com/rutrum/convert-case/issues/16 - text.split("\n") + text.split('\n') .map(|line| line.to_case(Case::UpperCamel)) .join("\n") }) @@ -9387,7 +9387,7 @@ impl Editor { let highlight = chunk .syntax_highlight_id .and_then(|id| id.name(&style.syntax)); - let mut chunk_lines = chunk.text.split("\n").peekable(); + let mut chunk_lines = chunk.text.split('\n').peekable(); while let Some(text) = chunk_lines.next() { let mut merged_with_last_token = false; if let Some(last_token) = line.back_mut() { diff --git a/crates/editor/src/git/permalink.rs b/crates/editor/src/git/permalink.rs index ec2a925d1b..90704b43d0 100644 --- a/crates/editor/src/git/permalink.rs +++ b/crates/editor/src/git/permalink.rs @@ -105,7 +105,7 @@ fn parse_git_remote_url(url: &str) -> Option { .trim_start_matches("https://github.com/") .trim_end_matches(".git"); - let (owner, repo) = repo_with_owner.split_once("/")?; + let (owner, repo) = repo_with_owner.split_once('/')?; return Some(ParsedGitRemote { provider: GitHostingProvider::Github, @@ -120,7 +120,7 @@ fn parse_git_remote_url(url: &str) -> Option { .trim_start_matches("https://gitlab.com/") .trim_end_matches(".git"); - let (owner, repo) = repo_with_owner.split_once("/")?; + let (owner, repo) = repo_with_owner.split_once('/')?; return Some(ParsedGitRemote { provider: GitHostingProvider::Gitlab, @@ -135,7 +135,7 @@ fn parse_git_remote_url(url: &str) -> Option { .trim_start_matches("https://gitee.com/") .trim_end_matches(".git"); - let (owner, repo) = repo_with_owner.split_once("/")?; + let (owner, repo) = repo_with_owner.split_once('/')?; return Some(ParsedGitRemote { provider: GitHostingProvider::Gitee, @@ -147,9 +147,9 @@ fn parse_git_remote_url(url: &str) -> Option { if url.contains("bitbucket.org") { let (_, repo_with_owner) = url.trim_end_matches(".git").split_once("bitbucket.org")?; let (owner, repo) = repo_with_owner - .trim_start_matches("/") - .trim_start_matches(":") - .split_once("/")?; + .trim_start_matches('/') + .trim_start_matches(':') + .split_once('/')?; return Some(ParsedGitRemote { provider: GitHostingProvider::Bitbucket, @@ -166,7 +166,7 @@ fn parse_git_remote_url(url: &str) -> Option { .trim_start_matches("git@git.sr.ht:~") .trim_start_matches("https://git.sr.ht/~"); - let (owner, repo) = repo_with_owner.split_once("/")?; + let (owner, repo) = repo_with_owner.split_once('/')?; return Some(ParsedGitRemote { provider: GitHostingProvider::Sourcehut, @@ -181,7 +181,7 @@ fn parse_git_remote_url(url: &str) -> Option { .trim_start_matches("https://codeberg.org/") .trim_end_matches(".git"); - let (owner, repo) = repo_with_owner.split_once("/")?; + let (owner, repo) = repo_with_owner.split_once('/')?; return Some(ParsedGitRemote { provider: GitHostingProvider::Codeberg, diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index dcade2781e..a8349a6335 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -701,7 +701,7 @@ impl PickerDelegate for FileFinderDelegate { raw_query: String, cx: &mut ViewContext>, ) -> Task<()> { - let raw_query = raw_query.replace(" ", ""); + let raw_query = raw_query.replace(' ', ""); let raw_query = raw_query.trim(); if raw_query.is_empty() { let project = self.project.read(cx); diff --git a/crates/gpui/src/app/test_context.rs b/crates/gpui/src/app/test_context.rs index 3a99bec94d..e2f4808ca5 100644 --- a/crates/gpui/src/app/test_context.rs +++ b/crates/gpui/src/app/test_context.rs @@ -331,7 +331,7 @@ impl TestAppContext { /// This will also run the background executor until it's parked. pub fn simulate_keystrokes(&mut self, window: AnyWindowHandle, keystrokes: &str) { for keystroke in keystrokes - .split(" ") + .split(' ') .map(Keystroke::parse) .map(Result::unwrap) { diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index 9750cec9bf..bc3eb692fa 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -1110,7 +1110,7 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppC b(); | " - .replace("|", "") // marker to preserve trailing whitespace + .replace('|', "") // marker to preserve trailing whitespace .unindent(), ) .with_language(Arc::new(rust_lang()), cx); @@ -1787,7 +1787,7 @@ fn test_language_scope_at_with_javascript(cx: &mut AppContext) { // In a JSX expression: use the default config. let expression_in_element_config = snapshot - .language_scope_at(text.find("{").unwrap() + 1) + .language_scope_at(text.find('{').unwrap() + 1) .unwrap(); assert_eq!( expression_in_element_config @@ -2321,7 +2321,7 @@ fn test_trailing_whitespace_ranges(mut rng: StdRng) { actual_ranges, expected_ranges, "wrong ranges for text lines:\n{:?}", - text.split("\n").collect::>() + text.split('\n').collect::>() ); } diff --git a/crates/languages/src/ocaml.rs b/crates/languages/src/ocaml.rs index 3b965b9ba0..f355bcb965 100644 --- a/crates/languages/src/ocaml.rs +++ b/crates/languages/src/ocaml.rs @@ -62,7 +62,7 @@ impl LspAdapter for OCamlLspAdapter { language: &Arc, ) -> Option { let name = &completion.label; - let detail = completion.detail.as_ref().map(|s| s.replace("\n", " ")); + let detail = completion.detail.as_ref().map(|s| s.replace('\n', " ")); match completion.kind.zip(detail) { // Error of 'b : ('a, 'b) result @@ -124,7 +124,7 @@ impl LspAdapter for OCamlLspAdapter { // version : string // NOTE: (~|?) are omitted as we don't use them in the fuzzy filtering Some((CompletionItemKind::FIELD, detail)) - if name.starts_with("~") || name.starts_with("?") => + if name.starts_with('~') || name.starts_with('?') => { let label = name.trim_start_matches(&['~', '?']); let text = format!("{} : {}", label, detail); diff --git a/crates/languages/src/terraform.rs b/crates/languages/src/terraform.rs index 6d685ca55c..d201b8aeff 100644 --- a/crates/languages/src/terraform.rs +++ b/crates/languages/src/terraform.rs @@ -129,7 +129,7 @@ impl LspAdapter for TerraformLspAdapter { } fn build_download_url(version: String) -> Result { - let v = version.strip_prefix("v").unwrap_or(&version); + let v = version.strip_prefix('v').unwrap_or(&version); let os = match std::env::consts::OS { "linux" => "linux", "macos" => "darwin", diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index ce021abf45..25db2d6e69 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -377,7 +377,7 @@ impl LanguageServer { let headers = std::str::from_utf8(&buffer)?; let message_len = headers - .split("\n") + .split('\n') .find(|line| line.starts_with(CONTENT_LEN_HEADER)) .and_then(|line| line.strip_prefix(CONTENT_LEN_HEADER)) .ok_or_else(|| anyhow!("invalid LSP message header {headers:?}"))? diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 96117be2a4..17e0e75e15 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -607,7 +607,7 @@ impl ProjectPanel { worktree_id, entry_id: NEW_ENTRY_ID, }); - let new_path = entry.path.join(&filename.trim_start_matches("/")); + let new_path = entry.path.join(&filename.trim_start_matches('/')); if path_already_exists(new_path.as_path()) { return None; } diff --git a/crates/semantic_index/src/semantic_index_tests.rs b/crates/semantic_index/src/semantic_index_tests.rs index 23ed45ff1d..f660aa8fb3 100644 --- a/crates/semantic_index/src/semantic_index_tests.rs +++ b/crates/semantic_index/src/semantic_index_tests.rs @@ -414,7 +414,7 @@ async fn test_code_context_retrieval_json() { } }"# .unindent(), - text.find("{").unwrap(), + text.find('{').unwrap(), )], ); @@ -443,7 +443,7 @@ async fn test_code_context_retrieval_json() { "age": 42 }]"# .unindent(), - text.find("[").unwrap(), + text.find('[').unwrap(), )], ); } diff --git a/crates/vim/src/command.rs b/crates/vim/src/command.rs index af571dd632..0b138129c9 100644 --- a/crates/vim/src/command.rs +++ b/crates/vim/src/command.rs @@ -41,7 +41,7 @@ pub fn command_interceptor(mut query: &str, cx: &AppContext) -> Option Option ("0", StartOfDocument.boxed_clone()), _ => { - if query.starts_with("/") || query.starts_with("?") { + if query.starts_with('/') || query.starts_with('?') { ( query, FindCommand { query: query[1..].to_string(), - backwards: query.starts_with("?"), + backwards: query.starts_with('?'), } .boxed_clone(), ) - } else if query.starts_with("%") { + } else if query.starts_with('%') { ( query, ReplaceCommand { diff --git a/crates/vim/src/normal/paste.rs b/crates/vim/src/normal/paste.rs index 9c15cc0cf4..b56bb33b5c 100644 --- a/crates/vim/src/normal/paste.rs +++ b/crates/vim/src/normal/paste.rs @@ -135,8 +135,8 @@ fn paste(_: &mut Workspace, action: &Paste, cx: &mut ViewContext) { } else { (clipboard_text.to_string(), first_selection_indent_column) }; - let line_mode = to_insert.ends_with("\n"); - let is_multiline = to_insert.contains("\n"); + let line_mode = to_insert.ends_with('\n'); + let is_multiline = to_insert.contains('\n'); if line_mode && !before { if selection.is_empty() { @@ -480,7 +480,7 @@ mod test { the_ ˇfox jumps over _dog"} - .replace("_", " "), // Hack for trailing whitespace + .replace('_', " "), // Hack for trailing whitespace ) .await; cx.assert_shared_clipboard("lazy").await; diff --git a/crates/vim/src/test/neovim_backed_test_context.rs b/crates/vim/src/test/neovim_backed_test_context.rs index 0a01a62742..f94770307b 100644 --- a/crates/vim/src/test/neovim_backed_test_context.rs +++ b/crates/vim/src/test/neovim_backed_test_context.rs @@ -72,7 +72,7 @@ impl NeovimBackedTestContext { let test_name = thread .name() .expect("thread is not named") - .split(":") + .split(':') .last() .unwrap() .to_string(); @@ -122,7 +122,7 @@ impl NeovimBackedTestContext { } pub async fn set_shared_state(&mut self, marked_text: &str) { - let mode = if marked_text.contains("»") { + let mode = if marked_text.contains('»') { Mode::Visual } else { Mode::Normal @@ -188,7 +188,7 @@ impl NeovimBackedTestContext { pub async fn assert_shared_state(&mut self, marked_text: &str) { self.is_dirty = false; - let marked_text = marked_text.replace("•", " "); + let marked_text = marked_text.replace('•', " "); let neovim = self.neovim_state().await; let neovim_mode = self.neovim_mode().await; let editor = self.editor_state(); diff --git a/crates/vim/src/test/neovim_connection.rs b/crates/vim/src/test/neovim_connection.rs index 5ea82e3661..63951f73d0 100644 --- a/crates/vim/src/test/neovim_connection.rs +++ b/crates/vim/src/test/neovim_connection.rs @@ -392,7 +392,7 @@ impl NeovimConnection { // the content of the selection via the "a register to get the shape correctly. self.nvim.input("\"aygv").await.unwrap(); let content = self.nvim.command_output("echo getreg('a')").await.unwrap(); - let lines = content.split("\n").collect::>(); + let lines = content.split('\n').collect::>(); let top = cmp::min(selection_row, cursor_row); let left = cmp::min(selection_col, cursor_col); for row in top..=cmp::max(selection_row, cursor_row) { diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 65315aec02..ec03afe05c 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -2586,8 +2586,8 @@ mod tests { let mut index = 0; let items = labels.map(|mut label| { - if label.ends_with("*") { - label = label.trim_end_matches("*"); + if label.ends_with('*') { + label = label.trim_end_matches('*'); active_item_index = index; } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 0cb3ff9912..123096b228 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1260,7 +1260,7 @@ impl Workspace { fn send_keystrokes(&mut self, action: &SendKeystrokes, cx: &mut ViewContext) { let mut keystrokes: Vec = action .0 - .split(" ") + .split(' ') .flat_map(|k| Keystroke::parse(k).log_err()) .collect(); keystrokes.reverse(); diff --git a/crates/zed/src/open_listener.rs b/crates/zed/src/open_listener.rs index d6fec52df8..61d0839ae6 100644 --- a/crates/zed/src/open_listener.rs +++ b/crates/zed/src/open_listener.rs @@ -95,10 +95,10 @@ impl OpenListener { } fn handle_zed_url_scheme(&self, request_path: &str) -> Option { - let mut parts = request_path.split("/"); + let mut parts = request_path.split('/'); if parts.next() == Some("channel") { if let Some(slug) = parts.next() { - if let Some(id_str) = slug.split("-").last() { + if let Some(id_str) = slug.split('-').last() { if let Ok(channel_id) = id_str.parse::() { let Some(next) = parts.next() else { return Some(OpenRequest::JoinChannel { channel_id }); diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 037071ff7e..253250998d 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -124,7 +124,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::redundant_locals", "clippy::reversed_empty_ranges", "clippy::search_is_some", - "clippy::single_char_pattern", "clippy::single_range_in_vec_init", "clippy::suspicious_to_owned", "clippy::to_string_in_format_args", From 973591296582e89566c8a99d77ce137d8a6810a3 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 17:37:48 -0500 Subject: [PATCH 011/121] Enable `clippy::clone_on_copy` (#8728) This PR enables the [`clippy::clone_on_copy`](https://rust-lang.github.io/rust-clippy/master/index.html#/clone_on_copy) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/ai/src/prompts/repository_context.rs | 2 +- crates/assistant/src/assistant_panel.rs | 6 +-- crates/call/src/call.rs | 2 +- crates/collab/src/tests/following_tests.rs | 14 +++--- .../random_project_collaboration_tests.rs | 2 +- crates/collab_ui/src/channel_view.rs | 6 +-- crates/collab_ui/src/collab_titlebar_item.rs | 2 +- .../incoming_call_notification.rs | 2 +- .../project_shared_notification.rs | 2 +- crates/diagnostics/src/diagnostics.rs | 18 +++---- crates/editor/src/display_map.rs | 2 +- crates/editor/src/display_map/fold_map.rs | 20 ++++---- crates/editor/src/display_map/inlay_map.rs | 4 +- crates/editor/src/editor.rs | 50 ++++++++----------- crates/editor/src/hover_links.rs | 21 +++----- crates/editor/src/hover_popover.rs | 6 +-- crates/editor/src/inlay_hint_cache.rs | 2 +- crates/editor/src/items.rs | 4 +- crates/editor/src/movement.rs | 2 +- crates/editor/src/selections_collection.rs | 10 ++-- crates/gpui/src/platform/mac/platform.rs | 5 +- crates/language/src/syntax_map.rs | 2 +- crates/lsp/src/lsp.rs | 2 +- .../markdown_preview/src/markdown_parser.rs | 8 +-- crates/multi_buffer/src/anchor.rs | 8 +-- crates/multi_buffer/src/multi_buffer.rs | 36 ++++++------- crates/project/src/lsp_command.rs | 4 +- crates/project_core/src/worktree.rs | 2 +- crates/search/src/project_search.rs | 13 +++-- crates/semantic_index/src/db.rs | 3 +- crates/semantic_index/src/semantic_index.rs | 6 +-- crates/storybook/src/storybook.rs | 2 +- crates/terminal/src/terminal.rs | 2 +- crates/terminal_view/src/terminal_element.rs | 11 ++-- crates/ui/src/components/right_click_menu.rs | 4 +- crates/vim/src/normal/repeat.rs | 4 +- crates/vim/src/test/vim_test_context.rs | 2 +- crates/vim/src/visual.rs | 2 +- crates/workspace/src/persistence.rs | 2 +- crates/workspace/src/workspace.rs | 6 +-- crates/zed/src/zed.rs | 2 +- tooling/xtask/src/main.rs | 1 - 42 files changed, 144 insertions(+), 160 deletions(-) diff --git a/crates/ai/src/prompts/repository_context.rs b/crates/ai/src/prompts/repository_context.rs index f74b06a5ce..b31a3f63c2 100644 --- a/crates/ai/src/prompts/repository_context.rs +++ b/crates/ai/src/prompts/repository_context.rs @@ -65,7 +65,7 @@ impl PromptTemplate for RepositoryContext { let template = "You are working inside a large repository, here are a few code snippets that may be useful."; let mut prompt = String::new(); - let mut remaining_tokens = max_token_length.clone(); + let mut remaining_tokens = max_token_length; let separator_token_length = args.model.count_tokens("\n")?; for snippet in &args.snippets { let mut snippet_prompt = template.to_string(); diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index d5bc08b7bf..178571369b 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -1483,7 +1483,7 @@ impl Conversation { max_token_count: tiktoken_rs::model::get_context_size(&model.full_name()), pending_token_count: Task::ready(None), api_url: Some(api_url), - model: model.clone(), + model, _subscriptions: vec![cx.subscribe(&buffer, Self::handle_buffer_event)], pending_save: Task::ready(Ok(())), path: None, @@ -1527,7 +1527,7 @@ impl Conversation { .as_ref() .map(|summary| summary.text.clone()) .unwrap_or_default(), - model: self.model.clone(), + model: self.model, api_url: self.api_url.clone(), } } @@ -1652,7 +1652,7 @@ impl Conversation { }) }) .collect::>(); - let model = self.model.clone(); + let model = self.model; self.pending_token_count = cx.spawn(|this, mut cx| { async move { cx.background_executor() diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 8c9ff71ba4..f0a0a22fb3 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -302,7 +302,7 @@ impl ActiveCall { return Task::ready(Ok(())); } - let room_id = call.room_id.clone(); + let room_id = call.room_id; let client = self.client.clone(); let user_store = self.user_store.clone(); let join = self diff --git a/crates/collab/src/tests/following_tests.rs b/crates/collab/src/tests/following_tests.rs index 8561346e97..ff53c339e6 100644 --- a/crates/collab/src/tests/following_tests.rs +++ b/crates/collab/src/tests/following_tests.rs @@ -1437,14 +1437,13 @@ async fn test_following_across_workspaces(cx_a: &mut TestAppContext, cx_b: &mut }); executor.run_until_parked(); - let window_b_project_a = cx_b + let window_b_project_a = *cx_b .windows() .iter() .max_by_key(|window| window.window_id()) - .unwrap() - .clone(); + .unwrap(); - let mut cx_b2 = VisualTestContext::from_window(window_b_project_a.clone(), cx_b); + let mut cx_b2 = VisualTestContext::from_window(window_b_project_a, cx_b); let workspace_b_project_a = window_b_project_a .downcast::() @@ -1548,13 +1547,12 @@ async fn test_following_across_workspaces(cx_a: &mut TestAppContext, cx_b: &mut executor.run_until_parked(); assert_eq!(visible_push_notifications(cx_a).len(), 0); - let window_a_project_b = cx_a + let window_a_project_b = *cx_a .windows() .iter() .max_by_key(|window| window.window_id()) - .unwrap() - .clone(); - let cx_a2 = &mut VisualTestContext::from_window(window_a_project_b.clone(), cx_a); + .unwrap(); + let cx_a2 = &mut VisualTestContext::from_window(window_a_project_b, cx_a); let workspace_a_project_b = window_a_project_b .downcast::() .unwrap() diff --git a/crates/collab/src/tests/random_project_collaboration_tests.rs b/crates/collab/src/tests/random_project_collaboration_tests.rs index 9957b785f2..3cb270e6ef 100644 --- a/crates/collab/src/tests/random_project_collaboration_tests.rs +++ b/crates/collab/src/tests/random_project_collaboration_tests.rs @@ -996,7 +996,7 @@ impl RandomizedTest for ProjectCollaborationTest { let statuses = statuses .iter() - .map(|(path, val)| (path.as_path(), val.clone())) + .map(|(path, val)| (path.as_path(), *val)) .collect::>(); if client.fs().metadata(&dot_git_dir).await?.is_none() { diff --git a/crates/collab_ui/src/channel_view.rs b/crates/collab_ui/src/channel_view.rs index f591c67f7c..a46499214d 100644 --- a/crates/collab_ui/src/channel_view.rs +++ b/crates/collab_ui/src/channel_view.rs @@ -171,10 +171,8 @@ impl ChannelView { let this = this.clone(); Some(ui::ContextMenu::build(cx, move |menu, _| { menu.entry("Copy link to section", None, move |cx| { - this.update(cx, |this, cx| { - this.copy_link_for_position(position.clone(), cx) - }) - .ok(); + this.update(cx, |this, cx| this.copy_link_for_position(position, cx)) + .ok(); }) })) }); diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 854809994b..14d0439237 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -403,7 +403,7 @@ impl CollabTitlebarItem { ) }) .on_click({ - let host_peer_id = host.peer_id.clone(); + let host_peer_id = host.peer_id; cx.listener(move |this, _, cx| { this.workspace .update(cx, |workspace, cx| { diff --git a/crates/collab_ui/src/notifications/incoming_call_notification.rs b/crates/collab_ui/src/notifications/incoming_call_notification.rs index f66194c52a..b6ba2f9507 100644 --- a/crates/collab_ui/src/notifications/incoming_call_notification.rs +++ b/crates/collab_ui/src/notifications/incoming_call_notification.rs @@ -119,7 +119,7 @@ impl Render for IncomingCallNotification { let theme_settings = ThemeSettings::get_global(cx); ( theme_settings.ui_font.family.clone(), - theme_settings.ui_font_size.clone(), + theme_settings.ui_font_size, ) }; diff --git a/crates/collab_ui/src/notifications/project_shared_notification.rs b/crates/collab_ui/src/notifications/project_shared_notification.rs index b8ceefcd76..08141d0c2e 100644 --- a/crates/collab_ui/src/notifications/project_shared_notification.rs +++ b/crates/collab_ui/src/notifications/project_shared_notification.rs @@ -123,7 +123,7 @@ impl Render for ProjectSharedNotification { let theme_settings = ThemeSettings::get_global(cx); ( theme_settings.ui_font.family.clone(), - theme_settings.ui_font_size.clone(), + theme_settings.ui_font_size, ) }; diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index d50daebb8a..793d5aa269 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -347,7 +347,7 @@ impl ProjectDiagnosticsEditor { .diagnostic_groups .last() .unwrap(); - prev_path_last_group.excerpts.last().unwrap().clone() + *prev_path_last_group.excerpts.last().unwrap() } else { ExcerptId::min() }; @@ -451,10 +451,10 @@ impl ProjectDiagnosticsEditor { .pop() .unwrap(); - prev_excerpt_id = excerpt_id.clone(); - first_excerpt_id.get_or_insert_with(|| prev_excerpt_id.clone()); - group_state.excerpts.push(excerpt_id.clone()); - let header_position = (excerpt_id.clone(), language::Anchor::MIN); + prev_excerpt_id = excerpt_id; + first_excerpt_id.get_or_insert_with(|| prev_excerpt_id); + group_state.excerpts.push(excerpt_id); + let header_position = (excerpt_id, language::Anchor::MIN); if is_first_excerpt_for_group { is_first_excerpt_for_group = false; @@ -483,7 +483,7 @@ impl ProjectDiagnosticsEditor { if !diagnostic.message.is_empty() { group_state.block_count += 1; blocks_to_add.push(BlockProperties { - position: (excerpt_id.clone(), entry.range.start), + position: (excerpt_id, entry.range.start), height: diagnostic.message.matches('\n').count() as u8 + 1, style: BlockStyle::Fixed, render: diagnostic_block_renderer(diagnostic, true), @@ -506,8 +506,8 @@ impl ProjectDiagnosticsEditor { group_ixs_to_remove.push(group_ix); blocks_to_remove.extend(group_state.blocks.iter().copied()); } else if let Some((_, group)) = to_keep { - prev_excerpt_id = group.excerpts.last().unwrap().clone(); - first_excerpt_id.get_or_insert_with(|| prev_excerpt_id.clone()); + prev_excerpt_id = *group.excerpts.last().unwrap(); + first_excerpt_id.get_or_insert_with(|| prev_excerpt_id); } } @@ -591,7 +591,7 @@ impl ProjectDiagnosticsEditor { if let Some(group) = groups.get(group_ix) { let offset = excerpts_snapshot .anchor_in_excerpt( - group.excerpts[group.primary_excerpt_ix].clone(), + group.excerpts[group.primary_excerpt_ix], group.primary_diagnostic.range.start, ) .to_offset(&excerpts_snapshot); diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 344abeda65..f3c65f7fb8 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -1291,7 +1291,7 @@ pub mod tests { let mut cx = EditorTestContext::new(cx).await; let editor = cx.editor.clone(); - let window = cx.window.clone(); + let window = cx.window; _ = cx.update_window(window, |_, cx| { let text_layout_details = diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index 4c38ff1416..29da2b41bb 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -884,10 +884,10 @@ impl sum_tree::Item for Fold { fn summary(&self) -> Self::Summary { FoldSummary { - start: self.range.start.clone(), - end: self.range.end.clone(), - min_start: self.range.start.clone(), - max_end: self.range.end.clone(), + start: self.range.start, + end: self.range.end, + min_start: self.range.start, + max_end: self.range.end, count: 1, } } @@ -919,10 +919,10 @@ impl sum_tree::Summary for FoldSummary { fn add_summary(&mut self, other: &Self, buffer: &Self::Context) { if other.min_start.cmp(&self.min_start, buffer) == Ordering::Less { - self.min_start = other.min_start.clone(); + self.min_start = other.min_start; } if other.max_end.cmp(&self.max_end, buffer) == Ordering::Greater { - self.max_end = other.max_end.clone(); + self.max_end = other.max_end; } #[cfg(debug_assertions)] @@ -934,16 +934,16 @@ impl sum_tree::Summary for FoldSummary { } } - self.start = other.start.clone(); - self.end = other.end.clone(); + self.start = other.start; + self.end = other.end; self.count += other.count; } } impl<'a> sum_tree::Dimension<'a, FoldSummary> for FoldRange { fn add_summary(&mut self, summary: &'a FoldSummary, _: &MultiBufferSnapshot) { - self.0.start = summary.start.clone(); - self.0.end = summary.end.clone(); + self.0.start = summary.start; + self.0.end = summary.end; } } diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index 5dcae58f48..229b05e955 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -283,7 +283,7 @@ impl<'a> Iterator for InlayChunks<'a> { self.output_offset.0 += prefix.len(); let mut prefix = Chunk { text: prefix, - ..chunk.clone() + ..*chunk }; if !self.active_highlights.is_empty() { let mut highlight_style = HighlightStyle::default(); @@ -322,7 +322,7 @@ impl<'a> Iterator for InlayChunks<'a> { next_inlay_highlight_endpoint = range.end - offset_in_inlay.0; highlight_style .get_or_insert_with(|| Default::default()) - .highlight(style.clone()); + .highlight(*style); } } else { next_inlay_highlight_endpoint = usize::MAX; diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 730e4181cd..0fb83f7908 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1873,7 +1873,7 @@ impl Editor { let new_cursor_position = self.selections.newest_anchor().head(); self.push_to_nav_history( - old_cursor_position.clone(), + *old_cursor_position, Some(new_cursor_position.to_point(buffer)), cx, ); @@ -1892,8 +1892,7 @@ impl Editor { if let Some(completion_menu) = completion_menu { let cursor_position = new_cursor_position.to_offset(buffer); - let (word_range, kind) = - buffer.surrounding_word(completion_menu.initial_position.clone()); + let (word_range, kind) = buffer.surrounding_word(completion_menu.initial_position); if kind == Some(CharKind::Word) && word_range.to_inclusive().contains(&cursor_position) { @@ -2114,7 +2113,7 @@ impl Editor { match click_count { 1 => { start = buffer.anchor_before(position.to_point(&display_map)); - end = start.clone(); + end = start; mode = SelectMode::Character; auto_scroll = true; } @@ -2122,7 +2121,7 @@ impl Editor { let range = movement::surrounding_word(&display_map, position); start = buffer.anchor_before(range.start.to_point(&display_map)); end = buffer.anchor_before(range.end.to_point(&display_map)); - mode = SelectMode::Word(start.clone()..end.clone()); + mode = SelectMode::Word(start..end); auto_scroll = true; } 3 => { @@ -2136,7 +2135,7 @@ impl Editor { ); start = buffer.anchor_before(line_start); end = buffer.anchor_before(next_line_start); - mode = SelectMode::Line(start.clone()..end.clone()); + mode = SelectMode::Line(start..end); auto_scroll = true; } _ => { @@ -3208,7 +3207,7 @@ impl Editor { let (buffer, buffer_position) = self .buffer .read(cx) - .text_anchor_for_position(position.clone(), cx)?; + .text_anchor_for_position(position, cx)?; // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances, // hence we do LSP request & edit on host side only — add formats to host's history. @@ -3252,17 +3251,14 @@ impl Editor { }; let position = self.selections.newest_anchor().head(); - let (buffer, buffer_position) = if let Some(output) = self - .buffer - .read(cx) - .text_anchor_for_position(position.clone(), cx) - { - output - } else { - return; - }; + let (buffer, buffer_position) = + if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) { + output + } else { + return; + }; - let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone()); + let query = Self::completion_query(&self.buffer.read(cx).read(cx), position); let completions = provider.completions(&buffer, buffer_position, cx); let id = post_inc(&mut self.next_completion_id); @@ -3700,7 +3696,7 @@ impl Editor { let newest_selection = self.selections.newest_anchor().clone(); let cursor_position = newest_selection.head(); let (cursor_buffer, cursor_buffer_position) = - buffer.text_anchor_for_position(cursor_position.clone(), cx)?; + buffer.text_anchor_for_position(cursor_position, cx)?; let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?; if cursor_buffer != tail_buffer { return None; @@ -3758,7 +3754,7 @@ impl Editor { let range = Anchor { buffer_id, - excerpt_id: excerpt_id.clone(), + excerpt_id: excerpt_id, text_anchor: start, }..Anchor { buffer_id, @@ -4741,7 +4737,7 @@ impl Editor { row_range.end - 1, snapshot.line_len(row_range.end - 1), )); - cursor_positions.push(anchor.clone()..anchor); + cursor_positions.push(anchor..anchor); } self.transact(cx, |this, cx| { @@ -7447,10 +7443,8 @@ impl Editor { pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext) { let position = self.selections.newest_anchor().head(); - let Some((buffer, buffer_position)) = self - .buffer - .read(cx) - .text_anchor_for_position(position.clone(), cx) + let Some((buffer, buffer_position)) = + self.buffer.read(cx).text_anchor_for_position(position, cx) else { return; }; @@ -7912,7 +7906,7 @@ impl Editor { let block_id = this.insert_blocks( [BlockProperties { style: BlockStyle::Flex, - position: range.start.clone(), + position: range.start, height: 1, render: Arc::new({ let rename_editor = rename_editor.clone(); @@ -7976,11 +7970,11 @@ impl Editor { let (start_buffer, start) = self .buffer .read(cx) - .text_anchor_for_position(rename.range.start.clone(), cx)?; + .text_anchor_for_position(rename.range.start, cx)?; let (end_buffer, end) = self .buffer .read(cx) - .text_anchor_for_position(rename.range.end.clone(), cx)?; + .text_anchor_for_position(rename.range.end, cx)?; if start_buffer != end_buffer { return None; } @@ -8817,7 +8811,7 @@ impl Editor { Ok(i) | Err(i) => i, }; - let right_position = right_position.clone(); + let right_position = right_position; ranges[start_ix..] .iter() .take_while(move |range| range.start.cmp(&right_position, buffer).is_le()) diff --git a/crates/editor/src/hover_links.rs b/crates/editor/src/hover_links.rs index 0c5e36dc68..e1afaa6591 100644 --- a/crates/editor/src/hover_links.rs +++ b/crates/editor/src/hover_links.rs @@ -381,7 +381,7 @@ pub fn show_link_definition( let Some((buffer, buffer_position)) = editor .buffer .read(cx) - .text_anchor_for_position(trigger_anchor.clone(), cx) + .text_anchor_for_position(*trigger_anchor, cx) else { return; }; @@ -389,7 +389,7 @@ pub fn show_link_definition( let Some((excerpt_id, _, _)) = editor .buffer() .read(cx) - .excerpt_containing(trigger_anchor.clone(), cx) + .excerpt_containing(*trigger_anchor, cx) else { return; }; @@ -424,9 +424,8 @@ pub fn show_link_definition( TriggerPoint::Text(_) => { if let Some((url_range, url)) = find_url(&buffer, buffer_position, cx.clone()) { this.update(&mut cx, |_, _| { - let start = - snapshot.anchor_in_excerpt(excerpt_id.clone(), url_range.start); - let end = snapshot.anchor_in_excerpt(excerpt_id.clone(), url_range.end); + let start = snapshot.anchor_in_excerpt(excerpt_id, url_range.start); + let end = snapshot.anchor_in_excerpt(excerpt_id, url_range.end); ( Some(RangeInEditor::Text(start..end)), vec![HoverLink::Url(url)], @@ -451,14 +450,10 @@ pub fn show_link_definition( ( definition_result.iter().find_map(|link| { link.origin.as_ref().map(|origin| { - let start = snapshot.anchor_in_excerpt( - excerpt_id.clone(), - origin.range.start, - ); - let end = snapshot.anchor_in_excerpt( - excerpt_id.clone(), - origin.range.end, - ); + let start = snapshot + .anchor_in_excerpt(excerpt_id, origin.range.start); + let end = snapshot + .anchor_in_excerpt(excerpt_id, origin.range.end); RangeInEditor::Text(start..end) }) }), diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 9d0b70f5a1..b2a168fb83 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -297,10 +297,10 @@ fn show_hover( let range = if let Some(range) = hover_result.range { let start = snapshot .buffer_snapshot - .anchor_in_excerpt(excerpt_id.clone(), range.start); + .anchor_in_excerpt(excerpt_id, range.start); let end = snapshot .buffer_snapshot - .anchor_in_excerpt(excerpt_id.clone(), range.end); + .anchor_in_excerpt(excerpt_id, range.end); start..end } else { @@ -597,7 +597,7 @@ impl DiagnosticPopover { .as_ref() .unwrap_or(&self.local_diagnostic); - (entry.diagnostic.group_id, entry.range.start.clone()) + (entry.diagnostic.group_id, entry.range.start) } } diff --git a/crates/editor/src/inlay_hint_cache.rs b/crates/editor/src/inlay_hint_cache.rs index 0db43d24de..a796c39a25 100644 --- a/crates/editor/src/inlay_hint_cache.rs +++ b/crates/editor/src/inlay_hint_cache.rs @@ -2197,7 +2197,7 @@ pub mod tests { "another change #3", ] { expected_changes.push(async_later_change); - let task_editor = editor.clone(); + let task_editor = editor; edits.push(cx.spawn(|mut cx| async move { task_editor .update(&mut cx, |editor, cx| { diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index a6ff8dd043..64f1d3770b 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -1147,8 +1147,8 @@ impl SearchableItem for Editor { let end = excerpt .buffer .anchor_before(excerpt_range.start + range.end); - buffer.anchor_in_excerpt(excerpt.id.clone(), start) - ..buffer.anchor_in_excerpt(excerpt.id.clone(), end) + buffer.anchor_in_excerpt(excerpt.id, start) + ..buffer.anchor_in_excerpt(excerpt.id, end) }), ); } diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index 65a6854ba9..5e0ca2c6eb 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -863,7 +863,7 @@ mod tests { let mut cx = EditorTestContext::new(cx).await; let editor = cx.editor.clone(); - let window = cx.window.clone(); + let window = cx.window; _ = cx.update_window(window, |_, cx| { let text_layout_details = editor.update(cx, |editor, cx| editor.text_layout_details(cx)); diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index b0595ee778..d4d40b8563 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -478,7 +478,7 @@ impl<'a> MutableSelectionsCollection<'a> { if !oldest.start.cmp(&oldest.end, &self.buffer()).is_eq() { let head = oldest.head(); - oldest.start = head.clone(); + oldest.start = head; oldest.end = head; self.collection.disjoint = Arc::from([oldest]); self.selections_changed = true; @@ -794,8 +794,8 @@ impl<'a> MutableSelectionsCollection<'a> { let adjusted_disjoint: Vec<_> = anchors_with_status .chunks(2) .map(|selection_anchors| { - let (anchor_ix, start, kept_start) = selection_anchors[0].clone(); - let (_, end, kept_end) = selection_anchors[1].clone(); + let (anchor_ix, start, kept_start) = selection_anchors[0]; + let (_, end, kept_end) = selection_anchors[1]; let selection = &self.disjoint[anchor_ix / 2]; let kept_head = if selection.reversed { kept_start @@ -826,8 +826,8 @@ impl<'a> MutableSelectionsCollection<'a> { let buffer = self.buffer(); let anchors = buffer.refresh_anchors([&pending.selection.start, &pending.selection.end]); - let (_, start, kept_start) = anchors[0].clone(); - let (_, end, kept_end) = anchors[1].clone(); + let (_, start, kept_start) = anchors[0]; + let (_, end, kept_end) = anchors[1]; let kept_head = if pending.selection.reversed { kept_start } else { diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 8b35114b7f..7603973505 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -496,11 +496,14 @@ impl Platform for MacPlatform { handle: AnyWindowHandle, options: WindowOptions, ) -> Box { + // Clippy thinks that this evaluates to `()`, for some reason. + #[allow(clippy::clone_on_copy)] + let renderer_context = self.0.lock().renderer_context.clone(); Box::new(MacWindow::open( handle, options, self.foreground_executor(), - self.0.lock().renderer_context.clone(), + renderer_context, )) } diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index c13d7f4894..7d9258ab01 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -1412,7 +1412,7 @@ fn insert_newlines_between_ranges( continue; } - let range_b = ranges[ix].clone(); + let range_b = ranges[ix]; let range_a = &mut ranges[ix - 1]; if range_a.end_point.column == 0 { continue; diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 25db2d6e69..b7c51539eb 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -229,7 +229,7 @@ impl LanguageServer { let stdout = server.stdout.take().unwrap(); let stderr = server.stderr.take().unwrap(); let mut server = Self::new_internal( - server_id.clone(), + server_id, stdin, stdout, Some(stderr), diff --git a/crates/markdown_preview/src/markdown_parser.rs b/crates/markdown_preview/src/markdown_parser.rs index b4e9d9829c..87e3266a22 100644 --- a/crates/markdown_preview/src/markdown_parser.rs +++ b/crates/markdown_preview/src/markdown_parser.rs @@ -105,7 +105,7 @@ impl<'a> MarkdownParser<'a> { classes: _, attrs: _, } => { - let level = level.clone(); + let level = *level; self.cursor += 1; let heading = self.parse_heading(level); Some(ParsedMarkdownElement::Heading(heading)) @@ -117,7 +117,7 @@ impl<'a> MarkdownParser<'a> { Some(ParsedMarkdownElement::Table(table)) } Tag::List(order) => { - let order = order.clone(); + let order = *order; self.cursor += 1; let list = self.parse_list(1, order); Some(ParsedMarkdownElement::List(list)) @@ -421,7 +421,7 @@ impl<'a> MarkdownParser<'a> { let (current, _source_range) = self.current().unwrap(); match current { Event::Start(Tag::List(order)) => { - let order = order.clone(); + let order = *order; self.cursor += 1; let inner_list = self.parse_list(depth + 1, order); @@ -467,7 +467,7 @@ impl<'a> MarkdownParser<'a> { let item_type = if let Some(checked) = task_item { ParsedMarkdownListItemType::Task(checked) - } else if let Some(order) = order.clone() { + } else if let Some(order) = order { ParsedMarkdownListItemType::Ordered(order) } else { ParsedMarkdownListItemType::Unordered diff --git a/crates/multi_buffer/src/anchor.rs b/crates/multi_buffer/src/anchor.rs index 877f74c21b..ee0862b957 100644 --- a/crates/multi_buffer/src/anchor.rs +++ b/crates/multi_buffer/src/anchor.rs @@ -55,12 +55,12 @@ impl Anchor { if let Some(excerpt) = snapshot.excerpt(self.excerpt_id) { return Self { buffer_id: self.buffer_id, - excerpt_id: self.excerpt_id.clone(), + excerpt_id: self.excerpt_id, text_anchor: self.text_anchor.bias_left(&excerpt.buffer), }; } } - self.clone() + *self } pub fn bias_right(&self, snapshot: &MultiBufferSnapshot) -> Anchor { @@ -68,12 +68,12 @@ impl Anchor { if let Some(excerpt) = snapshot.excerpt(self.excerpt_id) { return Self { buffer_id: self.buffer_id, - excerpt_id: self.excerpt_id.clone(), + excerpt_id: self.excerpt_id, text_anchor: self.text_anchor.bias_right(&excerpt.buffer), }; } } - self.clone() + *self } pub fn summary(&self, snapshot: &MultiBufferSnapshot) -> D diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index eedced9c2a..8a444c666c 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -950,12 +950,12 @@ impl MultiBuffer { for range in ranges.by_ref().take(range_count) { let start = Anchor { buffer_id: Some(buffer_id), - excerpt_id: excerpt_id.clone(), + excerpt_id: excerpt_id, text_anchor: range.start, }; let end = Anchor { buffer_id: Some(buffer_id), - excerpt_id: excerpt_id.clone(), + excerpt_id: excerpt_id, text_anchor: range.end, }; if tx.send(start..end).await.is_err() { @@ -1005,12 +1005,12 @@ impl MultiBuffer { anchor_ranges.extend(ranges.by_ref().take(range_count).map(|range| { let start = Anchor { buffer_id: Some(buffer_id), - excerpt_id: excerpt_id.clone(), + excerpt_id: excerpt_id, text_anchor: buffer_snapshot.anchor_after(range.start), }; let end = Anchor { buffer_id: Some(buffer_id), - excerpt_id: excerpt_id.clone(), + excerpt_id: excerpt_id, text_anchor: buffer_snapshot.anchor_after(range.end), }; start..end @@ -1206,7 +1206,7 @@ impl MultiBuffer { cursor.seek_forward(&Some(locator), Bias::Left, &()); if let Some(excerpt) = cursor.item() { if excerpt.locator == *locator { - excerpts.push((excerpt.id.clone(), excerpt.range.clone())); + excerpts.push((excerpt.id, excerpt.range.clone())); } } } @@ -1238,7 +1238,7 @@ impl MultiBuffer { .or_else(|| snapshot.excerpts.last()) .map(|excerpt| { ( - excerpt.id.clone(), + excerpt.id, self.buffers .borrow() .get(&excerpt.buffer_id) @@ -2689,7 +2689,7 @@ impl MultiBufferSnapshot { if !kept_position { for excerpt in [next_excerpt, prev_excerpt].iter().filter_map(|e| *e) { if excerpt.contains(&anchor) { - anchor.excerpt_id = excerpt.id.clone(); + anchor.excerpt_id = excerpt.id; kept_position = true; break; } @@ -2713,7 +2713,7 @@ impl MultiBufferSnapshot { } Anchor { buffer_id: Some(excerpt.buffer_id), - excerpt_id: excerpt.id.clone(), + excerpt_id: excerpt.id, text_anchor, } } else if let Some(excerpt) = prev_excerpt { @@ -2730,7 +2730,7 @@ impl MultiBufferSnapshot { } Anchor { buffer_id: Some(excerpt.buffer_id), - excerpt_id: excerpt.id.clone(), + excerpt_id: excerpt.id, text_anchor, } } else if anchor.text_anchor.bias == Bias::Left { @@ -2760,7 +2760,7 @@ impl MultiBufferSnapshot { if let Some((excerpt_id, buffer_id, buffer)) = self.as_singleton() { return Anchor { buffer_id: Some(buffer_id), - excerpt_id: excerpt_id.clone(), + excerpt_id: *excerpt_id, text_anchor: buffer.anchor_at(offset, bias), }; } @@ -2782,7 +2782,7 @@ impl MultiBufferSnapshot { excerpt.clip_anchor(excerpt.buffer.anchor_at(buffer_start + overshoot, bias)); Anchor { buffer_id: Some(excerpt.buffer_id), - excerpt_id: excerpt.id.clone(), + excerpt_id: excerpt.id, text_anchor, } } else if offset == 0 && bias == Bias::Left { @@ -2895,7 +2895,7 @@ impl MultiBufferSnapshot { let excerpt = cursor.item()?; let starts_new_buffer = Some(excerpt.buffer_id) != prev_buffer_id; let boundary = ExcerptBoundary { - id: excerpt.id.clone(), + id: excerpt.id, row: cursor.start().1.row, buffer: excerpt.buffer.clone(), range: excerpt.range.clone(), @@ -3280,8 +3280,8 @@ impl MultiBufferSnapshot { .into_iter() .map(|item| OutlineItem { depth: item.depth, - range: self.anchor_in_excerpt(excerpt_id.clone(), item.range.start) - ..self.anchor_in_excerpt(excerpt_id.clone(), item.range.end), + range: self.anchor_in_excerpt(*excerpt_id, item.range.start) + ..self.anchor_in_excerpt(*excerpt_id, item.range.end), text: item.text, highlight_ranges: item.highlight_ranges, name_ranges: item.name_ranges, @@ -3402,19 +3402,19 @@ impl MultiBufferSnapshot { selections.map(move |selection| { let mut start = Anchor { buffer_id: Some(excerpt.buffer_id), - excerpt_id: excerpt.id.clone(), + excerpt_id: excerpt.id, text_anchor: selection.start, }; let mut end = Anchor { buffer_id: Some(excerpt.buffer_id), - excerpt_id: excerpt.id.clone(), + excerpt_id: excerpt.id, text_anchor: selection.end, }; if range.start.cmp(&start, self).is_gt() { - start = range.start.clone(); + start = range.start; } if range.end.cmp(&end, self).is_lt() { - end = range.end.clone(); + end = range.end; } ( diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 128a11d5f3..2f8e409afa 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -1408,7 +1408,7 @@ impl LspCommand for GetHover { if let Some(range) = range.as_ref() { buffer .update(&mut cx, |buffer, _| { - buffer.wait_for_anchors([range.start.clone(), range.end.clone()]) + buffer.wait_for_anchors([range.start, range.end]) })? .await?; } @@ -1523,7 +1523,7 @@ impl LspCommand for GetCompletions { }); let range = if let Some(range) = default_edit_range { - let range = range_from_lsp(range.clone()); + let range = range_from_lsp(*range); let start = snapshot.clip_point_utf16(range.start, Bias::Left); let end = snapshot.clip_point_utf16(range.end, Bias::Left); if start != range.start.0 || end != range.end.0 { diff --git a/crates/project_core/src/worktree.rs b/crates/project_core/src/worktree.rs index 2bd3d10cc9..7bdb7cbd7d 100644 --- a/crates/project_core/src/worktree.rs +++ b/crates/project_core/src/worktree.rs @@ -991,7 +991,7 @@ impl LocalWorktree { pub fn scan_complete(&self) -> impl Future { let mut is_scanning_rx = self.is_scanning.1.clone(); async move { - let mut is_scanning = is_scanning_rx.borrow().clone(); + let mut is_scanning = *is_scanning_rx.borrow(); while is_scanning { if let Some(value) = is_scanning_rx.recv().await { is_scanning = value; diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index b3c52476fe..c2c3a9734d 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -219,7 +219,7 @@ impl ProjectSearch { active_query: self.active_query.clone(), search_id: self.search_id, search_history: self.search_history.clone(), - no_results: self.no_results.clone(), + no_results: self.no_results, }) } @@ -1279,7 +1279,7 @@ impl ProjectSearchView { fn focus_results_editor(&mut self, cx: &mut ViewContext) { self.query_editor.update(cx, |query_editor, cx| { let cursor = query_editor.selections.newest_anchor().head(); - query_editor.change_selections(None, cx, |s| s.select_ranges([cursor.clone()..cursor])); + query_editor.change_selections(None, cx, |s| s.select_ranges([cursor..cursor])); }); self.query_editor_was_focused = false; let results_handle = self.results_editor.focus_handle(cx); @@ -1299,7 +1299,6 @@ impl ProjectSearchView { if is_new_search { let range_to_select = match_ranges .first() - .clone() .map(|range| editor.range_for_match(range)); editor.change_selections(Some(Autoscroll::fit()), cx, |s| { s.select_ranges(range_to_select) @@ -2245,7 +2244,7 @@ pub mod tests { .await; let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); - let workspace = window.clone(); + let workspace = window; let search_bar = window.build_view(cx, |_| ProjectSearchBar::new()); let active_item = cx.read(|cx| { @@ -2475,7 +2474,7 @@ pub mod tests { .await; let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; let window = cx.add_window(|cx| Workspace::test_new(project, cx)); - let workspace = window.clone(); + let workspace = window; let search_bar = window.build_view(cx, |_| ProjectSearchBar::new()); let active_item = cx.read(|cx| { @@ -3410,7 +3409,7 @@ pub mod tests { let search_view = cx.add_window(|cx| ProjectSearchView::new(search.clone(), cx, None)); // First search - perform_search(search_view.clone(), "A", cx); + perform_search(search_view, "A", cx); search_view .update(cx, |search_view, cx| { search_view.results_editor.update(cx, |results_editor, cx| { @@ -3428,7 +3427,7 @@ pub mod tests { .expect("unable to update search view"); // Second search - perform_search(search_view.clone(), "B", cx); + perform_search(search_view, "B", cx); search_view .update(cx, |search_view, cx| { search_view.results_editor.update(cx, |results_editor, cx| { diff --git a/crates/semantic_index/src/db.rs b/crates/semantic_index/src/db.rs index f34baeaaae..ea299da411 100644 --- a/crates/semantic_index/src/db.rs +++ b/crates/semantic_index/src/db.rs @@ -456,8 +456,7 @@ impl VectorDatabase { if batch_ids.len() == batch_n { let embeddings = std::mem::take(&mut batch_embeddings); let ids = std::mem::take(&mut batch_ids); - let array = - Array2::from_shape_vec((ids.len(), embedding_len.clone()), embeddings); + let array = Array2::from_shape_vec((ids.len(), embedding_len), embeddings); match array { Ok(array) => { batches.push((ids, array)); diff --git a/crates/semantic_index/src/semantic_index.rs b/crates/semantic_index/src/semantic_index.rs index df277fbc9b..6f3bf9b3ac 100644 --- a/crates/semantic_index/src/semantic_index.rs +++ b/crates/semantic_index/src/semantic_index.rs @@ -329,7 +329,7 @@ impl SemanticIndex { SemanticIndexStatus::Indexed } else { SemanticIndexStatus::Indexing { - remaining_files: project_state.pending_file_count_rx.borrow().clone(), + remaining_files: *project_state.pending_file_count_rx.borrow(), rate_limit_expiry: self.embedding_provider.rate_limit_expiration(), } } @@ -497,7 +497,7 @@ impl SemanticIndex { changes: Arc<[(Arc, ProjectEntryId, PathChange)]>, cx: &mut ModelContext, ) { - let Some(worktree) = project.read(cx).worktree_for_id(worktree_id.clone(), cx) else { + let Some(worktree) = project.read(cx).worktree_for_id(worktree_id, cx) else { return; }; let project = project.downgrade(); @@ -840,7 +840,7 @@ impl SemanticIndex { let mut batch_results = Vec::new(); for batch in file_ids.chunks(batch_size) { let batch = batch.into_iter().map(|v| *v).collect::>(); - let limit = limit.clone(); + let limit = limit; let fs = fs.clone(); let db_path = db_path.clone(); let query = query.clone(); diff --git a/crates/storybook/src/storybook.rs b/crates/storybook/src/storybook.rs index 33b91a5eaa..2bd60cc680 100644 --- a/crates/storybook/src/storybook.rs +++ b/crates/storybook/src/storybook.rs @@ -42,7 +42,7 @@ fn main() { menu::init(); let args = Args::parse(); - let story_selector = args.story.clone().unwrap_or_else(|| { + let story_selector = args.story.unwrap_or_else(|| { let stories = ComponentStory::iter().collect::>(); ctrlc::set_handler(move || {}).unwrap(); diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index d0a145dad0..3ef3bab333 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -715,7 +715,7 @@ impl Terminal { new_size.size.height = cmp::max(new_size.line_height, new_size.height()); new_size.size.width = cmp::max(new_size.cell_width, new_size.width()); - self.last_content.size = new_size.clone(); + self.last_content.size = new_size; self.pty_tx.0.send(Msg::Resize(new_size.into())).ok(); diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index b121459d4a..7141f7cb45 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -406,11 +406,10 @@ impl TerminalElement { let font_features = terminal_settings .font_features - .clone() - .unwrap_or(settings.buffer_font.features.clone()); + .unwrap_or(settings.buffer_font.features); let line_height = terminal_settings.line_height.value(); - let font_size = terminal_settings.font_size.clone(); + let font_size = terminal_settings.font_size; let font_size = font_size.map_or(buffer_font_size, |size| theme::adjusted_font_size(size, cx)); @@ -462,7 +461,7 @@ impl TerminalElement { .width; gutter = cell_width; - let mut size = bounds.size.clone(); + let mut size = bounds.size; size.width -= gutter; // https://github.com/zed-industries/zed/issues/2750 @@ -646,7 +645,7 @@ impl TerminalElement { }); cx.on_mouse_event({ - let bounds = bounds.clone(); + let bounds = bounds; let focus = self.focus.clone(); let terminal = self.terminal.clone(); move |e: &MouseMoveEvent, phase, cx| { @@ -828,7 +827,7 @@ impl Element for TerminalElement { start_y, //Need to change this line_height: layout.dimensions.line_height, lines: highlighted_range_lines, - color: color.clone(), + color: *color, //Copied from editor. TODO: move to theme or something corner_radius: 0.15 * layout.dimensions.line_height, }; diff --git a/crates/ui/src/components/right_click_menu.rs b/crates/ui/src/components/right_click_menu.rs index b08f3911cb..8ad72c7b9e 100644 --- a/crates/ui/src/components/right_click_menu.rs +++ b/crates/ui/src/components/right_click_menu.rs @@ -132,8 +132,8 @@ impl Element for RightClickMenu { }; let menu = element_state.menu.clone(); let position = element_state.position.clone(); - let attach = self.attach.clone(); - let child_layout_id = element_state.child_layout_id.clone(); + let attach = self.attach; + let child_layout_id = element_state.child_layout_id; let child_bounds = cx.layout_bounds(child_layout_id.unwrap()); let interactive_bounds = InteractiveBounds { diff --git a/crates/vim/src/normal/repeat.rs b/crates/vim/src/normal/repeat.rs index 4850151d94..a91327e497 100644 --- a/crates/vim/src/normal/repeat.rs +++ b/crates/vim/src/normal/repeat.rs @@ -294,7 +294,7 @@ mod test { lsp::CompletionItem { label: "first".to_string(), text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit { - range: lsp::Range::new(position.clone(), position.clone()), + range: lsp::Range::new(position, position), new_text: "first".to_string(), })), ..Default::default() @@ -302,7 +302,7 @@ mod test { lsp::CompletionItem { label: "second".to_string(), text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit { - range: lsp::Range::new(position.clone(), position.clone()), + range: lsp::Range::new(position, position), new_text: "second".to_string(), })), ..Default::default() diff --git a/crates/vim/src/test/vim_test_context.rs b/crates/vim/src/test/vim_test_context.rs index b06ef803b6..f8cc658394 100644 --- a/crates/vim/src/test/vim_test_context.rs +++ b/crates/vim/src/test/vim_test_context.rs @@ -90,7 +90,7 @@ impl VimTestContext { T: 'static, F: FnOnce(&mut T, &mut ViewContext) -> R + 'static, { - let window = self.window.clone(); + let window = self.window; self.update_window(window, move |_, cx| view.update(cx, update)) .unwrap() } diff --git a/crates/vim/src/visual.rs b/crates/vim/src/visual.rs index 64a14784cd..4c05506f46 100644 --- a/crates/vim/src/visual.rs +++ b/crates/vim/src/visual.rs @@ -222,7 +222,7 @@ pub fn visual_block_motion( start: start.to_point(map), end: end.to_point(map), reversed: is_reversed, - goal: goal.clone(), + goal, }; selections.push(selection); diff --git a/crates/workspace/src/persistence.rs b/crates/workspace/src/persistence.rs index d02154a6aa..870d7b7673 100644 --- a/crates/workspace/src/persistence.rs +++ b/crates/workspace/src/persistence.rs @@ -367,7 +367,7 @@ impl WorkspaceDb { conn.exec_bound(sql!( DELETE FROM workspaces WHERE workspace_location = ? AND workspace_id != ? - ))?((&workspace.location, workspace.id.clone())) + ))?((&workspace.location, workspace.id)) .context("clearing out old locations")?; // Upsert diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 123096b228..a4a99ccc33 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -871,7 +871,7 @@ impl Workspace { cx.open_window(options, { let app_state = app_state.clone(); - let workspace_id = workspace_id.clone(); + let workspace_id = workspace_id; let project_handle = project_handle.clone(); move |cx| { cx.new_view(|cx| { @@ -3790,7 +3790,7 @@ impl Render for Workspace { let theme_settings = ThemeSettings::get_global(cx); ( theme_settings.ui_font.family.clone(), - theme_settings.ui_font_size.clone(), + theme_settings.ui_font_size, ) }; @@ -4393,7 +4393,7 @@ pub fn open_paths( cx.spawn(move |mut cx| async move { if let Some(existing) = existing { Ok(( - existing.clone(), + existing, existing .update(&mut cx, |workspace, cx| { workspace.open_paths(abs_paths, OpenVisible::All, None, cx) diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index fdbf173b2d..16d4516b5c 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -561,7 +561,7 @@ pub fn handle_keymap_file_changes( let new_base_keymap = *BaseKeymap::get_global(cx); let new_vim_enabled = VimModeSetting::get_global(cx).0; if new_base_keymap != old_base_keymap || new_vim_enabled != old_vim_enabled { - old_base_keymap = new_base_keymap.clone(); + old_base_keymap = new_base_keymap; old_vim_enabled = new_vim_enabled; base_keymap_tx.unbounded_send(()).unwrap(); } diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 253250998d..bdf3e1b368 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -84,7 +84,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::borrow_deref_ref", "clippy::borrowed_box", "clippy::cast_abs_to_unsigned", - "clippy::clone_on_copy", "clippy::cmp_owned", "clippy::crate_in_macro_def", "clippy::default_constructed_unit_structs", From d7962aa2d3c0ba517707edd830a33b7f9b8f8acb Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 17:56:57 -0500 Subject: [PATCH 012/121] Add license to Gleam extension (#8730) This PR adds a license to the Gleam extension crate, since the bundling script was unhappy that it didn't have one. Since extensions like this one may ultimately live outside of Zed itself, I went with the Apache 2.0 license. Release Notes: - N/A --- extensions/gleam/Cargo.toml | 2 ++ extensions/gleam/LICENSE-APACHE | 1 + 2 files changed, 3 insertions(+) create mode 120000 extensions/gleam/LICENSE-APACHE diff --git a/extensions/gleam/Cargo.toml b/extensions/gleam/Cargo.toml index 1e8ff5cdea..b4167dd1ca 100644 --- a/extensions/gleam/Cargo.toml +++ b/extensions/gleam/Cargo.toml @@ -2,6 +2,8 @@ name = "zed_gleam" version = "0.0.1" edition = "2021" +publish = false +license = "Apache-2.0" [dependencies] zed_extension_api = { path = "../../crates/extension_api" } diff --git a/extensions/gleam/LICENSE-APACHE b/extensions/gleam/LICENSE-APACHE new file mode 120000 index 0000000000..1cd601d0a3 --- /dev/null +++ b/extensions/gleam/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file From 52052f342bc5a71203856688cde544ddbfd254ec Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 18:02:39 -0500 Subject: [PATCH 013/121] Enable `clippy::map_identity` (#8731) This PR enables the [`clippy::map_identity`](https://rust-lang.github.io/rust-clippy/master/index.html#/map_identity) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/lsp/src/lsp.rs | 2 +- crates/project/src/project.rs | 4 ++-- tooling/xtask/src/main.rs | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index b7c51539eb..2f92ac71de 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -340,7 +340,7 @@ impl LanguageServer { io_tasks: Mutex::new(Some((input_task, output_task))), output_done_rx: Mutex::new(Some(output_done_rx)), root_path: root_path.to_path_buf(), - server: Arc::new(Mutex::new(server.map(|server| server))), + server: Arc::new(Mutex::new(server)), } } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 07a95198a0..11691aeb71 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -3665,7 +3665,7 @@ impl Project { proto::LspWorkStart { token, message: report.message, - percentage: report.percentage.map(|p| p), + percentage: report.percentage, }, ), }) @@ -3691,7 +3691,7 @@ impl Project { proto::LspWorkProgress { token, message: report.message, - percentage: report.percentage.map(|p| p), + percentage: report.percentage, }, ), }) diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index bdf3e1b368..cd7ce24170 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -105,7 +105,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::manual_flatten", "clippy::map_entry", "clippy::map_flatten", - "clippy::map_identity", "clippy::needless_arbitrary_self_type", "clippy::needless_borrowed_reference", "clippy::needless_lifetimes", From 8bc35c33c5e2d1dad521284af2d5e102cdfd2d11 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 18:13:49 -0500 Subject: [PATCH 014/121] Enable `clippy::to_string_in_format_args` (#8732) This PR enables the [`clippy::to_string_in_format_args`](https://rust-lang.github.io/rust-clippy/master/index.html#/to_string_in_format_args) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/editor/src/movement.rs | 2 +- crates/editor/src/test/editor_test_context.rs | 4 ++-- tooling/xtask/src/main.rs | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index 5e0ca2c6eb..d52255cd51 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -841,7 +841,7 @@ mod tests { surrounding_word(&snapshot, display_points[1]), display_points[0]..display_points[2], "{}", - marked_text.to_string() + marked_text ); } diff --git a/crates/editor/src/test/editor_test_context.rs b/crates/editor/src/test/editor_test_context.rs index 11ee49ac95..d4a5ee7c6e 100644 --- a/crates/editor/src/test/editor_test_context.rs +++ b/crates/editor/src/test/editor_test_context.rs @@ -236,7 +236,7 @@ impl EditorTestContext { pub fn set_state(&mut self, marked_text: &str) -> ContextHandle { let state_context = self.add_assertion_context(format!( "Initial Editor State: \"{}\"", - marked_text.escape_debug().to_string() + marked_text.escape_debug() )); let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true); self.editor.update(&mut self.cx, |editor, cx| { @@ -252,7 +252,7 @@ impl EditorTestContext { pub fn set_selections_state(&mut self, marked_text: &str) -> ContextHandle { let state_context = self.add_assertion_context(format!( "Initial Editor State: \"{}\"", - marked_text.escape_debug().to_string() + marked_text.escape_debug() )); let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true); self.editor.update(&mut self.cx, |editor, cx| { diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index cd7ce24170..0d8a4e9457 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -124,7 +124,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::search_is_some", "clippy::single_range_in_vec_init", "clippy::suspicious_to_owned", - "clippy::to_string_in_format_args", "clippy::too_many_arguments", "clippy::type_complexity", "clippy::unit_arg", From eaf2fbb21b41424ac5b269ae4ef6ca80fa9672e2 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 18:24:22 -0500 Subject: [PATCH 015/121] Enable `clippy::map_flatten` (#8733) This PR enables the [`clippy::map_flatten`](https://rust-lang.github.io/rust-clippy/master/index.html#/map_flatten) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/auto_update/src/auto_update.rs | 3 +-- crates/collab/src/rpc/connection_pool.rs | 6 ++---- crates/collab_ui/src/chat_panel.rs | 6 ++---- crates/copilot_ui/src/copilot_button.rs | 2 +- crates/editor/src/element.rs | 3 +-- crates/editor/src/movement.rs | 3 +-- crates/project/src/project.rs | 2 +- crates/terminal_view/src/terminal_view.rs | 9 +++------ crates/zed/src/main.rs | 4 +--- tooling/xtask/src/main.rs | 1 - 10 files changed, 13 insertions(+), 26 deletions(-) diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs index 23ca550516..f364304d59 100644 --- a/crates/auto_update/src/auto_update.rs +++ b/crates/auto_update/src/auto_update.rs @@ -343,8 +343,7 @@ impl AutoUpdater { )); cx.update(|cx| { if let Some(param) = ReleaseChannel::try_global(cx) - .map(|release_channel| release_channel.release_query_param()) - .flatten() + .and_then(|release_channel| release_channel.release_query_param()) { url_string += "&"; url_string += param; diff --git a/crates/collab/src/rpc/connection_pool.rs b/crates/collab/src/rpc/connection_pool.rs index e438fa2caf..2d28290373 100644 --- a/crates/collab/src/rpc/connection_pool.rs +++ b/crates/collab/src/rpc/connection_pool.rs @@ -94,21 +94,19 @@ impl ConnectionPool { self.connected_users .get(&user_id) .into_iter() - .map(|state| { + .flat_map(|state| { state .connection_ids .iter() .flat_map(|cid| self.connections.get(cid)) }) - .flatten() } pub fn user_connection_ids(&self, user_id: UserId) -> impl Iterator + '_ { self.connected_users .get(&user_id) .into_iter() - .map(|state| &state.connection_ids) - .flatten() + .flat_map(|state| &state.connection_ids) .copied() } diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index 1b6dc4887f..b2285c5142 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -444,8 +444,7 @@ impl ChatPanel { let reply_to_message = message .reply_to_message_id - .map(|id| active_chat.read(cx).find_loaded_message(id)) - .flatten() + .and_then(|id| active_chat.read(cx).find_loaded_message(id)) .cloned(); let replied_to_you = @@ -839,7 +838,7 @@ impl Render for ChatPanel { .when_some(reply_to_message_id, |el, reply_to_message_id| { let reply_message = self .active_chat() - .map(|active_chat| { + .and_then(|active_chat| { active_chat.read(cx).messages().iter().find_map(|m| { if m.id == ChannelMessageId::Saved(reply_to_message_id) { Some(m) @@ -848,7 +847,6 @@ impl Render for ChatPanel { } }) }) - .flatten() .cloned(); el.when_some(reply_message, |el, reply_message| { diff --git a/crates/copilot_ui/src/copilot_button.rs b/crates/copilot_ui/src/copilot_button.rs index 8ea106a3ff..ca9d2ae122 100644 --- a/crates/copilot_ui/src/copilot_button.rs +++ b/crates/copilot_ui/src/copilot_button.rs @@ -238,7 +238,7 @@ impl CopilotButton { impl StatusItemView for CopilotButton { fn set_active_pane_item(&mut self, item: Option<&dyn ItemHandle>, cx: &mut ViewContext) { - if let Some(editor) = item.map(|item| item.act_as::(cx)).flatten() { + if let Some(editor) = item.and_then(|item| item.act_as::(cx)) { self.editor_subscription = Some(( cx.observe(&editor, Self::update_enabled), editor.entity_id().as_u64() as usize, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 9dae0d0ee3..b1e372b91e 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -4083,8 +4083,7 @@ mod tests { .position_map .line_layouts .iter() - .map(|line_with_invisibles| &line_with_invisibles.invisibles) - .flatten() + .flat_map(|line_with_invisibles| &line_with_invisibles.invisibles) .cloned() .collect() } diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index d52255cd51..39c22dd5c4 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -688,7 +688,7 @@ mod tests { // add all kinds of inlays between two word boundaries: we should be able to cross them all, when looking for another boundary let mut id = 0; let inlays = (0..buffer_snapshot.len()) - .map(|offset| { + .flat_map(|offset| { [ Inlay { id: InlayId::Suggestion(post_inc(&mut id)), @@ -712,7 +712,6 @@ mod tests { }, ] }) - .flatten() .collect(); let snapshot = display_map.update(cx, |map, cx| { map.splice_inlays(Vec::new(), inlays, cx); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 11691aeb71..5aefc19e32 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -2775,7 +2775,7 @@ impl Project { let project_settings = ProjectSettings::get(Some((worktree_id.to_proto() as usize, Path::new(""))), cx); let lsp = project_settings.lsp.get(&adapter.name.0); - let override_options = lsp.map(|s| s.initialization_options.clone()).flatten(); + let override_options = lsp.and_then(|s| s.initialization_options.clone()); let server_id = pending_server.server_id; let container_dir = pending_server.container_dir.clone(); diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index e756dd454b..584469d353 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -879,12 +879,9 @@ impl Item for TerminalView { .or_else(|| { cx.update(|cx| { let strategy = TerminalSettings::get_global(cx).working_directory.clone(); - workspace - .upgrade() - .map(|workspace| { - get_working_directory(workspace.read(cx), cx, strategy) - }) - .flatten() + workspace.upgrade().and_then(|workspace| { + get_working_directory(workspace.read(cx), cx, strategy) + }) }) .ok() .flatten() diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 00af090d33..dd5a0b5be8 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -108,9 +108,7 @@ fn main() { let open_listener = listener.clone(); app.on_open_urls(move |urls, _| open_listener.open_urls(&urls)); app.on_reopen(move |cx| { - if let Some(app_state) = AppState::try_global(cx) - .map(|app_state| app_state.upgrade()) - .flatten() + if let Some(app_state) = AppState::try_global(cx).and_then(|app_state| app_state.upgrade()) { workspace::open_new(&app_state, cx, |workspace, cx| { Editor::new_file(workspace, &Default::default(), cx) diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 0d8a4e9457..92e8a9807a 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -104,7 +104,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::manual_find", "clippy::manual_flatten", "clippy::map_entry", - "clippy::map_flatten", "clippy::needless_arbitrary_self_type", "clippy::needless_borrowed_reference", "clippy::needless_lifetimes", From fc8e515fe865083ae28a208a89ac3c533aab6262 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 18:42:05 -0500 Subject: [PATCH 016/121] Enable `clippy::too_many_arguments` (#8734) This PR enables the [`clippy::too_many_arguments`](https://rust-lang.github.io/rust-clippy/master/index.html#/too_many_arguments) rule. I opted to add `#[allow(clippy::too_many_arguments)]` on the individual violations, as reworking them to take fewer arguments is a more involved task. Release Notes: - N/A --- crates/assistant/src/assistant_panel.rs | 1 + crates/collab/src/db/queries/messages.rs | 1 + crates/collab/src/rpc.rs | 1 + crates/collab/src/tests/randomized_test_helpers.rs | 1 + crates/collab_ui/src/collab_titlebar_item.rs | 1 + crates/editor/src/element.rs | 1 + crates/language/src/syntax_map.rs | 1 + crates/lsp/src/lsp.rs | 1 + crates/project/src/project.rs | 2 ++ crates/project_core/src/worktree.rs | 1 + crates/rich_text/src/rich_text.rs | 1 + crates/terminal/src/terminal.rs | 1 + crates/workspace/src/pane_group.rs | 5 +++++ tooling/xtask/src/main.rs | 1 - 14 files changed, 18 insertions(+), 1 deletion(-) diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index 178571369b..a69dc69f28 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -2835,6 +2835,7 @@ impl FocusableView for InlineAssistant { } impl InlineAssistant { + #[allow(clippy::too_many_arguments)] fn new( id: usize, measurements: Rc>, diff --git a/crates/collab/src/db/queries/messages.rs b/crates/collab/src/db/queries/messages.rs index d63b4cf1c5..8143007e19 100644 --- a/crates/collab/src/db/queries/messages.rs +++ b/crates/collab/src/db/queries/messages.rs @@ -200,6 +200,7 @@ impl Database { } /// Creates a new channel message. + #[allow(clippy::too_many_arguments)] pub async fn create_channel_message( &self, channel_id: ChannelId, diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 550f222840..074bb44bcc 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -543,6 +543,7 @@ impl Server { }) } + #[allow(clippy::too_many_arguments)] pub fn handle_connection( self: &Arc, connection: Connection, diff --git a/crates/collab/src/tests/randomized_test_helpers.rs b/crates/collab/src/tests/randomized_test_helpers.rs index 7d7abe7dfe..edce9c184d 100644 --- a/crates/collab/src/tests/randomized_test_helpers.rs +++ b/crates/collab/src/tests/randomized_test_helpers.rs @@ -464,6 +464,7 @@ impl TestPlan { }) } + #[allow(clippy::too_many_arguments)] async fn apply_server_operation( plan: Arc>, deterministic: BackgroundExecutor, diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 14d0439237..2df240564e 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -478,6 +478,7 @@ impl CollabTitlebarItem { ) } + #[allow(clippy::too_many_arguments)] fn render_collaborator( &self, user: &Arc, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index b1e372b91e..b0cc0822a4 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -2992,6 +2992,7 @@ impl LineWithInvisibles { ); } + #[allow(clippy::too_many_arguments)] fn draw_invisibles( &self, selection_ranges: &[Range], diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index 7d9258ab01..9c38fe8611 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -1180,6 +1180,7 @@ fn parse_text( }) } +#[allow(clippy::too_many_arguments)] fn get_injections( config: &InjectionConfig, text: &BufferSnapshot, diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 2f92ac71de..1652e34044 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -261,6 +261,7 @@ impl LanguageServer { Ok(server) } + #[allow(clippy::too_many_arguments)] fn new_internal( server_id: LanguageServerId, stdin: Stdin, diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 5aefc19e32..043dfe278e 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -2909,6 +2909,7 @@ impl Project { })) } + #[allow(clippy::too_many_arguments)] async fn setup_and_insert_language_server( this: WeakModel, worktree_path: &Path, @@ -6182,6 +6183,7 @@ impl Project { } /// Pick paths that might potentially contain a match of a given search query. + #[allow(clippy::too_many_arguments)] async fn background_search( unnamed_buffers: Vec>, opened_buffers: HashMap, (Model, BufferSnapshot)>, diff --git a/crates/project_core/src/worktree.rs b/crates/project_core/src/worktree.rs index 7bdb7cbd7d..2fe51e67ba 100644 --- a/crates/project_core/src/worktree.rs +++ b/crates/project_core/src/worktree.rs @@ -3299,6 +3299,7 @@ enum BackgroundScannerPhase { } impl BackgroundScanner { + #[allow(clippy::too_many_arguments)] fn new( snapshot: LocalSnapshot, next_entry_id: Arc, diff --git a/crates/rich_text/src/rich_text.rs b/crates/rich_text/src/rich_text.rs index effa671590..2e5a28351d 100644 --- a/crates/rich_text/src/rich_text.rs +++ b/crates/rich_text/src/rich_text.rs @@ -124,6 +124,7 @@ impl RichText { } } +#[allow(clippy::too_many_arguments)] pub fn render_markdown_mut( block: &str, mut mentions: &[Mention], diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 3ef3bab333..ec3503d228 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -303,6 +303,7 @@ pub struct TerminalBuilder { } impl TerminalBuilder { + #[allow(clippy::too_many_arguments)] pub fn new( working_directory: Option, task: Option, diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index 6f55dc800e..40c636c26f 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -88,6 +88,7 @@ impl PaneGroup { }; } + #[allow(clippy::too_many_arguments)] pub(crate) fn render( &self, project: &Model, @@ -159,6 +160,7 @@ impl Member { } } + #[allow(clippy::too_many_arguments)] pub fn render( &self, project: &Model, @@ -471,6 +473,7 @@ impl PaneAxis { None } + #[allow(clippy::too_many_arguments)] fn render( &self, project: &Model, @@ -640,6 +643,7 @@ mod element { self } + #[allow(clippy::too_many_arguments)] fn compute_resize( flexes: &Arc>>, e: &MouseMoveEvent, @@ -728,6 +732,7 @@ mod element { cx.refresh(); } + #[allow(clippy::too_many_arguments)] fn push_handle( flexes: Arc>>, dragged_handle: Rc>>, diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 92e8a9807a..84dec859d2 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -123,7 +123,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::search_is_some", "clippy::single_range_in_vec_init", "clippy::suspicious_to_owned", - "clippy::too_many_arguments", "clippy::type_complexity", "clippy::unit_arg", "clippy::unnecessary_filter_map", From a17c207217f69df0d3dd843183ae8739f92ccb60 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 20:03:49 -0500 Subject: [PATCH 017/121] Enable `clippy::manual_find` (#8737) This PR enables the [`clippy::manual_find`](https://rust-lang.github.io/rust-clippy/master/index.html#/manual_find) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/prettier/src/prettier.rs | 11 ++++------- tooling/xtask/src/main.rs | 1 - 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/crates/prettier/src/prettier.rs b/crates/prettier/src/prettier.rs index a2a168fb2d..2dff45de02 100644 --- a/crates/prettier/src/prettier.rs +++ b/crates/prettier/src/prettier.rs @@ -245,7 +245,7 @@ impl Prettier { ); let plugin_name_into_path = |plugin_name: &str| { let prettier_plugin_dir = prettier_node_modules.join(plugin_name); - for possible_plugin_path in [ + [ prettier_plugin_dir.join("dist").join("index.mjs"), prettier_plugin_dir.join("dist").join("index.js"), prettier_plugin_dir.join("dist").join("plugin.js"), @@ -255,12 +255,9 @@ impl Prettier { // this one is for @prettier/plugin-php prettier_plugin_dir.join("standalone.js"), prettier_plugin_dir, - ] { - if possible_plugin_path.is_file() { - return Some(possible_plugin_path); - } - } - None + ] + .into_iter() + .find(|possible_plugin_path| possible_plugin_path.is_file()) }; let (parser, located_plugins) = match parser_with_plugins { Some((parser, plugins)) => { diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 84dec859d2..cc1260868c 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -101,7 +101,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::iter_kv_map", "clippy::iter_overeager_cloned", "clippy::let_underscore_future", - "clippy::manual_find", "clippy::manual_flatten", "clippy::map_entry", "clippy::needless_arbitrary_self_type", From f79f56f8b4017d7ee9bd2e4e39207efdb430c53e Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 20:16:54 -0500 Subject: [PATCH 018/121] Enable `clippy::unnecessary_filter_map` (#8738) This PR enables the [`clippy::unnecessary_filter_map`](https://rust-lang.github.io/rust-clippy/master/index.html#/unnecessary_filter_map) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/assistant/src/assistant_panel.rs | 30 +++++++++++------------- crates/channel/src/channel_store.rs | 10 ++++---- crates/collab/src/db/queries/channels.rs | 5 +--- crates/collab_ui/src/collab_panel.rs | 18 ++++++-------- crates/multi_buffer/src/multi_buffer.rs | 12 +++++----- crates/zed/src/open_listener.rs | 4 ++-- tooling/xtask/src/main.rs | 1 - 7 files changed, 34 insertions(+), 46 deletions(-) diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index a69dc69f28..fcc033ef96 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -1634,22 +1634,20 @@ impl Conversation { let messages = self .messages(cx) .into_iter() - .filter_map(|message| { - Some(tiktoken_rs::ChatCompletionRequestMessage { - role: match message.role { - Role::User => "user".into(), - Role::Assistant => "assistant".into(), - Role::System => "system".into(), - }, - content: Some( - self.buffer - .read(cx) - .text_for_range(message.offset_range) - .collect(), - ), - name: None, - function_call: None, - }) + .map(|message| tiktoken_rs::ChatCompletionRequestMessage { + role: match message.role { + Role::User => "user".into(), + Role::Assistant => "assistant".into(), + Role::System => "system".into(), + }, + content: Some( + self.buffer + .read(cx) + .text_for_range(message.offset_range) + .collect(), + ), + name: None, + function_call: None, }) .collect::>(); let model = self.model; diff --git a/crates/channel/src/channel_store.rs b/crates/channel/src/channel_store.rs index ecda9481f2..dd7751c70a 100644 --- a/crates/channel/src/channel_store.rs +++ b/crates/channel/src/channel_store.rs @@ -839,12 +839,10 @@ impl ChannelStore { Ok(users .into_iter() .zip(response.members) - .filter_map(|(user, member)| { - Some(ChannelMembership { - user, - role: member.role(), - kind: member.kind(), - }) + .map(|(user, member)| ChannelMembership { + user, + role: member.role(), + kind: member.kind(), }) .collect()) }) diff --git a/crates/collab/src/db/queries/channels.rs b/crates/collab/src/db/queries/channels.rs index 7a9034e8d0..0896fedb13 100644 --- a/crates/collab/src/db/queries/channels.rs +++ b/crates/collab/src/db/queries/channels.rs @@ -529,10 +529,7 @@ impl Database { .all(&*tx) .await?; - let channels = channels - .into_iter() - .filter_map(|channel| Some(Channel::from_model(channel))) - .collect(); + let channels = channels.into_iter().map(Channel::from_model).collect(); Ok(channels) }) diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 2f85a71b15..d131e9feae 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -441,17 +441,13 @@ impl CollabPanel { // Populate remote participants. self.match_candidates.clear(); self.match_candidates - .extend( - room.remote_participants() - .iter() - .filter_map(|(_, participant)| { - Some(StringMatchCandidate { - id: participant.user.id as usize, - string: participant.user.github_login.clone(), - char_bag: participant.user.github_login.chars().collect(), - }) - }), - ); + .extend(room.remote_participants().values().map(|participant| { + StringMatchCandidate { + id: participant.user.id as usize, + string: participant.user.github_login.clone(), + char_bag: participant.user.github_login.chars().collect(), + } + })); let mut matches = executor.block(match_strings( &self.match_candidates, &query, diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 8a444c666c..daf06d9f86 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -3172,7 +3172,7 @@ impl MultiBufferSnapshot { let buffer_hunks = excerpt .buffer .git_diff_hunks_intersecting_range_rev(buffer_start..buffer_end) - .filter_map(move |hunk| { + .map(move |hunk| { let start = multibuffer_start.row + hunk .buffer_range @@ -3185,10 +3185,10 @@ impl MultiBufferSnapshot { .min(excerpt_end_point.row + 1) .saturating_sub(excerpt_start_point.row); - Some(DiffHunk { + DiffHunk { buffer_range: start..end, diff_base_byte_range: hunk.diff_base_byte_range.clone(), - }) + } }); cursor.prev(&()); @@ -3234,7 +3234,7 @@ impl MultiBufferSnapshot { let buffer_hunks = excerpt .buffer .git_diff_hunks_intersecting_range(buffer_start..buffer_end) - .filter_map(move |hunk| { + .map(move |hunk| { let start = multibuffer_start.row + hunk .buffer_range @@ -3247,10 +3247,10 @@ impl MultiBufferSnapshot { .min(excerpt_end_point.row + 1) .saturating_sub(excerpt_start_point.row); - Some(DiffHunk { + DiffHunk { buffer_range: start..end, diff_base_byte_range: hunk.diff_base_byte_range.clone(), - }) + } }); cursor.next(&()); diff --git a/crates/zed/src/open_listener.rs b/crates/zed/src/open_listener.rs index 61d0839ae6..4d3630a846 100644 --- a/crates/zed/src/open_listener.rs +++ b/crates/zed/src/open_listener.rs @@ -184,7 +184,7 @@ pub async fn handle_cli_connection( } else { paths .into_iter() - .filter_map(|path_with_position_string| { + .map(|path_with_position_string| { let path_with_position = PathLikeWithPosition::parse_str( &path_with_position_string, |path_str| { @@ -203,7 +203,7 @@ pub async fn handle_cli_connection( caret_positions.insert(path.clone(), Point::new(row, col)); } } - Some(path) + path }) .collect() }; diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index cc1260868c..4068a46e40 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -124,7 +124,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::suspicious_to_owned", "clippy::type_complexity", "clippy::unit_arg", - "clippy::unnecessary_filter_map", "clippy::unnecessary_find_map", "clippy::unnecessary_operation", "clippy::unnecessary_to_owned", From 503bebaacca3990fff30250d7db05d620718507e Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 20:28:26 -0500 Subject: [PATCH 019/121] Enable `clippy::manual_flatten` (#8739) This PR enables the [`clippy::manual_flatten`](https://rust-lang.github.io/rust-clippy/master/index.html#/manual_flatten) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/project_core/src/worktree.rs | 16 +++++++--------- crates/semantic_index/src/db.rs | 20 ++++++-------------- crates/workspace/src/workspace.rs | 10 ++++------ crates/zed/src/main.rs | 6 +++--- tooling/xtask/src/main.rs | 1 - 5 files changed, 20 insertions(+), 33 deletions(-) diff --git a/crates/project_core/src/worktree.rs b/crates/project_core/src/worktree.rs index 2fe51e67ba..a7b8e23b3c 100644 --- a/crates/project_core/src/worktree.rs +++ b/crates/project_core/src/worktree.rs @@ -3918,16 +3918,14 @@ impl BackgroundScanner { let repository = dotgit_path.and_then(|path| state.build_git_repository(path, self.fs.as_ref())); - for new_job in new_jobs { - if let Some(mut new_job) = new_job { - if let Some(containing_repository) = &repository { - new_job.containing_repository = Some(containing_repository.clone()); - } - - job.scan_queue - .try_send(new_job) - .expect("channel is unbounded"); + for mut new_job in new_jobs.into_iter().flatten() { + if let Some(containing_repository) = &repository { + new_job.containing_repository = Some(containing_repository.clone()); } + + job.scan_queue + .try_send(new_job) + .expect("channel is unbounded"); } Ok(()) diff --git a/crates/semantic_index/src/db.rs b/crates/semantic_index/src/db.rs index ea299da411..7deb05200e 100644 --- a/crates/semantic_index/src/db.rs +++ b/crates/semantic_index/src/db.rs @@ -278,11 +278,7 @@ impl VectorDatabase { let worktree_id = worktree_query .query_row(params![worktree_root_path], |row| Ok(row.get::<_, i64>(0)?)); - if worktree_id.is_ok() { - return Ok(true); - } else { - return Ok(false); - } + Ok(worktree_id.is_ok()) }) } @@ -302,17 +298,15 @@ impl VectorDatabase { let digests = Rc::new( digests .into_iter() - .map(|p| Value::Blob(p.0.to_vec())) + .map(|digest| Value::Blob(digest.0.to_vec())) .collect::>(), ); let rows = query.query_map(params![digests], |row| { Ok((row.get::<_, SpanDigest>(0)?, row.get::<_, Embedding>(1)?)) })?; - for row in rows { - if let Ok(row) = row { - embeddings_by_digest.insert(row.0, row.1); - } + for (digest, embedding) in rows.flatten() { + embeddings_by_digest.insert(digest, embedding); } Ok(embeddings_by_digest) @@ -344,10 +338,8 @@ impl VectorDatabase { Ok((row.get::<_, SpanDigest>(0)?, row.get::<_, Embedding>(1)?)) })?; - for row in rows { - if let Ok(row) = row { - embeddings_by_digest.insert(row.0, row.1); - } + for (digest, embedding) in rows.flatten() { + embeddings_by_digest.insert(digest, embedding); } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index a4a99ccc33..5452240e3c 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -3713,7 +3713,7 @@ fn open_items( project_paths_to_open .into_iter() .enumerate() - .map(|(i, (abs_path, project_path))| { + .map(|(ix, (abs_path, project_path))| { let workspace = workspace.clone(); cx.spawn(|mut cx| { let fs = app_state.fs.clone(); @@ -3721,7 +3721,7 @@ fn open_items( let file_project_path = project_path?; if fs.is_file(&abs_path).await { Some(( - i, + ix, workspace .update(&mut cx, |workspace, cx| { workspace.open_path(file_project_path, None, true, cx) @@ -3739,10 +3739,8 @@ fn open_items( let tasks = tasks.collect::>(); let tasks = futures::future::join_all(tasks.into_iter()); - for maybe_opened_path in tasks.await.into_iter() { - if let Some((i, path_open_result)) = maybe_opened_path { - opened_items[i] = Some(path_open_result); - } + for (ix, path_open_result) in tasks.await.into_iter().flatten() { + opened_items[ix] = Some(path_open_result); } Ok(opened_items) diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index dd5a0b5be8..dc9f43519d 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -296,9 +296,9 @@ fn main() { let task = workspace::open_paths(&paths, &app_state, None, cx); cx.spawn(|_| async move { if let Some((_window, results)) = task.await.log_err() { - for result in results { - if let Some(Err(e)) = result { - log::error!("Error opening path: {}", e); + for result in results.into_iter().flatten() { + if let Err(err) = result { + log::error!("Error opening path: {err}",); } } } diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 4068a46e40..11f3df1e3c 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -101,7 +101,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::iter_kv_map", "clippy::iter_overeager_cloned", "clippy::let_underscore_future", - "clippy::manual_flatten", "clippy::map_entry", "clippy::needless_arbitrary_self_type", "clippy::needless_borrowed_reference", From 3e287911c3a0515eda9b4d31a656516588c24638 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 20:39:03 -0500 Subject: [PATCH 020/121] Enable `clippy::unnecessary_find_map` (#8740) This PR enables the [`clippy::unnecessary_find_map`](https://rust-lang.github.io/rust-clippy/master/index.html#/unnecessary_find_map) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/collab_ui/src/chat_panel.rs | 8 ++------ tooling/xtask/src/main.rs | 1 - 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index b2285c5142..9ba422dd5a 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -839,12 +839,8 @@ impl Render for ChatPanel { let reply_message = self .active_chat() .and_then(|active_chat| { - active_chat.read(cx).messages().iter().find_map(|m| { - if m.id == ChannelMessageId::Saved(reply_to_message_id) { - Some(m) - } else { - None - } + active_chat.read(cx).messages().iter().find(|message| { + message.id == ChannelMessageId::Saved(reply_to_message_id) }) }) .cloned(); diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 11f3df1e3c..e75ec5a99c 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -123,7 +123,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::suspicious_to_owned", "clippy::type_complexity", "clippy::unit_arg", - "clippy::unnecessary_find_map", "clippy::unnecessary_operation", "clippy::unnecessary_to_owned", "clippy::unnecessary_unwrap", From 2bfc646f335d2e13f40be65ff5dcf479e97b1c6f Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 20:48:29 -0500 Subject: [PATCH 021/121] Enable `clippy::filter_map_identity` (#8741) This PR enables the [`clippy::filter_map_identity`](https://rust-lang.github.io/rust-clippy/master/index.html#/filter_map_identity) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/project/src/project.rs | 2 +- crates/recent_projects/src/recent_projects.rs | 5 +---- tooling/xtask/src/main.rs | 1 - 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 043dfe278e..a54ab68a43 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -7002,7 +7002,7 @@ impl Project { .spawn(async move { future_buffers .into_iter() - .filter_map(|e| e) + .flatten() .chain(current_buffers) .filter_map(|(buffer, path)| { let (work_directory, repo) = diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index eecd251537..c2cfd9713a 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -325,10 +325,7 @@ impl PickerDelegate for RecentProjectsDelegate { .unzip(); let highlighted_match = HighlightedMatchWithPaths { - match_label: HighlightedText::join( - match_labels.into_iter().filter_map(|name| name), - ", ", - ), + match_label: HighlightedText::join(match_labels.into_iter().flatten(), ", "), paths: if self.render_paths { paths } else { Vec::new() }, }; Some( diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index e75ec5a99c..ed28b5710e 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -95,7 +95,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::explicit_auto_deref", "clippy::explicit_counter_loop", "clippy::extra_unused_lifetimes", - "clippy::filter_map_identity", "clippy::identity_op", "clippy::implied_bounds_in_impls", "clippy::iter_kv_map", From 008c5053e63f39fbb63a39009ffc34485f75f13e Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 20:59:41 -0500 Subject: [PATCH 022/121] Enable `clippy::unit_arg` (#8742) This PR enables the [`clippy::unit_arg`](https://rust-lang.github.io/rust-clippy/master/index.html#/unit_arg) rule and suppresses the false positive that it flags. Release Notes: - N/A --- crates/gpui/src/platform/mac/platform.rs | 2 +- tooling/xtask/src/main.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 7603973505..0a60fd5172 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -497,7 +497,7 @@ impl Platform for MacPlatform { options: WindowOptions, ) -> Box { // Clippy thinks that this evaluates to `()`, for some reason. - #[allow(clippy::clone_on_copy)] + #[allow(clippy::unit_arg, clippy::clone_on_copy)] let renderer_context = self.0.lock().renderer_context.clone(); Box::new(MacWindow::open( handle, diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index ed28b5710e..a853dc29e6 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -121,7 +121,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::single_range_in_vec_init", "clippy::suspicious_to_owned", "clippy::type_complexity", - "clippy::unit_arg", "clippy::unnecessary_operation", "clippy::unnecessary_to_owned", "clippy::unnecessary_unwrap", From 4097e8870b881a7d3a879e59043c33a1a3cf449b Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 21:04:49 -0500 Subject: [PATCH 023/121] Enable `clippy::needless_arbitrary_self_type` (#8743) This PR enables the [`clippy::needless_arbitrary_self_type`](https://rust-lang.github.io/rust-clippy/master/index.html#/needless_arbitrary_self_type) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/editor/src/display_map.rs | 6 +++--- tooling/xtask/src/main.rs | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index f3c65f7fb8..67eea85bdc 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -847,7 +847,7 @@ impl DisplaySnapshot { self.block_snapshot.longest_row() } - pub fn fold_for_line(self: &Self, buffer_row: u32) -> Option { + pub fn fold_for_line(&self, buffer_row: u32) -> Option { if self.is_line_folded(buffer_row) { Some(FoldStatus::Folded) } else if self.is_foldable(buffer_row) { @@ -857,7 +857,7 @@ impl DisplaySnapshot { } } - pub fn is_foldable(self: &Self, buffer_row: u32) -> bool { + pub fn is_foldable(&self, buffer_row: u32) -> bool { let max_row = self.buffer_snapshot.max_buffer_row(); if buffer_row >= max_row { return false; @@ -880,7 +880,7 @@ impl DisplaySnapshot { false } - pub fn foldable_range(self: &Self, buffer_row: u32) -> Option> { + pub fn foldable_range(&self, buffer_row: u32) -> Option> { let start = Point::new(buffer_row, self.buffer_snapshot.line_len(buffer_row)); if self.is_foldable(start.row) && !self.is_line_folded(start.row) { let (start_indent, _) = self.line_indent_for_buffer_row(buffer_row); diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index a853dc29e6..2d3edee3e0 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -101,7 +101,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::iter_overeager_cloned", "clippy::let_underscore_future", "clippy::map_entry", - "clippy::needless_arbitrary_self_type", "clippy::needless_borrowed_reference", "clippy::needless_lifetimes", "clippy::needless_option_as_deref", From bd00aed7db8a5e9fef4c2420a4ef31b1ce609fd9 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 21:14:42 -0500 Subject: [PATCH 024/121] Enable `clippy::drain_collect` (#8745) This PR enables the [`clippy::drain_collect`](https://rust-lang.github.io/rust-clippy/master/index.html#/drain_collect) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/editor/src/element.rs | 2 +- tooling/xtask/src/main.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index b0cc0822a4..05ec9c2324 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -2883,7 +2883,7 @@ impl LineWithInvisibles { .unwrap(); layouts.push(Self { line: shaped_line, - invisibles: invisibles.drain(..).collect(), + invisibles: std::mem::take(&mut invisibles), }); line.clear(); diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 2d3edee3e0..174a6cbf13 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -89,7 +89,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::default_constructed_unit_structs", "clippy::derivable_impls", "clippy::derive_ord_xor_partial_ord", - "clippy::drain_collect", "clippy::eq_op", "clippy::expect_fun_call", "clippy::explicit_auto_deref", From ca9d5a2f6b2444c168becd80d1d9064614556520 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 21:24:56 -0500 Subject: [PATCH 025/121] Enable `clippy::needless_borrowed_reference` (#8746) This PR enables the [`clippy::needless_borrowed_reference`](https://rust-lang.github.io/rust-clippy/master/index.html#/needless_borrowed_reference) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/editor/src/element.rs | 2 +- tooling/xtask/src/main.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 05ec9c2324..49d6c4d44c 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -3273,7 +3273,7 @@ impl PositionMap { let (column, x_overshoot_after_line_end) = if let Some(line) = self .line_layouts .get(row as usize - scroll_position.y as usize) - .map(|&LineWithInvisibles { ref line, .. }| line) + .map(|LineWithInvisibles { line, .. }| line) { if let Some(ix) = line.index_for_x(x) { (ix as u32, px(0.)) diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 174a6cbf13..57c299b0fe 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -100,7 +100,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::iter_overeager_cloned", "clippy::let_underscore_future", "clippy::map_entry", - "clippy::needless_borrowed_reference", "clippy::needless_lifetimes", "clippy::needless_option_as_deref", "clippy::needless_question_mark", From 3ab16d801206e2132bc1c4c25f20a7c5dd0fb45d Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 21:36:42 -0500 Subject: [PATCH 026/121] Enable `clippy::option_as_ref_deref` (#8747) This PR enables the [`clippy::option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#/option_as_ref_deref) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/project/src/project.rs | 4 ++-- tooling/xtask/src/main.rs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index a54ab68a43..56ab5c3fc9 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -7123,7 +7123,7 @@ impl Project { .set_local_settings( worktree_id.as_u64() as usize, directory.clone(), - file_content.as_ref().map(String::as_str), + file_content.as_deref(), cx, ) .log_err(); @@ -7424,7 +7424,7 @@ impl Project { .set_local_settings( worktree.entity_id().as_u64() as usize, PathBuf::from(&envelope.payload.path).into(), - envelope.payload.content.as_ref().map(String::as_str), + envelope.payload.content.as_deref(), cx, ) .log_err(); diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 57c299b0fe..760be2bdb1 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -108,7 +108,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::non_canonical_clone_impl", "clippy::non_canonical_partial_ord_impl", "clippy::nonminimal_bool", - "clippy::option_as_ref_deref", "clippy::option_map_unit_fn", "clippy::redundant_closure_call", "clippy::redundant_guards", From 328c8a94b30ffa9935022f0c7ea97d834f511fa4 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 21:46:30 -0500 Subject: [PATCH 027/121] Enable `clippy::search_is_some` (#8748) This PR enables the [`clippy::search_is_some`](https://rust-lang.github.io/rust-clippy/master/index.html#/search_is_some) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/editor/src/editor.rs | 5 ++--- crates/feature_flags/src/feature_flags.rs | 2 +- crates/search/src/history.rs | 2 +- crates/sum_tree/src/tree_map.rs | 6 +++--- tooling/xtask/src/main.rs | 1 - 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 0fb83f7908..ed2c90f953 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -6444,10 +6444,9 @@ impl Editor { && !movement::is_inside_word(&display_map, display_range.end)) { // TODO: This is n^2, because we might check all the selections - if selections + if !selections .iter() - .find(|selection| selection.range().overlaps(&offset_range)) - .is_none() + .any(|selection| selection.range().overlaps(&offset_range)) { next_selected_range = Some(offset_range); break; diff --git a/crates/feature_flags/src/feature_flags.rs b/crates/feature_flags/src/feature_flags.rs index 0a3df28c69..700f70be78 100644 --- a/crates/feature_flags/src/feature_flags.rs +++ b/crates/feature_flags/src/feature_flags.rs @@ -8,7 +8,7 @@ struct FeatureFlags { impl FeatureFlags { fn has_flag(&self, flag: &str) -> bool { - self.staff || self.flags.iter().find(|f| f.as_str() == flag).is_some() + self.staff || self.flags.iter().any(|f| f.as_str() == flag) } } diff --git a/crates/search/src/history.rs b/crates/search/src/history.rs index 5571313acb..9d76d48e85 100644 --- a/crates/search/src/history.rs +++ b/crates/search/src/history.rs @@ -16,7 +16,7 @@ impl SearchHistory { } if let Some(previously_searched) = self.history.last_mut() { - if search_string.find(previously_searched.as_str()).is_some() { + if search_string.contains(previously_searched.as_str()) { *previously_searched = search_string; self.selected = Some(self.history.len() - 1); return; diff --git a/crates/sum_tree/src/tree_map.rs b/crates/sum_tree/src/tree_map.rs index f39e3ed19a..b46150e3c3 100644 --- a/crates/sum_tree/src/tree_map.rs +++ b/crates/sum_tree/src/tree_map.rs @@ -357,8 +357,8 @@ mod tests { .collect::>(); assert_eq!(result.len(), 2); - assert!(result.iter().find(|(k, _)| k == &&"baa").is_some()); - assert!(result.iter().find(|(k, _)| k == &&"baaab").is_some()); + assert!(result.iter().any(|(k, _)| k == &&"baa")); + assert!(result.iter().any(|(k, _)| k == &&"baaab")); let result = map .iter_from(&"c") @@ -366,7 +366,7 @@ mod tests { .collect::>(); assert_eq!(result.len(), 1); - assert!(result.iter().find(|(k, _)| k == &&"c").is_some()); + assert!(result.iter().any(|(k, _)| k == &&"c")); } #[test] diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 760be2bdb1..2089bd3cd4 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -113,7 +113,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::redundant_guards", "clippy::redundant_locals", "clippy::reversed_empty_ranges", - "clippy::search_is_some", "clippy::single_range_in_vec_init", "clippy::suspicious_to_owned", "clippy::type_complexity", From d19957b705ac2ef24af6258f602b50bada76d144 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 21:57:40 -0500 Subject: [PATCH 028/121] Enable `clippy::redundant_locals` (#8750) This PR enables the [`clippy::redundant_locals`](https://rust-lang.github.io/rust-clippy/master/index.html#/redundant_locals) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/editor/src/editor.rs | 1 - crates/rpc/src/conn.rs | 1 - crates/semantic_index/src/semantic_index.rs | 1 - crates/terminal_view/src/terminal_element.rs | 1 - crates/workspace/src/workspace.rs | 1 - tooling/xtask/src/main.rs | 1 - 6 files changed, 6 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index ed2c90f953..1e052aac72 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -8810,7 +8810,6 @@ impl Editor { Ok(i) | Err(i) => i, }; - let right_position = right_position; ranges[start_ix..] .iter() .take_while(move |range| range.start.cmp(&right_position, buffer).is_le()) diff --git a/crates/rpc/src/conn.rs b/crates/rpc/src/conn.rs index ae5c9fd226..18bce4abba 100644 --- a/crates/rpc/src/conn.rs +++ b/crates/rpc/src/conn.rs @@ -84,7 +84,6 @@ impl Connection { }); let rx = rx.then({ - let killed = killed; let executor = executor.clone(); move |msg| { let killed = killed.clone(); diff --git a/crates/semantic_index/src/semantic_index.rs b/crates/semantic_index/src/semantic_index.rs index 6f3bf9b3ac..f4b1b67aaa 100644 --- a/crates/semantic_index/src/semantic_index.rs +++ b/crates/semantic_index/src/semantic_index.rs @@ -840,7 +840,6 @@ impl SemanticIndex { let mut batch_results = Vec::new(); for batch in file_ids.chunks(batch_size) { let batch = batch.into_iter().map(|v| *v).collect::>(); - let limit = limit; let fs = fs.clone(); let db_path = db_path.clone(); let query = query.clone(); diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index 7141f7cb45..c70cd87df1 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -645,7 +645,6 @@ impl TerminalElement { }); cx.on_mouse_event({ - let bounds = bounds; let focus = self.focus.clone(); let terminal = self.terminal.clone(); move |e: &MouseMoveEvent, phase, cx| { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 5452240e3c..e7f0fb7792 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -871,7 +871,6 @@ impl Workspace { cx.open_window(options, { let app_state = app_state.clone(); - let workspace_id = workspace_id; let project_handle = project_handle.clone(); move |cx| { cx.new_view(|cx| { diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 2089bd3cd4..6b8000532d 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -111,7 +111,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::option_map_unit_fn", "clippy::redundant_closure_call", "clippy::redundant_guards", - "clippy::redundant_locals", "clippy::reversed_empty_ranges", "clippy::single_range_in_vec_init", "clippy::suspicious_to_owned", From ea68f86476ce05e1d94741a903f2c230c01bef74 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 22:08:37 -0500 Subject: [PATCH 029/121] Enable `clippy::option_map_unit_fn` (#8751) This PR enables the [`clippy::option_map_unit_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#/option_map_unit_fn) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/collab_ui/src/chat_panel.rs | 14 +++--- crates/copilot_ui/src/copilot_button.rs | 4 +- crates/editor/src/editor.rs | 32 +++++++------- crates/gpui/src/test.rs | 4 +- crates/semantic_index/src/semantic_index.rs | 4 +- crates/workspace/src/pane.rs | 47 +++++++++++++-------- crates/workspace/src/workspace.rs | 24 +++++++---- tooling/xtask/src/main.rs | 1 - 8 files changed, 75 insertions(+), 55 deletions(-) diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index 9ba422dd5a..6d2f3ea487 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -631,14 +631,12 @@ impl ChatPanel { "Copy message text", None, cx.handler_for(&this, move |this, cx| { - this.active_chat().map(|active_chat| { - if let Some(message) = - active_chat.read(cx).find_loaded_message(message_id) - { - let text = message.body.clone(); - cx.write_to_clipboard(ClipboardItem::new(text)) - } - }); + if let Some(message) = this.active_chat().and_then(|active_chat| { + active_chat.read(cx).find_loaded_message(message_id) + }) { + let text = message.body.clone(); + cx.write_to_clipboard(ClipboardItem::new(text)) + } }), ) .when(can_delete_message, move |menu| { diff --git a/crates/copilot_ui/src/copilot_button.rs b/crates/copilot_ui/src/copilot_button.rs index ca9d2ae122..818053b09e 100644 --- a/crates/copilot_ui/src/copilot_button.rs +++ b/crates/copilot_ui/src/copilot_button.rs @@ -119,7 +119,9 @@ impl Render for CopilotButton { impl CopilotButton { pub fn new(fs: Arc, cx: &mut ViewContext) -> Self { - Copilot::global(cx).map(|copilot| cx.observe(&copilot, |_, _, cx| cx.notify()).detach()); + if let Some(copilot) = Copilot::global(cx) { + cx.observe(&copilot, |_, _, cx| cx.notify()).detach() + } cx.observe_global::(move |_, cx| cx.notify()) .detach(); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 1e052aac72..f9aedb5d42 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -957,14 +957,14 @@ impl CompletionsMenu { .selected(item_ix == selected_item) .on_click(cx.listener(move |editor, _event, cx| { cx.stop_propagation(); - editor - .confirm_completion( - &ConfirmCompletion { - item_ix: Some(item_ix), - }, - cx, - ) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = editor.confirm_completion( + &ConfirmCompletion { + item_ix: Some(item_ix), + }, + cx, + ) { + task.detach_and_log_err(cx) + } })) .child(h_flex().overflow_hidden().child(completion_label)) .end_slot::
(documentation_label), @@ -1175,14 +1175,14 @@ impl CodeActionsMenu { MouseButton::Left, cx.listener(move |editor, _, cx| { cx.stop_propagation(); - editor - .confirm_code_action( - &ConfirmCodeAction { - item_ix: Some(item_ix), - }, - cx, - ) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = editor.confirm_code_action( + &ConfirmCodeAction { + item_ix: Some(item_ix), + }, + cx, + ) { + task.detach_and_log_err(cx) + } }), ) // TASK: It would be good to make lsp_action.title a SharedString to avoid allocating here. diff --git a/crates/gpui/src/test.rs b/crates/gpui/src/test.rs index 77540704f9..eab11e7b4c 100644 --- a/crates/gpui/src/test.rs +++ b/crates/gpui/src/test.rs @@ -72,7 +72,9 @@ pub fn run_test( if is_randomized { eprintln!("failing seed: {}", seed); } - on_fail_fn.map(|f| f()); + if let Some(f) = on_fail_fn { + f() + } panic::resume_unwind(error); } } diff --git a/crates/semantic_index/src/semantic_index.rs b/crates/semantic_index/src/semantic_index.rs index f4b1b67aaa..e57da7bc9b 100644 --- a/crates/semantic_index/src/semantic_index.rs +++ b/crates/semantic_index/src/semantic_index.rs @@ -657,9 +657,9 @@ impl SemanticIndex { if register.await.log_err().is_none() { // Stop tracking this worktree if the registration failed. this.update(&mut cx, |this, _| { - this.projects.get_mut(&project).map(|project_state| { + if let Some(project_state) = this.projects.get_mut(&project) { project_state.worktrees.remove(&worktree_id); - }); + } }) .ok(); } diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index ec03afe05c..5e90f86486 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1433,16 +1433,20 @@ impl Pane { "Close Clean", Some(Box::new(CloseCleanItems)), cx.handler_for(&pane, move |pane, cx| { - pane.close_clean_items(&CloseCleanItems, cx) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = pane.close_clean_items(&CloseCleanItems, cx) { + task.detach_and_log_err(cx) + } }), ) .entry( "Close All", Some(Box::new(CloseAllItems { save_intent: None })), cx.handler_for(&pane, |pane, cx| { - pane.close_all_items(&CloseAllItems { save_intent: None }, cx) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = + pane.close_all_items(&CloseAllItems { save_intent: None }, cx) + { + task.detach_and_log_err(cx) + } }), ); @@ -1783,42 +1787,49 @@ impl Render for Pane { })) .on_action( cx.listener(|pane: &mut Self, action: &CloseActiveItem, cx| { - pane.close_active_item(action, cx) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = pane.close_active_item(action, cx) { + task.detach_and_log_err(cx) + } }), ) .on_action( cx.listener(|pane: &mut Self, action: &CloseInactiveItems, cx| { - pane.close_inactive_items(action, cx) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = pane.close_inactive_items(action, cx) { + task.detach_and_log_err(cx) + } }), ) .on_action( cx.listener(|pane: &mut Self, action: &CloseCleanItems, cx| { - pane.close_clean_items(action, cx) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = pane.close_clean_items(action, cx) { + task.detach_and_log_err(cx) + } }), ) .on_action( cx.listener(|pane: &mut Self, action: &CloseItemsToTheLeft, cx| { - pane.close_items_to_the_left(action, cx) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = pane.close_items_to_the_left(action, cx) { + task.detach_and_log_err(cx) + } }), ) .on_action( cx.listener(|pane: &mut Self, action: &CloseItemsToTheRight, cx| { - pane.close_items_to_the_right(action, cx) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = pane.close_items_to_the_right(action, cx) { + task.detach_and_log_err(cx) + } }), ) .on_action(cx.listener(|pane: &mut Self, action: &CloseAllItems, cx| { - pane.close_all_items(action, cx) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = pane.close_all_items(action, cx) { + task.detach_and_log_err(cx) + } })) .on_action( cx.listener(|pane: &mut Self, action: &CloseActiveItem, cx| { - pane.close_active_item(action, cx) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = pane.close_active_item(action, cx) { + task.detach_and_log_err(cx) + } }), ) .on_action( diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index e7f0fb7792..ad964cfadf 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1627,8 +1627,11 @@ impl Workspace { action: &CloseInactiveTabsAndPanes, cx: &mut ViewContext, ) { - self.close_all_internal(true, action.save_intent.unwrap_or(SaveIntent::Close), cx) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = + self.close_all_internal(true, action.save_intent.unwrap_or(SaveIntent::Close), cx) + { + task.detach_and_log_err(cx) + } } pub fn close_all_items_and_panes( @@ -1636,8 +1639,11 @@ impl Workspace { action: &CloseAllItemsAndPanes, cx: &mut ViewContext, ) { - self.close_all_internal(false, action.save_intent.unwrap_or(SaveIntent::Close), cx) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = + self.close_all_internal(false, action.save_intent.unwrap_or(SaveIntent::Close), cx) + { + task.detach_and_log_err(cx) + } } fn close_all_internal( @@ -2599,8 +2605,9 @@ impl Workspace { if Some(leader_id) == self.unfollow(&pane, cx) { return; } - self.start_following(leader_id, cx) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = self.start_following(leader_id, cx) { + task.detach_and_log_err(cx) + } } pub fn follow(&mut self, leader_id: PeerId, cx: &mut ViewContext) { @@ -2642,8 +2649,9 @@ impl Workspace { } // Otherwise, follow. - self.start_following(leader_id, cx) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = self.start_following(leader_id, cx) { + task.detach_and_log_err(cx) + } } pub fn unfollow(&mut self, pane: &View, cx: &mut ViewContext) -> Option { diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 6b8000532d..955baffd67 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -108,7 +108,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::non_canonical_clone_impl", "clippy::non_canonical_partial_ord_impl", "clippy::nonminimal_bool", - "clippy::option_map_unit_fn", "clippy::redundant_closure_call", "clippy::redundant_guards", "clippy::reversed_empty_ranges", From 6a9e8faad2f724be1ddf6cff8c312f3c205193b0 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 22:19:56 -0500 Subject: [PATCH 030/121] Enable `clippy::unnecessary_operation` (#8752) This PR enables the [`clippy::unnecessary_operation`](https://rust-lang.github.io/rust-clippy/master/index.html#/unnecessary_operation) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/collab/src/db/tests/contributor_tests.rs | 3 +-- crates/collab/src/db/tests/db_tests.rs | 3 +-- tooling/xtask/src/main.rs | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/crates/collab/src/db/tests/contributor_tests.rs b/crates/collab/src/db/tests/contributor_tests.rs index c826f0083a..f3a9dfbd40 100644 --- a/crates/collab/src/db/tests/contributor_tests.rs +++ b/crates/collab/src/db/tests/contributor_tests.rs @@ -18,8 +18,7 @@ async fn test_contributors(db: &Arc) { }, ) .await - .unwrap() - .user_id; + .unwrap(); assert_eq!(db.get_contributors().await.unwrap(), Vec::::new()); diff --git a/crates/collab/src/db/tests/db_tests.rs b/crates/collab/src/db/tests/db_tests.rs index 3149c8a8b6..54ce3f6f41 100644 --- a/crates/collab/src/db/tests/db_tests.rs +++ b/crates/collab/src/db/tests/db_tests.rs @@ -87,8 +87,7 @@ async fn test_get_or_create_user_by_github_account(db: &Arc) { }, ) .await - .unwrap() - .user_id; + .unwrap(); let user_id2 = db .create_user( "user2@example.com", diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 955baffd67..836e8826c4 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -114,7 +114,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::single_range_in_vec_init", "clippy::suspicious_to_owned", "clippy::type_complexity", - "clippy::unnecessary_operation", "clippy::unnecessary_to_owned", "clippy::unnecessary_unwrap", "clippy::useless_conversion", From 659974411d297adee18da22b6928dc3d8fea0a53 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 22:30:18 -0500 Subject: [PATCH 031/121] Enable `clippy::explicit_auto_deref` (#8753) This PR enables the [`clippy::explicit_auto_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#/explicit_auto_deref) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/collab/src/db/queries/buffers.rs | 32 +++---- crates/collab/src/db/queries/channels.rs | 84 +++++++++---------- crates/collab/src/db/queries/contacts.rs | 8 +- crates/collab/src/db/queries/contributors.rs | 2 +- crates/collab/src/db/queries/messages.rs | 34 ++++---- crates/collab/src/db/queries/projects.rs | 4 +- crates/collab/src/db/queries/rooms.rs | 11 ++- crates/collab/src/db/queries/users.rs | 2 +- crates/collab/src/db/tests/buffer_tests.rs | 10 +-- crates/collab/src/db/tests/channel_tests.rs | 32 +++---- crates/collab/src/db/tests/message_tests.rs | 2 +- crates/collab/src/rpc.rs | 14 ++-- crates/collab/src/tests/test_server.rs | 2 +- crates/editor/src/editor.rs | 2 +- crates/editor/src/editor_tests.rs | 4 +- crates/gpui/src/app/test_context.rs | 2 +- crates/multi_buffer/src/multi_buffer.rs | 4 +- .../ui/src/components/stories/keybinding.rs | 2 +- crates/workspace/src/item.rs | 2 +- tooling/xtask/src/main.rs | 1 - 20 files changed, 126 insertions(+), 128 deletions(-) diff --git a/crates/collab/src/db/queries/buffers.rs b/crates/collab/src/db/queries/buffers.rs index e141ea8865..4ebca6cebb 100644 --- a/crates/collab/src/db/queries/buffers.rs +++ b/crates/collab/src/db/queries/buffers.rs @@ -18,7 +18,7 @@ impl Database { connection: ConnectionId, ) -> Result { self.transaction(|tx| async move { - let channel = self.get_channel_internal(channel_id, &*tx).await?; + let channel = self.get_channel_internal(channel_id, &tx).await?; self.check_user_is_channel_participant(&channel, user_id, &tx) .await?; @@ -134,10 +134,10 @@ impl Database { let mut results = Vec::new(); for client_buffer in buffers { let channel = self - .get_channel_internal(ChannelId::from_proto(client_buffer.channel_id), &*tx) + .get_channel_internal(ChannelId::from_proto(client_buffer.channel_id), &tx) .await?; if self - .check_user_is_channel_participant(&channel, user_id, &*tx) + .check_user_is_channel_participant(&channel, user_id, &tx) .await .is_err() { @@ -145,7 +145,7 @@ impl Database { continue; } - let buffer = self.get_channel_buffer(channel.id, &*tx).await?; + let buffer = self.get_channel_buffer(channel.id, &tx).await?; let mut collaborators = channel_buffer_collaborator::Entity::find() .filter(channel_buffer_collaborator::Column::ChannelId.eq(channel.id)) .all(&*tx) @@ -180,7 +180,7 @@ impl Database { let client_version = version_from_wire(&client_buffer.version); let serialization_version = self - .get_buffer_operation_serialization_version(buffer.id, buffer.epoch, &*tx) + .get_buffer_operation_serialization_version(buffer.id, buffer.epoch, &tx) .await?; let mut rows = buffer_operation::Entity::find() @@ -283,7 +283,7 @@ impl Database { connection: ConnectionId, ) -> Result { self.transaction(|tx| async move { - self.leave_channel_buffer_internal(channel_id, connection, &*tx) + self.leave_channel_buffer_internal(channel_id, connection, &tx) .await }) .await @@ -337,7 +337,7 @@ impl Database { let mut result = Vec::new(); for channel_id in channel_ids { let left_channel_buffer = self - .leave_channel_buffer_internal(channel_id, connection, &*tx) + .leave_channel_buffer_internal(channel_id, connection, &tx) .await?; result.push(left_channel_buffer); } @@ -406,7 +406,7 @@ impl Database { channel_id: ChannelId, ) -> Result> { self.transaction(|tx| async move { - self.get_channel_buffer_collaborators_internal(channel_id, &*tx) + self.get_channel_buffer_collaborators_internal(channel_id, &tx) .await }) .await @@ -447,7 +447,7 @@ impl Database { Vec, )> { self.transaction(move |tx| async move { - let channel = self.get_channel_internal(channel_id, &*tx).await?; + let channel = self.get_channel_internal(channel_id, &tx).await?; let mut requires_write_permission = false; for op in operations.iter() { @@ -457,10 +457,10 @@ impl Database { } } if requires_write_permission { - self.check_user_is_channel_member(&channel, user, &*tx) + self.check_user_is_channel_member(&channel, user, &tx) .await?; } else { - self.check_user_is_channel_participant(&channel, user, &*tx) + self.check_user_is_channel_participant(&channel, user, &tx) .await?; } @@ -471,7 +471,7 @@ impl Database { .ok_or_else(|| anyhow!("no such buffer"))?; let serialization_version = self - .get_buffer_operation_serialization_version(buffer.id, buffer.epoch, &*tx) + .get_buffer_operation_serialization_version(buffer.id, buffer.epoch, &tx) .await?; let operations = operations @@ -500,13 +500,13 @@ impl Database { buffer.epoch, *max_operation.replica_id.as_ref(), *max_operation.lamport_timestamp.as_ref(), - &*tx, + &tx, ) .await?; - channel_members = self.get_channel_participants(&channel, &*tx).await?; + channel_members = self.get_channel_participants(&channel, &tx).await?; let collaborators = self - .get_channel_buffer_collaborators_internal(channel_id, &*tx) + .get_channel_buffer_collaborators_internal(channel_id, &tx) .await?; channel_members.retain(|member| !collaborators.contains(member)); @@ -737,7 +737,7 @@ impl Database { epoch, component.replica_id as i32, component.timestamp as i32, - &*tx, + &tx, ) .await?; Ok(()) diff --git a/crates/collab/src/db/queries/channels.rs b/crates/collab/src/db/queries/channels.rs index 0896fedb13..376a44b6eb 100644 --- a/crates/collab/src/db/queries/channels.rs +++ b/crates/collab/src/db/queries/channels.rs @@ -53,8 +53,8 @@ impl Database { let mut membership = None; if let Some(parent_channel_id) = parent_channel_id { - let parent_channel = self.get_channel_internal(parent_channel_id, &*tx).await?; - self.check_user_is_channel_admin(&parent_channel, admin_id, &*tx) + let parent_channel = self.get_channel_internal(parent_channel_id, &tx).await?; + self.check_user_is_channel_admin(&parent_channel, admin_id, &tx) .await?; parent = Some(parent_channel); } @@ -105,14 +105,14 @@ impl Database { connection: ConnectionId, ) -> Result<(JoinRoom, Option, ChannelRole)> { self.transaction(move |tx| async move { - let channel = self.get_channel_internal(channel_id, &*tx).await?; - let mut role = self.channel_role_for_user(&channel, user_id, &*tx).await?; + let channel = self.get_channel_internal(channel_id, &tx).await?; + let mut role = self.channel_role_for_user(&channel, user_id, &tx).await?; let mut accept_invite_result = None; if role.is_none() { if let Some(invitation) = self - .pending_invite_for_channel(&channel, user_id, &*tx) + .pending_invite_for_channel(&channel, user_id, &tx) .await? { // note, this may be a parent channel @@ -125,12 +125,12 @@ impl Database { .await?; accept_invite_result = Some( - self.calculate_membership_updated(&channel, user_id, &*tx) + self.calculate_membership_updated(&channel, user_id, &tx) .await?, ); debug_assert!( - self.channel_role_for_user(&channel, user_id, &*tx).await? == role + self.channel_role_for_user(&channel, user_id, &tx).await? == role ); } else if channel.visibility == ChannelVisibility::Public { role = Some(ChannelRole::Guest); @@ -145,12 +145,12 @@ impl Database { .await?; accept_invite_result = Some( - self.calculate_membership_updated(&channel, user_id, &*tx) + self.calculate_membership_updated(&channel, user_id, &tx) .await?, ); debug_assert!( - self.channel_role_for_user(&channel, user_id, &*tx).await? == role + self.channel_role_for_user(&channel, user_id, &tx).await? == role ); } } @@ -162,10 +162,10 @@ impl Database { let live_kit_room = format!("channel-{}", nanoid::nanoid!(30)); let room_id = self - .get_or_create_channel_room(channel_id, &live_kit_room, &*tx) + .get_or_create_channel_room(channel_id, &live_kit_room, &tx) .await?; - self.join_channel_room_internal(room_id, user_id, connection, role, &*tx) + self.join_channel_room_internal(room_id, user_id, connection, role, &tx) .await .map(|jr| (jr, accept_invite_result, role)) }) @@ -180,13 +180,13 @@ impl Database { admin_id: UserId, ) -> Result<(Channel, Vec)> { self.transaction(move |tx| async move { - let channel = self.get_channel_internal(channel_id, &*tx).await?; - self.check_user_is_channel_admin(&channel, admin_id, &*tx) + let channel = self.get_channel_internal(channel_id, &tx).await?; + self.check_user_is_channel_admin(&channel, admin_id, &tx) .await?; if visibility == ChannelVisibility::Public { if let Some(parent_id) = channel.parent_id() { - let parent = self.get_channel_internal(parent_id, &*tx).await?; + let parent = self.get_channel_internal(parent_id, &tx).await?; if parent.visibility != ChannelVisibility::Public { Err(ErrorCode::BadPublicNesting @@ -196,7 +196,7 @@ impl Database { } } else if visibility == ChannelVisibility::Members { if self - .get_channel_descendants_excluding_self([&channel], &*tx) + .get_channel_descendants_excluding_self([&channel], &tx) .await? .into_iter() .any(|channel| channel.visibility == ChannelVisibility::Public) @@ -228,7 +228,7 @@ impl Database { requires_zed_cla: bool, ) -> Result<()> { self.transaction(move |tx| async move { - let channel = self.get_channel_internal(channel_id, &*tx).await?; + let channel = self.get_channel_internal(channel_id, &tx).await?; let mut model = channel.into_active_model(); model.requires_zed_cla = ActiveValue::Set(requires_zed_cla); model.update(&*tx).await?; @@ -244,8 +244,8 @@ impl Database { user_id: UserId, ) -> Result<(Vec, Vec)> { self.transaction(move |tx| async move { - let channel = self.get_channel_internal(channel_id, &*tx).await?; - self.check_user_is_channel_admin(&channel, user_id, &*tx) + let channel = self.get_channel_internal(channel_id, &tx).await?; + self.check_user_is_channel_admin(&channel, user_id, &tx) .await?; let members_to_notify: Vec = channel_member::Entity::find() @@ -258,7 +258,7 @@ impl Database { .await?; let channels_to_remove = self - .get_channel_descendants_excluding_self([&channel], &*tx) + .get_channel_descendants_excluding_self([&channel], &tx) .await? .into_iter() .map(|channel| channel.id) @@ -284,8 +284,8 @@ impl Database { role: ChannelRole, ) -> Result { self.transaction(move |tx| async move { - let channel = self.get_channel_internal(channel_id, &*tx).await?; - self.check_user_is_channel_admin(&channel, inviter_id, &*tx) + let channel = self.get_channel_internal(channel_id, &tx).await?; + self.check_user_is_channel_admin(&channel, inviter_id, &tx) .await?; if !channel.is_root() { Err(ErrorCode::NotARootChannel.anyhow())? @@ -312,7 +312,7 @@ impl Database { inviter_id: inviter_id.to_proto(), }, true, - &*tx, + &tx, ) .await? .into_iter() @@ -344,8 +344,8 @@ impl Database { self.transaction(move |tx| async move { let new_name = Self::sanitize_channel_name(new_name)?.to_string(); - let channel = self.get_channel_internal(channel_id, &*tx).await?; - self.check_user_is_channel_admin(&channel, admin_id, &*tx) + let channel = self.get_channel_internal(channel_id, &tx).await?; + self.check_user_is_channel_admin(&channel, admin_id, &tx) .await?; let mut model = channel.into_active_model(); @@ -370,7 +370,7 @@ impl Database { accept: bool, ) -> Result { self.transaction(move |tx| async move { - let channel = self.get_channel_internal(channel_id, &*tx).await?; + let channel = self.get_channel_internal(channel_id, &tx).await?; let membership_update = if accept { let rows_affected = channel_member::Entity::update_many() @@ -393,7 +393,7 @@ impl Database { } Some( - self.calculate_membership_updated(&channel, user_id, &*tx) + self.calculate_membership_updated(&channel, user_id, &tx) .await?, ) } else { @@ -425,7 +425,7 @@ impl Database { inviter_id: Default::default(), }, accept, - &*tx, + &tx, ) .await? .into_iter() @@ -466,10 +466,10 @@ impl Database { admin_id: UserId, ) -> Result { self.transaction(|tx| async move { - let channel = self.get_channel_internal(channel_id, &*tx).await?; + let channel = self.get_channel_internal(channel_id, &tx).await?; if member_id != admin_id { - self.check_user_is_channel_admin(&channel, admin_id, &*tx) + self.check_user_is_channel_admin(&channel, admin_id, &tx) .await?; } @@ -488,7 +488,7 @@ impl Database { Ok(RemoveChannelMemberResult { membership_update: self - .calculate_membership_updated(&channel, member_id, &*tx) + .calculate_membership_updated(&channel, member_id, &tx) .await?, notification_id: self .remove_notification( @@ -498,7 +498,7 @@ impl Database { channel_name: Default::default(), inviter_id: Default::default(), }, - &*tx, + &tx, ) .await?, }) @@ -674,8 +674,8 @@ impl Database { role: ChannelRole, ) -> Result { self.transaction(|tx| async move { - let channel = self.get_channel_internal(channel_id, &*tx).await?; - self.check_user_is_channel_admin(&channel, admin_id, &*tx) + let channel = self.get_channel_internal(channel_id, &tx).await?; + self.check_user_is_channel_admin(&channel, admin_id, &tx) .await?; let membership = channel_member::Entity::find() @@ -697,7 +697,7 @@ impl Database { if updated.accepted { Ok(SetMemberRoleResult::MembershipUpdated( - self.calculate_membership_updated(&channel, for_user, &*tx) + self.calculate_membership_updated(&channel, for_user, &tx) .await?, )) } else { @@ -717,13 +717,13 @@ impl Database { ) -> Result> { let (role, members) = self .transaction(move |tx| async move { - let channel = self.get_channel_internal(channel_id, &*tx).await?; + let channel = self.get_channel_internal(channel_id, &tx).await?; let role = self - .check_user_is_channel_participant(&channel, user_id, &*tx) + .check_user_is_channel_participant(&channel, user_id, &tx) .await?; Ok(( role, - self.get_channel_participant_details_internal(&channel, &*tx) + self.get_channel_participant_details_internal(&channel, &tx) .await?, )) }) @@ -915,8 +915,8 @@ impl Database { /// Returns the channel with the given ID. pub async fn get_channel(&self, channel_id: ChannelId, user_id: UserId) -> Result { self.transaction(|tx| async move { - let channel = self.get_channel_internal(channel_id, &*tx).await?; - self.check_user_is_channel_participant(&channel, user_id, &*tx) + let channel = self.get_channel_internal(channel_id, &tx).await?; + self.check_user_is_channel_participant(&channel, user_id, &tx) .await?; Ok(Channel::from_model(channel)) @@ -971,10 +971,10 @@ impl Database { admin_id: UserId, ) -> Result<(Vec, Vec)> { self.transaction(|tx| async move { - let channel = self.get_channel_internal(channel_id, &*tx).await?; - self.check_user_is_channel_admin(&channel, admin_id, &*tx) + let channel = self.get_channel_internal(channel_id, &tx).await?; + self.check_user_is_channel_admin(&channel, admin_id, &tx) .await?; - let new_parent = self.get_channel_internal(new_parent_id, &*tx).await?; + let new_parent = self.get_channel_internal(new_parent_id, &tx).await?; if new_parent.root_id() != channel.root_id() { Err(anyhow!(ErrorCode::WrongMoveTarget))?; diff --git a/crates/collab/src/db/queries/contacts.rs b/crates/collab/src/db/queries/contacts.rs index c66c33b80d..89bb07f3d9 100644 --- a/crates/collab/src/db/queries/contacts.rs +++ b/crates/collab/src/db/queries/contacts.rs @@ -177,7 +177,7 @@ impl Database { sender_id: sender_id.to_proto(), }, true, - &*tx, + &tx, ) .await? .into_iter() @@ -227,7 +227,7 @@ impl Database { rpc::Notification::ContactRequest { sender_id: requester_id.to_proto(), }, - &*tx, + &tx, ) .await?; } @@ -335,7 +335,7 @@ impl Database { sender_id: requester_id.to_proto(), }, accept, - &*tx, + &tx, ) .await?, ); @@ -348,7 +348,7 @@ impl Database { responder_id: responder_id.to_proto(), }, true, - &*tx, + &tx, ) .await?, ); diff --git a/crates/collab/src/db/queries/contributors.rs b/crates/collab/src/db/queries/contributors.rs index 0972779ce9..49194d6861 100644 --- a/crates/collab/src/db/queries/contributors.rs +++ b/crates/collab/src/db/queries/contributors.rs @@ -72,7 +72,7 @@ impl Database { github_login, github_user_id, github_email, - &*tx, + &tx, ) .await?; diff --git a/crates/collab/src/db/queries/messages.rs b/crates/collab/src/db/queries/messages.rs index 8143007e19..83796d4512 100644 --- a/crates/collab/src/db/queries/messages.rs +++ b/crates/collab/src/db/queries/messages.rs @@ -12,8 +12,8 @@ impl Database { user_id: UserId, ) -> Result<()> { self.transaction(|tx| async move { - let channel = self.get_channel_internal(channel_id, &*tx).await?; - self.check_user_is_channel_participant(&channel, user_id, &*tx) + let channel = self.get_channel_internal(channel_id, &tx).await?; + self.check_user_is_channel_participant(&channel, user_id, &tx) .await?; channel_chat_participant::ActiveModel { id: ActiveValue::NotSet, @@ -87,8 +87,8 @@ impl Database { before_message_id: Option, ) -> Result> { self.transaction(|tx| async move { - let channel = self.get_channel_internal(channel_id, &*tx).await?; - self.check_user_is_channel_participant(&channel, user_id, &*tx) + let channel = self.get_channel_internal(channel_id, &tx).await?; + self.check_user_is_channel_participant(&channel, user_id, &tx) .await?; let mut condition = @@ -105,7 +105,7 @@ impl Database { .all(&*tx) .await?; - self.load_channel_messages(rows, &*tx).await + self.load_channel_messages(rows, &tx).await }) .await } @@ -127,16 +127,16 @@ impl Database { for row in &rows { channels.insert( row.channel_id, - self.get_channel_internal(row.channel_id, &*tx).await?, + self.get_channel_internal(row.channel_id, &tx).await?, ); } for (_, channel) in channels { - self.check_user_is_channel_participant(&channel, user_id, &*tx) + self.check_user_is_channel_participant(&channel, user_id, &tx) .await?; } - let messages = self.load_channel_messages(rows, &*tx).await?; + let messages = self.load_channel_messages(rows, &tx).await?; Ok(messages) }) .await @@ -212,8 +212,8 @@ impl Database { reply_to_message_id: Option, ) -> Result { self.transaction(|tx| async move { - let channel = self.get_channel_internal(channel_id, &*tx).await?; - self.check_user_is_channel_participant(&channel, user_id, &*tx) + let channel = self.get_channel_internal(channel_id, &tx).await?; + self.check_user_is_channel_participant(&channel, user_id, &tx) .await?; let mut rows = channel_chat_participant::Entity::find() @@ -303,13 +303,13 @@ impl Database { channel_id: channel_id.to_proto(), }, false, - &*tx, + &tx, ) .await?, ); } - self.observe_channel_message_internal(channel_id, user_id, message_id, &*tx) + self.observe_channel_message_internal(channel_id, user_id, message_id, &tx) .await?; } _ => { @@ -322,7 +322,7 @@ impl Database { } } - let mut channel_members = self.get_channel_participants(&channel, &*tx).await?; + let mut channel_members = self.get_channel_participants(&channel, &tx).await?; channel_members.retain(|member| !participant_user_ids.contains(member)); Ok(CreatedChannelMessage { @@ -342,7 +342,7 @@ impl Database { message_id: MessageId, ) -> Result { self.transaction(|tx| async move { - self.observe_channel_message_internal(channel_id, user_id, message_id, &*tx) + self.observe_channel_message_internal(channel_id, user_id, message_id, &tx) .await?; let mut batch = NotificationBatch::default(); batch.extend( @@ -353,7 +353,7 @@ impl Database { sender_id: Default::default(), channel_id: Default::default(), }, - &*tx, + &tx, ) .await?, ); @@ -501,9 +501,9 @@ impl Database { .await?; if result.rows_affected == 0 { - let channel = self.get_channel_internal(channel_id, &*tx).await?; + let channel = self.get_channel_internal(channel_id, &tx).await?; if self - .check_user_is_channel_admin(&channel, user_id, &*tx) + .check_user_is_channel_admin(&channel, user_id, &tx) .await .is_ok() { diff --git a/crates/collab/src/db/queries/projects.rs b/crates/collab/src/db/queries/projects.rs index 33302eece1..7adf6efd72 100644 --- a/crates/collab/src/db/queries/projects.rs +++ b/crates/collab/src/db/queries/projects.rs @@ -1061,7 +1061,7 @@ impl Database { .insert(&*tx) .await?; - let room = self.get_room(room_id, &*tx).await?; + let room = self.get_room(room_id, &tx).await?; Ok(room) }) .await @@ -1095,7 +1095,7 @@ impl Database { .exec(&*tx) .await?; - let room = self.get_room(room_id, &*tx).await?; + let room = self.get_room(room_id, &tx).await?; Ok(room) }) .await diff --git a/crates/collab/src/db/queries/rooms.rs b/crates/collab/src/db/queries/rooms.rs index b5073ae7ab..f666391cde 100644 --- a/crates/collab/src/db/queries/rooms.rs +++ b/crates/collab/src/db/queries/rooms.rs @@ -321,7 +321,7 @@ impl Database { } let participant_index = self - .get_next_participant_index_internal(room_id, &*tx) + .get_next_participant_index_internal(room_id, &tx) .await?; let result = room_participant::Entity::update_many() @@ -1010,7 +1010,7 @@ impl Database { .ok_or_else(|| anyhow!("only admins can set participant role"))?; if role.requires_cla() { - self.check_user_has_signed_cla(user_id, room_id, &*tx) + self.check_user_has_signed_cla(user_id, room_id, &tx) .await?; } @@ -1076,10 +1076,9 @@ impl Database { pub async fn connection_lost(&self, connection: ConnectionId) -> Result<()> { self.transaction(|tx| async move { - self.room_connection_lost(connection, &*tx).await?; - self.channel_buffer_connection_lost(connection, &*tx) - .await?; - self.channel_chat_connection_lost(connection, &*tx).await?; + self.room_connection_lost(connection, &tx).await?; + self.channel_buffer_connection_lost(connection, &tx).await?; + self.channel_chat_connection_lost(connection, &tx).await?; Ok(()) }) .await diff --git a/crates/collab/src/db/queries/users.rs b/crates/collab/src/db/queries/users.rs index f0768a3a9c..e60ff63385 100644 --- a/crates/collab/src/db/queries/users.rs +++ b/crates/collab/src/db/queries/users.rs @@ -80,7 +80,7 @@ impl Database { github_login, github_user_id, github_email, - &*tx, + &tx, ) .await }) diff --git a/crates/collab/src/db/tests/buffer_tests.rs b/crates/collab/src/db/tests/buffer_tests.rs index c1015330bb..ebc11a7019 100644 --- a/crates/collab/src/db/tests/buffer_tests.rs +++ b/crates/collab/src/db/tests/buffer_tests.rs @@ -222,7 +222,7 @@ async fn test_channel_buffers_last_operations(db: &Database) { .unwrap(); buffers.push( - db.transaction(|tx| async move { db.get_channel_buffer(channel, &*tx).await }) + db.transaction(|tx| async move { db.get_channel_buffer(channel, &tx).await }) .await .unwrap(), ); @@ -238,7 +238,7 @@ async fn test_channel_buffers_last_operations(db: &Database) { .transaction(|tx| { let buffers = &buffers; async move { - db.get_latest_operations_for_buffers([buffers[0].id, buffers[2].id], &*tx) + db.get_latest_operations_for_buffers([buffers[0].id, buffers[2].id], &tx) .await } }) @@ -302,7 +302,7 @@ async fn test_channel_buffers_last_operations(db: &Database) { .transaction(|tx| { let buffers = &buffers; async move { - db.get_latest_operations_for_buffers([buffers[1].id, buffers[2].id], &*tx) + db.get_latest_operations_for_buffers([buffers[1].id, buffers[2].id], &tx) .await } }) @@ -320,7 +320,7 @@ async fn test_channel_buffers_last_operations(db: &Database) { .transaction(|tx| { let buffers = &buffers; async move { - db.get_latest_operations_for_buffers([buffers[0].id, buffers[1].id], &*tx) + db.get_latest_operations_for_buffers([buffers[0].id, buffers[1].id], &tx) .await } }) @@ -342,7 +342,7 @@ async fn test_channel_buffers_last_operations(db: &Database) { hash.insert(buffers[1].id, buffers[1].channel_id); hash.insert(buffers[2].id, buffers[2].channel_id); - async move { db.latest_channel_buffer_changes(&hash, &*tx).await } + async move { db.latest_channel_buffer_changes(&hash, &tx).await } }) .await .unwrap(); diff --git a/crates/collab/src/db/tests/channel_tests.rs b/crates/collab/src/db/tests/channel_tests.rs index 7f74bf15aa..99bf44744f 100644 --- a/crates/collab/src/db/tests/channel_tests.rs +++ b/crates/collab/src/db/tests/channel_tests.rs @@ -42,8 +42,8 @@ async fn test_channels(db: &Arc) { let mut members = db .transaction(|tx| async move { - let channel = db.get_channel_internal(replace_id, &*tx).await?; - Ok(db.get_channel_participants(&channel, &*tx).await?) + let channel = db.get_channel_internal(replace_id, &tx).await?; + Ok(db.get_channel_participants(&channel, &tx).await?) }) .await .unwrap(); @@ -464,9 +464,9 @@ async fn test_user_is_channel_participant(db: &Arc) { db.transaction(|tx| async move { db.check_user_is_channel_participant( - &db.get_channel_internal(public_channel_id, &*tx).await?, + &db.get_channel_internal(public_channel_id, &tx).await?, admin, - &*tx, + &tx, ) .await }) @@ -474,9 +474,9 @@ async fn test_user_is_channel_participant(db: &Arc) { .unwrap(); db.transaction(|tx| async move { db.check_user_is_channel_participant( - &db.get_channel_internal(public_channel_id, &*tx).await?, + &db.get_channel_internal(public_channel_id, &tx).await?, member, - &*tx, + &tx, ) .await }) @@ -517,9 +517,9 @@ async fn test_user_is_channel_participant(db: &Arc) { db.transaction(|tx| async move { db.check_user_is_channel_participant( - &db.get_channel_internal(public_channel_id, &*tx).await?, + &db.get_channel_internal(public_channel_id, &tx).await?, guest, - &*tx, + &tx, ) .await }) @@ -547,11 +547,11 @@ async fn test_user_is_channel_participant(db: &Arc) { assert!(db .transaction(|tx| async move { db.check_user_is_channel_participant( - &db.get_channel_internal(public_channel_id, &*tx) + &db.get_channel_internal(public_channel_id, &tx) .await .unwrap(), guest, - &*tx, + &tx, ) .await }) @@ -629,9 +629,9 @@ async fn test_user_is_channel_participant(db: &Arc) { db.transaction(|tx| async move { db.check_user_is_channel_participant( - &db.get_channel_internal(zed_channel, &*tx).await.unwrap(), + &db.get_channel_internal(zed_channel, &tx).await.unwrap(), guest, - &*tx, + &tx, ) .await }) @@ -640,11 +640,11 @@ async fn test_user_is_channel_participant(db: &Arc) { assert!(db .transaction(|tx| async move { db.check_user_is_channel_participant( - &db.get_channel_internal(internal_channel_id, &*tx) + &db.get_channel_internal(internal_channel_id, &tx) .await .unwrap(), guest, - &*tx, + &tx, ) .await }) @@ -653,11 +653,11 @@ async fn test_user_is_channel_participant(db: &Arc) { db.transaction(|tx| async move { db.check_user_is_channel_participant( - &db.get_channel_internal(public_channel_id, &*tx) + &db.get_channel_internal(public_channel_id, &tx) .await .unwrap(), guest, - &*tx, + &tx, ) .await }) diff --git a/crates/collab/src/db/tests/message_tests.rs b/crates/collab/src/db/tests/message_tests.rs index c785e3cb73..e20473d3bd 100644 --- a/crates/collab/src/db/tests/message_tests.rs +++ b/crates/collab/src/db/tests/message_tests.rs @@ -297,7 +297,7 @@ async fn test_unseen_channel_messages(db: &Arc) { // Check that observer has new messages let latest_messages = db .transaction(|tx| async move { - db.latest_channel_messages(&[channel_1, channel_2], &*tx) + db.latest_channel_messages(&[channel_1, channel_2], &tx) .await }) .await diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 074bb44bcc..28f16e1763 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -353,7 +353,7 @@ impl Server { &refreshed_room.room, &refreshed_room.channel_members, &peer, - &*pool.lock(), + &pool.lock(), ); } contacts_to_update @@ -754,13 +754,13 @@ impl<'a> Deref for ConnectionPoolGuard<'a> { type Target = ConnectionPool; fn deref(&self) -> &Self::Target { - &*self.guard + &self.guard } } impl<'a> DerefMut for ConnectionPoolGuard<'a> { fn deref_mut(&mut self) -> &mut Self::Target { - &mut *self.guard + &mut self.guard } } @@ -2226,7 +2226,7 @@ async fn request_contact( session.peer.send(connection_id, update.clone())?; } - send_notifications(&*connection_pool, &session.peer, notifications); + send_notifications(&connection_pool, &session.peer, notifications); response.send(proto::Ack {})?; Ok(()) @@ -2283,7 +2283,7 @@ async fn respond_to_contact_request( session.peer.send(connection_id, update.clone())?; } - send_notifications(&*pool, &session.peer, notifications); + send_notifications(&pool, &session.peer, notifications); } response.send(proto::Ack {})?; @@ -2451,7 +2451,7 @@ async fn invite_channel_member( session.peer.send(connection_id, update.clone())?; } - send_notifications(&*connection_pool, &session.peer, notifications); + send_notifications(&connection_pool, &session.peer, notifications); response.send(proto::Ack {})?; Ok(()) @@ -2713,7 +2713,7 @@ async fn respond_to_channel_invite( } }; - send_notifications(&*connection_pool, &session.peer, notifications); + send_notifications(&connection_pool, &session.peer, notifications); response.send(proto::Ack {})?; diff --git a/crates/collab/src/tests/test_server.rs b/crates/collab/src/tests/test_server.rs index 08e6bf9a68..7397e51f79 100644 --- a/crates/collab/src/tests/test_server.rs +++ b/crates/collab/src/tests/test_server.rs @@ -466,7 +466,7 @@ impl TestServer { let active_call_a = cx_a.read(ActiveCall::global); for (client_b, cx_b) in right { - let user_id_b = client_b.current_user_id(*cx_b).to_proto(); + let user_id_b = client_b.current_user_id(cx_b).to_proto(); active_call_a .update(*cx_a, |call, cx| call.invite(user_id_b, None, cx)) .await diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index f9aedb5d42..86312b0134 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -10052,7 +10052,7 @@ impl ViewInputHandler for Editor { .disjoint_anchors() .iter() .map(|selection| { - selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot) + selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot) }) .collect::>() }; diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 04b8c01a90..21072c908d 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -6774,7 +6774,7 @@ async fn test_following(cx: &mut gpui::TestAppContext) { move |_, leader, event, cx| { leader .read(cx) - .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx); + .add_event_to_update_proto(event, &mut update.borrow_mut(), cx); }, ) .detach(); @@ -6943,7 +6943,7 @@ async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) { cx.subscribe(&leader, move |_, leader, event, cx| { leader .read(cx) - .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx); + .add_event_to_update_proto(event, &mut update.borrow_mut(), cx); }) .detach(); } diff --git a/crates/gpui/src/app/test_context.rs b/crates/gpui/src/app/test_context.rs index e2f4808ca5..20383047e7 100644 --- a/crates/gpui/src/app/test_context.rs +++ b/crates/gpui/src/app/test_context.rs @@ -160,7 +160,7 @@ impl TestAppContext { /// Gives you an `&AppContext` for the duration of the closure pub fn read(&self, f: impl FnOnce(&AppContext) -> R) -> R { let cx = self.app.borrow(); - f(&*cx) + f(&cx) } /// Adds a new window. The Window will always be backed by a `TestWindow` which diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index daf06d9f86..0c0d434825 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -1859,7 +1859,7 @@ impl MultiBuffer { .cloned() .collect::>(); let snapshot = self.snapshot.borrow(); - excerpts_to_remove.sort_unstable_by(|a, b| a.cmp(b, &*snapshot)); + excerpts_to_remove.sort_unstable_by(|a, b| a.cmp(b, &snapshot)); drop(snapshot); log::info!("Removing excerpts {:?}", excerpts_to_remove); self.remove_excerpts(excerpts_to_remove, cx); @@ -1920,7 +1920,7 @@ impl MultiBuffer { for (ix, entry) in excerpt_ids.iter().enumerate() { if ix == 0 { - if entry.id.cmp(&ExcerptId::min(), &*snapshot).is_le() { + if entry.id.cmp(&ExcerptId::min(), &snapshot).is_le() { panic!("invalid first excerpt id {:?}", entry.id); } } else { diff --git a/crates/ui/src/components/stories/keybinding.rs b/crates/ui/src/components/stories/keybinding.rs index 980a57d7da..f7a9a86cd0 100644 --- a/crates/ui/src/components/stories/keybinding.rs +++ b/crates/ui/src/components/stories/keybinding.rs @@ -42,7 +42,7 @@ impl Render for KeybindingStory { .gap_4() .py_3() .children(chunk.map(|permutation| { - KeyBinding::new(binding(&*(permutation.join("-") + "-x"))) + KeyBinding::new(binding(&(permutation.join("-") + "-x"))) })) }), ), diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index d5d8aed39d..580703d6b0 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -451,7 +451,7 @@ impl ItemHandle for View { if item.focus_handle(cx).contains_focused(cx) && item.add_event_to_update_proto( event, - &mut *pending_update.borrow_mut(), + &mut pending_update.borrow_mut(), cx, ) && !pending_update_scheduled.load(Ordering::SeqCst) diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 836e8826c4..684091b460 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -91,7 +91,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::derive_ord_xor_partial_ord", "clippy::eq_op", "clippy::expect_fun_call", - "clippy::explicit_auto_deref", "clippy::explicit_counter_loop", "clippy::extra_unused_lifetimes", "clippy::identity_op", From 2f876471a12c3cacc4120e1d9fb3ec71a3021d05 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 22:43:00 -0500 Subject: [PATCH 032/121] Enable `clippy::extra_unused_lifetimes` (#8754) This PR enables the [`clippy::extra_unused_lifetimes`](https://rust-lang.github.io/rust-clippy/master/index.html#/extra_unused_lifetimes) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/collab_ui/src/collab_panel.rs | 2 +- crates/gpui/src/app/test_context.rs | 2 +- crates/project_core/src/worktree.rs | 2 +- tooling/xtask/src/main.rs | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index d131e9feae..1018cb97d7 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -1629,7 +1629,7 @@ impl CollabPanel { self.toggle_channel_collapsed(id, cx) } - fn toggle_channel_collapsed<'a>(&mut self, channel_id: ChannelId, cx: &mut ViewContext) { + fn toggle_channel_collapsed(&mut self, channel_id: ChannelId, cx: &mut ViewContext) { match self.collapsed_channels.binary_search(&channel_id) { Ok(ix) => { self.collapsed_channels.remove(ix); diff --git a/crates/gpui/src/app/test_context.rs b/crates/gpui/src/app/test_context.rs index 20383047e7..6ceff76c38 100644 --- a/crates/gpui/src/app/test_context.rs +++ b/crates/gpui/src/app/test_context.rs @@ -575,7 +575,7 @@ pub struct VisualTestContext { window: AnyWindowHandle, } -impl<'a> VisualTestContext { +impl VisualTestContext { /// Get the underlying window handle underlying this context. pub fn handle(&self) -> AnyWindowHandle { self.window diff --git a/crates/project_core/src/worktree.rs b/crates/project_core/src/worktree.rs index a7b8e23b3c..e5d50557f9 100644 --- a/crates/project_core/src/worktree.rs +++ b/crates/project_core/src/worktree.rs @@ -220,7 +220,7 @@ impl Deref for WorkDirectoryEntry { } } -impl<'a> From for WorkDirectoryEntry { +impl From for WorkDirectoryEntry { fn from(value: ProjectEntryId) -> Self { WorkDirectoryEntry(value) } diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 684091b460..d97ea6ebbd 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -92,7 +92,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::eq_op", "clippy::expect_fun_call", "clippy::explicit_counter_loop", - "clippy::extra_unused_lifetimes", "clippy::identity_op", "clippy::implied_bounds_in_impls", "clippy::iter_kv_map", From 191fcf67d1f07de76c1a2cd3a2751e34f5aeb8c5 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 22:57:37 -0500 Subject: [PATCH 033/121] Enable `clippy::nonminimal_bool` (#8755) This PR enables the [`clippy::nonminimal_bool`](https://rust-lang.github.io/rust-clippy/master/index.html#/nonminimal_bool) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/collab_ui/src/collab_panel.rs | 2 +- crates/project/src/project.rs | 1 + crates/terminal_view/src/terminal_view.rs | 4 ++-- crates/vim/src/object.rs | 2 +- crates/vim/src/visual.rs | 1 + tooling/xtask/src/main.rs | 1 - 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 1018cb97d7..8ef00d82b3 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -2506,7 +2506,7 @@ impl CollabPanel { .map(|channel| channel.visibility) == Some(proto::ChannelVisibility::Public); let disclosed = - has_children.then(|| !self.collapsed_channels.binary_search(&channel.id).is_ok()); + has_children.then(|| self.collapsed_channels.binary_search(&channel.id).is_err()); let has_messages_notification = channel_store.has_new_messages(channel_id); let has_notes_notification = channel_store.has_channel_buffer_changed(channel_id); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 56ab5c3fc9..5254ec4d8b 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4283,6 +4283,7 @@ impl Project { }) .collect(); + #[allow(clippy::nonminimal_bool)] if !code_actions.is_empty() && !(trigger == FormatTrigger::Save && settings.format_on_save == FormatOnSave::Off) diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 584469d353..00cda8fc32 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -454,7 +454,7 @@ fn subscribe_for_terminal_events( Event::TitleChanged => { cx.emit(ItemEvent::UpdateTab); let terminal = this.terminal().read(cx); - if !terminal.task().is_some() { + if terminal.task().is_none() { if let Some(foreground_info) = &terminal.foreground_process_info { let cwd = foreground_info.cwd.clone(); @@ -897,7 +897,7 @@ impl Item for TerminalView { } fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext) { - if !self.terminal().read(cx).task().is_some() { + if self.terminal().read(cx).task().is_none() { cx.background_executor() .spawn(TERMINAL_DB.update_workspace_id( workspace.database_id(), diff --git a/crates/vim/src/object.rs b/crates/vim/src/object.rs index e439df48f8..6abe66c1fd 100644 --- a/crates/vim/src/object.rs +++ b/crates/vim/src/object.rs @@ -458,7 +458,7 @@ fn argument( parent_covers_bracket_range = covers_bracket_range; // Unable to find a child node with a parent that covers the bracket range, so no argument to select - if !cursor.goto_first_child_for_byte(offset).is_some() { + if cursor.goto_first_child_for_byte(offset).is_none() { return None; } } diff --git a/crates/vim/src/visual.rs b/crates/vim/src/visual.rs index 4c05506f46..3f68ddce75 100644 --- a/crates/vim/src/visual.rs +++ b/crates/vim/src/visual.rs @@ -87,6 +87,7 @@ pub fn visual_motion(motion: Motion, times: Option, cx: &mut WindowContex // If the file ends with a newline (which is common) we don't do this. // so that if you go to the end of such a file you can use "up" to go // to the previous line and have it work somewhat as expected. + #[allow(clippy::nonminimal_bool)] if !selection.reversed && !selection.is_empty() && !(selection.end.column() == 0 && selection.end == map.max_point()) diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index d97ea6ebbd..241cb96bc6 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -105,7 +105,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::never_loop", "clippy::non_canonical_clone_impl", "clippy::non_canonical_partial_ord_impl", - "clippy::nonminimal_bool", "clippy::redundant_closure_call", "clippy::redundant_guards", "clippy::reversed_empty_ranges", From 373e18bc88e4aa9c55c660da72f38ceb14fed986 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 23:10:56 -0500 Subject: [PATCH 034/121] Enable `clippy::unnecessary_unwrap` (#8756) This PR enables the [`clippy::unnecessary_unwrap`](https://rust-lang.github.io/rust-clippy/master/index.html#/unnecessary_unwrap) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/ai/src/embedding.rs | 8 +++----- crates/ui/src/components/popover_menu.rs | 6 ++++-- crates/ui/src/components/right_click_menu.rs | 15 ++++++++------- tooling/xtask/src/main.rs | 1 - 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/crates/ai/src/embedding.rs b/crates/ai/src/embedding.rs index 1be99d7b06..49611e002a 100644 --- a/crates/ai/src/embedding.rs +++ b/crates/ai/src/embedding.rs @@ -19,11 +19,9 @@ pub struct Embedding(pub Vec); impl FromSql for Embedding { fn column_result(value: ValueRef) -> FromSqlResult { let bytes = value.as_blob()?; - let embedding: Result, Box> = bincode::deserialize(bytes); - if embedding.is_err() { - return Err(rusqlite::types::FromSqlError::Other(embedding.unwrap_err())); - } - Ok(Embedding(embedding.unwrap())) + let embedding = + bincode::deserialize(bytes).map_err(|err| rusqlite::types::FromSqlError::Other(err))?; + Ok(Embedding(embedding)) } } diff --git a/crates/ui/src/components/popover_menu.rs b/crates/ui/src/components/popover_menu.rs index 0bbbee2900..de4de73f42 100644 --- a/crates/ui/src/components/popover_menu.rs +++ b/crates/ui/src/components/popover_menu.rs @@ -51,8 +51,10 @@ impl PopoverMenu { cx.subscribe(&new_menu, move |modal, _: &DismissEvent, cx| { if modal.focus_handle(cx).contains_focused(cx) { - if previous_focus_handle.is_some() { - cx.focus(previous_focus_handle.as_ref().unwrap()) + if let Some(previous_focus_handle) = + previous_focus_handle.as_ref() + { + cx.focus(previous_focus_handle); } } *menu2.borrow_mut() = None; diff --git a/crates/ui/src/components/right_click_menu.rs b/crates/ui/src/components/right_click_menu.rs index 8ad72c7b9e..4f7f062ec4 100644 --- a/crates/ui/src/components/right_click_menu.rs +++ b/crates/ui/src/components/right_click_menu.rs @@ -154,8 +154,8 @@ impl Element for RightClickMenu { cx.subscribe(&new_menu, move |modal, _: &DismissEvent, cx| { if modal.focus_handle(cx).contains_focused(cx) { - if previous_focus_handle.is_some() { - cx.focus(previous_focus_handle.as_ref().unwrap()) + if let Some(previous_focus_handle) = previous_focus_handle.as_ref() { + cx.focus(previous_focus_handle); } } *menu2.borrow_mut() = None; @@ -165,11 +165,12 @@ impl Element for RightClickMenu { cx.focus_view(&new_menu); *menu.borrow_mut() = Some(new_menu); - *position.borrow_mut() = if attach.is_some() && child_layout_id.is_some() { - attach.unwrap().corner(child_bounds) - } else { - cx.mouse_position() - }; + *position.borrow_mut() = + if let Some(attach) = attach.filter(|_| child_layout_id.is_some()) { + attach.corner(child_bounds) + } else { + cx.mouse_position() + }; cx.refresh(); } }); diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 241cb96bc6..515436e58c 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -112,7 +112,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::suspicious_to_owned", "clippy::type_complexity", "clippy::unnecessary_to_owned", - "clippy::unnecessary_unwrap", "clippy::useless_conversion", "clippy::useless_format", "clippy::vec_init_then_push", From 33790b81fc67721b5c840ef83cb065c0f66b8876 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 23:31:58 -0500 Subject: [PATCH 035/121] Enable `clippy::useless_format` (#8758) This PR enables the [`clippy::useless_format`](https://rust-lang.github.io/rust-clippy/master/index.html#/useless_format) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/collab/src/api/events.rs | 72 +++++++++++++------ crates/collab/src/db/tests.rs | 2 +- .../collab/src/db/tests/contributor_tests.rs | 4 +- crates/collab/src/db/tests/db_tests.rs | 4 +- .../collab/src/db/tests/feature_flag_tests.rs | 8 +-- crates/collab_ui/src/channel_view.rs | 2 +- .../src/chat_panel/message_editor.rs | 4 +- crates/collab_ui/src/collab_panel.rs | 4 +- crates/collab_ui/src/notification_panel.rs | 2 +- crates/editor/src/movement.rs | 8 +-- crates/vim/src/test/neovim_connection.rs | 5 +- tooling/xtask/src/main.rs | 1 - 12 files changed, 70 insertions(+), 46 deletions(-) diff --git a/crates/collab/src/api/events.rs b/crates/collab/src/api/events.rs index f15383232a..40700a859b 100644 --- a/crates/collab/src/api/events.rs +++ b/crates/collab/src/api/events.rs @@ -355,40 +355,68 @@ struct ToUpload { impl ToUpload { pub async fn upload(&self, clickhouse_client: &clickhouse::Client) -> anyhow::Result<()> { - Self::upload_to_table("editor_events", &self.editor_events, clickhouse_client) + const EDITOR_EVENTS_TABLE: &str = "editor_events"; + Self::upload_to_table(EDITOR_EVENTS_TABLE, &self.editor_events, clickhouse_client) .await - .with_context(|| format!("failed to upload to table 'editor_events'"))?; - Self::upload_to_table("copilot_events", &self.copilot_events, clickhouse_client) - .await - .with_context(|| format!("failed to upload to table 'copilot_events'"))?; + .with_context(|| format!("failed to upload to table '{EDITOR_EVENTS_TABLE}'"))?; + + const COPILOT_EVENTS_TABLE: &str = "copilot_events"; Self::upload_to_table( - "assistant_events", + COPILOT_EVENTS_TABLE, + &self.copilot_events, + clickhouse_client, + ) + .await + .with_context(|| format!("failed to upload to table '{COPILOT_EVENTS_TABLE}'"))?; + + const ASSISTANT_EVENTS_TABLE: &str = "assistant_events"; + Self::upload_to_table( + ASSISTANT_EVENTS_TABLE, &self.assistant_events, clickhouse_client, ) .await - .with_context(|| format!("failed to upload to table 'assistant_events'"))?; - Self::upload_to_table("call_events", &self.call_events, clickhouse_client) + .with_context(|| format!("failed to upload to table '{ASSISTANT_EVENTS_TABLE}'"))?; + + const CALL_EVENTS_TABLE: &str = "call_events"; + Self::upload_to_table(CALL_EVENTS_TABLE, &self.call_events, clickhouse_client) .await - .with_context(|| format!("failed to upload to table 'call_events'"))?; - Self::upload_to_table("cpu_events", &self.cpu_events, clickhouse_client) + .with_context(|| format!("failed to upload to table '{CALL_EVENTS_TABLE}'"))?; + + const CPU_EVENTS_TABLE: &str = "cpu_events"; + Self::upload_to_table(CPU_EVENTS_TABLE, &self.cpu_events, clickhouse_client) .await - .with_context(|| format!("failed to upload to table 'cpu_events'"))?; - Self::upload_to_table("memory_events", &self.memory_events, clickhouse_client) + .with_context(|| format!("failed to upload to table '{CPU_EVENTS_TABLE}'"))?; + + const MEMORY_EVENTS_TABLE: &str = "memory_events"; + Self::upload_to_table(MEMORY_EVENTS_TABLE, &self.memory_events, clickhouse_client) .await - .with_context(|| format!("failed to upload to table 'memory_events'"))?; - Self::upload_to_table("app_events", &self.app_events, clickhouse_client) + .with_context(|| format!("failed to upload to table '{MEMORY_EVENTS_TABLE}'"))?; + + const APP_EVENTS_TABLE: &str = "app_events"; + Self::upload_to_table(APP_EVENTS_TABLE, &self.app_events, clickhouse_client) .await - .with_context(|| format!("failed to upload to table 'app_events'"))?; - Self::upload_to_table("setting_events", &self.setting_events, clickhouse_client) + .with_context(|| format!("failed to upload to table '{APP_EVENTS_TABLE}'"))?; + + const SETTING_EVENTS_TABLE: &str = "setting_events"; + Self::upload_to_table( + SETTING_EVENTS_TABLE, + &self.setting_events, + clickhouse_client, + ) + .await + .with_context(|| format!("failed to upload to table '{SETTING_EVENTS_TABLE}'"))?; + + const EDIT_EVENTS_TABLE: &str = "edit_events"; + Self::upload_to_table(EDIT_EVENTS_TABLE, &self.edit_events, clickhouse_client) .await - .with_context(|| format!("failed to upload to table 'setting_events'"))?; - Self::upload_to_table("edit_events", &self.edit_events, clickhouse_client) + .with_context(|| format!("failed to upload to table '{EDIT_EVENTS_TABLE}'"))?; + + const ACTION_EVENTS_TABLE: &str = "action_events"; + Self::upload_to_table(ACTION_EVENTS_TABLE, &self.action_events, clickhouse_client) .await - .with_context(|| format!("failed to upload to table 'edit_events'"))?; - Self::upload_to_table("action_events", &self.action_events, clickhouse_client) - .await - .with_context(|| format!("failed to upload to table 'action_events'"))?; + .with_context(|| format!("failed to upload to table '{ACTION_EVENTS_TABLE}'"))?; + Ok(()) } diff --git a/crates/collab/src/db/tests.rs b/crates/collab/src/db/tests.rs index b6116c2e77..b07f7a84e1 100644 --- a/crates/collab/src/db/tests.rs +++ b/crates/collab/src/db/tests.rs @@ -23,7 +23,7 @@ pub struct TestDb { impl TestDb { pub fn sqlite(background: BackgroundExecutor) -> Self { - let url = format!("sqlite::memory:"); + let url = "sqlite::memory:"; let runtime = tokio::runtime::Builder::new_current_thread() .enable_io() .enable_time() diff --git a/crates/collab/src/db/tests/contributor_tests.rs b/crates/collab/src/db/tests/contributor_tests.rs index f3a9dfbd40..a51817574a 100644 --- a/crates/collab/src/db/tests/contributor_tests.rs +++ b/crates/collab/src/db/tests/contributor_tests.rs @@ -10,10 +10,10 @@ test_both_dbs!( async fn test_contributors(db: &Arc) { db.create_user( - &format!("user1@example.com"), + "user1@example.com", false, NewUserParams { - github_login: format!("user1"), + github_login: "user1".to_string(), github_user_id: 1, }, ) diff --git a/crates/collab/src/db/tests/db_tests.rs b/crates/collab/src/db/tests/db_tests.rs index 54ce3f6f41..2edaaa4314 100644 --- a/crates/collab/src/db/tests/db_tests.rs +++ b/crates/collab/src/db/tests/db_tests.rs @@ -494,7 +494,7 @@ async fn test_project_count(db: &Arc) { let user1 = db .create_user( - &format!("admin@example.com"), + "admin@example.com", true, NewUserParams { github_login: "admin".into(), @@ -505,7 +505,7 @@ async fn test_project_count(db: &Arc) { .unwrap(); let user2 = db .create_user( - &format!("user@example.com"), + "user@example.com", false, NewUserParams { github_login: "user".into(), diff --git a/crates/collab/src/db/tests/feature_flag_tests.rs b/crates/collab/src/db/tests/feature_flag_tests.rs index 32ce4c4d23..5269d5354f 100644 --- a/crates/collab/src/db/tests/feature_flag_tests.rs +++ b/crates/collab/src/db/tests/feature_flag_tests.rs @@ -13,10 +13,10 @@ test_both_dbs!( async fn test_get_user_flags(db: &Arc) { let user_1 = db .create_user( - &format!("user1@example.com"), + "user1@example.com", false, NewUserParams { - github_login: format!("user1"), + github_login: "user1".to_string(), github_user_id: 1, }, ) @@ -26,10 +26,10 @@ async fn test_get_user_flags(db: &Arc) { let user_2 = db .create_user( - &format!("user2@example.com"), + "user2@example.com", false, NewUserParams { - github_login: format!("user2"), + github_login: "user2".to_string(), github_user_id: 2, }, ) diff --git a/crates/collab_ui/src/channel_view.rs b/crates/collab_ui/src/channel_view.rs index a46499214d..1cc48591c7 100644 --- a/crates/collab_ui/src/channel_view.rs +++ b/crates/collab_ui/src/channel_view.rs @@ -376,7 +376,7 @@ impl Item for ChannelView { (_, false) => format!("#{} (disconnected)", channel.name), } } else { - format!("channel notes (disconnected)") + "channel notes (disconnected)".to_string() }; Label::new(label) .color(if selected { diff --git a/crates/collab_ui/src/chat_panel/message_editor.rs b/crates/collab_ui/src/chat_panel/message_editor.rs index 5e95cdc450..0f0717326a 100644 --- a/crates/collab_ui/src/chat_panel/message_editor.rs +++ b/crates/collab_ui/src/chat_panel/message_editor.rs @@ -137,9 +137,9 @@ impl MessageEditor { ) { self.editor.update(cx, |editor, cx| { if let Some(channel_name) = channel_name { - editor.set_placeholder_text(format!("Message #{}", channel_name), cx); + editor.set_placeholder_text(format!("Message #{channel_name}"), cx); } else { - editor.set_placeholder_text(format!("Message Channel"), cx); + editor.set_placeholder_text("Message Channel", cx); } }); self.channel_id = Some(channel_id); diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 8ef00d82b3..54591bd5de 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -952,7 +952,7 @@ impl CollabPanel { }) .ok(); })) - .tooltip(move |cx| Tooltip::text(format!("Open shared screen"), cx)) + .tooltip(move |cx| Tooltip::text("Open shared screen", cx)) }) } @@ -2220,7 +2220,7 @@ impl CollabPanel { }); if let Some(name) = channel_name { - SharedString::from(format!("{}", name)) + SharedString::from(name.to_string()) } else { SharedString::from("Current Call") } diff --git a/crates/collab_ui/src/notification_panel.rs b/crates/collab_ui/src/notification_panel.rs index 15ec8410bc..6bd4abe588 100644 --- a/crates/collab_ui/src/notification_panel.rs +++ b/crates/collab_ui/src/notification_panel.rs @@ -778,7 +778,7 @@ fn format_timestamp( "just now".to_string() } } else if date.next_day() == Some(today) { - format!("yesterday") + "yesterday".to_string() } else { format!("{:02}/{}/{}", date.month() as u32, date.day(), date.year()) } diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index 39c22dd5c4..34299ffad6 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -693,22 +693,22 @@ mod tests { Inlay { id: InlayId::Suggestion(post_inc(&mut id)), position: buffer_snapshot.anchor_at(offset, Bias::Left), - text: format!("test").into(), + text: "test".into(), }, Inlay { id: InlayId::Suggestion(post_inc(&mut id)), position: buffer_snapshot.anchor_at(offset, Bias::Right), - text: format!("test").into(), + text: "test".into(), }, Inlay { id: InlayId::Hint(post_inc(&mut id)), position: buffer_snapshot.anchor_at(offset, Bias::Left), - text: format!("test").into(), + text: "test".into(), }, Inlay { id: InlayId::Hint(post_inc(&mut id)), position: buffer_snapshot.anchor_at(offset, Bias::Right), - text: format!("test").into(), + text: "test".into(), }, ] }) diff --git a/crates/vim/src/test/neovim_connection.rs b/crates/vim/src/test/neovim_connection.rs index 63951f73d0..3d47789fac 100644 --- a/crates/vim/src/test/neovim_connection.rs +++ b/crates/vim/src/test/neovim_connection.rs @@ -272,10 +272,7 @@ impl NeovimConnection { #[cfg(feature = "neovim")] pub async fn exec(&mut self, value: &str) { - self.nvim - .command_output(format!("{}", value).as_str()) - .await - .unwrap(); + self.nvim.command_output(value).await.unwrap(); self.data.push_back(NeovimData::Exec { command: value.to_string(), diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 515436e58c..df5e689044 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -113,7 +113,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::type_complexity", "clippy::unnecessary_to_owned", "clippy::useless_conversion", - "clippy::useless_format", "clippy::vec_init_then_push", ]; From a6dbaac6538c1648de1943f071d006d5acb5a5bc Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 2 Mar 2024 23:40:39 -0500 Subject: [PATCH 036/121] Enable `clippy::needless_question_mark` (#8759) This PR enables the [`clippy::needless_question_mark`](https://rust-lang.github.io/rust-clippy/master/index.html#/needless_question_mark) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/collab/src/api.rs | 5 ++--- crates/collab/src/db/queries/rooms.rs | 2 +- crates/collab/src/db/tests/channel_tests.rs | 2 +- crates/editor/src/items.rs | 4 ++-- crates/project/src/project.rs | 12 ++++++------ crates/project_panel/src/project_panel.rs | 2 +- crates/semantic_index/src/db.rs | 8 ++++---- crates/workspace/src/persistence.rs | 4 ++-- crates/workspace/src/workspace.rs | 9 ++++----- tooling/xtask/src/main.rs | 1 - 10 files changed, 23 insertions(+), 26 deletions(-) diff --git a/crates/collab/src/api.rs b/crates/collab/src/api.rs index 2d42beccc8..d227211fc1 100644 --- a/crates/collab/src/api.rs +++ b/crates/collab/src/api.rs @@ -179,14 +179,13 @@ async fn add_contributor( Json(params): Json, Extension(app): Extension>, ) -> Result<()> { - Ok(app - .db + app.db .add_contributor( ¶ms.github_login, params.github_user_id, params.github_email.as_deref(), ) - .await?) + .await } #[derive(Deserialize)] diff --git a/crates/collab/src/db/queries/rooms.rs b/crates/collab/src/db/queries/rooms.rs index f666391cde..c3a0735a37 100644 --- a/crates/collab/src/db/queries/rooms.rs +++ b/crates/collab/src/db/queries/rooms.rs @@ -1030,7 +1030,7 @@ impl Database { if result.rows_affected != 1 { Err(anyhow!("could not update room participant role"))?; } - Ok(self.get_room(room_id, &tx).await?) + self.get_room(room_id, &tx).await }) .await } diff --git a/crates/collab/src/db/tests/channel_tests.rs b/crates/collab/src/db/tests/channel_tests.rs index 99bf44744f..54be002c41 100644 --- a/crates/collab/src/db/tests/channel_tests.rs +++ b/crates/collab/src/db/tests/channel_tests.rs @@ -43,7 +43,7 @@ async fn test_channels(db: &Arc) { let mut members = db .transaction(|tx| async move { let channel = db.get_channel_internal(replace_id, &tx).await?; - Ok(db.get_channel_participants(&channel, &tx).await?) + db.get_channel_participants(&channel, &tx).await }) .await .unwrap(); diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 64f1d3770b..8eb1119110 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -952,14 +952,14 @@ impl Item for Editor { let buffer = project_item .downcast::() .map_err(|_| anyhow!("Project item at stored path was not a buffer"))?; - Ok(pane.update(&mut cx, |_, cx| { + pane.update(&mut cx, |_, cx| { cx.new_view(|cx| { let mut editor = Editor::for_buffer(buffer, Some(project), cx); editor.read_scroll_position_from_db(item_id, workspace_id, cx); editor }) - })?) + }) }) }) .unwrap_or_else(|error| Task::ready(Err(error))) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 5254ec4d8b..c630a7dc9d 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -5424,11 +5424,11 @@ impl Project { return Err(err); } - return Ok(this.update(&mut cx, |this, _| { + return this.update(&mut cx, |this, _| { this.last_workspace_edits_by_language_server .remove(&lang_server.server_id()) .unwrap_or_default() - })?); + }); } Ok(ProjectTransaction::default()) @@ -7865,13 +7865,13 @@ impl Project { this.update(&mut cx, |this, cx| this.save_buffer(buffer.clone(), cx))? .await?; - Ok(buffer.update(&mut cx, |buffer, _| proto::BufferSaved { + buffer.update(&mut cx, |buffer, _| proto::BufferSaved { project_id, buffer_id: buffer_id.into(), version: serialize_version(buffer.saved_version()), mtime: Some(buffer.saved_mtime().into()), fingerprint: language::proto::serialize_fingerprint(buffer.saved_version_fingerprint()), - })?) + }) } async fn handle_reload_buffers( @@ -8206,7 +8206,7 @@ impl Project { .await .context("inlay hints fetch")?; - Ok(this.update(&mut cx, |project, cx| { + this.update(&mut cx, |project, cx| { InlayHints::response_to_proto( buffer_hints, project, @@ -8214,7 +8214,7 @@ impl Project { &buffer.read(cx).version(), cx, ) - })?) + }) } async fn handle_resolve_inlay_hint( diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 17e0e75e15..a065ad4b80 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1028,7 +1028,7 @@ impl ProjectPanel { cx.foreground_executor().spawn(task).detach_and_log_err(cx); } - Some(project.worktree_id_for_entry(destination, cx)?) + project.worktree_id_for_entry(destination, cx) }); if let Some(destination_worktree) = destination_worktree { diff --git a/crates/semantic_index/src/db.rs b/crates/semantic_index/src/db.rs index 7deb05200e..242e80026a 100644 --- a/crates/semantic_index/src/db.rs +++ b/crates/semantic_index/src/db.rs @@ -125,7 +125,7 @@ impl VectorDatabase { // Delete existing tables, if SEMANTIC_INDEX_VERSION is bumped let version_query = db.prepare("SELECT version from semantic_index_config"); let version = version_query - .and_then(|mut query| query.query_row([], |row| Ok(row.get::<_, i64>(0)?))); + .and_then(|mut query| query.query_row([], |row| row.get::<_, i64>(0))); if version.map_or(false, |version| version == SEMANTIC_INDEX_VERSION as i64) { log::trace!("vector database schema up to date"); return Ok(()); @@ -275,8 +275,8 @@ impl VectorDatabase { self.transact(move |db| { let mut worktree_query = db.prepare("SELECT id FROM worktrees WHERE absolute_path = ?1")?; - let worktree_id = worktree_query - .query_row(params![worktree_root_path], |row| Ok(row.get::<_, i64>(0)?)); + let worktree_id = + worktree_query.query_row(params![worktree_root_path], |row| row.get::<_, i64>(0)); Ok(worktree_id.is_ok()) }) @@ -356,7 +356,7 @@ impl VectorDatabase { db.prepare("SELECT id FROM worktrees WHERE absolute_path = ?1")?; let worktree_id = worktree_query .query_row(params![worktree_root_path.to_string_lossy()], |row| { - Ok(row.get::<_, i64>(0)?) + row.get::<_, i64>(0) }); if worktree_id.is_ok() { diff --git a/crates/workspace/src/persistence.rs b/crates/workspace/src/persistence.rs index 870d7b7673..9f99233c27 100644 --- a/crates/workspace/src/persistence.rs +++ b/crates/workspace/src/persistence.rs @@ -622,11 +622,11 @@ impl WorkspaceDb { } fn get_items(&self, pane_id: PaneId) -> Result> { - Ok(self.select_bound(sql!( + self.select_bound(sql!( SELECT kind, item_id, active FROM items WHERE pane_id = ? ORDER BY position - ))?(pane_id)?) + ))?(pane_id) } fn save_items( diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index ad964cfadf..f469538472 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1243,11 +1243,10 @@ impl Workspace { } } - Ok(this - .update(&mut cx, |this, cx| { - this.save_all_internal(SaveIntent::Close, cx) - })? - .await?) + this.update(&mut cx, |this, cx| { + this.save_all_internal(SaveIntent::Close, cx) + })? + .await }) } diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index df5e689044..9881eb4c66 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -100,7 +100,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::map_entry", "clippy::needless_lifetimes", "clippy::needless_option_as_deref", - "clippy::needless_question_mark", "clippy::needless_update", "clippy::never_loop", "clippy::non_canonical_clone_impl", From 20d133322a39bde613091221e4849c79d90ccf14 Mon Sep 17 00:00:00 2001 From: geekvest <126322776+geekvest@users.noreply.github.com> Date: Sun, 3 Mar 2024 20:55:42 +0800 Subject: [PATCH 037/121] Fix some comments (#8760) Release Notes: - N/A Signed-off-by: geekvest --- crates/gpui/README.md | 2 +- crates/gpui/src/gpui.rs | 2 +- crates/language/src/buffer.rs | 2 +- crates/languages/src/python.rs | 2 +- docs/src/tasks.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/gpui/README.md b/crates/gpui/README.md index 5278b024ae..ac2c04bf07 100644 --- a/crates/gpui/README.md +++ b/crates/gpui/README.md @@ -21,7 +21,7 @@ GPUI offers three different [registers](https://en.wikipedia.org/wiki/Register_( - High level, declarative UI with Views. All UI in GPUI starts with a View. A view is simply a model that can be rendered, via the `Render` trait. At the start of each frame, GPUI will call this render method on the root view of a given window. Views build a tree of `elements`, lay them out and style them with a tailwind-style API, and then give them to GPUI to turn into pixels. See the `div` element for an all purpose swiss-army knife of rendering. -- Low level, imperative UI with Elements. Elements are the building blocks of UI in GPUI, and they provide a nice wrapper around an imperative API that provides as much flexibility and control as you need. Elements have total control over how they and their child elements are rendered and and can be used for making efficient views into large lists, implement custom layouting for a code editor, and anything else you can think of. See the `element` module for more information. +- Low level, imperative UI with Elements. Elements are the building blocks of UI in GPUI, and they provide a nice wrapper around an imperative API that provides as much flexibility and control as you need. Elements have total control over how they and their child elements are rendered and can be used for making efficient views into large lists, implement custom layouting for a code editor, and anything else you can think of. See the `element` module for more information. Each of these registers has one or more corresponding contexts that can be accessed from all GPUI services. This context is your main interface to GPUI, and is used extensively throughout the framework. diff --git a/crates/gpui/src/gpui.rs b/crates/gpui/src/gpui.rs index ff1daa59ea..ab9898d3da 100644 --- a/crates/gpui/src/gpui.rs +++ b/crates/gpui/src/gpui.rs @@ -35,7 +35,7 @@ //! //! - Low level, imperative UI with Elements. Elements are the building blocks of UI in GPUI, and they //! provide a nice wrapper around an imperative API that provides as much flexibility and control as -//! you need. Elements have total control over how they and their child elements are rendered and and +//! you need. Elements have total control over how they and their child elements are rendered and //! can be used for making efficient views into large lists, implement custom layouting for a code editor, //! and anything else you can think of. See the [`element`] module for more information. //! diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 428e4b33f9..e1d98d23bd 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1328,7 +1328,7 @@ impl Buffer { self.edit(edits, None, cx); } - /// Create a minimal edit that will cause the the given row to be indented + /// Create a minimal edit that will cause the given row to be indented /// with the given size. After applying this edit, the length of the line /// will always be at least `new_size.len`. pub fn edit_for_indent_size_adjustment( diff --git a/crates/languages/src/python.rs b/crates/languages/src/python.rs index 09c3ddb312..bf1cae7d9d 100644 --- a/crates/languages/src/python.rs +++ b/crates/languages/src/python.rs @@ -83,7 +83,7 @@ impl LspAdapter for PythonLspAdapter { // Where `XX` is the sorting category, `YYYY` is based on most recent usage, // and `name` is the symbol name itself. // - // Because the the symbol name is included, there generally are not ties when + // Because the symbol name is included, there generally are not ties when // sorting by the `sortText`, so the symbol's fuzzy match score is not taken // into account. Here, we remove the symbol name from the sortText in order // to allow our own fuzzy score to be used to break ties. diff --git a/docs/src/tasks.md b/docs/src/tasks.md index bfe6f8f983..cebcf75cf2 100644 --- a/docs/src/tasks.md +++ b/docs/src/tasks.md @@ -4,7 +4,7 @@ Zed supports ways to spawn (and rerun) commands using its integrated terminal to Currently, two kinds of tasks are supported, but more will be added in the future. -All tasks are are sorted in LRU order and their names can be used (with `menu::UseSelectedQuery`, `shift-enter` by default) as an input text for quicker oneshot task edit-spawn cycle. +All tasks are sorted in LRU order and their names can be used (with `menu::UseSelectedQuery`, `shift-enter` by default) as an input text for quicker oneshot task edit-spawn cycle. ## Static tasks From fe04f69caff9cf5e9075bcd7772518bc9787bea0 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sun, 3 Mar 2024 10:22:55 -0500 Subject: [PATCH 038/121] Enable `clippy::useless_conversion` (#8767) This PR enables the [`clippy::useless_conversion`](https://rust-lang.github.io/rust-clippy/master/index.html#/useless_conversion) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/assistant/src/assistant_panel.rs | 5 ++--- crates/collab/src/api/ips_file.rs | 2 +- crates/collab/src/db/queries/rooms.rs | 2 +- crates/collab/src/rpc.rs | 12 +++++------ crates/collab/src/tests/channel_tests.rs | 2 +- crates/collab/src/tests/editor_tests.rs | 2 +- .../src/chat_panel/message_editor.rs | 2 +- crates/collab_ui/src/collab_panel.rs | 2 +- crates/copilot_ui/src/copilot_button.rs | 2 +- crates/diagnostics/src/diagnostics.rs | 2 +- crates/editor/src/display_map.rs | 20 +++++++------------ crates/editor/src/element.rs | 15 ++++++-------- crates/editor/src/rust_analyzer_ext.rs | 1 - crates/editor/src/scroll/autoscroll.rs | 7 +++---- crates/extension/src/extension_lsp_adapter.rs | 2 +- crates/extensions_ui/src/extensions_ui.rs | 2 +- crates/gpui/src/app/test_context.rs | 4 ++-- crates/language/src/syntax_map.rs | 4 ++-- .../src/markdown_preview_view.rs | 2 +- crates/outline/src/outline.rs | 2 +- crates/recent_projects/src/recent_projects.rs | 2 +- crates/search/src/buffer_search.rs | 2 +- crates/search/src/project_search.rs | 2 +- crates/terminal/src/terminal.rs | 8 ++++---- crates/vim/src/command.rs | 2 +- crates/workspace/src/dock.rs | 2 +- crates/workspace/src/pane.rs | 2 +- crates/workspace/src/workspace.rs | 4 ++-- crates/zed/src/main.rs | 2 +- tooling/xtask/src/main.rs | 1 - 30 files changed, 53 insertions(+), 66 deletions(-) diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index fcc033ef96..97f0bca083 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -979,7 +979,7 @@ impl AssistantPanel { font_size: rems(0.875).into(), font_weight: FontWeight::NORMAL, font_style: FontStyle::Normal, - line_height: relative(1.3).into(), + line_height: relative(1.3), background_color: None, underline: None, strikethrough: None, @@ -1633,7 +1633,6 @@ impl Conversation { fn count_remaining_tokens(&mut self, cx: &mut ModelContext) { let messages = self .messages(cx) - .into_iter() .map(|message| tiktoken_rs::ChatCompletionRequestMessage { role: match message.role { Role::User => "user".into(), @@ -3199,7 +3198,7 @@ impl InlineAssistant { font_size: rems(0.875).into(), font_weight: FontWeight::NORMAL, font_style: FontStyle::Normal, - line_height: relative(1.3).into(), + line_height: relative(1.3), background_color: None, underline: None, strikethrough: None, diff --git a/crates/collab/src/api/ips_file.rs b/crates/collab/src/api/ips_file.rs index 6f1b4cd8f4..cbbf042abf 100644 --- a/crates/collab/src/api/ips_file.rs +++ b/crates/collab/src/api/ips_file.rs @@ -45,7 +45,7 @@ impl IpsFile { pub fn description(&self, panic: Option<&str>) -> String { let mut desc = if self.body.termination.indicator == "Abort trap: 6" { match panic { - Some(panic_message) => format!("Panic `{}`", panic_message).into(), + Some(panic_message) => format!("Panic `{}`", panic_message), None => "Crash `Abort trap: 6` (possible panic)".into(), } } else if let Some(msg) = &self.body.exception.message { diff --git a/crates/collab/src/db/queries/rooms.rs b/crates/collab/src/db/queries/rooms.rs index c3a0735a37..cd960ec5a7 100644 --- a/crates/collab/src/db/queries/rooms.rs +++ b/crates/collab/src/db/queries/rooms.rs @@ -1021,7 +1021,7 @@ impl Database { .add(room_participant::Column::UserId.eq(user_id)), ) .set(room_participant::ActiveModel { - role: ActiveValue::set(Some(ChannelRole::from(role))), + role: ActiveValue::set(Some(role)), ..Default::default() }) .exec(&*tx) diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 28f16e1763..60388928c6 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -2883,7 +2883,7 @@ async fn update_channel_buffer( .flat_map(|user_id| pool.user_connection_ids(*user_id)), |peer_id| { session.peer.send( - peer_id.into(), + peer_id, proto::UpdateChannels { latest_channel_buffer_versions: vec![proto::ChannelBufferVersion { channel_id: channel_id.to_proto(), @@ -2968,8 +2968,8 @@ fn channel_buffer_updated( message: &T, peer: &Peer, ) { - broadcast(Some(sender_id), collaborators.into_iter(), |peer_id| { - peer.send(peer_id.into(), message.clone()) + broadcast(Some(sender_id), collaborators, |peer_id| { + peer.send(peer_id, message.clone()) }); } @@ -3074,7 +3074,7 @@ async fn send_channel_message( .flat_map(|user_id| pool.user_connection_ids(*user_id)), |peer_id| { session.peer.send( - peer_id.into(), + peer_id, proto::UpdateChannels { latest_channel_message_ids: vec![proto::ChannelMessageId { channel_id: channel_id.to_proto(), @@ -3447,7 +3447,7 @@ fn room_updated(room: &proto::Room, peer: &Peer) { .filter_map(|participant| Some(participant.peer_id?.into())), |peer_id| { peer.send( - peer_id.into(), + peer_id, proto::RoomUpdated { room: Some(room.clone()), }, @@ -3476,7 +3476,7 @@ fn channel_updated( .flat_map(|user_id| pool.user_connection_ids(*user_id)), |peer_id| { peer.send( - peer_id.into(), + peer_id, proto::UpdateChannels { channel_participants: vec![proto::ChannelParticipants { channel_id: channel_id.to_proto(), diff --git a/crates/collab/src/tests/channel_tests.rs b/crates/collab/src/tests/channel_tests.rs index 9d2f333304..14b0a87485 100644 --- a/crates/collab/src/tests/channel_tests.rs +++ b/crates/collab/src/tests/channel_tests.rs @@ -1431,7 +1431,7 @@ fn assert_channels( .ordered_channels() .map(|(depth, channel)| ExpectedChannel { depth, - name: channel.name.clone().into(), + name: channel.name.clone(), id: channel.id, }) .collect::>() diff --git a/crates/collab/src/tests/editor_tests.rs b/crates/collab/src/tests/editor_tests.rs index 9bf92e87f2..50a798b5a6 100644 --- a/crates/collab/src/tests/editor_tests.rs +++ b/crates/collab/src/tests/editor_tests.rs @@ -833,7 +833,7 @@ async fn test_language_server_statuses(cx_a: &mut TestAppContext, cx_b: &mut Tes let mut fake_language_servers = client_a.language_registry().register_fake_lsp_adapter( "Rust", FakeLspAdapter { - name: "the-language-server".into(), + name: "the-language-server", ..Default::default() }, ); diff --git a/crates/collab_ui/src/chat_panel/message_editor.rs b/crates/collab_ui/src/chat_panel/message_editor.rs index 0f0717326a..21c49c97dd 100644 --- a/crates/collab_ui/src/chat_panel/message_editor.rs +++ b/crates/collab_ui/src/chat_panel/message_editor.rs @@ -357,7 +357,7 @@ impl Render for MessageEditor { font_size: UiTextSize::Small.rems().into(), font_weight: FontWeight::NORMAL, font_style: FontStyle::Normal, - line_height: relative(1.3).into(), + line_height: relative(1.3), background_color: None, underline: None, strikethrough: None, diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 54591bd5de..1df8e7e73b 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -2171,7 +2171,7 @@ impl CollabPanel { font_size: rems(0.875).into(), font_weight: FontWeight::NORMAL, font_style: FontStyle::Normal, - line_height: relative(1.3).into(), + line_height: relative(1.3), background_color: None, underline: None, strikethrough: None, diff --git a/crates/copilot_ui/src/copilot_button.rs b/crates/copilot_ui/src/copilot_button.rs index 818053b09e..dca7411388 100644 --- a/crates/copilot_ui/src/copilot_button.rs +++ b/crates/copilot_ui/src/copilot_button.rs @@ -311,7 +311,7 @@ async fn configure_disabled_globs( fn toggle_copilot_globally(fs: Arc, cx: &mut AppContext) { let show_copilot_suggestions = all_language_settings(None, cx).copilot_enabled(None, None); update_settings_file::(fs, cx, move |file| { - file.defaults.show_copilot_suggestions = Some((!show_copilot_suggestions).into()) + file.defaults.show_copilot_suggestions = Some(!show_copilot_suggestions) }); } diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 793d5aa269..94e78390d6 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -797,7 +797,7 @@ impl Item for ProjectDiagnosticsEditor { fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock { let (message, code_ranges) = highlight_diagnostic_message(&diagnostic); - let message: SharedString = message.into(); + let message: SharedString = message; Arc::new(move |cx| { let highlight_style: HighlightStyle = cx.theme().colors().text_accent.into(); h_flex() diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 67eea85bdc..e55df06d7f 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -502,7 +502,7 @@ impl DisplaySnapshot { /// Returns text chunks starting at the end of the given display row in reverse until the start of the file pub fn reverse_text_chunks(&self, display_row: u32) -> impl Iterator { - (0..=display_row).into_iter().rev().flat_map(|row| { + (0..=display_row).rev().flat_map(|row| { self.block_snapshot .chunks(row..row + 1, false, Highlights::default()) .map(|h| h.text) @@ -1455,10 +1455,8 @@ pub mod tests { }"# .unindent(); - let theme = SyntaxTheme::new_test(vec![ - ("mod.body", Hsla::red().into()), - ("fn.name", Hsla::blue().into()), - ]); + let theme = + SyntaxTheme::new_test(vec![("mod.body", Hsla::red()), ("fn.name", Hsla::blue())]); let language = Arc::new( Language::new( LanguageConfig { @@ -1545,10 +1543,8 @@ pub mod tests { }"# .unindent(); - let theme = SyntaxTheme::new_test(vec![ - ("mod.body", Hsla::red().into()), - ("fn.name", Hsla::blue().into()), - ]); + let theme = + SyntaxTheme::new_test(vec![("mod.body", Hsla::red()), ("fn.name", Hsla::blue())]); let language = Arc::new( Language::new( LanguageConfig { @@ -1616,10 +1612,8 @@ pub mod tests { async fn test_chunks_with_text_highlights(cx: &mut gpui::TestAppContext) { cx.update(|cx| init_test(cx, |_| {})); - let theme = SyntaxTheme::new_test(vec![ - ("operator", Hsla::red().into()), - ("string", Hsla::green().into()), - ]); + let theme = + SyntaxTheme::new_test(vec![("operator", Hsla::red()), ("string", Hsla::green())]); let language = Arc::new( Language::new( LanguageConfig { diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 49d6c4d44c..7823140a12 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -2004,7 +2004,7 @@ impl EditorElement { let text_width = bounds.size.width - gutter_dimensions.width; let overscroll = size(em_width, px(0.)); let _snapshot = { - editor.set_visible_line_count((bounds.size.height / line_height).into(), cx); + editor.set_visible_line_count(bounds.size.height / line_height, cx); let editor_width = text_width - gutter_dimensions.margin - overscroll.width - em_width; let wrap_width = match editor.soft_wrap_mode(cx) { @@ -2037,7 +2037,7 @@ impl EditorElement { // The scroll position is a fractional point, the whole number of which represents // the top of the window in terms of display rows. let start_row = scroll_position.y as u32; - let height_in_lines = f32::from(bounds.size.height / line_height); + let height_in_lines = bounds.size.height / line_height; let max_row = snapshot.max_point().row(); // Add 1 to ensure selections bleed off screen @@ -2262,7 +2262,7 @@ impl EditorElement { }); let scroll_max = point( - f32::from((scroll_width - text_size.width) / em_width).max(0.0), + ((scroll_width - text_size.width) / em_width).max(0.0), max_row as f32, ); @@ -2722,11 +2722,8 @@ impl EditorElement { }; let scroll_position = position_map.snapshot.scroll_position(); - let x = f32::from( - (scroll_position.x * max_glyph_width - delta.x) / max_glyph_width, - ); - let y = - f32::from((scroll_position.y * line_height - delta.y) / line_height); + let x = (scroll_position.x * max_glyph_width - delta.x) / max_glyph_width; + let y = (scroll_position.y * line_height - delta.y) / line_height; let scroll_position = point(x, y).clamp(&point(0., 0.), &position_map.scroll_max); editor.scroll(scroll_position, axis, cx); @@ -3268,7 +3265,7 @@ impl PositionMap { let position = position - text_bounds.origin; let y = position.y.max(px(0.)).min(self.size.height); let x = position.x + (scroll_position.x * self.em_width); - let row = (f32::from(y / self.line_height) + scroll_position.y) as u32; + let row = ((y / self.line_height) + scroll_position.y) as u32; let (column, x_overshoot_after_line_end) = if let Some(line) = self .line_layouts diff --git a/crates/editor/src/rust_analyzer_ext.rs b/crates/editor/src/rust_analyzer_ext.rs index 31622a8c5e..1a950e0ea4 100644 --- a/crates/editor/src/rust_analyzer_ext.rs +++ b/crates/editor/src/rust_analyzer_ext.rs @@ -62,7 +62,6 @@ pub fn expand_macro_recursively( project .read(cx) .language_servers_for_buffer(buffer.read(cx), cx) - .into_iter() .find_map(|(adapter, server)| { if adapter.name.0.as_ref() == "rust-analyzer" { Some(( diff --git a/crates/editor/src/scroll/autoscroll.rs b/crates/editor/src/scroll/autoscroll.rs index 191dbd04dc..3dfe2d250a 100644 --- a/crates/editor/src/scroll/autoscroll.rs +++ b/crates/editor/src/scroll/autoscroll.rs @@ -62,7 +62,7 @@ impl Editor { line_height: Pixels, cx: &mut ViewContext, ) -> bool { - let visible_lines = f32::from(viewport_height / line_height); + let visible_lines = viewport_height / line_height; let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let mut scroll_position = self.scroll_manager.scroll_position(&display_map); let max_scroll_top = if matches!(self.mode, EditorMode::AutoHeight { .. }) { @@ -241,11 +241,10 @@ impl Editor { let scroll_right = scroll_left + viewport_width; if target_left < scroll_left { - self.scroll_manager.anchor.offset.x = (target_left / max_glyph_width).into(); + self.scroll_manager.anchor.offset.x = target_left / max_glyph_width; true } else if target_right > scroll_right { - self.scroll_manager.anchor.offset.x = - ((target_right - viewport_width) / max_glyph_width).into(); + self.scroll_manager.anchor.offset.x = (target_right - viewport_width) / max_glyph_width; true } else { false diff --git a/crates/extension/src/extension_lsp_adapter.rs b/crates/extension/src/extension_lsp_adapter.rs index 7e3cc9cdc5..a981facef0 100644 --- a/crates/extension/src/extension_lsp_adapter.rs +++ b/crates/extension/src/extension_lsp_adapter.rs @@ -52,7 +52,7 @@ impl LspAdapter for ExtensionLspAdapter { .map_err(|e| anyhow!("{}", e))?; Ok(LanguageServerBinary { - path: self.work_dir.join(&command.command).into(), + path: self.work_dir.join(&command.command), arguments: command.args.into_iter().map(|arg| arg.into()).collect(), env: Some(command.env.into_iter().collect()), }) diff --git a/crates/extensions_ui/src/extensions_ui.rs b/crates/extensions_ui/src/extensions_ui.rs index 45778c85d6..cd07f8244e 100644 --- a/crates/extensions_ui/src/extensions_ui.rs +++ b/crates/extensions_ui/src/extensions_ui.rs @@ -368,7 +368,7 @@ impl ExtensionsPage { font_size: rems(0.875).into(), font_weight: FontWeight::NORMAL, font_style: FontStyle::Normal, - line_height: relative(1.3).into(), + line_height: relative(1.3), background_color: None, underline: None, strikethrough: None, diff --git a/crates/gpui/src/app/test_context.rs b/crates/gpui/src/app/test_context.rs index 6ceff76c38..2b7d170e04 100644 --- a/crates/gpui/src/app/test_context.rs +++ b/crates/gpui/src/app/test_context.rs @@ -335,7 +335,7 @@ impl TestAppContext { .map(Keystroke::parse) .map(Result::unwrap) { - self.dispatch_keystroke(window, keystroke.into()); + self.dispatch_keystroke(window, keystroke); } self.background_executor.run_until_parked() @@ -347,7 +347,7 @@ impl TestAppContext { /// This will also run the background executor until it's parked. pub fn simulate_input(&mut self, window: AnyWindowHandle, input: &str) { for keystroke in input.split("").map(Keystroke::parse).map(Result::unwrap) { - self.dispatch_keystroke(window, keystroke.into()); + self.dispatch_keystroke(window, keystroke); } self.background_executor.run_until_parked() diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index 9c38fe8611..e517a9f092 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -766,7 +766,7 @@ impl SyntaxSnapshot { SyntaxMapCaptures::new( range.clone(), buffer.as_rope(), - self.layers_for_range(range, buffer).into_iter(), + self.layers_for_range(range, buffer), query, ) } @@ -780,7 +780,7 @@ impl SyntaxSnapshot { SyntaxMapMatches::new( range.clone(), buffer.as_rope(), - self.layers_for_range(range, buffer).into_iter(), + self.layers_for_range(range, buffer), query, ) } diff --git a/crates/markdown_preview/src/markdown_preview_view.rs b/crates/markdown_preview/src/markdown_preview_view.rs index 2b0296fc30..50eb958987 100644 --- a/crates/markdown_preview/src/markdown_preview_view.rs +++ b/crates/markdown_preview/src/markdown_preview_view.rs @@ -132,7 +132,7 @@ impl MarkdownPreviewView { workspace, contents, list_state, - tab_description: tab_description.into(), + tab_description: tab_description, } }) } diff --git a/crates/outline/src/outline.rs b/crates/outline/src/outline.rs index 627c4a0de2..0a8b9a08ec 100644 --- a/crates/outline/src/outline.rs +++ b/crates/outline/src/outline.rs @@ -279,7 +279,7 @@ impl PickerDelegate for OutlineViewDelegate { font_size: settings.buffer_font_size(cx).into(), font_weight: FontWeight::NORMAL, font_style: FontStyle::Normal, - line_height: relative(1.).into(), + line_height: relative(1.), background_color: None, underline: None, strikethrough: None, diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index c2cfd9713a..14a8ae7e9c 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -536,7 +536,7 @@ mod tests { !cx.has_pending_prompt(), "Should have no pending prompt on dirty project before opening the new recent project" ); - cx.dispatch_action((*workspace).into(), menu::Confirm); + cx.dispatch_action(*workspace, menu::Confirm); workspace .update(cx, |workspace, cx| { assert!( diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 1cb5af4b7e..7d7626dd2f 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -98,7 +98,7 @@ impl BufferSearchBar { font_size: rems(0.875).into(), font_weight: FontWeight::NORMAL, font_style: FontStyle::Normal, - line_height: relative(1.3).into(), + line_height: relative(1.3), background_color: None, underline: None, strikethrough: None, diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index c2c3a9734d..b26e4193d2 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -1631,7 +1631,7 @@ impl ProjectSearchBar { font_size: rems(0.875).into(), font_weight: FontWeight::NORMAL, font_style: FontStyle::Normal, - line_height: relative(1.3).into(), + line_height: relative(1.3), background_color: None, underline: None, strikethrough: None, diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index ec3503d228..bb388a3510 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -158,11 +158,11 @@ impl TerminalSize { } pub fn num_lines(&self) -> usize { - f32::from((self.size.height / self.line_height).floor()) as usize + (self.size.height / self.line_height).floor() as usize } pub fn num_columns(&self) -> usize { - f32::from((self.size.width / self.cell_width).floor()) as usize + (self.size.width / self.cell_width).floor() as usize } pub fn height(&self) -> Pixels { @@ -1663,9 +1663,9 @@ mod tests { fn get_cells(size: TerminalSize, rng: &mut ThreadRng) -> Vec> { let mut cells = Vec::new(); - for _ in 0..(f32::from(size.height() / size.line_height()) as usize) { + for _ in 0..((size.height() / size.line_height()) as usize) { let mut row_vec = Vec::new(); - for _ in 0..(f32::from(size.width() / size.cell_width()) as usize) { + for _ in 0..((size.width() / size.cell_width()) as usize) { let cell_char = rng.sample(Alphanumeric) as char; row_vec.push(cell_char) } diff --git a/crates/vim/src/command.rs b/crates/vim/src/command.rs index 0b138129c9..d8623d9c75 100644 --- a/crates/vim/src/command.rs +++ b/crates/vim/src/command.rs @@ -358,7 +358,7 @@ pub fn command_interceptor(mut query: &str, cx: &AppContext) -> Option Vec { let mut positions = Vec::new(); - let mut chars = query.chars().into_iter(); + let mut chars = query.chars(); let Some(mut current) = chars.next() else { return positions; diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 70059e47b6..278ccded11 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -221,7 +221,7 @@ impl Dock { return; }; if panel.is_zoomed(cx) { - workspace.zoomed = Some(panel.to_any().downgrade().into()); + workspace.zoomed = Some(panel.to_any().downgrade()); workspace.zoomed_position = Some(position); } else { workspace.zoomed = None; diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 5e90f86486..58a2ee6241 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -899,7 +899,7 @@ impl Pane { if not_shown_files == 1 { file_names.push(".. 1 file not shown".into()); } else { - file_names.push(format!(".. {} files not shown", not_shown_files).into()); + file_names.push(format!(".. {} files not shown", not_shown_files)); } } ( diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index f469538472..f1b93b19b5 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -2961,7 +2961,7 @@ impl Workspace { })?; let Some(id) = view.id.clone() else { - return Err(anyhow!("no id for view")).into(); + return Err(anyhow!("no id for view")); }; let id = ViewId::from_proto(id)?; @@ -3744,7 +3744,7 @@ fn open_items( let tasks = tasks.collect::>(); - let tasks = futures::future::join_all(tasks.into_iter()); + let tasks = futures::future::join_all(tasks); for (ix, path_open_result) in tasks.await.into_iter().flatten() { opened_items[ix] = Some(path_open_result); } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index dc9f43519d..c7356216a1 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -662,7 +662,7 @@ fn init_panic_hook(app: &App, installation_id: Option, session_id: Strin let panic_data = Panic { thread: thread_name.into(), - payload: payload.into(), + payload, location_data: info.location().map(|location| LocationData { file: location.file().into(), line: location.line(), diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 9881eb4c66..73acaacf34 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -111,7 +111,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::suspicious_to_owned", "clippy::type_complexity", "clippy::unnecessary_to_owned", - "clippy::useless_conversion", "clippy::vec_init_then_push", ]; From 5fad319cb5b0056e190010db53bd9cedbdd25a17 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sun, 3 Mar 2024 10:27:37 -0500 Subject: [PATCH 039/121] Enable `clippy::expect_fun_call` (#8768) This PR enables the [`clippy::expect_fun_call`](https://rust-lang.github.io/rust-clippy/master/index.html#/expect_fun_call) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/collab/src/bin/seed.rs | 4 ++-- tooling/xtask/src/main.rs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/collab/src/bin/seed.rs b/crates/collab/src/bin/seed.rs index 632116a363..8a998d0bf3 100644 --- a/crates/collab/src/bin/seed.rs +++ b/crates/collab/src/bin/seed.rs @@ -88,9 +88,9 @@ async fn fetch_github(client: &reqwest::Client, url: &str) .header("user-agent", "zed") .send() .await - .expect(&format!("failed to fetch '{}'", url)); + .unwrap_or_else(|_| panic!("failed to fetch '{}'", url)); response .json() .await - .expect(&format!("failed to deserialize github user from '{}'", url)) + .unwrap_or_else(|_| panic!("failed to deserialize github user from '{}'", url)) } diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 73acaacf34..45e5241160 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -90,7 +90,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::derivable_impls", "clippy::derive_ord_xor_partial_ord", "clippy::eq_op", - "clippy::expect_fun_call", "clippy::explicit_counter_loop", "clippy::identity_op", "clippy::implied_bounds_in_impls", From bfc648553fee4f378eacfc6bd12dcfcf4b688652 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sun, 3 Mar 2024 10:34:30 -0500 Subject: [PATCH 040/121] Enable `clippy::bool_comparison` (#8769) This PR enables the [`clippy::bool_comparison`](https://rust-lang.github.io/rust-clippy/master/index.html#/bool_comparison) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/terminal/src/terminal.rs | 24 ++++++++++++------------ tooling/xtask/src/main.rs | 1 - 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index bb388a3510..2ae53d40d2 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -487,21 +487,21 @@ impl TerminalBuilder { } } - if events.is_empty() && wakeup == false { + if events.is_empty() && !wakeup { smol::future::yield_now().await; break 'outer; - } else { - terminal.update(&mut cx, |this, cx| { - if wakeup { - this.process_event(&AlacTermEvent::Wakeup, cx); - } - - for event in events { - this.process_event(&event, cx); - } - })?; - smol::future::yield_now().await; } + + terminal.update(&mut cx, |this, cx| { + if wakeup { + this.process_event(&AlacTermEvent::Wakeup, cx); + } + + for event in events { + this.process_event(&event, cx); + } + })?; + smol::future::yield_now().await; } } diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 45e5241160..2862d27d51 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -80,7 +80,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::almost_complete_range", "clippy::arc_with_non_send_sync", "clippy::await_holding_lock", - "clippy::bool_comparison", "clippy::borrow_deref_ref", "clippy::borrowed_box", "clippy::cast_abs_to_unsigned", From 6685d3ac976acd3c64b93b7d9bcc67e10b9b3f9f Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sun, 3 Mar 2024 10:43:13 -0500 Subject: [PATCH 041/121] Enable `clippy::redundant_guards` (#8770) This PR enables the [`clippy::redundant_guards`](https://rust-lang.github.io/rust-clippy/master/index.html#/redundant_guards) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/project/src/project.rs | 2 +- tooling/xtask/src/main.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index c630a7dc9d..71c68a989d 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -9379,7 +9379,7 @@ fn relativize_path(base: &Path, path: &Path) -> PathBuf { } (None, _) => components.push(Component::ParentDir), (Some(a), Some(b)) if components.is_empty() && a == b => (), - (Some(a), Some(b)) if b == Component::CurDir => components.push(a), + (Some(a), Some(Component::CurDir)) => components.push(a), (Some(a), Some(_)) => { components.push(Component::ParentDir); for _ in base_components { diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 2862d27d51..a0ad0fcd83 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -103,7 +103,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::non_canonical_clone_impl", "clippy::non_canonical_partial_ord_impl", "clippy::redundant_closure_call", - "clippy::redundant_guards", "clippy::reversed_empty_ranges", "clippy::single_range_in_vec_init", "clippy::suspicious_to_owned", From 83f6a1ea49b709cc0b4500a803fca1c3cc7655f2 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sun, 3 Mar 2024 10:53:23 -0500 Subject: [PATCH 042/121] Enable `clippy::vec_init_then_push` (#8771) This PR enables the [`clippy::vec_init_then_push`](https://rust-lang.github.io/rust-clippy/master/index.html#/vec_init_then_push) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/collab/src/db/tests/buffer_tests.rs | 11 ++++++----- crates/workspace/src/workspace.rs | 3 +-- tooling/xtask/src/main.rs | 1 - 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/crates/collab/src/db/tests/buffer_tests.rs b/crates/collab/src/db/tests/buffer_tests.rs index ebc11a7019..8f9492d648 100644 --- a/crates/collab/src/db/tests/buffer_tests.rs +++ b/crates/collab/src/db/tests/buffer_tests.rs @@ -68,11 +68,12 @@ async fn test_channel_buffers(db: &Arc) { .unwrap(); let mut buffer_a = Buffer::new(0, text::BufferId::new(1).unwrap(), "".to_string()); - let mut operations = Vec::new(); - operations.push(buffer_a.edit([(0..0, "hello world")])); - operations.push(buffer_a.edit([(5..5, ", cruel")])); - operations.push(buffer_a.edit([(0..5, "goodbye")])); - operations.push(buffer_a.undo().unwrap().1); + let operations = vec![ + buffer_a.edit([(0..0, "hello world")]), + buffer_a.edit([(5..5, ", cruel")]), + buffer_a.edit([(0..5, "goodbye")]), + buffer_a.undo().unwrap().1, + ]; assert_eq!(buffer_a.text(), "hello, cruel world"); let operations = operations diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index f1b93b19b5..5acfd988b9 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -679,8 +679,7 @@ impl Workspace { let mut active_call = None; if let Some(call) = ActiveCall::try_global(cx) { let call = call.clone(); - let mut subscriptions = Vec::new(); - subscriptions.push(cx.subscribe(&call, Self::on_active_call_event)); + let subscriptions = vec![cx.subscribe(&call, Self::on_active_call_event)]; active_call = Some((call, subscriptions)); } diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index a0ad0fcd83..72c1588fe7 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -108,7 +108,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::suspicious_to_owned", "clippy::type_complexity", "clippy::unnecessary_to_owned", - "clippy::vec_init_then_push", ]; // When fixing violations automatically for a single package we don't care From 2964a01d73fbc9cf9591f4f392786788e35ca909 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sun, 3 Mar 2024 11:05:08 -0500 Subject: [PATCH 043/121] Enable `clippy::identity_op` (#8773) This PR enables the [`clippy::identity_op`](https://rust-lang.github.io/rust-clippy/master/index.html#/identity_op) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/languages/src/ocaml.rs | 2 +- tooling/xtask/src/main.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/languages/src/ocaml.rs b/crates/languages/src/ocaml.rs index f355bcb965..0dc4bfcd2b 100644 --- a/crates/languages/src/ocaml.rs +++ b/crates/languages/src/ocaml.rs @@ -140,7 +140,7 @@ impl LspAdapter for OCamlLspAdapter { } let mut label_highlight = vec![( - 0..0 + label.len(), + 0..label.len(), language.grammar()?.highlight_id_for_name("property")?, )]; diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 72c1588fe7..a83ae283ad 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -90,7 +90,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::derive_ord_xor_partial_ord", "clippy::eq_op", "clippy::explicit_counter_loop", - "clippy::identity_op", "clippy::implied_bounds_in_impls", "clippy::iter_kv_map", "clippy::iter_overeager_cloned", From 56f0418c9380a8d9dc69f9c4c868d71e840c1b10 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sun, 3 Mar 2024 11:16:27 -0500 Subject: [PATCH 044/121] Enable `clippy::needless_option_as_deref` (#8775) This PR enables the [`clippy::needless_option_as_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#/needless_option_as_deref) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/editor/src/display_map.rs | 2 +- tooling/xtask/src/main.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index e55df06d7f..8285644826 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -326,7 +326,7 @@ impl DisplayMap { .read(cx) .as_singleton() .and_then(|buffer| buffer.read(cx).language()); - language_settings(language.as_deref(), None, cx).tab_size + language_settings(language, None, cx).tab_size } #[cfg(test)] diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index a83ae283ad..4cfb96af67 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -96,7 +96,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::let_underscore_future", "clippy::map_entry", "clippy::needless_lifetimes", - "clippy::needless_option_as_deref", "clippy::needless_update", "clippy::never_loop", "clippy::non_canonical_clone_impl", From 08f9c3f5686dfda440bbeaa541ed8457848492fd Mon Sep 17 00:00:00 2001 From: Jonathan <57756132+jdk-21@users.noreply.github.com> Date: Sun, 3 Mar 2024 17:24:48 +0100 Subject: [PATCH 045/121] Accept partial copilot suggestions (#8682) Fixes https://github.com/zed-industries/zed/issues/8020 This PR adds a new shortcut cmd-right, if a copilot suggestion exists. The suggestions is accepted word by word. It emulates the behaviour of VS Code's Github Copilot implementation. Release Notes: - Added ability to accept partial copilot suggestions ([8020](https://github.com/zed-industries/zed/issues/8020)) --- assets/keymaps/default-linux.json | 15 +++- assets/keymaps/default-macos.json | 15 +++- crates/editor/src/actions.rs | 1 + crates/editor/src/editor.rs | 37 +++++++++ crates/editor/src/editor_tests.rs | 122 ++++++++++++++++++++++++++++++ crates/editor/src/element.rs | 1 + 6 files changed, 187 insertions(+), 4 deletions(-) diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index 865d064884..459f7e04db 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -135,10 +135,21 @@ "focus": true } ], - "alt-\\": "copilot::Suggest", + "ctrl->": "assistant::QuoteSelection" + } + }, + { + "context": "Editor && mode == full && copilot_suggestion", + "bindings": { "alt-]": "copilot::NextSuggestion", "alt-[": "copilot::PreviousSuggestion", - "ctrl->": "assistant::QuoteSelection" + "alt-right": "editor::AcceptPartialCopilotSuggestion" + } + }, + { + "context": "Editor && !copilot_suggestion", + "bindings": { + "alt-\\": "copilot::Suggest" } }, { diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json index 957820e586..dcf0817815 100644 --- a/assets/keymaps/default-macos.json +++ b/assets/keymaps/default-macos.json @@ -176,10 +176,21 @@ "focus": false } ], - "alt-\\": "copilot::Suggest", + "cmd->": "assistant::QuoteSelection" + } + }, + { + "context": "Editor && mode == full && copilot_suggestion", + "bindings": { "alt-]": "copilot::NextSuggestion", "alt-[": "copilot::PreviousSuggestion", - "cmd->": "assistant::QuoteSelection" + "alt-right": "editor::AcceptPartialCopilotSuggestion" + } + }, + { + "context": "Editor && !copilot_suggestion", + "bindings": { + "alt-\\": "copilot::Suggest" } }, { diff --git a/crates/editor/src/actions.rs b/crates/editor/src/actions.rs index ae7cd99c07..d0bbab0335 100644 --- a/crates/editor/src/actions.rs +++ b/crates/editor/src/actions.rs @@ -119,6 +119,7 @@ impl_actions!( gpui::actions!( editor, [ + AcceptPartialCopilotSuggestion, AddSelectionAbove, AddSelectionBelow, Backspace, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 86312b0134..258e3fb8a9 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1627,6 +1627,10 @@ impl Editor { key_context.set("extension", extension.to_string()); } + if self.has_active_copilot_suggestion(cx) { + key_context.add("copilot_suggestion"); + } + key_context } @@ -3965,6 +3969,39 @@ impl Editor { } } + fn accept_partial_copilot_suggestion( + &mut self, + _: &AcceptPartialCopilotSuggestion, + cx: &mut ViewContext, + ) { + if self.selections.count() == 1 && self.has_active_copilot_suggestion(cx) { + if let Some(suggestion) = self.take_active_copilot_suggestion(cx) { + let mut partial_suggestion = suggestion + .text + .chars() + .by_ref() + .take_while(|c| c.is_alphabetic()) + .collect::(); + if partial_suggestion.is_empty() { + partial_suggestion = suggestion + .text + .chars() + .by_ref() + .take_while(|c| c.is_whitespace() || !c.is_alphabetic()) + .collect::(); + } + + cx.emit(EditorEvent::InputHandled { + utf16_range_to_replace: None, + text: partial_suggestion.clone().into(), + }); + self.insert_with_autoindent_mode(&partial_suggestion, None, cx); + self.refresh_copilot_suggestions(true, cx); + cx.notify(); + } + } + } + fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext) -> bool { if let Some(suggestion) = self.take_active_copilot_suggestion(cx) { if let Some(copilot) = Copilot::global(cx) { diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 21072c908d..c1d5e052e1 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -7623,6 +7623,128 @@ async fn test_copilot(executor: BackgroundExecutor, cx: &mut gpui::TestAppContex }); } +#[gpui::test(iterations = 10)] +async fn test_accept_partial_copilot_suggestion( + executor: BackgroundExecutor, + cx: &mut gpui::TestAppContext, +) { + // flaky + init_test(cx, |_| {}); + + let (copilot, copilot_lsp) = Copilot::fake(cx); + _ = cx.update(|cx| Copilot::set_global(copilot, cx)); + let mut cx = EditorLspTestContext::new_rust( + lsp::ServerCapabilities { + completion_provider: Some(lsp::CompletionOptions { + trigger_characters: Some(vec![".".to_string(), ":".to_string()]), + ..Default::default() + }), + ..Default::default() + }, + cx, + ) + .await; + + // Setup the editor with a completion request. + cx.set_state(indoc! {" + oneˇ + two + three + "}); + cx.simulate_keystroke("."); + let _ = handle_completion_request( + &mut cx, + indoc! {" + one.|<> + two + three + "}, + vec![], + ); + handle_copilot_completion_request( + &copilot_lsp, + vec![copilot::request::Completion { + text: "one.copilot1".into(), + range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)), + ..Default::default() + }], + vec![], + ); + executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT); + cx.update_editor(|editor, cx| { + assert!(editor.has_active_copilot_suggestion(cx)); + + // Accepting the first word of the suggestion should only accept the first word and still show the rest. + editor.accept_partial_copilot_suggestion(&Default::default(), cx); + assert!(editor.has_active_copilot_suggestion(cx)); + assert_eq!(editor.text(cx), "one.copilot\ntwo\nthree\n"); + assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n"); + + // Accepting next word should accept the non-word and copilot suggestion should be gone + editor.accept_partial_copilot_suggestion(&Default::default(), cx); + assert!(!editor.has_active_copilot_suggestion(cx)); + assert_eq!(editor.text(cx), "one.copilot1\ntwo\nthree\n"); + assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n"); + }); + + // Reset the editor and check non-word and whitespace completion + cx.set_state(indoc! {" + oneˇ + two + three + "}); + cx.simulate_keystroke("."); + let _ = handle_completion_request( + &mut cx, + indoc! {" + one.|<> + two + three + "}, + vec![], + ); + handle_copilot_completion_request( + &copilot_lsp, + vec![copilot::request::Completion { + text: "one.123. copilot\n 456".into(), + range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)), + ..Default::default() + }], + vec![], + ); + executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT); + cx.update_editor(|editor, cx| { + assert!(editor.has_active_copilot_suggestion(cx)); + + // Accepting the first word (non-word) of the suggestion should only accept the first word and still show the rest. + editor.accept_partial_copilot_suggestion(&Default::default(), cx); + assert!(editor.has_active_copilot_suggestion(cx)); + assert_eq!(editor.text(cx), "one.123. \ntwo\nthree\n"); + assert_eq!( + editor.display_text(cx), + "one.123. copilot\n 456\ntwo\nthree\n" + ); + + // Accepting next word should accept the next word and copilot suggestion should still exist + editor.accept_partial_copilot_suggestion(&Default::default(), cx); + assert!(editor.has_active_copilot_suggestion(cx)); + assert_eq!(editor.text(cx), "one.123. copilot\ntwo\nthree\n"); + assert_eq!( + editor.display_text(cx), + "one.123. copilot\n 456\ntwo\nthree\n" + ); + + // Accepting the whitespace should accept the non-word/whitespaces with newline and copilot suggestion should be gone + editor.accept_partial_copilot_suggestion(&Default::default(), cx); + assert!(!editor.has_active_copilot_suggestion(cx)); + assert_eq!(editor.text(cx), "one.123. copilot\n 456\ntwo\nthree\n"); + assert_eq!( + editor.display_text(cx), + "one.123. copilot\n 456\ntwo\nthree\n" + ); + }); +} + #[gpui::test] async fn test_copilot_completion_invalidation( executor: BackgroundExecutor, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 7823140a12..43a9c8f746 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -338,6 +338,7 @@ impl EditorElement { register_action(view, cx, Editor::display_cursor_names); register_action(view, cx, Editor::unique_lines_case_insensitive); register_action(view, cx, Editor::unique_lines_case_sensitive); + register_action(view, cx, Editor::accept_partial_copilot_suggestion); } fn register_key_listeners( From 1fa949633436df7d16e99ba7faaba12225b5cd11 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sun, 3 Mar 2024 11:40:22 -0500 Subject: [PATCH 046/121] Enable `clippy::explicit_counter_loop` (#8776) This PR enables the [`clippy::explicit_counter_loop`](https://rust-lang.github.io/rust-clippy/master/index.html#/explicit_counter_loop) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/editor/src/editor.rs | 6 ++---- tooling/xtask/src/main.rs | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 258e3fb8a9..78e0afd2b6 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2735,9 +2735,8 @@ impl Editor { let mut edits = Vec::new(); let mut rows = Vec::new(); - let mut rows_inserted = 0; - for selection in self.selections.all_adjusted(cx) { + for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() { let cursor = selection.head(); let row = cursor.row; @@ -2746,8 +2745,7 @@ impl Editor { let newline = "\n".to_string(); edits.push((start_of_line..start_of_line, newline)); - rows.push(row + rows_inserted); - rows_inserted += 1; + rows.push(row + rows_inserted as u32); } self.transact(cx, |editor, cx| { diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 4cfb96af67..d716842cb4 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -89,7 +89,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::derivable_impls", "clippy::derive_ord_xor_partial_ord", "clippy::eq_op", - "clippy::explicit_counter_loop", "clippy::implied_bounds_in_impls", "clippy::iter_kv_map", "clippy::iter_overeager_cloned", From 53630dc74cae93c777c079cb2581629589b654c1 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sun, 3 Mar 2024 11:52:58 -0500 Subject: [PATCH 047/121] Enable `clippy::needless_lifetimes` (#8777) This PR enables the [`clippy::needless_lifetimes`](https://rust-lang.github.io/rust-clippy/master/index.html#/needless_lifetimes) rule and fixes the outstanding violations. Release Notes: - N/A --- .../src/channel_store/channel_index.rs | 4 +- crates/collab/src/tests/test_server.rs | 20 ++++---- crates/editor/src/display_map.rs | 14 +++--- crates/editor/src/display_map/inlay_map.rs | 2 +- crates/editor/src/editor.rs | 12 ++--- crates/editor/src/editor_tests.rs | 2 +- crates/editor/src/items.rs | 2 +- crates/git/src/diff.rs | 4 +- crates/language/src/buffer.rs | 30 ++++++------ crates/multi_buffer/src/multi_buffer.rs | 46 +++++++++---------- crates/project/src/project.rs | 4 +- crates/zed/src/zed.rs | 4 +- tooling/xtask/src/main.rs | 1 - 13 files changed, 67 insertions(+), 78 deletions(-) diff --git a/crates/channel/src/channel_store/channel_index.rs b/crates/channel/src/channel_store/channel_index.rs index fc753f7444..02a8cd333b 100644 --- a/crates/channel/src/channel_store/channel_index.rs +++ b/crates/channel/src/channel_store/channel_index.rs @@ -97,9 +97,9 @@ impl<'a> Drop for ChannelPathsInsertGuard<'a> { } } -fn channel_path_sorting_key<'a>( +fn channel_path_sorting_key( id: ChannelId, - channels_by_id: &'a BTreeMap>, + channels_by_id: &BTreeMap>, ) -> impl Iterator { let (parent_path, name) = channels_by_id .get(&id) diff --git a/crates/collab/src/tests/test_server.rs b/crates/collab/src/tests/test_server.rs index 7397e51f79..469c4176ab 100644 --- a/crates/collab/src/tests/test_server.rs +++ b/crates/collab/src/tests/test_server.rs @@ -138,7 +138,7 @@ impl TestServer { (server, client_a, client_b, channel_id) } - pub async fn start1<'a>(cx: &'a mut TestAppContext) -> TestClient { + pub async fn start1(cx: &mut TestAppContext) -> TestClient { let mut server = Self::start(cx.executor().clone()).await; server.create_client(cx, "user_a").await } @@ -589,19 +589,19 @@ impl TestClient { .await; } - pub fn local_projects<'a>(&'a self) -> impl Deref>> + 'a { + pub fn local_projects(&self) -> impl Deref>> + '_ { Ref::map(self.state.borrow(), |state| &state.local_projects) } - pub fn remote_projects<'a>(&'a self) -> impl Deref>> + 'a { + pub fn remote_projects(&self) -> impl Deref>> + '_ { Ref::map(self.state.borrow(), |state| &state.remote_projects) } - pub fn local_projects_mut<'a>(&'a self) -> impl DerefMut>> + 'a { + pub fn local_projects_mut(&self) -> impl DerefMut>> + '_ { RefMut::map(self.state.borrow_mut(), |state| &mut state.local_projects) } - pub fn remote_projects_mut<'a>(&'a self) -> impl DerefMut>> + 'a { + pub fn remote_projects_mut(&self) -> impl DerefMut>> + '_ { RefMut::map(self.state.borrow_mut(), |state| &mut state.remote_projects) } @@ -614,16 +614,14 @@ impl TestClient { }) } - pub fn buffers<'a>( - &'a self, - ) -> impl DerefMut, HashSet>>> + 'a + pub fn buffers( + &self, + ) -> impl DerefMut, HashSet>>> + '_ { RefMut::map(self.state.borrow_mut(), |state| &mut state.buffers) } - pub fn channel_buffers<'a>( - &'a self, - ) -> impl DerefMut>> + 'a { + pub fn channel_buffers(&self) -> impl DerefMut>> + '_ { RefMut::map(self.state.borrow_mut(), |state| &mut state.channel_buffers) } diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 8285644826..c0213bc331 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -512,13 +512,13 @@ impl DisplaySnapshot { }) } - pub fn chunks<'a>( - &'a self, + pub fn chunks( + &self, display_rows: Range, language_aware: bool, inlay_highlight_style: Option, suggestion_highlight_style: Option, - ) -> DisplayChunks<'a> { + ) -> DisplayChunks<'_> { self.block_snapshot.chunks( display_rows, language_aware, @@ -1826,10 +1826,10 @@ pub mod tests { ) } - fn syntax_chunks<'a>( + fn syntax_chunks( rows: Range, map: &Model, - theme: &'a SyntaxTheme, + theme: &SyntaxTheme, cx: &mut AppContext, ) -> Vec<(String, Option)> { chunks(rows, map, theme, cx) @@ -1838,10 +1838,10 @@ pub mod tests { .collect() } - fn chunks<'a>( + fn chunks( rows: Range, map: &Model, - theme: &'a SyntaxTheme, + theme: &SyntaxTheme, cx: &mut AppContext, ) -> Vec<(String, Option, Option)> { let snapshot = map.update(cx, |map, cx| map.snapshot(cx)); diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index 229b05e955..19c6e8970d 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -982,7 +982,7 @@ impl InlaySnapshot { summary } - pub fn buffer_rows<'a>(&'a self, row: u32) -> InlayBufferRows<'a> { + pub fn buffer_rows(&self, row: u32) -> InlayBufferRows<'_> { let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>(); let inlay_point = InlayPoint::new(row, 0); cursor.seek(&inlay_point, Bias::Left, &()); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 78e0afd2b6..ce15b36f6a 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1705,18 +1705,14 @@ impl Editor { } } - pub fn language_at<'a, T: ToOffset>( - &self, - point: T, - cx: &'a AppContext, - ) -> Option> { + pub fn language_at(&self, point: T, cx: &AppContext) -> Option> { self.buffer.read(cx).language_at(point, cx) } - pub fn file_at<'a, T: ToOffset>( + pub fn file_at( &self, point: T, - cx: &'a AppContext, + cx: &AppContext, ) -> Option> { self.buffer.read(cx).read(cx).file_at(point).cloned() } @@ -10439,7 +10435,7 @@ pub fn styled_runs_for_code_label<'a>( }) } -pub(crate) fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator + 'a { +pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator + '_ { let mut index = 0; let mut codepoints = text.char_indices().peekable(); diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index c1d5e052e1..cecc81fb4b 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -7310,7 +7310,7 @@ async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) #[test] fn test_split_words() { - fn split<'a>(text: &'a str) -> Vec<&'a str> { + fn split(text: &str) -> Vec<&str> { split_words(text).collect() } diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 8eb1119110..96fdcfdefc 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -593,7 +593,7 @@ impl Item for Editor { None } - fn tab_description<'a>(&self, detail: usize, cx: &'a AppContext) -> Option { + fn tab_description(&self, detail: usize, cx: &AppContext) -> Option { let path = path_for_buffer(&self.buffer, detail, true, cx)?; Some(path.to_string_lossy().to_string().into()) } diff --git a/crates/git/src/diff.rs b/crates/git/src/diff.rs index 07b0240f60..20f425f42c 100644 --- a/crates/git/src/diff.rs +++ b/crates/git/src/diff.rs @@ -208,8 +208,8 @@ impl BufferDiff { } } - fn process_patch_hunk<'a>( - patch: &GitPatch<'a>, + fn process_patch_hunk( + patch: &GitPatch<'_>, hunk_index: usize, buffer: &text::BufferSnapshot, buffer_row_divergence: &mut i64, diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index e1d98d23bd..0727123e84 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -2839,10 +2839,10 @@ impl BufferSnapshot { } /// Returns bracket range pairs overlapping or adjacent to `range` - pub fn bracket_ranges<'a, T: ToOffset>( - &'a self, + pub fn bracket_ranges( + &self, range: Range, - ) -> impl Iterator, Range)> + 'a { + ) -> impl Iterator, Range)> + '_ { // Find bracket pairs that *inclusively* contain the given range. let range = range.start.to_offset(self).saturating_sub(1) ..self.len().min(range.end.to_offset(self) + 1); @@ -2935,10 +2935,10 @@ impl BufferSnapshot { /// Returns anchor ranges for any matches of the redaction query. /// The buffer can be associated with multiple languages, and the redaction query associated with each /// will be run on the relevant section of the buffer. - pub fn redacted_ranges<'a, T: ToOffset>( - &'a self, + pub fn redacted_ranges( + &self, range: Range, - ) -> impl Iterator> + 'a { + ) -> impl Iterator> + '_ { let offset_range = range.start.to_offset(self)..range.end.to_offset(self); let mut syntax_matches = self.syntax.matches(offset_range, self, |grammar| { grammar @@ -3015,28 +3015,28 @@ impl BufferSnapshot { /// Returns all the Git diff hunks intersecting the given /// row range. - pub fn git_diff_hunks_in_row_range<'a>( - &'a self, + pub fn git_diff_hunks_in_row_range( + &self, range: Range, - ) -> impl 'a + Iterator> { + ) -> impl '_ + Iterator> { self.git_diff.hunks_in_row_range(range, self) } /// Returns all the Git diff hunks intersecting the given /// range. - pub fn git_diff_hunks_intersecting_range<'a>( - &'a self, + pub fn git_diff_hunks_intersecting_range( + &self, range: Range, - ) -> impl 'a + Iterator> { + ) -> impl '_ + Iterator> { self.git_diff.hunks_intersecting_range(range, self) } /// Returns all the Git diff hunks intersecting the given /// range, in reverse order. - pub fn git_diff_hunks_intersecting_range_rev<'a>( - &'a self, + pub fn git_diff_hunks_intersecting_range_rev( + &self, range: Range, - ) -> impl 'a + Iterator> { + ) -> impl '_ + Iterator> { self.git_diff.hunks_intersecting_range_rev(range, self) } diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 0c0d434825..61af2d366c 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -1525,11 +1525,7 @@ impl MultiBuffer { .unwrap_or(false) } - pub fn language_at<'a, T: ToOffset>( - &self, - point: T, - cx: &'a AppContext, - ) -> Option> { + pub fn language_at(&self, point: T, cx: &AppContext) -> Option> { self.point_to_buffer_offset(point, cx) .and_then(|(buffer, offset, _)| buffer.read(cx).language_at(offset)) } @@ -2828,10 +2824,10 @@ impl MultiBufferSnapshot { .map(|excerpt| (excerpt.id, &excerpt.buffer, excerpt.range.clone())) } - fn excerpts_for_range<'a, T: ToOffset>( - &'a self, + fn excerpts_for_range( + &self, range: Range, - ) -> impl Iterator + 'a { + ) -> impl Iterator + '_ { let range = range.start.to_offset(self)..range.end.to_offset(self); let mut cursor = self.excerpts.cursor::(); @@ -2956,10 +2952,10 @@ impl MultiBufferSnapshot { /// Returns enclosing bracket ranges containing the given range or returns None if the range is /// not contained in a single excerpt - pub fn enclosing_bracket_ranges<'a, T: ToOffset>( - &'a self, + pub fn enclosing_bracket_ranges( + &self, range: Range, - ) -> Option, Range)> + 'a> { + ) -> Option, Range)> + '_> { let range = range.start.to_offset(self)..range.end.to_offset(self); let excerpt = self.excerpt_containing(range.clone())?; @@ -2973,10 +2969,10 @@ impl MultiBufferSnapshot { /// Returns bracket range pairs overlapping the given `range` or returns None if the `range` is /// not contained in a single excerpt - pub fn bracket_ranges<'a, T: ToOffset>( - &'a self, + pub fn bracket_ranges( + &self, range: Range, - ) -> Option, Range)> + 'a> { + ) -> Option, Range)> + '_> { let range = range.start.to_offset(self)..range.end.to_offset(self); let excerpt = self.excerpt_containing(range.clone())?; @@ -3041,12 +3037,12 @@ impl MultiBufferSnapshot { self.trailing_excerpt_update_count } - pub fn file_at<'a, T: ToOffset>(&'a self, point: T) -> Option<&'a Arc> { + pub fn file_at(&self, point: T) -> Option<&Arc> { self.point_to_buffer_offset(point) .and_then(|(buffer, _)| buffer.file()) } - pub fn language_at<'a, T: ToOffset>(&'a self, point: T) -> Option<&'a Arc> { + pub fn language_at(&self, point: T) -> Option<&Arc> { self.point_to_buffer_offset(point) .and_then(|(buffer, offset)| buffer.language_at(offset)) } @@ -3065,7 +3061,7 @@ impl MultiBufferSnapshot { language_settings(language, file, cx) } - pub fn language_scope_at<'a, T: ToOffset>(&'a self, point: T) -> Option { + pub fn language_scope_at(&self, point: T) -> Option { self.point_to_buffer_offset(point) .and_then(|(buffer, offset)| buffer.language_scope_at(offset)) } @@ -3133,10 +3129,10 @@ impl MultiBufferSnapshot { false } - pub fn git_diff_hunks_in_range_rev<'a>( - &'a self, + pub fn git_diff_hunks_in_range_rev( + &self, row_range: Range, - ) -> impl 'a + Iterator> { + ) -> impl Iterator> + '_ { let mut cursor = self.excerpts.cursor::(); cursor.seek(&Point::new(row_range.end, 0), Bias::Left, &()); @@ -3198,10 +3194,10 @@ impl MultiBufferSnapshot { .flatten() } - pub fn git_diff_hunks_in_range<'a>( - &'a self, + pub fn git_diff_hunks_in_range( + &self, row_range: Range, - ) -> impl 'a + Iterator> { + ) -> impl Iterator> + '_ { let mut cursor = self.excerpts.cursor::(); cursor.seek(&Point::new(row_range.start, 0), Bias::Right, &()); @@ -3317,7 +3313,7 @@ impl MultiBufferSnapshot { )) } - fn excerpt_locator_for_id<'a>(&'a self, id: ExcerptId) -> &'a Locator { + fn excerpt_locator_for_id(&self, id: ExcerptId) -> &Locator { if id == ExcerptId::min() { Locator::min_ref() } else if id == ExcerptId::max() { @@ -3342,7 +3338,7 @@ impl MultiBufferSnapshot { Some(&self.excerpt(excerpt_id)?.buffer) } - fn excerpt<'a>(&'a self, excerpt_id: ExcerptId) -> Option<&'a Excerpt> { + fn excerpt(&self, excerpt_id: ExcerptId) -> Option<&Excerpt> { let mut cursor = self.excerpts.cursor::>(); let locator = self.excerpt_locator_for_id(excerpt_id); cursor.seek(&Some(locator), Bias::Left, &()); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 71c68a989d..332967875d 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1016,7 +1016,7 @@ impl Project { } /// Collect all worktrees, including ones that don't appear in the project panel - pub fn worktrees<'a>(&'a self) -> impl 'a + DoubleEndedIterator> { + pub fn worktrees(&self) -> impl '_ + DoubleEndedIterator> { self.worktrees .iter() .filter_map(move |worktree| worktree.upgrade()) @@ -9155,7 +9155,7 @@ fn subscribe_for_copilot_events( ) } -fn glob_literal_prefix<'a>(glob: &'a str) -> &'a str { +fn glob_literal_prefix(glob: &str) -> &str { let mut literal_end = 0; for (i, part) in glob.split(path::MAIN_SEPARATOR).enumerate() { if part.contains(&['*', '?', '{', '}']) { diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 16d4516b5c..3954015907 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -2845,10 +2845,10 @@ mod tests { )) } #[track_caller] - fn assert_key_bindings_for<'a>( + fn assert_key_bindings_for( window: AnyWindowHandle, cx: &TestAppContext, - actions: Vec<(&'static str, &'a dyn Action)>, + actions: Vec<(&'static str, &dyn Action)>, line: u32, ) { let available_actions = cx diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index d716842cb4..f6e7496669 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -94,7 +94,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::iter_overeager_cloned", "clippy::let_underscore_future", "clippy::map_entry", - "clippy::needless_lifetimes", "clippy::needless_update", "clippy::never_loop", "clippy::non_canonical_clone_impl", From a88df2c1034faf83d99b22c9876263f3182305f6 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sun, 3 Mar 2024 12:07:53 -0500 Subject: [PATCH 048/121] Enable `clippy::default_constructed_unit_structs` (#8778) This PR enables the [`clippy::default_constructed_unit_structs`](https://rust-lang.github.io/rust-clippy/master/index.html#/default_constructed_unit_structs) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/editor/src/editor_tests.rs | 2 +- tooling/xtask/src/main.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index cecc81fb4b..d7679c72cd 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -3999,7 +3999,7 @@ async fn test_select_all_matches(cx: &mut gpui::TestAppContext) { let mut cx = EditorTestContext::new(cx).await; cx.set_state("abc\nˇabc abc\ndefabc\nabc"); - cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches::default(), cx)) + cx.update_editor(|e, cx| e.select_all_matches(&SelectAllMatches, cx)) .unwrap(); cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»"); } diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index f6e7496669..c4247e9be6 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -85,7 +85,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::cast_abs_to_unsigned", "clippy::cmp_owned", "clippy::crate_in_macro_def", - "clippy::default_constructed_unit_structs", "clippy::derivable_impls", "clippy::derive_ord_xor_partial_ord", "clippy::eq_op", From 69e0474ebb18e414c312f4d59a0898f2b35e8474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E5=B1=B1=E9=A2=A8=E9=9C=B2?= Date: Mon, 4 Mar 2024 03:53:22 +0900 Subject: [PATCH 049/121] Windows gpui platform (#8490) First implementation of gpui platform for Windows. Release Notes: - N/A --------- Co-authored-by: Marshall Bowers --- Cargo.lock | 78 ++- crates/gpui/Cargo.toml | 25 +- crates/gpui/src/platform.rs | 9 +- crates/gpui/src/platform/windows.rs | 13 + .../gpui/src/platform/windows/dispatcher.rs | 159 ++++++ crates/gpui/src/platform/windows/display.rs | 36 ++ crates/gpui/src/platform/windows/platform.rs | 317 +++++++++++ .../gpui/src/platform/windows/text_system.rs | 440 ++++++++++++++ crates/gpui/src/platform/windows/util.rs | 26 + crates/gpui/src/platform/windows/window.rs | 535 ++++++++++++++++++ crates/zed/build.rs | 5 + 11 files changed, 1610 insertions(+), 33 deletions(-) create mode 100644 crates/gpui/src/platform/windows.rs create mode 100644 crates/gpui/src/platform/windows/dispatcher.rs create mode 100644 crates/gpui/src/platform/windows/display.rs create mode 100644 crates/gpui/src/platform/windows/platform.rs create mode 100644 crates/gpui/src/platform/windows/text_system.rs create mode 100644 crates/gpui/src/platform/windows/util.rs create mode 100644 crates/gpui/src/platform/windows/window.rs diff --git a/Cargo.lock b/Cargo.lock index 41aa25e9b3..ce408e1017 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4249,6 +4249,7 @@ dependencies = [ "wayland-backend", "wayland-client", "wayland-protocols", + "windows 0.53.0", "xcb", "xkbcommon", ] @@ -11732,6 +11733,35 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efc5cf48f83140dcaab716eeaea345f9e93d0018fb81162753a3f76c3397b538" +dependencies = [ + "windows-core", + "windows-targets 0.52.3", +] + +[[package]] +name = "windows-core" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dcc5b895a6377f1ab9fa55acedab1fd5ac0db66ad1e6c7f47e28a22e446a5dd" +dependencies = [ + "windows-result", + "windows-targets 0.52.3", +] + +[[package]] +name = "windows-result" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd19df78e5168dfb0aedc343d1d1b8d422ab2db6756d2dc3fef75035402a3f64" +dependencies = [ + "windows-targets 0.52.3", +] + [[package]] name = "windows-sys" version = "0.45.0" @@ -11756,7 +11786,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.3", ] [[package]] @@ -11791,17 +11821,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.3", + "windows_aarch64_msvc 0.52.3", + "windows_i686_gnu 0.52.3", + "windows_i686_msvc 0.52.3", + "windows_x86_64_gnu 0.52.3", + "windows_x86_64_gnullvm 0.52.3", + "windows_x86_64_msvc 0.52.3", ] [[package]] @@ -11818,9 +11848,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" [[package]] name = "windows_aarch64_msvc" @@ -11836,9 +11866,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" [[package]] name = "windows_i686_gnu" @@ -11854,9 +11884,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" [[package]] name = "windows_i686_msvc" @@ -11872,9 +11902,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" [[package]] name = "windows_x86_64_gnu" @@ -11890,9 +11920,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" [[package]] name = "windows_x86_64_gnullvm" @@ -11908,9 +11938,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" [[package]] name = "windows_x86_64_msvc" @@ -11926,9 +11956,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" [[package]] name = "winnow" diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 70d4fe6e57..297c8a06ed 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -92,8 +92,16 @@ media.workspace = true metal = "0.25" objc = "0.2" -[target.'cfg(target_os = "linux")'.dependencies] +[target.'cfg(any(target_os = "linux", target_os = "windows"))'.dependencies] flume = "0.11" +#TODO: use these on all platforms +blade-graphics.workspace = true +blade-macros.workspace = true +blade-rwh.workspace = true +bytemuck = "1" +cosmic-text = "0.10.0" + +[target.'cfg(target_os = "linux")'.dependencies] open = "5.0.1" ashpd = "0.7.0" xcb = { version = "1.3", features = ["as-raw-xcb-connection", "randr", "xkb"] } @@ -104,12 +112,15 @@ xkbcommon = { version = "0.7", features = ["wayland", "x11"] } as-raw-xcb-connection = "1" calloop = "0.12.4" calloop-wayland-source = "0.2.0" -#TODO: use these on all platforms -blade-graphics.workspace = true -blade-macros.workspace = true -blade-rwh.workspace = true -bytemuck = "1" -cosmic-text = "0.10.0" + +[target.'cfg(windows)'.dependencies.windows] +version = "0.53.0" +features = [ + "Win32_Graphics_Gdi", + "Win32_UI_WindowsAndMessaging", + "Win32_Security", + "Win32_System_Threading", +] [[example]] name = "hello_world" diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 346687bbf2..8222b88fe1 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -12,12 +12,15 @@ mod linux; #[cfg(target_os = "macos")] mod mac; -#[cfg(any(target_os = "linux", feature = "macos-blade"))] +#[cfg(any(target_os = "linux", target_os = "windows", feature = "macos-blade"))] mod blade; #[cfg(any(test, feature = "test-support"))] mod test; +#[cfg(target_os = "windows")] +mod windows; + use crate::{ Action, AnyWindowHandle, AsyncWindowContext, BackgroundExecutor, Bounds, DevicePixels, Font, FontId, FontMetrics, FontRun, ForegroundExecutor, GlobalPixels, GlyphId, Keymap, LineLayout, @@ -54,6 +57,8 @@ pub(crate) use mac::*; pub(crate) use test::*; use time::UtcOffset; pub use util::SemanticVersion; +#[cfg(target_os = "windows")] +pub(crate) use windows::*; #[cfg(target_os = "macos")] pub(crate) fn current_platform() -> Rc { @@ -66,7 +71,7 @@ pub(crate) fn current_platform() -> Rc { // todo("windows") #[cfg(target_os = "windows")] pub(crate) fn current_platform() -> Rc { - unimplemented!() + Rc::new(WindowsPlatform::new()) } pub(crate) trait Platform: 'static { diff --git a/crates/gpui/src/platform/windows.rs b/crates/gpui/src/platform/windows.rs new file mode 100644 index 0000000000..4347b9169a --- /dev/null +++ b/crates/gpui/src/platform/windows.rs @@ -0,0 +1,13 @@ +mod dispatcher; +mod display; +mod platform; +mod text_system; +mod util; +mod window; + +pub(crate) use dispatcher::*; +pub(crate) use display::*; +pub(crate) use platform::*; +pub(crate) use text_system::*; +pub(crate) use util::*; +pub(crate) use window::*; diff --git a/crates/gpui/src/platform/windows/dispatcher.rs b/crates/gpui/src/platform/windows/dispatcher.rs new file mode 100644 index 0000000000..16fdcd2b85 --- /dev/null +++ b/crates/gpui/src/platform/windows/dispatcher.rs @@ -0,0 +1,159 @@ +use std::{ + cmp::Ordering, + thread::{current, JoinHandle, ThreadId}, + time::{Duration, Instant}, +}; + +use async_task::Runnable; +use collections::BinaryHeap; +use flume::{RecvTimeoutError, Sender}; +use parking::Parker; +use parking_lot::Mutex; +use windows::Win32::{Foundation::HANDLE, System::Threading::SetEvent}; + +use crate::{PlatformDispatcher, TaskLabel}; + +pub(crate) struct WindowsDispatcher { + background_sender: Sender<(Runnable, Option)>, + main_sender: Sender, + timer_sender: Sender<(Runnable, Duration)>, + background_threads: Vec>, + timer_thread: JoinHandle<()>, + parker: Mutex, + main_thread_id: ThreadId, + event: HANDLE, +} + +impl WindowsDispatcher { + pub(crate) fn new(main_sender: Sender, event: HANDLE) -> Self { + let parker = Mutex::new(Parker::new()); + let (background_sender, background_receiver) = + flume::unbounded::<(Runnable, Option)>(); + let background_threads = (0..std::thread::available_parallelism() + .map(|i| i.get()) + .unwrap_or(1)) + .map(|_| { + let receiver = background_receiver.clone(); + std::thread::spawn(move || { + for (runnable, label) in receiver { + if let Some(label) = label { + log::debug!("TaskLabel: {label:?}"); + } + runnable.run(); + } + }) + }) + .collect::>(); + let (timer_sender, timer_receiver) = flume::unbounded::<(Runnable, Duration)>(); + let timer_thread = std::thread::spawn(move || { + let mut runnables = BinaryHeap::::new(); + let mut timeout_dur = None; + loop { + let recv = if let Some(dur) = timeout_dur { + match timer_receiver.recv_timeout(dur) { + Ok(recv) => Some(recv), + Err(RecvTimeoutError::Timeout) => None, + Err(RecvTimeoutError::Disconnected) => break, + } + } else if let Ok(recv) = timer_receiver.recv() { + Some(recv) + } else { + break; + }; + let now = Instant::now(); + if let Some((runnable, dur)) = recv { + runnables.push(RunnableAfter { + runnable, + instant: now + dur, + }); + while let Ok((runnable, dur)) = timer_receiver.try_recv() { + runnables.push(RunnableAfter { + runnable, + instant: now + dur, + }) + } + } + while runnables.peek().is_some_and(|entry| entry.instant <= now) { + runnables.pop().unwrap().runnable.run(); + } + timeout_dur = runnables.peek().map(|entry| entry.instant - now); + } + }); + let main_thread_id = current().id(); + Self { + background_sender, + main_sender, + timer_sender, + background_threads, + timer_thread, + parker, + main_thread_id, + event, + } + } +} + +impl PlatformDispatcher for WindowsDispatcher { + fn is_main_thread(&self) -> bool { + current().id() == self.main_thread_id + } + + fn dispatch(&self, runnable: Runnable, label: Option) { + self.background_sender + .send((runnable, label)) + .inspect_err(|e| log::error!("Dispatch failed: {e}")) + .ok(); + } + + fn dispatch_on_main_thread(&self, runnable: Runnable) { + self.main_sender + .send(runnable) + .inspect_err(|e| log::error!("Dispatch failed: {e}")) + .ok(); + unsafe { SetEvent(self.event) }.ok(); + } + + fn dispatch_after(&self, duration: std::time::Duration, runnable: Runnable) { + self.timer_sender + .send((runnable, duration)) + .inspect_err(|e| log::error!("Dispatch failed: {e}")) + .ok(); + } + + fn tick(&self, _background_only: bool) -> bool { + false + } + + fn park(&self) { + self.parker.lock().park(); + } + + fn unparker(&self) -> parking::Unparker { + self.parker.lock().unparker() + } +} + +struct RunnableAfter { + runnable: Runnable, + instant: Instant, +} + +impl PartialEq for RunnableAfter { + fn eq(&self, other: &Self) -> bool { + self.instant == other.instant + } +} + +impl Eq for RunnableAfter {} + +impl Ord for RunnableAfter { + fn cmp(&self, other: &Self) -> Ordering { + self.instant.cmp(&other.instant).reverse() + } +} + +impl PartialOrd for RunnableAfter { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} diff --git a/crates/gpui/src/platform/windows/display.rs b/crates/gpui/src/platform/windows/display.rs new file mode 100644 index 0000000000..448433b6fb --- /dev/null +++ b/crates/gpui/src/platform/windows/display.rs @@ -0,0 +1,36 @@ +use anyhow::{anyhow, Result}; +use uuid::Uuid; + +use crate::{Bounds, DisplayId, GlobalPixels, PlatformDisplay, Point, Size}; + +#[derive(Debug)] +pub(crate) struct WindowsDisplay; + +impl WindowsDisplay { + pub(crate) fn new() -> Self { + Self + } +} + +impl PlatformDisplay for WindowsDisplay { + // todo!("windows") + fn id(&self) -> DisplayId { + DisplayId(1) + } + + // todo!("windows") + fn uuid(&self) -> Result { + Err(anyhow!("not implemented yet.")) + } + + // todo!("windows") + fn bounds(&self) -> Bounds { + Bounds::new( + Point::new(0.0.into(), 0.0.into()), + Size { + width: 1920.0.into(), + height: 1280.0.into(), + }, + ) + } +} diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs new file mode 100644 index 0000000000..e7751579c9 --- /dev/null +++ b/crates/gpui/src/platform/windows/platform.rs @@ -0,0 +1,317 @@ +// todo!("windows"): remove +#![allow(unused_variables)] + +use std::{ + cell::RefCell, + collections::HashSet, + path::{Path, PathBuf}, + rc::Rc, + sync::Arc, + time::Duration, +}; + +use anyhow::{anyhow, Result}; +use async_task::Runnable; +use futures::channel::oneshot::Receiver; +use parking_lot::Mutex; +use time::UtcOffset; +use util::SemanticVersion; +use windows::Win32::{ + Foundation::{CloseHandle, HANDLE, HWND}, + System::Threading::{CreateEventW, INFINITE}, + UI::WindowsAndMessaging::{ + DispatchMessageW, MsgWaitForMultipleObjects, PeekMessageW, PostQuitMessage, + TranslateMessage, MSG, PM_REMOVE, QS_ALLINPUT, WM_QUIT, + }, +}; + +use crate::{ + Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, ForegroundExecutor, + Keymap, Menu, PathPromptOptions, Platform, PlatformDisplay, PlatformInput, PlatformTextSystem, + PlatformWindow, Task, WindowAppearance, WindowOptions, WindowsDispatcher, WindowsDisplay, + WindowsTextSystem, WindowsWindow, +}; + +pub(crate) struct WindowsPlatform { + inner: Rc, +} + +pub(crate) struct WindowsPlatformInner { + background_executor: BackgroundExecutor, + pub(crate) foreground_executor: ForegroundExecutor, + main_receiver: flume::Receiver, + text_system: Arc, + callbacks: Mutex, + pub(crate) window_handles: RefCell>, + pub(crate) event: HANDLE, +} + +impl Drop for WindowsPlatformInner { + fn drop(&mut self) { + unsafe { CloseHandle(self.event) }.ok(); + } +} + +#[derive(Default)] +struct Callbacks { + open_urls: Option)>>, + become_active: Option>, + resign_active: Option>, + quit: Option>, + reopen: Option>, + event: Option bool>>, + app_menu_action: Option>, + will_open_app_menu: Option>, + validate_app_menu_command: Option bool>>, +} + +impl WindowsPlatform { + pub(crate) fn new() -> Self { + let (main_sender, main_receiver) = flume::unbounded::(); + let event = unsafe { CreateEventW(None, false, false, None) }.unwrap(); + let dispatcher = Arc::new(WindowsDispatcher::new(main_sender, event)); + let background_executor = BackgroundExecutor::new(dispatcher.clone()); + let foreground_executor = ForegroundExecutor::new(dispatcher); + let text_system = Arc::new(WindowsTextSystem::new()); + let callbacks = Mutex::new(Callbacks::default()); + let window_handles = RefCell::new(HashSet::new()); + let inner = Rc::new(WindowsPlatformInner { + background_executor, + foreground_executor, + main_receiver, + text_system, + callbacks, + window_handles, + event, + }); + Self { inner } + } +} + +impl Platform for WindowsPlatform { + fn background_executor(&self) -> BackgroundExecutor { + self.inner.background_executor.clone() + } + + fn foreground_executor(&self) -> ForegroundExecutor { + self.inner.foreground_executor.clone() + } + + fn text_system(&self) -> Arc { + self.inner.text_system.clone() + } + + fn run(&self, on_finish_launching: Box) { + on_finish_launching(); + 'a: loop { + unsafe { + MsgWaitForMultipleObjects(Some(&[self.inner.event]), false, INFINITE, QS_ALLINPUT) + }; + let mut msg = MSG::default(); + while unsafe { PeekMessageW(&mut msg, HWND::default(), 0, 0, PM_REMOVE) }.as_bool() { + if msg.message == WM_QUIT { + break 'a; + } + unsafe { TranslateMessage(&msg) }; + unsafe { DispatchMessageW(&msg) }; + } + while let Ok(runnable) = self.inner.main_receiver.try_recv() { + runnable.run(); + } + } + let mut callbacks = self.inner.callbacks.lock(); + if let Some(callback) = callbacks.quit.as_mut() { + callback() + } + } + + fn quit(&self) { + self.foreground_executor() + .spawn(async { unsafe { PostQuitMessage(0) } }) + .detach(); + } + + // todo!("windows") + fn restart(&self) { + unimplemented!() + } + + // todo!("windows") + fn activate(&self, ignoring_other_apps: bool) {} + + // todo!("windows") + fn hide(&self) { + unimplemented!() + } + + // todo!("windows") + fn hide_other_apps(&self) { + unimplemented!() + } + + // todo!("windows") + fn unhide_other_apps(&self) { + unimplemented!() + } + + // todo!("windows") + fn displays(&self) -> Vec> { + vec![Rc::new(WindowsDisplay::new())] + } + + // todo!("windows") + fn display(&self, id: crate::DisplayId) -> Option> { + Some(Rc::new(WindowsDisplay::new())) + } + + // todo!("windows") + fn active_window(&self) -> Option { + unimplemented!() + } + + fn open_window( + &self, + handle: AnyWindowHandle, + options: WindowOptions, + ) -> Box { + Box::new(WindowsWindow::new(self.inner.clone(), handle, options)) + } + + // todo!("windows") + fn window_appearance(&self) -> WindowAppearance { + WindowAppearance::Dark + } + + // todo!("windows") + fn open_url(&self, url: &str) { + // todo!("windows") + } + + // todo!("windows") + fn on_open_urls(&self, callback: Box)>) { + self.inner.callbacks.lock().open_urls = Some(callback); + } + + // todo!("windows") + fn prompt_for_paths(&self, options: PathPromptOptions) -> Receiver>> { + unimplemented!() + } + + // todo!("windows") + fn prompt_for_new_path(&self, directory: &Path) -> Receiver> { + unimplemented!() + } + + // todo!("windows") + fn reveal_path(&self, path: &Path) { + unimplemented!() + } + + fn on_become_active(&self, callback: Box) { + self.inner.callbacks.lock().become_active = Some(callback); + } + + fn on_resign_active(&self, callback: Box) { + self.inner.callbacks.lock().resign_active = Some(callback); + } + + fn on_quit(&self, callback: Box) { + self.inner.callbacks.lock().quit = Some(callback); + } + + fn on_reopen(&self, callback: Box) { + self.inner.callbacks.lock().reopen = Some(callback); + } + + fn on_event(&self, callback: Box bool>) { + self.inner.callbacks.lock().event = Some(callback); + } + + // todo!("windows") + fn set_menus(&self, menus: Vec, keymap: &Keymap) {} + + fn on_app_menu_action(&self, callback: Box) { + self.inner.callbacks.lock().app_menu_action = Some(callback); + } + + fn on_will_open_app_menu(&self, callback: Box) { + self.inner.callbacks.lock().will_open_app_menu = Some(callback); + } + + fn on_validate_app_menu_command(&self, callback: Box bool>) { + self.inner.callbacks.lock().validate_app_menu_command = Some(callback); + } + + fn os_name(&self) -> &'static str { + "Windows" + } + + fn os_version(&self) -> Result { + Ok(SemanticVersion { + major: 1, + minor: 0, + patch: 0, + }) + } + + fn app_version(&self) -> Result { + Ok(SemanticVersion { + major: 1, + minor: 0, + patch: 0, + }) + } + + // todo!("windows") + fn app_path(&self) -> Result { + Err(anyhow!("not yet implemented")) + } + + // todo!("windows") + fn local_timezone(&self) -> UtcOffset { + UtcOffset::from_hms(9, 0, 0).unwrap() + } + + // todo!("windows") + fn double_click_interval(&self) -> Duration { + Duration::from_millis(100) + } + + // todo!("windows") + fn path_for_auxiliary_executable(&self, name: &str) -> Result { + Err(anyhow!("not yet implemented")) + } + + // todo!("windows") + fn set_cursor_style(&self, style: CursorStyle) {} + + // todo!("windows") + fn should_auto_hide_scrollbars(&self) -> bool { + false + } + + // todo!("windows") + fn write_to_clipboard(&self, item: ClipboardItem) { + unimplemented!() + } + + // todo!("windows") + fn read_from_clipboard(&self) -> Option { + unimplemented!() + } + + // todo!("windows") + fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task> { + Task::Ready(Some(Err(anyhow!("not implemented yet.")))) + } + + // todo!("windows") + fn read_credentials(&self, url: &str) -> Task)>>> { + Task::Ready(Some(Err(anyhow!("not implemented yet.")))) + } + + // todo!("windows") + fn delete_credentials(&self, url: &str) -> Task> { + Task::Ready(Some(Err(anyhow!("not implemented yet.")))) + } +} diff --git a/crates/gpui/src/platform/windows/text_system.rs b/crates/gpui/src/platform/windows/text_system.rs new file mode 100644 index 0000000000..411e1a8d28 --- /dev/null +++ b/crates/gpui/src/platform/windows/text_system.rs @@ -0,0 +1,440 @@ +use crate::{ + point, size, Bounds, DevicePixels, Font, FontFeatures, FontId, FontMetrics, FontRun, FontStyle, + FontWeight, GlyphId, LineLayout, Pixels, PlatformTextSystem, Point, RenderGlyphParams, + ShapedGlyph, SharedString, Size, +}; +use anyhow::{anyhow, Context, Ok, Result}; +use collections::HashMap; +use cosmic_text::{ + fontdb::Query, Attrs, AttrsList, BufferLine, CacheKey, Family, Font as CosmicTextFont, + FontSystem, SwashCache, +}; +use parking_lot::{RwLock, RwLockUpgradableReadGuard}; +use pathfinder_geometry::{ + rect::{RectF, RectI}, + vector::{Vector2F, Vector2I}, +}; +use smallvec::SmallVec; +use std::{borrow::Cow, sync::Arc}; + +pub(crate) struct WindowsTextSystem(RwLock); + +struct WindowsTextSystemState { + swash_cache: SwashCache, + font_system: FontSystem, + fonts: Vec>, + font_selections: HashMap, + font_ids_by_family_name: HashMap>, + postscript_names_by_font_id: HashMap, +} + +impl WindowsTextSystem { + pub(crate) fn new() -> Self { + let mut font_system = FontSystem::new(); + + // todo!("windows") make font loading non-blocking + font_system.db_mut().load_system_fonts(); + + Self(RwLock::new(WindowsTextSystemState { + font_system, + swash_cache: SwashCache::new(), + fonts: Vec::new(), + font_selections: HashMap::default(), + // font_ids_by_postscript_name: HashMap::default(), + font_ids_by_family_name: HashMap::default(), + postscript_names_by_font_id: HashMap::default(), + })) + } +} + +impl Default for WindowsTextSystem { + fn default() -> Self { + Self::new() + } +} + +#[allow(unused)] +impl PlatformTextSystem for WindowsTextSystem { + fn add_fonts(&self, fonts: Vec>) -> Result<()> { + self.0.write().add_fonts(fonts) + } + + // todo!("windows") ensure that this integrates with platform font loading + // do we need to do more than call load_system_fonts()? + fn all_font_names(&self) -> Vec { + self.0 + .read() + .font_system + .db() + .faces() + .map(|face| face.post_script_name.clone()) + .collect() + } + + // todo!("windows") + fn all_font_families(&self) -> Vec { + Vec::new() + } + + fn font_id(&self, font: &Font) -> Result { + // todo!("windows"): Do we need to use CosmicText's Font APIs? Can we consolidate this to use font_kit? + let lock = self.0.upgradable_read(); + if let Some(font_id) = lock.font_selections.get(font) { + Ok(*font_id) + } else { + let mut lock = RwLockUpgradableReadGuard::upgrade(lock); + let candidates = if let Some(font_ids) = lock.font_ids_by_family_name.get(&font.family) + { + font_ids.as_slice() + } else { + let font_ids = lock.load_family(&font.family, font.features)?; + lock.font_ids_by_family_name + .insert(font.family.clone(), font_ids); + lock.font_ids_by_family_name[&font.family].as_ref() + }; + + let id = lock + .font_system + .db() + .query(&Query { + families: &[Family::Name(&font.family)], + weight: font.weight.into(), + style: font.style.into(), + stretch: Default::default(), + }) + .context("no font")?; + + let font_id = if let Some(font_id) = lock.fonts.iter().position(|font| font.id() == id) + { + FontId(font_id) + } else { + // Font isn't in fonts so add it there, this is because we query all the fonts in the db + // and maybe we haven't loaded it yet + let font_id = FontId(lock.fonts.len()); + let font = lock.font_system.get_font(id).unwrap(); + lock.fonts.push(font); + font_id + }; + + lock.font_selections.insert(font.clone(), font_id); + Ok(font_id) + } + } + + fn font_metrics(&self, font_id: FontId) -> FontMetrics { + let metrics = self.0.read().fonts[font_id.0].as_swash().metrics(&[]); + + FontMetrics { + units_per_em: metrics.units_per_em as u32, + ascent: metrics.ascent, + descent: -metrics.descent, // todo!("windows") confirm this is correct + line_gap: metrics.leading, + underline_position: metrics.underline_offset, + underline_thickness: metrics.stroke_size, + cap_height: metrics.cap_height, + x_height: metrics.x_height, + // todo!("windows"): Compute this correctly + bounding_box: Bounds { + origin: point(0.0, 0.0), + size: size(metrics.max_width, metrics.ascent + metrics.descent), + }, + } + } + + fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { + let lock = self.0.read(); + let metrics = lock.fonts[font_id.0].as_swash().metrics(&[]); + let glyph_metrics = lock.fonts[font_id.0].as_swash().glyph_metrics(&[]); + let glyph_id = glyph_id.0 as u16; + // todo!("windows"): Compute this correctly + // see https://github.com/servo/font-kit/blob/master/src/loaders/freetype.rs#L614-L620 + Ok(Bounds { + origin: point(0.0, 0.0), + size: size( + glyph_metrics.advance_width(glyph_id), + glyph_metrics.advance_height(glyph_id), + ), + }) + } + + fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { + self.0.read().advance(font_id, glyph_id) + } + + fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option { + self.0.read().glyph_for_char(font_id, ch) + } + + fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result> { + self.0.write().raster_bounds(params) + } + + fn rasterize_glyph( + &self, + params: &RenderGlyphParams, + raster_bounds: Bounds, + ) -> Result<(Size, Vec)> { + self.0.write().rasterize_glyph(params, raster_bounds) + } + + fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout { + self.0.write().layout_line(text, font_size, runs) + } + + // todo!("windows") Confirm that this has been superseded by the LineWrapper + fn wrap_line( + &self, + text: &str, + font_id: FontId, + font_size: Pixels, + width: Pixels, + ) -> Vec { + unimplemented!() + } +} + +impl WindowsTextSystemState { + #[profiling::function] + fn add_fonts(&mut self, fonts: Vec>) -> Result<()> { + let db = self.font_system.db_mut(); + for bytes in fonts { + match bytes { + Cow::Borrowed(embedded_font) => { + db.load_font_data(embedded_font.to_vec()); + } + Cow::Owned(bytes) => { + db.load_font_data(bytes); + } + } + } + Ok(()) + } + + #[profiling::function] + fn load_family( + &mut self, + name: &SharedString, + _features: FontFeatures, + ) -> Result> { + let mut font_ids = SmallVec::new(); + let family = self + .font_system + .get_font_matches(Attrs::new().family(cosmic_text::Family::Name(name))); + for font in family.as_ref() { + let font = self.font_system.get_font(*font).unwrap(); + if font.as_swash().charmap().map('m') == 0 { + self.font_system.db_mut().remove_face(font.id()); + continue; + }; + + let font_id = FontId(self.fonts.len()); + font_ids.push(font_id); + self.fonts.push(font); + } + Ok(font_ids) + } + + fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { + let width = self.fonts[font_id.0] + .as_swash() + .glyph_metrics(&[]) + .advance_width(glyph_id.0 as u16); + let height = self.fonts[font_id.0] + .as_swash() + .glyph_metrics(&[]) + .advance_height(glyph_id.0 as u16); + Ok(Size { width, height }) + } + + fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option { + let glyph_id = self.fonts[font_id.0].as_swash().charmap().map(ch); + if glyph_id == 0 { + None + } else { + Some(GlyphId(glyph_id.into())) + } + } + + fn is_emoji(&self, font_id: FontId) -> bool { + // todo!("windows"): implement this correctly + self.postscript_names_by_font_id + .get(&font_id) + .map_or(false, |postscript_name| { + postscript_name == "AppleColorEmoji" + }) + } + + // todo!("windows") both raster functions have problems because I am not sure this is the correct mapping from cosmic text to gpui system + fn raster_bounds(&mut self, params: &RenderGlyphParams) -> Result> { + let font = &self.fonts[params.font_id.0]; + let font_system = &mut self.font_system; + let image = self + .swash_cache + .get_image( + font_system, + CacheKey::new( + font.id(), + params.glyph_id.0 as u16, + (params.font_size * params.scale_factor).into(), + (0.0, 0.0), + ) + .0, + ) + .clone() + .unwrap(); + Ok(Bounds { + origin: point(image.placement.left.into(), (-image.placement.top).into()), + size: size(image.placement.width.into(), image.placement.height.into()), + }) + } + + #[profiling::function] + fn rasterize_glyph( + &mut self, + params: &RenderGlyphParams, + glyph_bounds: Bounds, + ) -> Result<(Size, Vec)> { + if glyph_bounds.size.width.0 == 0 || glyph_bounds.size.height.0 == 0 { + Err(anyhow!("glyph bounds are empty")) + } else { + // todo!("windows") handle subpixel variants + let bitmap_size = glyph_bounds.size; + let font = &self.fonts[params.font_id.0]; + let font_system = &mut self.font_system; + let image = self + .swash_cache + .get_image( + font_system, + CacheKey::new( + font.id(), + params.glyph_id.0 as u16, + (params.font_size * params.scale_factor).into(), + (0.0, 0.0), + ) + .0, + ) + .clone() + .unwrap(); + + Ok((bitmap_size, image.data)) + } + } + + // todo!("windows") This is all a quick first pass, maybe we should be using cosmic_text::Buffer + #[profiling::function] + fn layout_line(&mut self, text: &str, font_size: Pixels, font_runs: &[FontRun]) -> LineLayout { + let mut attrs_list = AttrsList::new(Attrs::new()); + let mut offs = 0; + for run in font_runs { + // todo!("windows") We need to check we are doing utf properly + let font = &self.fonts[run.font_id.0]; + let font = self.font_system.db().face(font.id()).unwrap(); + attrs_list.add_span( + offs..offs + run.len, + Attrs::new() + .family(Family::Name(&font.families.first().unwrap().0)) + .stretch(font.stretch) + .style(font.style) + .weight(font.weight), + ); + offs += run.len; + } + let mut line = BufferLine::new(text, attrs_list, cosmic_text::Shaping::Advanced); + let layout = line.layout( + &mut self.font_system, + font_size.0, + f32::MAX, // todo!("windows") we don't have a width cause this should technically not be wrapped I believe + cosmic_text::Wrap::None, + ); + let mut runs = Vec::new(); + // todo!("windows") what I think can happen is layout returns possibly multiple lines which means we should be probably working with it higher up in the text rendering + let layout = layout.first().unwrap(); + for glyph in &layout.glyphs { + let font_id = glyph.font_id; + let font_id = FontId( + self.fonts + .iter() + .position(|font| font.id() == font_id) + .unwrap(), + ); + let mut glyphs = SmallVec::new(); + // todo!("windows") this is definitely wrong, each glyph in glyphs from cosmic-text is a cluster with one glyph, ShapedRun takes a run of glyphs with the same font and direction + glyphs.push(ShapedGlyph { + id: GlyphId(glyph.glyph_id as u32), + position: point((glyph.x).into(), glyph.y.into()), + index: glyph.start, + is_emoji: self.is_emoji(font_id), + }); + runs.push(crate::ShapedRun { font_id, glyphs }); + } + LineLayout { + font_size, + width: layout.w.into(), + ascent: layout.max_ascent.into(), + descent: layout.max_descent.into(), + runs, + len: text.len(), + } + } +} + +impl From for Bounds { + fn from(rect: RectF) -> Self { + Bounds { + origin: point(rect.origin_x(), rect.origin_y()), + size: size(rect.width(), rect.height()), + } + } +} + +impl From for Bounds { + fn from(rect: RectI) -> Self { + Bounds { + origin: point(DevicePixels(rect.origin_x()), DevicePixels(rect.origin_y())), + size: size(DevicePixels(rect.width()), DevicePixels(rect.height())), + } + } +} + +impl From for Size { + fn from(value: Vector2I) -> Self { + size(value.x().into(), value.y().into()) + } +} + +impl From for Bounds { + fn from(rect: RectI) -> Self { + Bounds { + origin: point(rect.origin_x(), rect.origin_y()), + size: size(rect.width(), rect.height()), + } + } +} + +impl From> for Vector2I { + fn from(size: Point) -> Self { + Vector2I::new(size.x as i32, size.y as i32) + } +} + +impl From for Size { + fn from(vec: Vector2F) -> Self { + size(vec.x(), vec.y()) + } +} + +impl From for cosmic_text::Weight { + fn from(value: FontWeight) -> Self { + cosmic_text::Weight(value.0 as u16) + } +} + +impl From for cosmic_text::Style { + fn from(style: FontStyle) -> Self { + match style { + FontStyle::Normal => cosmic_text::Style::Normal, + FontStyle::Italic => cosmic_text::Style::Italic, + FontStyle::Oblique => cosmic_text::Style::Oblique, + } + } +} diff --git a/crates/gpui/src/platform/windows/util.rs b/crates/gpui/src/platform/windows/util.rs new file mode 100644 index 0000000000..bba6f132ab --- /dev/null +++ b/crates/gpui/src/platform/windows/util.rs @@ -0,0 +1,26 @@ +use windows::Win32::Foundation::{LPARAM, WPARAM}; + +pub(crate) trait HiLoWord { + fn hiword(&self) -> u16; + fn loword(&self) -> u16; +} + +impl HiLoWord for WPARAM { + fn hiword(&self) -> u16 { + ((self.0 >> 16) & 0xFFFF) as u16 + } + + fn loword(&self) -> u16 { + (self.0 & 0xFFFF) as u16 + } +} + +impl HiLoWord for LPARAM { + fn hiword(&self) -> u16 { + ((self.0 >> 16) & 0xFFFF) as u16 + } + + fn loword(&self) -> u16 { + (self.0 & 0xFFFF) as u16 + } +} diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs new file mode 100644 index 0000000000..f43ac7cec8 --- /dev/null +++ b/crates/gpui/src/platform/windows/window.rs @@ -0,0 +1,535 @@ +#![deny(unsafe_op_in_unsafe_fn)] +// todo!("windows"): remove +#![allow(unused_variables)] + +use std::{ + any::Any, + cell::{Cell, RefCell}, + ffi::c_void, + num::NonZeroIsize, + rc::{Rc, Weak}, + sync::{Arc, Once}, +}; + +use blade_graphics as gpu; +use futures::channel::oneshot::Receiver; +use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; +use windows::{ + core::{w, HSTRING, PCWSTR}, + Win32::{ + Foundation::{HINSTANCE, HWND, LPARAM, LRESULT, WPARAM}, + UI::WindowsAndMessaging::{ + CreateWindowExW, DefWindowProcW, GetWindowLongPtrW, LoadCursorW, PostQuitMessage, + RegisterClassW, SetWindowLongPtrW, SetWindowTextW, ShowWindow, CREATESTRUCTW, + CW_USEDEFAULT, GWLP_USERDATA, HMENU, IDC_ARROW, SW_MAXIMIZE, SW_SHOW, WINDOW_EX_STYLE, + WINDOW_LONG_PTR_INDEX, WM_CLOSE, WM_DESTROY, WM_MOVE, WM_NCCREATE, WM_NCDESTROY, + WM_PAINT, WM_SIZE, WNDCLASSW, WS_OVERLAPPEDWINDOW, WS_VISIBLE, + }, + }, +}; + +use crate::{ + platform::blade::BladeRenderer, AnyWindowHandle, Bounds, GlobalPixels, HiLoWord, Modifiers, + Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, + Point, PromptLevel, Scene, Size, WindowAppearance, WindowBounds, WindowOptions, WindowsDisplay, + WindowsPlatformInner, +}; + +struct WindowsWindowInner { + hwnd: HWND, + origin: Cell>, + size: Cell>, + mouse_position: Cell>, + input_handler: Cell>, + renderer: RefCell, + callbacks: RefCell, + platform_inner: Rc, + handle: AnyWindowHandle, +} + +impl WindowsWindowInner { + fn new( + hwnd: HWND, + cs: &CREATESTRUCTW, + platform_inner: Rc, + handle: AnyWindowHandle, + ) -> Self { + let origin = Cell::new(Point::new((cs.x as f64).into(), (cs.y as f64).into())); + let size = Cell::new(Size { + width: (cs.cx as f64).into(), + height: (cs.cy as f64).into(), + }); + let mouse_position = Cell::new(Point::default()); + let input_handler = Cell::new(None); + struct RawWindow { + hwnd: *mut c_void, + } + unsafe impl blade_rwh::HasRawWindowHandle for RawWindow { + fn raw_window_handle(&self) -> blade_rwh::RawWindowHandle { + let mut handle = blade_rwh::Win32WindowHandle::empty(); + handle.hwnd = self.hwnd; + handle.into() + } + } + unsafe impl blade_rwh::HasRawDisplayHandle for RawWindow { + fn raw_display_handle(&self) -> blade_rwh::RawDisplayHandle { + blade_rwh::WindowsDisplayHandle::empty().into() + } + } + let raw = RawWindow { hwnd: hwnd.0 as _ }; + let gpu = Arc::new( + unsafe { + gpu::Context::init_windowed( + &raw, + gpu::ContextDesc { + validation: false, + capture: false, + }, + ) + } + .unwrap(), + ); + let extent = gpu::Extent { + width: 1, + height: 1, + depth: 1, + }; + let renderer = RefCell::new(BladeRenderer::new(gpu, extent)); + let callbacks = RefCell::new(Callbacks::default()); + Self { + hwnd, + origin, + size, + mouse_position, + input_handler, + renderer, + callbacks, + platform_inner, + handle, + } + } + + fn handle_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT { + log::debug!("msg: {msg}, wparam: {}, lparam: {}", wparam.0, lparam.0); + match msg { + WM_MOVE => { + let x = lparam.loword() as f64; + let y = lparam.hiword() as f64; + self.origin.set(Point::new(x.into(), y.into())); + let mut callbacks = self.callbacks.borrow_mut(); + if let Some(callback) = callbacks.moved.as_mut() { + callback() + } + } + WM_SIZE => { + // todo!("windows"): handle maximized or minimized + let width = lparam.loword().max(1) as f64; + let height = lparam.hiword().max(1) as f64; + self.renderer + .borrow_mut() + .update_drawable_size(Size { width, height }); + let width = width.into(); + let height = height.into(); + self.size.set(Size { width, height }); + let mut callbacks = self.callbacks.borrow_mut(); + if let Some(callback) = callbacks.resize.as_mut() { + callback( + Size { + width: Pixels(width.0), + height: Pixels(height.0), + }, + 1.0, + ) + } + } + WM_PAINT => { + let mut callbacks = self.callbacks.borrow_mut(); + if let Some(callback) = callbacks.request_frame.as_mut() { + callback() + } + } + WM_CLOSE => { + let mut callbacks: std::cell::RefMut<'_, Callbacks> = self.callbacks.borrow_mut(); + if let Some(callback) = callbacks.should_close.as_mut() { + if callback() { + return LRESULT(0); + } + } + drop(callbacks); + return unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) }; + } + WM_DESTROY => { + let mut callbacks: std::cell::RefMut<'_, Callbacks> = self.callbacks.borrow_mut(); + if let Some(callback) = callbacks.close.take() { + callback() + } + let mut window_handles = self.platform_inner.window_handles.borrow_mut(); + window_handles.remove(&self.handle); + if window_handles.is_empty() { + self.platform_inner + .foreground_executor + .spawn(async { + unsafe { PostQuitMessage(0) }; + }) + .detach(); + } + return LRESULT(1); + } + _ => return unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) }, + } + LRESULT(0) + } +} + +#[derive(Default)] +struct Callbacks { + request_frame: Option>, + input: Option bool>>, + active_status_change: Option>, + resize: Option, f32)>>, + fullscreen: Option>, + moved: Option>, + should_close: Option bool>>, + close: Option>, + appearance_changed: Option>, +} + +pub(crate) struct WindowsWindow { + inner: Rc, +} + +struct WindowCreateContext { + inner: Option>, + platform_inner: Rc, + handle: AnyWindowHandle, +} + +impl WindowsWindow { + pub(crate) fn new( + platform_inner: Rc, + handle: AnyWindowHandle, + options: WindowOptions, + ) -> Self { + let dwexstyle = WINDOW_EX_STYLE::default(); + let classname = register_wnd_class(); + let windowname = HSTRING::from( + options + .titlebar + .as_ref() + .and_then(|titlebar| titlebar.title.as_ref()) + .map(|title| title.as_ref()) + .unwrap_or(""), + ); + let dwstyle = WS_OVERLAPPEDWINDOW & !WS_VISIBLE; + let mut x = CW_USEDEFAULT; + let mut y = CW_USEDEFAULT; + let mut nwidth = CW_USEDEFAULT; + let mut nheight = CW_USEDEFAULT; + match options.bounds { + WindowBounds::Fullscreen => {} + WindowBounds::Maximized => {} + WindowBounds::Fixed(bounds) => { + x = bounds.origin.x.0 as i32; + y = bounds.origin.y.0 as i32; + nwidth = bounds.size.width.0 as i32; + nheight = bounds.size.height.0 as i32; + } + }; + let hwndparent = HWND::default(); + let hmenu = HMENU::default(); + let hinstance = HINSTANCE::default(); + let mut context = WindowCreateContext { + inner: None, + platform_inner: platform_inner.clone(), + handle, + }; + let lpparam = Some(&context as *const _ as *const _); + unsafe { + CreateWindowExW( + dwexstyle, + classname, + &windowname, + dwstyle, + x, + y, + nwidth, + nheight, + hwndparent, + hmenu, + hinstance, + lpparam, + ) + }; + let wnd = Self { + inner: context.inner.unwrap(), + }; + platform_inner.window_handles.borrow_mut().insert(handle); + match options.bounds { + WindowBounds::Fullscreen => wnd.toggle_full_screen(), + WindowBounds::Maximized => wnd.maximize(), + WindowBounds::Fixed(_) => {} + } + unsafe { ShowWindow(wnd.inner.hwnd, SW_SHOW) }; + wnd + } + + fn maximize(&self) { + unsafe { ShowWindow(self.inner.hwnd, SW_MAXIMIZE) }; + } +} + +impl HasWindowHandle for WindowsWindow { + fn window_handle( + &self, + ) -> Result, raw_window_handle::HandleError> { + let raw = raw_window_handle::Win32WindowHandle::new(unsafe { + NonZeroIsize::new_unchecked(self.inner.hwnd.0) + }) + .into(); + Ok(unsafe { raw_window_handle::WindowHandle::borrow_raw(raw) }) + } +} + +// todo!("windows") +impl HasDisplayHandle for WindowsWindow { + fn display_handle( + &self, + ) -> Result, raw_window_handle::HandleError> { + unimplemented!() + } +} + +impl PlatformWindow for WindowsWindow { + fn bounds(&self) -> WindowBounds { + WindowBounds::Fixed(Bounds { + origin: self.inner.origin.get(), + size: self.inner.size.get(), + }) + } + + // todo!("windows") + fn content_size(&self) -> Size { + let size = self.inner.size.get(); + Size { + width: size.width.0.into(), + height: size.height.0.into(), + } + } + + // todo!("windows") + fn scale_factor(&self) -> f32 { + 1.0 + } + + // todo!("windows") + fn titlebar_height(&self) -> Pixels { + 20.0.into() + } + + // todo!("windows") + fn appearance(&self) -> WindowAppearance { + WindowAppearance::Dark + } + + // todo!("windows") + fn display(&self) -> Rc { + Rc::new(WindowsDisplay::new()) + } + + fn mouse_position(&self) -> Point { + self.inner.mouse_position.get() + } + + // todo!("windows") + fn modifiers(&self) -> Modifiers { + Modifiers::none() + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + // todo!("windows") + fn set_input_handler(&mut self, input_handler: PlatformInputHandler) { + self.inner.input_handler.set(Some(input_handler)); + } + + // todo!("windows") + fn take_input_handler(&mut self) -> Option { + self.inner.input_handler.take() + } + + // todo!("windows") + fn prompt( + &self, + level: PromptLevel, + msg: &str, + detail: Option<&str>, + answers: &[&str], + ) -> Receiver { + unimplemented!() + } + + // todo!("windows") + fn activate(&self) {} + + // todo!("windows") + fn set_title(&mut self, title: &str) { + unsafe { SetWindowTextW(self.inner.hwnd, &HSTRING::from(title)) } + .inspect_err(|e| log::error!("Set title failed: {e}")) + .ok(); + } + + // todo!("windows") + fn set_edited(&mut self, edited: bool) {} + + // todo!("windows") + fn show_character_palette(&self) {} + + // todo!("windows") + fn minimize(&self) {} + + // todo!("windows") + fn zoom(&self) {} + + // todo!("windows") + fn toggle_full_screen(&self) {} + + // todo!("windows") + fn on_request_frame(&self, callback: Box) { + self.inner.callbacks.borrow_mut().request_frame = Some(callback); + } + + // todo!("windows") + fn on_input(&self, callback: Box bool>) { + self.inner.callbacks.borrow_mut().input = Some(callback); + } + + // todo!("windows") + fn on_active_status_change(&self, callback: Box) { + self.inner.callbacks.borrow_mut().active_status_change = Some(callback); + } + + // todo!("windows") + fn on_resize(&self, callback: Box, f32)>) { + self.inner.callbacks.borrow_mut().resize = Some(callback); + } + + // todo!("windows") + fn on_fullscreen(&self, callback: Box) { + self.inner.callbacks.borrow_mut().fullscreen = Some(callback); + } + + // todo!("windows") + fn on_moved(&self, callback: Box) { + self.inner.callbacks.borrow_mut().moved = Some(callback); + } + + // todo!("windows") + fn on_should_close(&self, callback: Box bool>) { + self.inner.callbacks.borrow_mut().should_close = Some(callback); + } + + // todo!("windows") + fn on_close(&self, callback: Box) { + self.inner.callbacks.borrow_mut().close = Some(callback); + } + + // todo!("windows") + fn on_appearance_changed(&self, callback: Box) { + self.inner.callbacks.borrow_mut().appearance_changed = Some(callback); + } + + // todo!("windows") + fn is_topmost_for_position(&self, position: Point) -> bool { + true + } + + // todo!("windows") + fn draw(&self, scene: &Scene) { + self.inner.renderer.borrow_mut().draw(scene) + } + + // todo!("windows") + fn sprite_atlas(&self) -> Arc { + self.inner.renderer.borrow().sprite_atlas().clone() + } +} + +fn register_wnd_class() -> PCWSTR { + const CLASS_NAME: PCWSTR = w!("Zed::Window"); + + static ONCE: Once = Once::new(); + ONCE.call_once(|| { + let wc = WNDCLASSW { + lpfnWndProc: Some(wnd_proc), + hCursor: unsafe { LoadCursorW(None, IDC_ARROW).ok().unwrap() }, + lpszClassName: PCWSTR(CLASS_NAME.as_ptr()), + ..Default::default() + }; + unsafe { RegisterClassW(&wc) }; + }); + + CLASS_NAME +} + +unsafe extern "system" fn wnd_proc( + hwnd: HWND, + msg: u32, + wparam: WPARAM, + lparam: LPARAM, +) -> LRESULT { + if msg == WM_NCCREATE { + let cs = lparam.0 as *const CREATESTRUCTW; + let cs = unsafe { &*cs }; + let ctx = cs.lpCreateParams as *mut WindowCreateContext; + let ctx = unsafe { &mut *ctx }; + let inner = Rc::new(WindowsWindowInner::new( + hwnd, + cs, + ctx.platform_inner.clone(), + ctx.handle, + )); + let weak = Box::new(Rc::downgrade(&inner)); + unsafe { set_window_long(hwnd, GWLP_USERDATA, Box::into_raw(weak) as isize) }; + ctx.inner = Some(inner); + return LRESULT(1); + } + let ptr = unsafe { get_window_long(hwnd, GWLP_USERDATA) } as *mut Weak; + if ptr.is_null() { + return unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) }; + } + let inner = unsafe { &*ptr }; + let r = if let Some(inner) = inner.upgrade() { + inner.handle_msg(msg, wparam, lparam) + } else { + unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) } + }; + if msg == WM_NCDESTROY { + unsafe { set_window_long(hwnd, GWLP_USERDATA, 0) }; + unsafe { std::mem::drop(Box::from_raw(ptr)) }; + } + r +} + +unsafe fn get_window_long(hwnd: HWND, nindex: WINDOW_LONG_PTR_INDEX) -> isize { + #[cfg(target_pointer_width = "64")] + unsafe { + GetWindowLongPtrW(hwnd, nindex) + } + #[cfg(target_pointer_width = "32")] + unsafe { + GetWindowLongW(hwnd, nindex) as isize + } +} + +unsafe fn set_window_long(hwnd: HWND, nindex: WINDOW_LONG_PTR_INDEX, dwnewlong: isize) -> isize { + #[cfg(target_pointer_width = "64")] + unsafe { + SetWindowLongPtrW(hwnd, nindex, dwnewlong) + } + #[cfg(target_pointer_width = "32")] + unsafe { + SetWindowLongW(hwnd, nindex, dwnewlong as i32) as isize + } +} diff --git a/crates/zed/build.rs b/crates/zed/build.rs index 6905d492e1..daea199e21 100644 --- a/crates/zed/build.rs +++ b/crates/zed/build.rs @@ -43,4 +43,9 @@ fn main() { } } } + + // todo!("windows"): This is to avoid stack overflow. Remove it when solved. + if std::env::var("CARGO_CFG_TARGET_ENV").ok() == Some("msvc".to_string()) { + println!("cargo:rustc-link-arg=/stack:{}", 8 * 1024 * 1024); + } } From 9095a6b04e1d7eb4e6f2241701b2eec29bf4ca5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Sun, 3 Mar 2024 20:18:08 +0100 Subject: [PATCH 050/121] Replace todo calls with error values in linux/platform (#8531) We currently use a mix of unimplemented methods with empty bodies and `todo!()` calls in linux/platform. `todo!()`s cause crashes in runtime with accidental key presses or clicks. To avoid this, this PR replaces `todo!()`s in linux/platform with error values. This helps when working on Zed itself, testing PRs etc. Release Notes: - N/A --- crates/gpui/src/platform/linux/platform.rs | 25 +++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 74a2bd301f..feadee0ad6 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -321,8 +321,11 @@ impl Platform for LinuxPlatform { }) } + //todo!(linux) fn app_path(&self) -> Result { - unimplemented!() + Err(anyhow::Error::msg( + "Platform::app_path is not implemented yet", + )) } // todo(linux) @@ -332,8 +335,11 @@ impl Platform for LinuxPlatform { UtcOffset::UTC } + //todo!(linux) fn path_for_auxiliary_executable(&self, name: &str) -> Result { - unimplemented!() + Err(anyhow::Error::msg( + "Platform::path_for_auxiliary_executable is not implemented yet", + )) } // todo(linux) @@ -352,16 +358,25 @@ impl Platform for LinuxPlatform { None } + //todo!(linux) fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task> { - unimplemented!() + Task::Ready(Some(Err(anyhow::Error::msg( + "Platform::with_credentials is not implemented yet", + )))) } + //todo!(linux) fn read_credentials(&self, url: &str) -> Task)>>> { - unimplemented!() + Task::Ready(Some(Err(anyhow::Error::msg( + "Platform::read_credentials is not implemented yet", + )))) } + //todo!(linux) fn delete_credentials(&self, url: &str) -> Task> { - unimplemented!() + Task::Ready(Some(Err(anyhow::Error::msg( + "Platform::delete_credentials is not implemented yet", + )))) } fn window_appearance(&self) -> crate::WindowAppearance { From 37ffa8604321558d7dace405751fc11cce7189e2 Mon Sep 17 00:00:00 2001 From: aryal <141743392+aryalaadi@users.noreply.github.com> Date: Mon, 4 Mar 2024 01:06:36 +0545 Subject: [PATCH 051/121] Add libzstd to script/linux (#8780) add a missing dependency to linux script --- script/linux | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/script/linux b/script/linux index b5bd621b0d..fd562573f1 100755 --- a/script/linux +++ b/script/linux @@ -13,6 +13,7 @@ if [[ -n $apt ]]; then libwayland-dev libxkbcommon-x11-dev libssl-dev + libzstd-dev ) $maysudo "$apt" install -y "${deps[@]}" exit 0 @@ -28,6 +29,7 @@ if [[ -n $dnf ]]; then wayland-devel libxkbcommon-x11-devel openssl-devel + libzstd-devel ) $maysudo "$dnf" install -y "${deps[@]}" exit 0 @@ -43,6 +45,7 @@ if [[ -n $zyp ]]; then wayland-devel libxkbcommon-x11-devel openssl-devel + libzstd-devel ) $maysudo "$zyp" install -y "${deps[@]}" exit 0 @@ -58,6 +61,7 @@ if [[ -n $pacman ]]; then wayland libxkbcommon-x11 openssl + libzstd ) $maysudo "$pacman" -S --needed --noconfirm "${deps[@]}" exit 0 @@ -73,6 +77,7 @@ if [[ -n $xbps ]]; then wayland-devel libxkbcommon-devel openssl-devel + libzstd-devel ) $maysudo "$xbps" -Syu "${deps[@]}" exit 0 From 6a6dbe3aa1e67cd0c52d56d46499b7462419f84e Mon Sep 17 00:00:00 2001 From: Joel Selvaraj Date: Sun, 3 Mar 2024 13:28:20 -0600 Subject: [PATCH 052/121] linux: wayland: implement cursor style handling (#8632) Release Notes: - Implemented cursor style changing in wayland [zed_cursor_wayland.webm](https://github.com/zed-industries/zed/assets/12579216/cbc03f85-41c1-4687-88b5-2aa5612d7129) --------- Co-authored-by: Mikayla --- Cargo.lock | 18 ++++ crates/gpui/Cargo.toml | 1 + crates/gpui/src/platform/linux/client.rs | 3 +- crates/gpui/src/platform/linux/platform.rs | 5 +- crates/gpui/src/platform/linux/wayland.rs | 1 + .../gpui/src/platform/linux/wayland/client.rs | 92 ++++++++++++++++--- .../gpui/src/platform/linux/wayland/cursor.rs | 67 ++++++++++++++ crates/gpui/src/platform/linux/x11/client.rs | 7 +- 8 files changed, 174 insertions(+), 20 deletions(-) create mode 100644 crates/gpui/src/platform/linux/wayland/cursor.rs diff --git a/Cargo.lock b/Cargo.lock index ce408e1017..34c5933844 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4248,6 +4248,7 @@ dependencies = [ "waker-fn", "wayland-backend", "wayland-client", + "wayland-cursor", "wayland-protocols", "windows 0.53.0", "xcb", @@ -11501,6 +11502,17 @@ dependencies = [ "wayland-scanner", ] +[[package]] +name = "wayland-cursor" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71ce5fa868dd13d11a0d04c5e2e65726d0897be8de247c0c5a65886e283231ba" +dependencies = [ + "rustix 0.38.30", + "wayland-client", + "xcursor", +] + [[package]] name = "wayland-protocols" version = "0.31.2" @@ -12198,6 +12210,12 @@ dependencies = [ "quick-xml 0.30.0", ] +[[package]] +name = "xcursor" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911" + [[package]] name = "xdg-home" version = "1.1.0" diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 297c8a06ed..da6fd0af36 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -106,6 +106,7 @@ open = "5.0.1" ashpd = "0.7.0" xcb = { version = "1.3", features = ["as-raw-xcb-connection", "randr", "xkb"] } wayland-client= { version = "0.31.2" } +wayland-cursor = "0.31.1" wayland-protocols = { version = "0.31.2", features = ["client", "staging", "unstable"] } wayland-backend = { version = "0.3.3", features = ["client_system"] } xkbcommon = { version = "0.7", features = ["wayland", "x11"] } diff --git a/crates/gpui/src/platform/linux/client.rs b/crates/gpui/src/platform/linux/client.rs index 0d71e4ef00..d74aac7369 100644 --- a/crates/gpui/src/platform/linux/client.rs +++ b/crates/gpui/src/platform/linux/client.rs @@ -1,7 +1,7 @@ use std::rc::Rc; use crate::platform::PlatformWindow; -use crate::{AnyWindowHandle, DisplayId, PlatformDisplay, WindowOptions}; +use crate::{AnyWindowHandle, CursorStyle, DisplayId, PlatformDisplay, WindowOptions}; pub trait Client { fn displays(&self) -> Vec>; @@ -11,4 +11,5 @@ pub trait Client { handle: AnyWindowHandle, options: WindowOptions, ) -> Box; + fn set_cursor_style(&self, style: CursorStyle); } diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index feadee0ad6..cad0d1074b 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -342,8 +342,9 @@ impl Platform for LinuxPlatform { )) } - // todo(linux) - fn set_cursor_style(&self, style: CursorStyle) {} + fn set_cursor_style(&self, style: CursorStyle) { + self.client.set_cursor_style(style) + } // todo(linux) fn should_auto_hide_scrollbars(&self) -> bool { diff --git a/crates/gpui/src/platform/linux/wayland.rs b/crates/gpui/src/platform/linux/wayland.rs index 79f3aa223c..ebb592d375 100644 --- a/crates/gpui/src/platform/linux/wayland.rs +++ b/crates/gpui/src/platform/linux/wayland.rs @@ -4,5 +4,6 @@ pub(crate) use client::*; mod client; +mod cursor; mod display; mod window; diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index 957515c8ff..0bbd716e06 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -32,12 +32,13 @@ use xkbcommon::xkb::ffi::XKB_KEYMAP_FORMAT_TEXT_V1; use xkbcommon::xkb::{self, Keycode, KEYMAP_COMPILE_NO_FLAGS}; use crate::platform::linux::client::Client; +use crate::platform::linux::wayland::cursor::Cursor; use crate::platform::linux::wayland::window::{WaylandDecorationState, WaylandWindow}; use crate::platform::{LinuxPlatformInner, PlatformWindow}; use crate::{ - platform::linux::wayland::window::WaylandWindowState, AnyWindowHandle, DisplayId, KeyDownEvent, - KeyUpEvent, Keystroke, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, - NavigationDirection, Pixels, PlatformDisplay, PlatformInput, Point, ScrollDelta, + platform::linux::wayland::window::WaylandWindowState, AnyWindowHandle, CursorStyle, DisplayId, + KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, + MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay, PlatformInput, Point, ScrollDelta, ScrollWheelEvent, TouchPhase, WindowOptions, }; @@ -47,6 +48,7 @@ const MIN_KEYCODE: u32 = 8; pub(crate) struct WaylandClientStateInner { compositor: wl_compositor::WlCompositor, wm_base: xdg_wm_base::XdgWmBase, + shm: wl_shm::WlShm, viewporter: Option, fractional_scale_manager: Option, decoration_manager: Option, @@ -63,8 +65,16 @@ pub(crate) struct WaylandClientStateInner { loop_handle: Rc>, } +pub(crate) struct CursorState { + cursor_icon_name: String, + cursor: Option, +} + #[derive(Clone)] -pub(crate) struct WaylandClientState(Rc>); +pub(crate) struct WaylandClientState { + client_state_inner: Rc>, + cursor_state: Rc>, +} pub(crate) struct KeyRepeat { characters_per_second: u32, @@ -104,6 +114,7 @@ impl WaylandClient { let mut state_inner = Rc::new(RefCell::new(WaylandClientStateInner { compositor: globals.bind(&qh, 1..=1, ()).unwrap(), wm_base: globals.bind(&qh, 1..=1, ()).unwrap(), + shm: globals.bind(&qh, 1..=1, ()).unwrap(), viewporter: globals.bind(&qh, 1..=1, ()).ok(), fractional_scale_manager: globals.bind(&qh, 1..=1, ()).ok(), decoration_manager: globals.bind(&qh, 1..=1, ()).ok(), @@ -131,9 +142,17 @@ impl WaylandClient { loop_handle: Rc::clone(&linux_platform_inner.loop_handle), })); + let mut cursor_state = Rc::new(RefCell::new(CursorState { + cursor_icon_name: "arrow".to_string(), + cursor: None, + })); + let source = WaylandSource::new(conn, event_queue); - let mut state = WaylandClientState(Rc::clone(&state_inner)); + let mut state = WaylandClientState { + client_state_inner: Rc::clone(&state_inner), + cursor_state: Rc::clone(&cursor_state), + }; linux_platform_inner .loop_handle .insert_source(source, move |_, queue, _| { @@ -143,7 +162,10 @@ impl WaylandClient { Self { platform_inner: linux_platform_inner, - state: WaylandClientState(state_inner), + state: WaylandClientState { + client_state_inner: state_inner, + cursor_state, + }, qh: Arc::new(qh), } } @@ -163,7 +185,7 @@ impl Client for WaylandClient { handle: AnyWindowHandle, options: WindowOptions, ) -> Box { - let mut state = self.state.0.borrow_mut(); + let mut state = self.state.client_state_inner.borrow_mut(); let wl_surface = state.compositor.create_surface(&self.qh, ()); let xdg_surface = state.wm_base.get_xdg_surface(&wl_surface, &self.qh, ()); @@ -217,6 +239,32 @@ impl Client for WaylandClient { state.windows.push((xdg_surface, Rc::clone(&window_state))); Box::new(WaylandWindow(window_state)) } + + fn set_cursor_style(&self, style: CursorStyle) { + let cursor_icon_name = match style { + CursorStyle::Arrow => "arrow".to_string(), + CursorStyle::IBeam => "text".to_string(), + CursorStyle::Crosshair => "crosshair".to_string(), + CursorStyle::ClosedHand => "grabbing".to_string(), + CursorStyle::OpenHand => "openhand".to_string(), + CursorStyle::PointingHand => "hand".to_string(), + CursorStyle::ResizeLeft => "w-resize".to_string(), + CursorStyle::ResizeRight => "e-resize".to_string(), + CursorStyle::ResizeLeftRight => "ew-resize".to_string(), + CursorStyle::ResizeUp => "n-resize".to_string(), + CursorStyle::ResizeDown => "s-resize".to_string(), + CursorStyle::ResizeUpDown => "ns-resize".to_string(), + CursorStyle::DisappearingItem => "grabbing".to_string(), // todo!(linux) - couldn't find equivalent icon in linux + CursorStyle::IBeamCursorForVerticalLayout => "vertical-text".to_string(), + CursorStyle::OperationNotAllowed => "not-allowed".to_string(), + CursorStyle::DragLink => "dnd-link".to_string(), + CursorStyle::DragCopy => "dnd-copy".to_string(), + CursorStyle::ContextualMenu => "context-menu".to_string(), + }; + + let mut cursor_state = self.state.cursor_state.borrow_mut(); + cursor_state.cursor_icon_name = cursor_icon_name; + } } impl Dispatch for WaylandClientState { @@ -263,7 +311,7 @@ impl Dispatch> for WaylandClientState { _: &Connection, qh: &QueueHandle, ) { - let mut state = state.0.borrow_mut(); + let mut state = state.client_state_inner.borrow_mut(); if let wl_callback::Event::Done { .. } = event { for window in &state.windows { if window.1.surface.id() == surf.id() { @@ -285,7 +333,7 @@ impl Dispatch for WaylandClientState { _: &Connection, _: &QueueHandle, ) { - let mut state = state.0.borrow_mut(); + let mut state = state.client_state_inner.borrow_mut(); if let xdg_surface::Event::Configure { serial, .. } = event { xdg_surface.ack_configure(serial); for window in &state.windows { @@ -308,7 +356,7 @@ impl Dispatch for WaylandClientState { _: &Connection, _: &QueueHandle, ) { - let mut state = state.0.borrow_mut(); + let mut state = state.client_state_inner.borrow_mut(); if let xdg_toplevel::Event::Configure { width, height, @@ -386,7 +434,7 @@ impl Dispatch for WaylandClientState { conn: &Connection, qh: &QueueHandle, ) { - let mut state = this.0.borrow_mut(); + let mut state = this.client_state_inner.borrow_mut(); match event { wl_keyboard::Event::RepeatInfo { rate, delay } => { state.repeat.characters_per_second = rate as u32; @@ -497,7 +545,7 @@ impl Dispatch for WaylandClientState { let this = this.clone(); let timer = Timer::from_duration(delay); - let state_ = Rc::clone(&this.0); + let state_ = Rc::clone(&this.client_state_inner); state .loop_handle .insert_source(timer, move |event, _metadata, shared_data| { @@ -567,9 +615,18 @@ impl Dispatch for WaylandClientState { conn: &Connection, qh: &QueueHandle, ) { - let mut state = state.0.borrow_mut(); + let mut cursor_state = state.cursor_state.borrow_mut(); + let mut state = state.client_state_inner.borrow_mut(); + + if cursor_state.cursor.is_none() { + cursor_state.cursor = Some(Cursor::new(&conn, &state.compositor, &qh, &state.shm, 24)); + } + let cursor_icon_name = cursor_state.cursor_icon_name.clone(); + let mut cursor: &mut Cursor = cursor_state.cursor.as_mut().unwrap(); + match event { wl_pointer::Event::Enter { + serial, surface, surface_x, surface_y, @@ -578,11 +635,14 @@ impl Dispatch for WaylandClientState { let mut mouse_focused_window = None; for window in &state.windows { if window.1.surface.id() == surface.id() { + window.1.set_focused(true); mouse_focused_window = Some(Rc::clone(&window.1)); } } if mouse_focused_window.is_some() { state.mouse_focused_window = mouse_focused_window; + cursor.set_serial_id(serial); + cursor.set_icon(&wl_pointer, cursor_icon_name); } state.mouse_location = Some(Point { @@ -610,6 +670,7 @@ impl Dispatch for WaylandClientState { modifiers: state.modifiers, }), ); + cursor.set_icon(&wl_pointer, cursor_icon_name); } wl_pointer::Event::Button { button, @@ -693,6 +754,7 @@ impl Dispatch for WaylandClientState { pressed_button: None, modifiers: Modifiers::default(), })); + focused_window.set_focused(false); } state.mouse_focused_window = None; state.mouse_location = None; @@ -711,7 +773,7 @@ impl Dispatch for Wayland _: &Connection, _: &QueueHandle, ) { - let mut state = state.0.borrow_mut(); + let mut state = state.client_state_inner.borrow_mut(); if let wp_fractional_scale_v1::Event::PreferredScale { scale, .. } = event { for window in &state.windows { if window.0.id() == *id { @@ -734,7 +796,7 @@ impl Dispatch _: &Connection, _: &QueueHandle, ) { - let mut state = state.0.borrow_mut(); + let mut state = state.client_state_inner.borrow_mut(); if let zxdg_toplevel_decoration_v1::Event::Configure { mode, .. } = event { for window in &state.windows { if window.0.id() == *surface_id { diff --git a/crates/gpui/src/platform/linux/wayland/cursor.rs b/crates/gpui/src/platform/linux/wayland/cursor.rs new file mode 100644 index 0000000000..8b641972e3 --- /dev/null +++ b/crates/gpui/src/platform/linux/wayland/cursor.rs @@ -0,0 +1,67 @@ +use crate::platform::linux::wayland::WaylandClientState; +use wayland_backend::client::InvalidId; +use wayland_client::protocol::wl_compositor::WlCompositor; +use wayland_client::protocol::wl_pointer::WlPointer; +use wayland_client::protocol::wl_shm::WlShm; +use wayland_client::protocol::wl_surface::WlSurface; +use wayland_client::{Connection, QueueHandle}; +use wayland_cursor::{CursorImageBuffer, CursorTheme}; + +pub(crate) struct Cursor { + theme: Result, + current_icon_name: String, + surface: WlSurface, + serial_id: u32, +} + +impl Cursor { + pub fn new( + connection: &Connection, + compositor: &WlCompositor, + qh: &QueueHandle, + shm: &WlShm, + size: u32, + ) -> Self { + Self { + theme: CursorTheme::load(&connection, shm.clone(), size), + current_icon_name: "".to_string(), + surface: compositor.create_surface(qh, ()), + serial_id: 0, + } + } + + pub fn set_serial_id(&mut self, serial_id: u32) { + self.serial_id = serial_id; + } + + pub fn set_icon(&mut self, wl_pointer: &WlPointer, cursor_icon_name: String) { + if self.current_icon_name != cursor_icon_name { + if self.theme.is_ok() { + if let Some(cursor) = self.theme.as_mut().unwrap().get_cursor(&cursor_icon_name) { + let buffer: &CursorImageBuffer = &cursor[0]; + let (width, height) = buffer.dimensions(); + let (hot_x, hot_y) = buffer.hotspot(); + + wl_pointer.set_cursor( + self.serial_id, + Some(&self.surface), + hot_x as i32, + hot_y as i32, + ); + self.surface.attach(Some(&buffer), 0, 0); + self.surface.damage(0, 0, width as i32, height as i32); + self.surface.commit(); + + self.current_icon_name = cursor_icon_name; + } else { + log::warn!( + "Linux: Wayland: Unable to get cursor icon: {}", + cursor_icon_name + ); + } + } else { + log::warn!("Linux: Wayland: Unable to load cursor themes"); + } + } + } +} diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index 8af38b512d..dd01102276 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -10,8 +10,8 @@ use collections::HashMap; use crate::platform::linux::client::Client; use crate::platform::{LinuxPlatformInner, PlatformWindow}; use crate::{ - AnyWindowHandle, Bounds, DisplayId, PlatformDisplay, PlatformInput, Point, ScrollDelta, Size, - TouchPhase, WindowOptions, + AnyWindowHandle, Bounds, CursorStyle, DisplayId, PlatformDisplay, PlatformInput, Point, + ScrollDelta, Size, TouchPhase, WindowOptions, }; use super::{X11Display, X11Window, X11WindowState, XcbAtoms}; @@ -351,6 +351,9 @@ impl Client for X11Client { self.state.borrow_mut().windows.insert(x_window, window_ref); Box::new(X11Window(window_ptr)) } + + //todo!(linux) + fn set_cursor_style(&self, _style: CursorStyle) {} } // Adatpted from: From a5eab29662e82458115841b10d1e7c7322644ba7 Mon Sep 17 00:00:00 2001 From: Maharshi Basu <84385565+MashyBasker@users.noreply.github.com> Date: Mon, 4 Mar 2024 00:58:53 +0530 Subject: [PATCH 053/121] Implement `all_font_families` for the LinuxTextSystem (#8331) Implemented the function to get all font family names for `LinuxTextSystem` which was previously kept as `unimplemented`. Release Notes: - N/A Change Explanation: - We get the [`&Database`](https://docs.rs/fontdb/0.16.1/fontdb/struct.Database.html) struct from the [`FontSystem`](https://docs.rs/cosmic-text/latest/cosmic_text/struct.FontSystem.html) by using the `.db` method. - From the `Database` struct we get the [`FaceInfo`](https://docs.rs/fontdb/0.16.1/fontdb/struct.FaceInfo.html) which the provides a method to get the family names([`families`](https://docs.rs/fontdb/0.16.1/fontdb/struct.FaceInfo.html#structfield.families)) - The `families` function returns a tuple of Vec. The tuple consists of the `String` containing the name and the [`Language`](https://docs.rs/fontdb/0.16.1/fontdb/enum.Language.html) struct. *It is noted that for the `families` function, the first family is always `English US` unless it is unavailable* Since the empty function provided [here](https://github.com/zed-industries/zed/blob/main/crates/gpui/src/platform/linux/text_system.rs#L75) explicitly declares a `Vec` as the return type so I am prioritizing the `English US` font family unless advised otherwise by the reviewer. --------- Signed-off-by: Maharshi Basu Co-authored-by: Mikayla Maki --- crates/gpui/src/platform/linux/text_system.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/gpui/src/platform/linux/text_system.rs b/crates/gpui/src/platform/linux/text_system.rs index 8fd1be323b..70caa7175b 100644 --- a/crates/gpui/src/platform/linux/text_system.rs +++ b/crates/gpui/src/platform/linux/text_system.rs @@ -9,6 +9,8 @@ use cosmic_text::{ fontdb::Query, Attrs, AttrsList, BufferLine, CacheKey, Family, Font as CosmicTextFont, FontSystem, SwashCache, }; + +use itertools::Itertools; use parking_lot::{RwLock, RwLockUpgradableReadGuard}; use pathfinder_geometry::{ rect::{RectF, RectI}, @@ -71,9 +73,14 @@ impl PlatformTextSystem for LinuxTextSystem { .collect() } - // todo(linux) fn all_font_families(&self) -> Vec { - Vec::new() + self.0 + .read() + .font_system + .db() + .faces() + .filter_map(|face| Some(face.families.get(0)?.0.clone())) + .collect_vec() } fn font_id(&self, font: &Font) -> Result { From 36d9b3d48364e7423a3c660ba5dc00633c33cfa6 Mon Sep 17 00:00:00 2001 From: Ezekiel Warren Date: Sun, 3 Mar 2024 11:42:36 -0800 Subject: [PATCH 054/121] windows: get pid with win32 api (#8785) While trying to get mouse/keyboard support in for Windows I ran into a stack overflow issue related to the pid being `-1`. Getting the proper process ID seems to fix it. Release Notes: - Fixed stack overflow on Windows --- Cargo.lock | 1 + crates/terminal/Cargo.toml | 6 ++++++ crates/terminal/src/terminal.rs | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 34c5933844..5e1eff04a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9463,6 +9463,7 @@ dependencies = [ "theme", "thiserror", "util", + "windows 0.53.0", ] [[package]] diff --git a/crates/terminal/Cargo.toml b/crates/terminal/Cargo.toml index 7123264948..c41168d639 100644 --- a/crates/terminal/Cargo.toml +++ b/crates/terminal/Cargo.toml @@ -30,5 +30,11 @@ theme.workspace = true thiserror.workspace = true util.workspace = true +[target.'cfg(windows)'.dependencies.windows] +version = "0.53.0" +features = [ + "Win32_System_Threading", +] + [dev-dependencies] rand.workspace = true diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 2ae53d40d2..342d4c6cc2 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -670,7 +670,7 @@ impl Terminal { let mut pid = unsafe { libc::tcgetpgrp(self.shell_fd as i32) }; // todo("windows") #[cfg(windows)] - let mut pid = -1; + let mut pid = unsafe { windows::Win32::System::Threading::GetCurrentProcessId() } as i32; if pid < 0 { pid = self.shell_pid as i32; } From 0717d30389bf261619c207dc37b76c623cb6d497 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Sun, 3 Mar 2024 11:58:31 -0800 Subject: [PATCH 055/121] Move windows up to workspace dependency (#8786) This way we can keep track of what we're using. Release Notes: - N/A --- Cargo.toml | 11 +++++++++++ crates/gpui/Cargo.toml | 10 ++-------- crates/terminal/Cargo.toml | 7 ++----- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 067c08786d..682908e2f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -309,6 +309,17 @@ wasmtime-wasi = "18.0" which = "6.0.0" sys-locale = "0.3.1" +[workspace.dependencies.windows] +version = "0.53.0" +features = [ + "Win32_Graphics_Gdi", + "Win32_UI_WindowsAndMessaging", + "Win32_Security", + "Win32_System_Threading", +] + + + [patch.crates-io] tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "e4a23971ec3071a09c1e84816954c98f96e98e52" } # Workaround for a broken nightly build of gpui: See #7644 and revisit once 0.5.3 is released. diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index da6fd0af36..273443791d 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -114,14 +114,8 @@ as-raw-xcb-connection = "1" calloop = "0.12.4" calloop-wayland-source = "0.2.0" -[target.'cfg(windows)'.dependencies.windows] -version = "0.53.0" -features = [ - "Win32_Graphics_Gdi", - "Win32_UI_WindowsAndMessaging", - "Win32_Security", - "Win32_System_Threading", -] +[target.'cfg(windows)'.dependencies] +windows.workspace = true [[example]] name = "hello_world" diff --git a/crates/terminal/Cargo.toml b/crates/terminal/Cargo.toml index c41168d639..884b754aac 100644 --- a/crates/terminal/Cargo.toml +++ b/crates/terminal/Cargo.toml @@ -30,11 +30,8 @@ theme.workspace = true thiserror.workspace = true util.workspace = true -[target.'cfg(windows)'.dependencies.windows] -version = "0.53.0" -features = [ - "Win32_System_Threading", -] +[target.'cfg(windows)'.dependencies] +windows.workspace = true [dev-dependencies] rand.workspace = true From 49f378ead317528b76242091480e710a935d55b9 Mon Sep 17 00:00:00 2001 From: feelamee <106318766+feelamee@users.noreply.github.com> Date: Sun, 3 Mar 2024 21:26:09 +0000 Subject: [PATCH 056/121] Fix name of zstd package in Arch Linux repos (#8789) Fixed name of [zstd](https://archlinux.org/packages/core/x86_64/zstd/) package in Arch Linux repos in script for installing deps, required for building Zed on linux Release Notes: - N/A --- script/linux | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/linux b/script/linux index fd562573f1..ea248260ad 100755 --- a/script/linux +++ b/script/linux @@ -61,7 +61,7 @@ if [[ -n $pacman ]]; then wayland libxkbcommon-x11 openssl - libzstd + zstd ) $maysudo "$pacman" -S --needed --noconfirm "${deps[@]}" exit 0 From 1442fcb4974515b60bab23f745c977d1e47f5583 Mon Sep 17 00:00:00 2001 From: feelamee <106318766+feelamee@users.noreply.github.com> Date: Sun, 3 Mar 2024 21:44:39 +0000 Subject: [PATCH 057/121] Add doas support in script/linux (#8788) Release Notes: - n/a --- script/linux | 2 +- typos.toml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/script/linux b/script/linux index ea248260ad..a35918ce73 100755 --- a/script/linux +++ b/script/linux @@ -1,7 +1,7 @@ #!/usr/bin/bash -e # if sudo is not installed, define an empty alias -maysudo=$(command -v sudo || true) +maysudo=$(command -v sudo || command -v doas || true) # Ubuntu, Debian, etc. # https://packages.ubuntu.com/ diff --git a/typos.toml b/typos.toml index af70fb77c7..91e95e35e9 100644 --- a/typos.toml +++ b/typos.toml @@ -26,6 +26,7 @@ extend-ignore-re = [ "COLUMN enviroment", # Typo in ClickHouse column name. # crates/collab/src/api/events.rs - "rename = \"sesssion_id\"" + "rename = \"sesssion_id\"", + "doas", ] check-filename = true From ff6500831693886288dc789d27f1b2d601c73a60 Mon Sep 17 00:00:00 2001 From: Niklas Wimmer Date: Sun, 3 Mar 2024 22:54:06 +0100 Subject: [PATCH 058/121] linux: add credentials impl via oo7 (#8035) This change implements gpui's credentials API for the linux platform, using the [`oo7`](https://lib.rs/crates/oo7) library. We had a short discussion on Discord about where to store credentials and landed on the two dbus APIs [`org.freedesktop.Secrets`](https://specifications.freedesktop.org/secret-service/latest/index.html) and [`org.freedesktop.portal.Secrets`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Secret.html). The first one provides access to a more or less general purpose keystore, the second provides a way of obtaining a unique masterkey which in turn can be used for encrypting stuff and storing it to disk (especially interesting for sandboxed apps, think flatpak/snap). I decided to give the implementation a try with `oo7`, which uses the portal if the app is sandboxed and the secret service otherwise. If we do not want to use that library, we would probably have to more or less copy its functionality anyways. I also heard rumors of eventually changing the credentials API and I think this implementation serves as a starting point to discuss the need for this? With a working credentials implementation the sign in button now works (it panicked before). Todos: - [x] implement keystore unlocking - [x] try the change with oo7's tracing enabled? - [x] test the password deletion Release Notes: - N/A --------- Signed-off-by: Niklas Wimmer Co-authored-by: Mikayla Maki --- Cargo.lock | 625 ++++++++++++++++----- crates/gpui/Cargo.toml | 1 + crates/gpui/src/platform/linux/platform.rs | 68 ++- 3 files changed, 531 insertions(+), 163 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5e1eff04a8..6ba0cd82ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,6 +40,18 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if 1.0.0", + "cipher 0.4.4", + "cpufeatures", + "zeroize", +] + [[package]] name = "ahash" version = "0.7.6" @@ -312,7 +324,7 @@ dependencies = [ "serde", "serde_repr", "url", - "zbus", + "zbus 3.15.1", ] [[package]] @@ -384,6 +396,18 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-broadcast" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258b52a1aa741b9f09783b2d86cf0aeeb617bbf847f6933340a39644227acbdb" +dependencies = [ + "event-listener 5.1.0", + "event-listener-strategy 0.5.0", + "futures-core", + "pin-project-lite", +] + [[package]] name = "async-channel" version = "1.9.0" @@ -395,6 +419,19 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-channel" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" +dependencies = [ + "concurrent-queue", + "event-listener 5.1.0", + "event-listener-strategy 0.5.0", + "futures-core", + "pin-project-lite", +] + [[package]] name = "async-compat" version = "0.2.1" @@ -455,7 +492,7 @@ checksum = "bc19683171f287921f2405677dd2ed2549c3b3bda697a563ebc3a121ace2aba1" dependencies = [ "async-lock 3.3.0", "blocking", - "futures-lite 2.0.0", + "futures-lite 2.2.0", ] [[package]] @@ -464,7 +501,7 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" dependencies = [ - "async-channel", + "async-channel 1.9.0", "async-executor", "async-io 1.13.0", "async-lock 2.8.0", @@ -503,7 +540,7 @@ dependencies = [ "cfg-if 1.0.0", "concurrent-queue", "futures-io", - "futures-lite 2.0.0", + "futures-lite 2.2.0", "parking", "polling 3.3.2", "rustix 0.38.30", @@ -528,7 +565,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" dependencies = [ "event-listener 4.0.3", - "event-listener-strategy", + "event-listener-strategy 0.4.0", "pin-project-lite", ] @@ -564,7 +601,7 @@ checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" dependencies = [ "async-io 2.3.1", "blocking", - "futures-lite 2.0.0", + "futures-lite 2.2.0", ] [[package]] @@ -594,6 +631,24 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "async-process" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451e3cf68011bd56771c79db04a9e333095ab6349f7e47592b788e9b98720cc8" +dependencies = [ + "async-channel 2.2.0", + "async-io 2.3.1", + "async-lock 3.3.0", + "async-signal", + "blocking", + "cfg-if 1.0.0", + "event-listener 5.1.0", + "futures-lite 2.2.0", + "rustix 0.38.30", + "windows-sys 0.52.0", +] + [[package]] name = "async-recursion" version = "0.3.2" @@ -616,17 +671,35 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "async-signal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +dependencies = [ + "async-io 2.3.1", + "async-lock 2.8.0", + "atomic-waker", + "cfg-if 1.0.0", + "futures-core", + "futures-io", + "rustix 0.38.30", + "signal-hook-registry", + "slab", + "windows-sys 0.48.0", +] + [[package]] name = "async-std" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" dependencies = [ - "async-channel", + "async-channel 1.9.0", "async-global-executor", "async-io 1.13.0", "async-lock 2.8.0", - "async-process", + "async-process 1.7.0", "crossbeam-utils", "futures-channel", "futures-core", @@ -1440,18 +1513,28 @@ dependencies = [ ] [[package]] -name = "blocking" -version = "1.3.1" +name = "block-padding" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" dependencies = [ - "async-channel", - "async-lock 2.8.0", + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +dependencies = [ + "async-channel 2.2.0", + "async-lock 3.3.0", "async-task", - "atomic-waker", - "fastrand 1.9.0", - "futures-lite 1.13.0", - "log", + "fastrand 2.0.0", + "futures-io", + "futures-lite 2.2.0", + "piper", + "tracing", ] [[package]] @@ -1746,6 +1829,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher 0.4.4", +] + [[package]] name = "cbindgen" version = "0.26.0" @@ -1853,6 +1945,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + [[package]] name = "clang-sys" version = "1.6.1" @@ -2540,18 +2643,18 @@ dependencies = [ [[package]] name = "cranelift-bforest" -version = "0.105.1" +version = "0.105.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29a6391a9172a93f413370fa561c6bca786e06c89cf85f23f02f6345b1c8ee34" +checksum = "9515fcc42b6cb5137f76b84c1a6f819782d0cf12473d145d3bc5cd67eedc8bc2" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.105.1" +version = "0.105.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "409c6cbb326604a53ec47eb6341fc85128f24c81012a014b4c728ed24f6e9350" +checksum = "1ad827c6071bfe6d22de1bc331296a29f9ddc506ff926d8415b435ec6a6efce0" dependencies = [ "bumpalo", "cranelift-bforest", @@ -2570,33 +2673,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.105.1" +version = "0.105.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff55e100130995b9ad9ac6b03a24ed5da3c1a1261dcdeb8a7a0292656994fb3" +checksum = "10e6b36237a9ca2ce2fb4cc7741d418a080afa1327402138412ef85d5367bef1" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.105.1" +version = "0.105.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1446e2eb395fc7b3019a36dccb7eccea923f6caf581b903c8e7e751b6d214a7" +checksum = "c36bf4bfb86898a94ccfa773a1f86e8a5346b1983ff72059bdd2db4600325251" [[package]] name = "cranelift-control" -version = "0.105.1" +version = "0.105.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24076ecf69cbf8b9e1e532ae8e7ac01d850a1c2e127058a26eb3245f9d5b89d1" +checksum = "7cbf36560e7a6bd1409ca91e7b43b2cc7ed8429f343d7605eadf9046e8fac0d0" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.105.1" +version = "0.105.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f40df95180ad317c60459bb90dd87803d35e538f4c54376d8b26c851f6f0a1b" +checksum = "a71e11061a75b1184c09bea97c026a88f08b59ade96a7bb1f259d4ea0df2e942" dependencies = [ "serde", "serde_derive", @@ -2604,9 +2707,9 @@ dependencies = [ [[package]] name = "cranelift-frontend" -version = "0.105.1" +version = "0.105.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c3974cc665b699b626742775dae1c1cdea5170f5028ab1f3eb61a7a9a6e2979" +checksum = "af5d4da63143ee3485c7bcedde0a818727d737d1083484a0ceedb8950c89e495" dependencies = [ "cranelift-codegen", "log", @@ -2616,15 +2719,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.105.1" +version = "0.105.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99543f92b9c361f3c54a29e945adb5b9ef1318feaa5944453cabbfcb3c495919" +checksum = "457a9832b089e26f5eea70dcf49bed8ec6edafed630ce7c83161f24d46ab8085" [[package]] name = "cranelift-native" -version = "0.105.1" +version = "0.105.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c0d84dc7d9b3f73ad565eacc4ab36525c407ef5150893b4b94d5f5f904eb48a" +checksum = "9b490d579df1ce365e1ea359e24ed86d82289fa785153327c2f6a69a59a731e4" dependencies = [ "cranelift-codegen", "libc", @@ -2633,9 +2736,9 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.105.1" +version = "0.105.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53781039219944d59c6d3ec57e6cae31a1a33db71573a945d84ba6d875d0a743" +checksum = "8cd747ed7f9a461dda9c388415392f6bb95d1a6ef3b7694d17e0817eb74b7798" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -2762,6 +2865,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core 0.6.4", "typenum", ] @@ -3231,6 +3335,12 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + [[package]] name = "enumflags2" version = "0.7.9" @@ -3346,6 +3456,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ad6fd685ce13acd6d9541a30f6db6567a7a24c9ffd4ba2955d29e3f22c8b27" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + [[package]] name = "event-listener-strategy" version = "0.4.0" @@ -3356,6 +3477,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener-strategy" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" +dependencies = [ + "event-listener 5.1.0", + "pin-project-lite", +] + [[package]] name = "extension" version = "0.1.0" @@ -3913,17 +4044,15 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.0.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1155db57329dca6d018b61e76b1488ce9a2e5e44028cac420a5898f4fcef63" +checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" dependencies = [ "fastrand 2.0.0", "futures-core", "futures-io", - "memchr", "parking", "pin-project-lite", - "waker-fn", ] [[package]] @@ -4218,6 +4347,7 @@ dependencies = [ "metal", "num_cpus", "objc", + "oo7", "open", "parking", "parking_lot 0.11.2", @@ -4709,6 +4839,16 @@ dependencies = [ "libc", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "block-padding", + "generic-array", +] + [[package]] name = "install_cli" version = "0.1.0" @@ -4814,7 +4954,7 @@ version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9" dependencies = [ - "async-channel", + "async-channel 1.9.0", "castaway", "crossbeam-utils", "curl", @@ -5915,6 +6055,7 @@ dependencies = [ "bitflags 2.4.2", "cfg-if 1.0.0", "libc", + "memoffset 0.9.0", ] [[package]] @@ -6012,6 +6153,20 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +dependencies = [ + "num-bigint 0.4.4", + "num-complex 0.4.4", + "num-integer", + "num-iter", + "num-rational 0.4.1", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.2.6" @@ -6064,6 +6219,7 @@ dependencies = [ "num-iter", "num-traits", "rand 0.8.5", + "serde", "smallvec", "zeroize", ] @@ -6142,6 +6298,18 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint 0.4.4", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.16" @@ -6258,6 +6426,35 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "oo7" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37558cac1af63a81fd2ff7f3469c02a4da06b163c5671791553b8dac10f07c82" +dependencies = [ + "aes", + "async-fs 2.1.1", + "async-io 2.3.1", + "async-lock 3.3.0", + "blocking", + "cbc", + "cipher 0.4.4", + "digest 0.10.7", + "futures-lite 2.2.0", + "futures-util", + "hkdf", + "hmac 0.12.1", + "num 0.4.1", + "num-bigint-dig 0.8.4", + "pbkdf2 0.12.2", + "rand 0.8.5", + "serde", + "sha2 0.10.7", + "zbus 4.0.1", + "zeroize", + "zvariant 4.0.2", +] + [[package]] name = "opaque-debug" version = "0.3.0" @@ -6456,9 +6653,9 @@ dependencies = [ [[package]] name = "parking" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" @@ -6515,7 +6712,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7037e5e93e0172a5a96874380bf73bc6ecef022e26fa25f2be26864d6b3ba95d" dependencies = [ "lazy_static", - "num", + "num 0.2.1", "regex", ] @@ -6569,6 +6766,16 @@ dependencies = [ "crypto-mac", ] +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest 0.10.7", + "hmac 0.12.1", +] + [[package]] name = "peeking_take_while" version = "0.1.2" @@ -6853,6 +7060,15 @@ dependencies = [ "toml_edit 0.19.15", ] +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -8036,7 +8252,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecbd2eb639fd7cab5804a0837fe373cc2172d15437e804c054a9fb885cb923b0" dependencies = [ - "cipher", + "cipher 0.3.0", ] [[package]] @@ -8109,7 +8325,7 @@ dependencies = [ "base64ct", "hmac 0.11.0", "password-hash", - "pbkdf2", + "pbkdf2 0.8.0", "salsa20", "sha2 0.9.9", ] @@ -8688,7 +8904,7 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" dependencies = [ - "async-channel", + "async-channel 1.9.0", "futures-core", "futures-io", ] @@ -8705,13 +8921,13 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13f2b548cd8447f8de0fdf1c592929f70f4fc7039a05e47404b0d096ec6987a1" dependencies = [ - "async-channel", + "async-channel 1.9.0", "async-executor", "async-fs 1.6.0", "async-io 1.13.0", "async-lock 2.8.0", "async-net 1.7.0", - "async-process", + "async-process 1.7.0", "blocking", "futures-lite 1.13.0", ] @@ -9867,6 +10083,17 @@ dependencies = [ "winnow 0.5.15", ] +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap 2.0.0", + "toml_datetime", + "winnow 0.5.15", +] + [[package]] name = "toml_edit" version = "0.22.6" @@ -10956,9 +11183,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi-common" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "082a661fe31df4dbb34409f4835ad3d8ba65036bf74aaec9b21fde779978aba7" +checksum = "880c1461417b2bf90262591bf8a5f04358fb86dac8a585a49b87024971296763" dependencies = [ "anyhow", "bitflags 2.4.2", @@ -11057,9 +11284,9 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.200.0" +version = "0.201.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e3fb0c8fbddd78aa6095b850dfeedbc7506cf5f81e633f69cf8f2333ab84b9" +checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a" dependencies = [ "leb128", ] @@ -11103,9 +11330,9 @@ dependencies = [ [[package]] name = "wasmtime" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06f80b13fdeba0ea5267813d0f06af822309f7125fc8db6094bcd485f0a4ae7" +checksum = "4c843b8bc4dd4f3a76173ba93405c71111d570af0d90ea5f6299c705d0c2add2" dependencies = [ "addr2line", "anyhow", @@ -11147,18 +11374,18 @@ dependencies = [ [[package]] name = "wasmtime-asm-macros" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d7395b475c6f858c7edfce375f00d8282a32fbf5d1ebc93eddfac5c2458a52" +checksum = "86b9d329c718b3a18412a6a017c912b539baa8fe1210d21b651f6b4dbafed743" dependencies = [ "cfg-if 1.0.0", ] [[package]] name = "wasmtime-c-api-impl" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c09ac0c18464f8ef0b554c12defc94e3fc082b62309a3da229de60d47cf75a" +checksum = "cc93587c24d8e3cb28912eb7abf95f7e350380656faccc46cff04c0821ec58c2" dependencies = [ "anyhow", "log", @@ -11170,9 +11397,9 @@ dependencies = [ [[package]] name = "wasmtime-c-api-macros" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864c4a337294fe690f02b39f2b3f45414447d9321d0ed24d3dc7696bf291e789" +checksum = "2e571a71eba52dfe81ef653a3a336888141f00fc2208a9962722e036fe2a34be" dependencies = [ "proc-macro2", "quote", @@ -11180,9 +11407,9 @@ dependencies = [ [[package]] name = "wasmtime-cache" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0a78f86b27f099bea3aaa0894464e22e84a08cadf3d8cd353378d3d15385535" +checksum = "6fb4fc2bbf9c790a57875eba65588fa97acf57a7d784dc86d057e648d9a1ed91" dependencies = [ "anyhow", "base64 0.21.4", @@ -11200,9 +11427,9 @@ dependencies = [ [[package]] name = "wasmtime-component-macro" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93e54483c542e304e17fa73d3f9263bf071e21915c8f048c7d42916da5b4bfd6" +checksum = "d8d55ddfd02898885c39638eae9631cd430c83a368f5996ed0f7bfb181d02157" dependencies = [ "anyhow", "proc-macro2", @@ -11215,15 +11442,15 @@ dependencies = [ [[package]] name = "wasmtime-component-util" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9f72619f484df95fc03162cdef9cb98778abc4103811849501bb34e79a3aac" +checksum = "1d6d69c430cddc70ec42159506962c66983ce0192ebde4eb125b7aabc49cff88" [[package]] name = "wasmtime-cranelift" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "974d9455611e26c97d31705e19545de58fa8867416592bd93b7a54a7fc37cedb" +checksum = "31ca62f519225492bd555d0ec85a2dacb0c10315db3418c8b9aeb3824bf54a24" dependencies = [ "anyhow", "cfg-if 1.0.0", @@ -11246,9 +11473,9 @@ dependencies = [ [[package]] name = "wasmtime-cranelift-shared" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40667ba458634db703aea3bd960e80bc9352c21d5e765b69f43e3b0c964eb611" +checksum = "fd5f2071f42e61490bf7cb95b9acdbe6a29dd577a398019304a960585f28b844" dependencies = [ "anyhow", "cranelift-codegen", @@ -11262,9 +11489,9 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8da991421528c2767053cb0cfa70b5d28279100dbcf70ed7f74b51abe1656ef" +checksum = "82bf1a47f384610da19f58b0fd392ca6a3b720974315c08afb0392c0f3951fed" dependencies = [ "anyhow", "bincode", @@ -11288,9 +11515,9 @@ dependencies = [ [[package]] name = "wasmtime-fiber" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fdd780272515bfcdf316e2efe20231719ec40223d67fcdd7d17068a16d39384" +checksum = "0e31aecada2831e067ebfe93faa3001cc153d506f8af40bbea58aa1d20fe4820" dependencies = [ "anyhow", "cc", @@ -11303,9 +11530,9 @@ dependencies = [ [[package]] name = "wasmtime-jit-debug" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87be9ed561dbe2aca3bde30d442c292fda53748343d0220873d1df65270c8fcf" +checksum = "833dae95bc7a4f9177bf93f9497419763535b74e37eb8c37be53937d3281e287" dependencies = [ "object", "once_cell", @@ -11315,9 +11542,9 @@ dependencies = [ [[package]] name = "wasmtime-jit-icache-coherence" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3346431a41fbb0c5af0081c2322361b00289f2902e54ee7b115e9b2ad32b156b" +checksum = "33f4121cb29dda08139b2824a734dd095d83ce843f2d613a84eb580b9cfc17ac" dependencies = [ "cfg-if 1.0.0", "libc", @@ -11326,9 +11553,9 @@ dependencies = [ [[package]] name = "wasmtime-runtime" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a489353aa297b46a66cde8da48cab8e1e967e7f4b0ae3d9889a0550bf274810b" +checksum = "4e517f2b996bb3b0e34a82a2bce194f850d9bcfc25c08328ef5fb71b071066b8" dependencies = [ "anyhow", "cc", @@ -11356,9 +11583,9 @@ dependencies = [ [[package]] name = "wasmtime-types" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12c56e31fd7fa707fbd7720b2b29ac42ccfb092fe9d85c98f1d3988f9a1d4558" +checksum = "54a327d7a0ef57bd52a507d28b4561a74126c7a8535a2fc6f2025716bc6a52e8" dependencies = [ "cranelift-entity", "serde", @@ -11369,9 +11596,9 @@ dependencies = [ [[package]] name = "wasmtime-versioned-export-macros" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b0300976c36a9427d184e3ecf7c121c2cb3f030844faf9fcb767821e9d4c382" +checksum = "8ef32eea9fc7035a55159a679d1e89b43ece5ae45d24eed4808e6a92c99a0da4" dependencies = [ "proc-macro2", "quote", @@ -11380,9 +11607,9 @@ dependencies = [ [[package]] name = "wasmtime-wasi" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f7d9cfaf9f70e83a164f5d772e376fafa2d7b7b0ca2ef88f9bcaf8b2363a38b" +checksum = "d04d2fb2257245aa05ff799ded40520ae4d8cd31b0d14972afac89061f12fe12" dependencies = [ "anyhow", "async-trait", @@ -11413,9 +11640,9 @@ dependencies = [ [[package]] name = "wasmtime-winch" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f773a904d2bd5ecd8ad095f4c965ad56a836929d8c26368621f75328d500649" +checksum = "db3378c0e808a744b5d4df2a9a9d2746a53b151811926731f04fc401707f7d54" dependencies = [ "anyhow", "cranelift-codegen", @@ -11430,9 +11657,9 @@ dependencies = [ [[package]] name = "wasmtime-wit-bindgen" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e9754e0a526238ea66da9ba21965a54846a2b22d9de89a298fb8998389507" +checksum = "ca677c36869e45602617b25a9968ec0d895ad9a0aee3756d9dee1ddd89456f91" dependencies = [ "anyhow", "heck 0.4.1", @@ -11442,9 +11669,9 @@ dependencies = [ [[package]] name = "wasmtime-wmemcheck" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdf5b8da6ebf7549dad0cd32ca4a3a0461449ef4feec9d0d8450d8da9f51f9b" +checksum = "7f4cbfb052d66f03603a9b77f18171ea245c7805714caad370a549a6344bf86b" [[package]] name = "wast" @@ -11457,24 +11684,24 @@ dependencies = [ [[package]] name = "wast" -version = "200.0.0" +version = "201.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1810d14e6b03ebb8fb05eef4009ad5749c989b65197d83bce7de7172ed91366" +checksum = "1ef6e1ef34d7da3e2b374fd2b1a9c0227aff6cad596e1b24df9b58d0f6222faa" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.200.0", + "wasm-encoder 0.201.0", ] [[package]] name = "wat" -version = "1.200.0" +version = "1.201.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "776cbd10e217f83869beaa3f40e312bb9e91d5eee29bbf6f560db1261b6a4c3d" +checksum = "453d5b37a45b98dee4f4cb68015fc73634d7883bbef1c65e6e9c78d454cf3f32" dependencies = [ - "wast 200.0.0", + "wast 201.0.0", ] [[package]] @@ -11629,9 +11856,9 @@ checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" [[package]] name = "wiggle" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "454570f4fecadb881f0ba157e98b575a2850607a9eac79d8868f3ab70633f632" +checksum = "b69812e493f8a43d8551abfaaf9539e1aff0cf56a58cdd276845fc4af035d0cd" dependencies = [ "anyhow", "async-trait", @@ -11644,9 +11871,9 @@ dependencies = [ [[package]] name = "wiggle-generate" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443ac1ebb753ca22bca98d01742762de1243ff722839907c35ea683a8264c74e" +checksum = "0446357a5a7af0172848b6eca7b2aa1ab7d90065cd2ab02b633a322e1a52f636" dependencies = [ "anyhow", "heck 0.4.1", @@ -11659,9 +11886,9 @@ dependencies = [ [[package]] name = "wiggle-macro" -version = "18.0.1" +version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e9e2f1f06ae07bac15273774782c04ab14e9adfbf414762fc84dbbfcf7fb1ac" +checksum = "9498ef53a12cf25dc6de9baef6ccd8b58d159202c412a19f4d72b218393086c5" dependencies = [ "proc-macro2", "quote", @@ -11714,9 +11941,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winch-codegen" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f7eaac56988f986181099c15860946fea93ed826322a1f92c4ff04541b7744" +checksum = "8197ed4a2ebf612f0624ddda10de71f8cd2d3a4ecf8ffac0586a264599708d63" dependencies = [ "anyhow", "cranelift-codegen", @@ -11753,7 +11980,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efc5cf48f83140dcaab716eeaea345f9e93d0018fb81162753a3f76c3397b538" dependencies = [ "windows-core", - "windows-targets 0.52.3", + "windows-targets 0.52.4", ] [[package]] @@ -11763,7 +11990,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dcc5b895a6377f1ab9fa55acedab1fd5ac0db66ad1e6c7f47e28a22e446a5dd" dependencies = [ "windows-result", - "windows-targets 0.52.3", + "windows-targets 0.52.4", ] [[package]] @@ -11772,7 +11999,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd19df78e5168dfb0aedc343d1d1b8d422ab2db6756d2dc3fef75035402a3f64" dependencies = [ - "windows-targets 0.52.3", + "windows-targets 0.52.4", ] [[package]] @@ -11799,7 +12026,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.3", + "windows-targets 0.52.4", ] [[package]] @@ -11834,17 +12061,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ - "windows_aarch64_gnullvm 0.52.3", - "windows_aarch64_msvc 0.52.3", - "windows_i686_gnu 0.52.3", - "windows_i686_msvc 0.52.3", - "windows_x86_64_gnu 0.52.3", - "windows_x86_64_gnullvm 0.52.3", - "windows_x86_64_msvc 0.52.3", + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", ] [[package]] @@ -11861,9 +12088,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" @@ -11879,9 +12106,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" @@ -11897,9 +12124,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" @@ -11915,9 +12142,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" @@ -11933,9 +12160,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" @@ -11951,9 +12178,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" @@ -11969,9 +12196,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winnow" @@ -12291,16 +12518,16 @@ dependencies = [ [[package]] name = "zbus" -version = "3.15.0" +version = "3.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c45d06ae3b0f9ba1fb2671268b975557d8f5a84bb5ec6e43964f87e763d8bca8" +checksum = "5acecd3f8422f198b1a2f954bcc812fe89f3fa4281646f3da1da7925db80085d" dependencies = [ "async-broadcast 0.5.1", "async-executor", "async-fs 1.6.0", "async-io 1.13.0", "async-lock 2.8.0", - "async-process", + "async-process 1.7.0", "async-recursion 1.0.5", "async-task", "async-trait", @@ -12325,16 +12552,69 @@ dependencies = [ "uds_windows", "winapi 0.3.9", "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", + "zbus_macros 3.15.1", + "zbus_names 2.6.1", + "zvariant 3.15.1", +] + +[[package]] +name = "zbus" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b8e3d6ae3342792a6cc2340e4394334c7402f3d793b390d2c5494a4032b3030" +dependencies = [ + "async-broadcast 0.7.0", + "async-executor", + "async-fs 2.1.1", + "async-io 2.3.1", + "async-lock 3.3.0", + "async-process 2.1.0", + "async-recursion 1.0.5", + "async-task", + "async-trait", + "blocking", + "derivative", + "enumflags2", + "event-listener 5.1.0", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix 0.27.1", + "ordered-stream", + "rand 0.8.5", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros 4.0.1", + "zbus_names 3.0.0", + "zvariant 4.0.2", ] [[package]] name = "zbus_macros" -version = "3.15.0" +version = "3.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4a1ba45ed0ad344b85a2bb5a1fe9830aed23d67812ea39a586e7d0136439c7d" +checksum = "2207eb71efebda17221a579ca78b45c4c5f116f074eb745c3a172e688ccf89f5" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zbus_macros" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a3e850ff1e7217a3b7a07eba90d37fe9bb9e89a310f718afcde5885ca9b6d7" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", @@ -12346,13 +12626,24 @@ dependencies = [ [[package]] name = "zbus_names" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb80bb776dbda6e23d705cf0123c3b95df99c4ebeaec6c2599d4a5419902b4a9" +checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d" dependencies = [ "serde", "static_assertions", - "zvariant", + "zvariant 3.15.1", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant 4.0.2", ] [[package]] @@ -12537,9 +12828,9 @@ dependencies = [ [[package]] name = "zvariant" -version = "3.15.0" +version = "3.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c" +checksum = "c5b4fcf3660d30fc33ae5cd97e2017b23a96e85afd7a1dd014534cd0bf34ba67" dependencies = [ "byteorder", "enumflags2", @@ -12547,14 +12838,27 @@ dependencies = [ "serde", "static_assertions", "url", - "zvariant_derive", + "zvariant_derive 3.15.1", +] + +[[package]] +name = "zvariant" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1b3ca6db667bfada0f1ebfc94b2b1759ba25472ee5373d4551bb892616389a" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "zvariant_derive 4.0.2", ] [[package]] name = "zvariant_derive" -version = "3.15.0" +version = "3.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd" +checksum = "0277758a8a0afc0e573e80ed5bfd9d9c2b48bd3108ffe09384f9f738c83f4a55" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", @@ -12564,10 +12868,23 @@ dependencies = [ ] [[package]] -name = "zvariant_utils" -version = "1.0.1" +name = "zvariant_derive" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" +checksum = "b7a4b236063316163b69039f77ce3117accb41a09567fd24c168e43491e521bc" +dependencies = [ + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" dependencies = [ "proc-macro2", "quote", diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 273443791d..c917f92cbd 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -113,6 +113,7 @@ xkbcommon = { version = "0.7", features = ["wayland", "x11"] } as-raw-xcb-connection = "1" calloop = "0.12.4" calloop-wayland-source = "0.2.0" +oo7 = "0.3.0" [target.'cfg(windows)'.dependencies] windows.workspace = true diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index cad0d1074b..8db32e7264 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -9,6 +9,7 @@ use std::{ time::Duration, }; +use anyhow::anyhow; use ashpd::desktop::file_chooser::{OpenFileRequest, SaveFileRequest}; use async_task::Runnable; use calloop::{EventLoop, LoopHandle, LoopSignal}; @@ -107,6 +108,8 @@ impl LinuxPlatform { } } +const KEYRING_LABEL: &str = "zed-github-account"; + impl Platform for LinuxPlatform { fn background_executor(&self) -> BackgroundExecutor { self.inner.background_executor.clone() @@ -361,23 +364,70 @@ impl Platform for LinuxPlatform { //todo!(linux) fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task> { - Task::Ready(Some(Err(anyhow::Error::msg( - "Platform::with_credentials is not implemented yet", - )))) + let url = url.to_string(); + let username = username.to_string(); + let password = password.to_vec(); + self.background_executor().spawn(async move { + let keyring = oo7::Keyring::new().await?; + keyring.unlock().await?; + keyring + .create_item( + KEYRING_LABEL, + &vec![("url", &url), ("username", &username)], + password, + true, + ) + .await?; + Ok(()) + }) } //todo!(linux) fn read_credentials(&self, url: &str) -> Task)>>> { - Task::Ready(Some(Err(anyhow::Error::msg( - "Platform::read_credentials is not implemented yet", - )))) + let url = url.to_string(); + self.background_executor().spawn(async move { + let keyring = oo7::Keyring::new().await?; + keyring.unlock().await?; + + let items = keyring.search_items(&vec![("url", &url)]).await?; + + for item in items.into_iter() { + if item.label().await.is_ok_and(|label| label == KEYRING_LABEL) { + let attributes = item.attributes().await?; + let username = attributes + .get("username") + .ok_or_else(|| anyhow!("Cannot find username in stored credentials"))?; + let secret = item.secret().await?; + + // we lose the zeroizing capabilities at this boundary, + // a current limitation GPUI's credentials api + return Ok(Some((username.to_string(), secret.to_vec()))); + } else { + continue; + } + } + Ok(None) + }) } //todo!(linux) fn delete_credentials(&self, url: &str) -> Task> { - Task::Ready(Some(Err(anyhow::Error::msg( - "Platform::delete_credentials is not implemented yet", - )))) + let url = url.to_string(); + self.background_executor().spawn(async move { + let keyring = oo7::Keyring::new().await?; + keyring.unlock().await?; + + let items = keyring.search_items(&vec![("url", &url)]).await?; + + for item in items.into_iter() { + if item.label().await.is_ok_and(|label| label == KEYRING_LABEL) { + item.delete().await?; + return Ok(()); + } + } + + Ok(()) + }) } fn window_appearance(&self) -> crate::WindowAppearance { From 20acc123afc1257b396cf76701e55c4707954901 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Sun, 3 Mar 2024 21:47:34 -0800 Subject: [PATCH 059/121] Implement 'format without save' (#8806) This solves a major usability problem in Zed, that there's no way to temporarily disable auto formatting without toggling the whole feature off. fixes https://github.com/zed-industries/zed/issues/5230 Release Notes: - Added a new `workspace::SaveWithoutFormatting`, bound to `cmd-k s`, to save a file without invoking the auto formatter. --- assets/keymaps/default-linux.json | 1 + assets/keymaps/default-macos.json | 3 ++- crates/diagnostics/src/diagnostics.rs | 9 +++++++-- crates/editor/src/editor_tests.rs | 12 ++++++------ crates/editor/src/items.rs | 17 ++++++++++++----- crates/search/src/project_search.rs | 3 ++- crates/workspace/src/item.rs | 24 ++++++++++++++++++++---- crates/workspace/src/pane.rs | 16 ++++++++++++---- crates/workspace/src/workspace.rs | 6 ++++++ crates/zed/src/zed.rs | 2 +- 10 files changed, 69 insertions(+), 24 deletions(-) diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index 459f7e04db..29e3d19d78 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -365,6 +365,7 @@ "ctrl-alt-b": "branches::OpenRecent", "ctrl-~": "workspace::NewTerminal", "ctrl-s": "workspace::Save", + "ctrl-k s": "workspace::SaveWithoutFormat", "ctrl-shift-s": "workspace::SaveAs", "ctrl-n": "workspace::NewFile", "ctrl-shift-n": "workspace::NewWindow", diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json index dcf0817815..02d05c0409 100644 --- a/assets/keymaps/default-macos.json +++ b/assets/keymaps/default-macos.json @@ -408,6 +408,7 @@ "alt-cmd-b": "branches::OpenRecent", "ctrl-~": "workspace::NewTerminal", "cmd-s": "workspace::Save", + "cmd-k s": "workspace::SaveWithoutFormat", "cmd-shift-s": "workspace::SaveAs", "cmd-n": "workspace::NewFile", "cmd-shift-n": "workspace::NewWindow", @@ -426,8 +427,8 @@ "cmd-j": "workspace::ToggleBottomDock", "alt-cmd-y": "workspace::CloseAllDocks", "cmd-shift-f": "pane::DeploySearch", - "cmd-k cmd-t": "theme_selector::Toggle", "cmd-k cmd-s": "zed::OpenKeymap", + "cmd-k cmd-t": "theme_selector::Toggle", "cmd-t": "project_symbols::Toggle", "cmd-p": "file_finder::Toggle", "cmd-shift-p": "command_palette::Toggle", diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 94e78390d6..6d6a946fa0 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -735,8 +735,13 @@ impl Item for ProjectDiagnosticsEditor { true } - fn save(&mut self, project: Model, cx: &mut ViewContext) -> Task> { - self.editor.save(project, cx) + fn save( + &mut self, + format: bool, + project: Model, + cx: &mut ViewContext, + ) -> Task> { + self.editor.save(format, project, cx) } fn save_as( diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index d7679c72cd..36daaec5d9 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -5265,7 +5265,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) { assert!(cx.read(|cx| editor.is_dirty(cx))); let save = editor - .update(cx, |editor, cx| editor.save(project.clone(), cx)) + .update(cx, |editor, cx| editor.save(true, project.clone(), cx)) .unwrap(); fake_server .handle_request::(move |params, _| async move { @@ -5303,7 +5303,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) { unreachable!() }); let save = editor - .update(cx, |editor, cx| editor.save(project.clone(), cx)) + .update(cx, |editor, cx| editor.save(true, project.clone(), cx)) .unwrap(); cx.executor().advance_clock(super::FORMAT_TIMEOUT); cx.executor().start_waiting(); @@ -5326,7 +5326,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) { }); let save = editor - .update(cx, |editor, cx| editor.save(project.clone(), cx)) + .update(cx, |editor, cx| editor.save(true, project.clone(), cx)) .unwrap(); fake_server .handle_request::(move |params, _| async move { @@ -5379,7 +5379,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) { assert!(cx.read(|cx| editor.is_dirty(cx))); let save = editor - .update(cx, |editor, cx| editor.save(project.clone(), cx)) + .update(cx, |editor, cx| editor.save(true, project.clone(), cx)) .unwrap(); fake_server .handle_request::(move |params, _| async move { @@ -5418,7 +5418,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) { }, ); let save = editor - .update(cx, |editor, cx| editor.save(project.clone(), cx)) + .update(cx, |editor, cx| editor.save(true, project.clone(), cx)) .unwrap(); cx.executor().advance_clock(super::FORMAT_TIMEOUT); cx.executor().start_waiting(); @@ -5441,7 +5441,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) { }); let save = editor - .update(cx, |editor, cx| editor.save(project.clone(), cx)) + .update(cx, |editor, cx| editor.save(true, project.clone(), cx)) .unwrap(); fake_server .handle_request::(move |params, _| async move { diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 96fdcfdefc..d1324adad0 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -702,14 +702,21 @@ impl Item for Editor { } } - fn save(&mut self, project: Model, cx: &mut ViewContext) -> Task> { + fn save( + &mut self, + format: bool, + project: Model, + cx: &mut ViewContext, + ) -> Task> { self.report_editor_event("save", None, cx); let buffers = self.buffer().clone().read(cx).all_buffers(); cx.spawn(|this, mut cx| async move { - this.update(&mut cx, |this, cx| { - this.perform_format(project.clone(), FormatTrigger::Save, cx) - })? - .await?; + if format { + this.update(&mut cx, |this, cx| { + this.perform_format(project.clone(), FormatTrigger::Save, cx) + })? + .await?; + } if buffers.len() == 1 { project diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index b26e4193d2..c5d5f667dc 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -539,11 +539,12 @@ impl Item for ProjectSearchView { fn save( &mut self, + format: bool, project: Model, cx: &mut ViewContext, ) -> Task> { self.results_editor - .update(cx, |editor, cx| editor.save(project, cx)) + .update(cx, |editor, cx| editor.save(format, project, cx)) } fn save_as( diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index 580703d6b0..25d9f5ed89 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -146,7 +146,12 @@ pub trait Item: FocusableView + EventEmitter { fn can_save(&self, _cx: &AppContext) -> bool { false } - fn save(&mut self, _project: Model, _cx: &mut ViewContext) -> Task> { + fn save( + &mut self, + _format: bool, + _project: Model, + _cx: &mut ViewContext, + ) -> Task> { unimplemented!("save() must be implemented if can_save() returns true") } fn save_as( @@ -258,7 +263,12 @@ pub trait ItemHandle: 'static + Send { fn is_dirty(&self, cx: &AppContext) -> bool; fn has_conflict(&self, cx: &AppContext) -> bool; fn can_save(&self, cx: &AppContext) -> bool; - fn save(&self, project: Model, cx: &mut WindowContext) -> Task>; + fn save( + &self, + format: bool, + project: Model, + cx: &mut WindowContext, + ) -> Task>; fn save_as( &self, project: Model, @@ -566,8 +576,13 @@ impl ItemHandle for View { self.read(cx).can_save(cx) } - fn save(&self, project: Model, cx: &mut WindowContext) -> Task> { - self.update(cx, |item, cx| item.save(project, cx)) + fn save( + &self, + format: bool, + project: Model, + cx: &mut WindowContext, + ) -> Task> { + self.update(cx, |item, cx| item.save(format, project, cx)) } fn save_as( @@ -1018,6 +1033,7 @@ pub mod test { fn save( &mut self, + _: bool, _: Model, _: &mut ViewContext, ) -> Task> { diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 58a2ee6241..e99bb3f193 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -44,6 +44,8 @@ pub enum SaveIntent { /// write all files (even if unchanged) /// prompt before overwriting on-disk changes Save, + /// same as Save, but without auto formatting + SaveWithoutFormat, /// write any files that have local changes /// prompt before overwriting on-disk changes SaveAll, @@ -1122,7 +1124,7 @@ impl Pane { })?; // when saving a single buffer, we ignore whether or not it's dirty. - if save_intent == SaveIntent::Save { + if save_intent == SaveIntent::Save || save_intent == SaveIntent::SaveWithoutFormat { is_dirty = true; } @@ -1136,6 +1138,8 @@ impl Pane { has_conflict = false; } + let should_format = save_intent != SaveIntent::SaveWithoutFormat; + if has_conflict && can_save { let answer = pane.update(cx, |pane, cx| { pane.activate_item(item_ix, true, true, cx); @@ -1147,7 +1151,10 @@ impl Pane { ) })?; match answer.await { - Ok(0) => pane.update(cx, |_, cx| item.save(project, cx))?.await?, + Ok(0) => { + pane.update(cx, |_, cx| item.save(should_format, project, cx))? + .await? + } Ok(1) => pane.update(cx, |_, cx| item.reload(project, cx))?.await?, _ => return Ok(false), } @@ -1179,7 +1186,8 @@ impl Pane { } if can_save { - pane.update(cx, |_, cx| item.save(project, cx))?.await?; + pane.update(cx, |_, cx| item.save(should_format, project, cx))? + .await?; } else if can_save_as { let start_abs_path = project .update(cx, |project, cx| { @@ -1211,7 +1219,7 @@ impl Pane { cx: &mut WindowContext, ) -> Task> { if Self::can_autosave_item(item, cx) { - item.save(project, cx) + item.save(true, project, cx) } else { Task::ready(Ok(())) } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 5acfd988b9..390b36a7f0 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -106,6 +106,7 @@ actions!( AddFolderToProject, Unfollow, SaveAs, + SaveWithoutFormat, ReloadActiveItem, ActivatePreviousPane, ActivateNextPane, @@ -3532,6 +3533,11 @@ impl Workspace { .save_active_item(action.save_intent.unwrap_or(SaveIntent::Save), cx) .detach_and_log_err(cx); })) + .on_action(cx.listener(|workspace, _: &SaveWithoutFormat, cx| { + workspace + .save_active_item(SaveIntent::SaveWithoutFormat, cx) + .detach_and_log_err(cx); + })) .on_action(cx.listener(|workspace, _: &SaveAs, cx| { workspace .save_active_item(SaveIntent::SaveAs, cx) diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 3954015907..7c47a6b6b5 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1979,7 +1979,7 @@ mod tests { editor.newline(&Default::default(), cx); editor.move_down(&Default::default(), cx); editor.move_down(&Default::default(), cx); - editor.save(project.clone(), cx) + editor.save(true, project.clone(), cx) }) }) .unwrap() From 38f106cde34199243ab40cc95338337287b58475 Mon Sep 17 00:00:00 2001 From: Stanislav Alekseev <43210583+WeetHet@users.noreply.github.com> Date: Mon, 4 Mar 2024 10:30:49 +0200 Subject: [PATCH 060/121] Add a fish-specific fix for #8633 (#8659) Release Notes: - Fixed detection of `direnv` not working in `fish` when an LSP adapter (`gopls`, for example) tries to detect user-installed binaries. (#8633) --------- Co-authored-by: Thorsten Ball --- crates/project/src/project.rs | 47 +++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 332967875d..aee2f5e6c2 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -9456,23 +9456,38 @@ async fn load_shell_environment(dir: &Path) -> Result> { let shell = env::var("SHELL").context( "SHELL environment variable is not assigned so we can't source login environment variables", )?; + + // What we're doing here is to spawn a shell and then `cd` into + // the project directory to get the env in there as if the user + // `cd`'d into it. We do that because tools like direnv, asdf, ... + // hook into `cd` and only set up the env after that. + // + // In certain shells we need to execute additional_command in order to + // trigger the behavior of direnv, etc. + // + // + // The `exit 0` is the result of hours of debugging, trying to find out + // why running this command here, without `exit 0`, would mess + // up signal process for our process so that `ctrl-c` doesn't work + // anymore. + // + // We still don't know why `$SHELL -l -i -c '/usr/bin/env -0'` would + // do that, but it does, and `exit 0` helps. + let additional_command = PathBuf::from(&shell) + .file_name() + .and_then(|f| f.to_str()) + .and_then(|shell| match shell { + "fish" => Some("emit fish_prompt;"), + _ => None, + }); + + let command = format!( + "cd {dir:?};{} echo {marker}; /usr/bin/env -0; exit 0;", + additional_command.unwrap_or("") + ); + let output = smol::process::Command::new(&shell) - .args([ - "-i", - "-c", - // What we're doing here is to spawn a shell and then `cd` into - // the project directory to get the env in there as if the user - // `cd`'d into it. We do that because tools like direnv, asdf, ... - // hook into `cd` and only set up the env after that. - // - // The `exit 0` is the result of hours of debugging, trying to find out - // why running this command here, without `exit 0`, would mess - // up signal process for our process so that `ctrl-c` doesn't work - // anymore. - // We still don't know why `$SHELL -l -i -c '/usr/bin/env -0'` would - // do that, but it does, and `exit 0` helps. - &format!("cd {dir:?}; echo {marker}; /usr/bin/env -0; exit 0;"), - ]) + .args(["-i", "-c", &command]) .output() .await .context("failed to spawn login shell to source login environment variables")?; From 3a184bbaddabd27086203320f195e78879c25375 Mon Sep 17 00:00:00 2001 From: Ole Herman Schumacher Elgesem <4048546+olehermanse@users.noreply.github.com> Date: Mon, 4 Mar 2024 09:54:29 +0100 Subject: [PATCH 061/121] Docs: Made "Configuring Zed" a link (#8790) Seems like this was intended to be a link (?). Easier to click it than find the section in the navigation. Release Notes: - N/A --- docs/src/getting_started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/getting_started.md b/docs/src/getting_started.md index f1fbdd163d..cccc25d06b 100644 --- a/docs/src/getting_started.md +++ b/docs/src/getting_started.md @@ -8,7 +8,7 @@ You can obtain the release build via the [download page](https://zed.dev/downloa ## Configure Zed -Use `⌘` + `,` to open your custom settings to set things like fonts, formatting settings, per-language settings and more. You can access the default configuration using the `Zed > Settings > Open Default Settings` menu item. See Configuring Zed for all available settings. +Use `⌘` + `,` to open your custom settings to set things like fonts, formatting settings, per-language settings and more. You can access the default configuration using the `Zed > Settings > Open Default Settings` menu item. See [Configuring Zed](https://zed.dev/docs/configuring-zed) for all available settings. ## Set up your key bindings From 538298378a352b4c911f9c61b2435e4fb6816130 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Mon, 4 Mar 2024 17:42:30 +0800 Subject: [PATCH 062/121] Return "open in new window" as default in recent projects (#8798) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/zed-industries/zed/assets/5518/8bbd13a7-9144-48b0-9bc8-6651725476f8 Closes https://github.com/zed-industries/zed/issues/8651 Reworks `recent_projects::OpenRecent` action with collab projects in mind: * keep the "open in new window" behavior for corresponding menu and command entries * use new, "reuse current window" behavior in the recent projects picker up in the toolbar This way, old Zed behavior is not customizable, kept as original in all main use cases — so that projects shared via remote entities: a channel and a call, are never accidentally closed, breaking the sharing. Release Notes: - Return "open in new window" as default in recent projects --- crates/recent_projects/src/recent_projects.rs | 14 +++++++------- crates/zed/src/app_menus.rs | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index 14a8ae7e9c..f2d0ad0f84 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -16,10 +16,14 @@ use workspace::{ModalView, Workspace, WorkspaceId, WorkspaceLocation, WORKSPACE_ #[derive(PartialEq, Clone, Deserialize, Default)] pub struct OpenRecent { - #[serde(default)] + #[serde(default = "default_create_new_window")] pub create_new_window: bool, } +fn default_create_new_window() -> bool { + true +} + gpui::impl_actions!(projects, [OpenRecent]); pub fn init(cx: &mut AppContext) { @@ -269,7 +273,7 @@ impl PickerDelegate for RecentProjectsDelegate { workspace .update(&mut cx, |workspace, cx| { workspace.open_workspace_for_paths( - replace_current_window, + true, candidate_paths, cx, ) @@ -280,11 +284,7 @@ impl PickerDelegate for RecentProjectsDelegate { } }) } else { - workspace.open_workspace_for_paths( - replace_current_window, - candidate_paths, - cx, - ) + workspace.open_workspace_for_paths(false, candidate_paths, cx) } } else { Task::ready(Ok(())) diff --git a/crates/zed/src/app_menus.rs b/crates/zed/src/app_menus.rs index 43b1d45c15..12a88ed216 100644 --- a/crates/zed/src/app_menus.rs +++ b/crates/zed/src/app_menus.rs @@ -40,7 +40,7 @@ pub fn app_menus() -> Vec> { MenuItem::action( "Open Recent...", recent_projects::OpenRecent { - create_new_window: false, + create_new_window: true, }, ), MenuItem::separator(), From c91969d828d79fb772116e8796af21b3dba1c6a4 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Mon, 4 Mar 2024 08:57:47 -0500 Subject: [PATCH 063/121] linux/wayland: prevent possible panic (#8824) Prevent a panic from arising from this case: https://github.com/zed-industries/zed/pull/8632#discussion_r1510015928 It's not really safe to dispatch any action before dropping the state borrow, because it may need to be modified. --- crates/gpui/src/platform/linux/wayland/client.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index 0bbd716e06..8ac18b78c3 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -558,11 +558,12 @@ impl Dispatch for WaylandClientState { return TimeoutAction::Drop; } - state_ - .keyboard_focused_window - .as_ref() - .unwrap() - .handle_input(input.clone()); + let focused_window = + state_.keyboard_focused_window.as_ref().unwrap().clone(); + + drop(state_); + + focused_window.handle_input(input.clone()); TimeoutAction::ToDuration(Duration::from_secs(1) / rate) }) From 6121c286b78001ea4428653386e2c6e505563296 Mon Sep 17 00:00:00 2001 From: Thorsten Ball Date: Mon, 4 Mar 2024 15:37:50 +0100 Subject: [PATCH 064/121] Fix argument order when printing prettier debug info (#8826) Release Notes: - N/A --- crates/prettier/src/prettier.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/prettier/src/prettier.rs b/crates/prettier/src/prettier.rs index 2dff45de02..c9e94cdb24 100644 --- a/crates/prettier/src/prettier.rs +++ b/crates/prettier/src/prettier.rs @@ -335,9 +335,9 @@ impl Prettier { .collect(); log::debug!( "Formatting file {:?} with prettier, plugins :{:?}, options: {:?}", + buffer.file().map(|f| f.full_path(cx)), plugins, prettier_options, - buffer.file().map(|f| f.full_path(cx)) ); anyhow::Ok(FormatParams { From 98a1e87fbea5ed19aa5f31aab6cd7c47655edeb9 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 4 Mar 2024 15:57:30 +0100 Subject: [PATCH 065/121] task: Spawn static tasks in separate shell (#8827) That way one can use environment variables in task definitions. Fixes: #8660 /cc @SomeoneToIgnore it looks like we don't ever set `separate_shell` to false anymore, it might be worth streamlining? Release Notes: - Fixed static tasks not being run under a separate shell. - Removed `separate_shell` setting from task definitions. It is now a default for tasks defined in tasks.json file. --- crates/task/src/lib.rs | 2 -- crates/task/src/oneshot_source.rs | 1 - crates/task/src/static_source.rs | 1 - crates/terminal_view/src/terminal_panel.rs | 32 ++++++++++++---------- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/crates/task/src/lib.rs b/crates/task/src/lib.rs index 84d7bd4934..66e4249c7e 100644 --- a/crates/task/src/lib.rs +++ b/crates/task/src/lib.rs @@ -34,8 +34,6 @@ pub struct SpawnInTerminal { pub use_new_terminal: bool, /// Whether to allow multiple instances of the same task to be run, or rather wait for the existing ones to finish. pub allow_concurrent_runs: bool, - /// Whether the command should be spawned in a separate shell instance. - pub separate_shell: bool, } /// Represents a short lived recipe of a task, whose main purpose diff --git a/crates/task/src/oneshot_source.rs b/crates/task/src/oneshot_source.rs index 829c2e2f3e..bda14f894e 100644 --- a/crates/task/src/oneshot_source.rs +++ b/crates/task/src/oneshot_source.rs @@ -47,7 +47,6 @@ impl Task for OneshotTask { env: Default::default(), use_new_terminal: Default::default(), allow_concurrent_runs: Default::default(), - separate_shell: true, }) } } diff --git a/crates/task/src/static_source.rs b/crates/task/src/static_source.rs index 7a97560541..9b022fdb0f 100644 --- a/crates/task/src/static_source.rs +++ b/crates/task/src/static_source.rs @@ -34,7 +34,6 @@ impl Task for StaticTask { command: self.definition.command.clone(), args: self.definition.args.clone(), env: self.definition.env.clone(), - separate_shell: false, }) } diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index 77eb0c2ef4..d4174d61c8 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -303,23 +303,25 @@ impl TerminalPanel { args: spawn_in_terminal.args.clone(), env: spawn_in_terminal.env.clone(), }; - if spawn_in_terminal.separate_shell { - let Some((shell, mut user_args)) = (match TerminalSettings::get_global(cx).shell.clone() - { - Shell::System => std::env::var("SHELL").ok().map(|shell| (shell, vec![])), - Shell::Program(shell) => Some((shell, vec![])), - Shell::WithArguments { program, args } => Some((program, args)), - }) else { - return; - }; + // Set up shell args unconditionally, as tasks are always spawned inside of a shell. + let Some((shell, mut user_args)) = (match TerminalSettings::get_global(cx).shell.clone() { + Shell::System => std::env::var("SHELL").ok().map(|shell| (shell, vec![])), + Shell::Program(shell) => Some((shell, vec![])), + Shell::WithArguments { program, args } => Some((program, args)), + }) else { + return; + }; - let command = std::mem::take(&mut spawn_task.command); - let args = std::mem::take(&mut spawn_task.args); - spawn_task.command = shell; - user_args.extend(["-i".to_owned(), "-c".to_owned(), command]); - user_args.extend(args); - spawn_task.args = user_args; + let mut command = std::mem::take(&mut spawn_task.command); + let args = std::mem::take(&mut spawn_task.args); + for arg in args { + command.push(' '); + command.push_str(&arg); } + spawn_task.command = shell; + user_args.extend(["-i".to_owned(), "-c".to_owned(), command]); + spawn_task.args = user_args; + let working_directory = spawn_in_terminal.cwd.clone(); let allow_concurrent_runs = spawn_in_terminal.allow_concurrent_runs; let use_new_terminal = spawn_in_terminal.use_new_terminal; From 94593dca4bd906ad38aa5a15e8992e2ee188bd29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorben=20Kr=C3=B6ger?= Date: Mon, 4 Mar 2024 15:58:58 +0100 Subject: [PATCH 066/121] clangd: download the correct binary on Linux (#8820) Release Notes: - Fixed clangd LSP download not working on Linux --- crates/languages/src/c.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/languages/src/c.rs b/crates/languages/src/c.rs index bad4e0a076..28ceedf7ad 100644 --- a/crates/languages/src/c.rs +++ b/crates/languages/src/c.rs @@ -1,10 +1,10 @@ -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; use async_trait::async_trait; use futures::StreamExt; pub use language::*; use lsp::LanguageServerBinary; use smol::fs::{self, File}; -use std::{any::Any, path::PathBuf, sync::Arc}; +use std::{any::Any, env::consts, path::PathBuf, sync::Arc}; use util::{ async_maybe, fs::remove_matching, @@ -26,7 +26,12 @@ impl super::LspAdapter for CLspAdapter { ) -> Result> { let release = latest_github_release("clangd/clangd", true, false, delegate.http_client()).await?; - let asset_name = format!("clangd-mac-{}.zip", release.tag_name); + let os_suffix = match consts::OS { + "macos" => "mac", + "linux" => "linux", + other => bail!("Running on unsupported os: {other}"), + }; + let asset_name = format!("clangd-{}-{}.zip", os_suffix, release.tag_name); let asset = release .assets .iter() From 16be3912117233c83b3f4b8c0240984fff8de2d1 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 4 Mar 2024 10:35:07 -0500 Subject: [PATCH 067/121] Enable `clippy::needless_update` (#8830) This PR enables the [`clippy::needless_update`](https://rust-lang.github.io/rust-clippy/master/index.html#/needless_update) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/collab/src/db/queries/projects.rs | 2 -- crates/collab/src/rpc.rs | 1 - crates/collab/src/tests/integration_tests.rs | 2 -- tooling/xtask/src/main.rs | 1 - 4 files changed, 6 deletions(-) diff --git a/crates/collab/src/db/queries/projects.rs b/crates/collab/src/db/queries/projects.rs index 7adf6efd72..ed489169c9 100644 --- a/crates/collab/src/db/queries/projects.rs +++ b/crates/collab/src/db/queries/projects.rs @@ -382,7 +382,6 @@ impl Database { language_server_id: ActiveValue::set(summary.language_server_id as i64), error_count: ActiveValue::set(summary.error_count as i32), warning_count: ActiveValue::set(summary.warning_count as i32), - ..Default::default() }) .on_conflict( OnConflict::columns([ @@ -434,7 +433,6 @@ impl Database { project_id: ActiveValue::set(project_id), id: ActiveValue::set(server.id as i64), name: ActiveValue::set(server.name.clone()), - ..Default::default() }) .on_conflict( OnConflict::columns([ diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 60388928c6..d6a0ba8e89 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -3370,7 +3370,6 @@ fn build_update_user_channels(channels: &ChannelsForUser) -> proto::UpdateUserCh .collect(), observed_channel_buffer_version: channels.observed_buffer_versions.clone(), observed_channel_message_id: channels.observed_channel_messages.clone(), - ..Default::default() } } diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 7dd6c7c48b..08e811adcf 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -3885,7 +3885,6 @@ async fn test_collaborating_with_diagnostics( DiagnosticSummary { error_count: 1, warning_count: 0, - ..Default::default() }, )] ) @@ -3922,7 +3921,6 @@ async fn test_collaborating_with_diagnostics( DiagnosticSummary { error_count: 1, warning_count: 0, - ..Default::default() }, )] ); diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index c4247e9be6..ca268668d8 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -93,7 +93,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::iter_overeager_cloned", "clippy::let_underscore_future", "clippy::map_entry", - "clippy::needless_update", "clippy::never_loop", "clippy::non_canonical_clone_impl", "clippy::non_canonical_partial_ord_impl", From 33ef5b773100e2e3e108e3d9917e7f82147d6140 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 4 Mar 2024 10:54:33 -0500 Subject: [PATCH 068/121] Enable `clippy::iter_kv_map` (#8832) This PR enables the [`clippy::iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#/iter_kv_map) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/extension/src/extension_store.rs | 4 ++-- tooling/xtask/src/main.rs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/extension/src/extension_store.rs b/crates/extension/src/extension_store.rs index 1386ae016c..55fd214e3e 100644 --- a/crates/extension/src/extension_store.rs +++ b/crates/extension/src/extension_store.rs @@ -798,8 +798,8 @@ impl ExtensionStore { }, grammars: manifest_json .grammars - .into_iter() - .map(|(grammar_name, _)| (grammar_name, Default::default())) + .into_keys() + .map(|grammar_name| (grammar_name, Default::default())) .collect(), language_servers: Default::default(), }; diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index ca268668d8..e40a92c0a0 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -89,7 +89,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::derive_ord_xor_partial_ord", "clippy::eq_op", "clippy::implied_bounds_in_impls", - "clippy::iter_kv_map", "clippy::iter_overeager_cloned", "clippy::let_underscore_future", "clippy::map_entry", From 996f1036fcc86dc95f6402311175929ed3c00909 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Mon, 4 Mar 2024 11:00:24 -0500 Subject: [PATCH 069/121] linux: clipboard (#8822) Linux clipboard implementation with `copypasta`. Release Notes: - Added linux clipboard support --- Cargo.lock | 158 ++++++++++++++++++ crates/gpui/Cargo.toml | 1 + crates/gpui/src/platform/linux/client.rs | 5 + crates/gpui/src/platform/linux/platform.rs | 19 ++- .../gpui/src/platform/linux/wayland/client.rs | 25 ++- crates/gpui/src/platform/linux/x11/client.rs | 17 ++ 6 files changed, 216 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6ba0cd82ac..5e7864f9b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2141,6 +2141,16 @@ dependencies = [ "util", ] +[[package]] +name = "clipboard-win" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fdf5e01086b6be750428ba4a40619f847eb2e95756eee84b18e06e5f0b50342" +dependencies = [ + "lazy-bytes-cast", + "winapi 0.3.9", +] + [[package]] name = "clock" version = "0.1.0" @@ -2490,6 +2500,20 @@ dependencies = [ "zed_actions", ] +[[package]] +name = "copypasta" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb85422867ca93da58b7f95fb5c0c10f6183ed6e1ef8841568968a896d3a858" +dependencies = [ + "clipboard-win", + "objc", + "objc-foundation", + "objc_id", + "smithay-clipboard", + "x11-clipboard", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -4147,6 +4171,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -4325,6 +4359,7 @@ dependencies = [ "cbindgen", "cocoa", "collections", + "copypasta", "core-foundation", "core-graphics", "core-text", @@ -5333,6 +5368,12 @@ dependencies = [ "util", ] +[[package]] +name = "lazy-bytes-cast" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10257499f089cd156ad82d0a9cd57d9501fa2c989068992a97eb3c27836f206b" + [[package]] name = "lazy_static" version = "1.4.0" @@ -5733,6 +5774,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memmap2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.7.1" @@ -6376,6 +6426,17 @@ dependencies = [ "objc_exception", ] +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + [[package]] name = "objc_exception" version = "0.1.2" @@ -6385,6 +6446,15 @@ dependencies = [ "cc", ] +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + [[package]] name = "object" version = "0.32.1" @@ -8915,6 +8985,42 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +[[package]] +name = "smithay-client-toolkit" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a" +dependencies = [ + "bitflags 2.4.2", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2 0.9.4", + "rustix 0.38.30", + "thiserror", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smithay-clipboard" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c091e7354ea8059d6ad99eace06dd13ddeedbb0ac72d40a9a6e7ff790525882d" +dependencies = [ + "libc", + "smithay-client-toolkit", + "wayland-backend", +] + [[package]] name = "smol" version = "1.3.0" @@ -11730,6 +11836,17 @@ dependencies = [ "wayland-scanner", ] +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.4.2", + "cursor-icon", + "wayland-backend", +] + [[package]] name = "wayland-cursor" version = "0.31.1" @@ -11753,6 +11870,19 @@ dependencies = [ "wayland-scanner", ] +[[package]] +name = "wayland-protocols-wlr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +dependencies = [ + "bitflags 2.4.2", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + [[package]] name = "wayland-scanner" version = "0.31.1" @@ -11772,6 +11902,7 @@ checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" dependencies = [ "dlib", "log", + "once_cell", "pkg-config", ] @@ -12417,6 +12548,33 @@ dependencies = [ "tap", ] +[[package]] +name = "x11-clipboard" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98785a09322d7446e28a13203d2cae1059a0dd3dfb32cb06d0a225f023d8286" +dependencies = [ + "libc", + "x11rb", +] + +[[package]] +name = "x11rb" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a" +dependencies = [ + "gethostname", + "rustix 0.38.30", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34" + [[package]] name = "xattr" version = "0.2.3" diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index c917f92cbd..de83868b35 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -113,6 +113,7 @@ xkbcommon = { version = "0.7", features = ["wayland", "x11"] } as-raw-xcb-connection = "1" calloop = "0.12.4" calloop-wayland-source = "0.2.0" +copypasta = "0.10.1" oo7 = "0.3.0" [target.'cfg(windows)'.dependencies] diff --git a/crates/gpui/src/platform/linux/client.rs b/crates/gpui/src/platform/linux/client.rs index d74aac7369..b5d154b7a7 100644 --- a/crates/gpui/src/platform/linux/client.rs +++ b/crates/gpui/src/platform/linux/client.rs @@ -1,5 +1,8 @@ +use std::cell::RefCell; use std::rc::Rc; +use copypasta::ClipboardProvider; + use crate::platform::PlatformWindow; use crate::{AnyWindowHandle, CursorStyle, DisplayId, PlatformDisplay, WindowOptions}; @@ -12,4 +15,6 @@ pub trait Client { options: WindowOptions, ) -> Box; fn set_cursor_style(&self, style: CursorStyle); + fn get_clipboard(&self) -> Rc>; + fn get_primary(&self) -> Rc>; } diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 8db32e7264..016282b444 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -354,12 +354,21 @@ impl Platform for LinuxPlatform { false } - // todo(linux) - fn write_to_clipboard(&self, item: ClipboardItem) {} + fn write_to_clipboard(&self, item: ClipboardItem) { + let clipboard = self.client.get_clipboard(); + clipboard.borrow_mut().set_contents(item.text); + } - // todo(linux) fn read_from_clipboard(&self) -> Option { - None + let clipboard = self.client.get_clipboard(); + let contents = clipboard.borrow_mut().get_contents(); + match contents { + Ok(text) => Some(ClipboardItem { + metadata: None, + text, + }), + _ => None, + } } //todo!(linux) @@ -382,6 +391,8 @@ impl Platform for LinuxPlatform { }) } + //todo!(linux): add trait methods for accessing the primary selection + //todo!(linux) fn read_credentials(&self, url: &str) -> Task)>>> { let url = url.to_string(); diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index 8ac18b78c3..e8f20ebc0e 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -6,6 +6,8 @@ use std::time::Duration; use calloop::timer::{TimeoutAction, Timer}; use calloop::LoopHandle; use calloop_wayland_source::WaylandSource; +use copypasta::wayland_clipboard::{create_clipboards_from_external, Clipboard, Primary}; +use copypasta::ClipboardProvider; use wayland_backend::client::ObjectId; use wayland_backend::protocol::WEnum; use wayland_client::globals::{registry_queue_init, GlobalListContents}; @@ -74,6 +76,8 @@ pub(crate) struct CursorState { pub(crate) struct WaylandClientState { client_state_inner: Rc>, cursor_state: Rc>, + clipboard: Rc>, + primary: Rc>, } pub(crate) struct KeyRepeat { @@ -111,6 +115,9 @@ impl WaylandClient { } }); + let display = conn.backend().display_ptr() as *mut std::ffi::c_void; + let (primary, clipboard) = unsafe { create_clipboards_from_external(display) }; + let mut state_inner = Rc::new(RefCell::new(WaylandClientStateInner { compositor: globals.bind(&qh, 1..=1, ()).unwrap(), wm_base: globals.bind(&qh, 1..=1, ()).unwrap(), @@ -152,20 +159,20 @@ impl WaylandClient { let mut state = WaylandClientState { client_state_inner: Rc::clone(&state_inner), cursor_state: Rc::clone(&cursor_state), + clipboard: Rc::new(RefCell::new(clipboard)), + primary: Rc::new(RefCell::new(primary)), }; + let mut state_loop = state.clone(); linux_platform_inner .loop_handle .insert_source(source, move |_, queue, _| { - queue.dispatch_pending(&mut state) + queue.dispatch_pending(&mut state_loop) }) .unwrap(); Self { platform_inner: linux_platform_inner, - state: WaylandClientState { - client_state_inner: state_inner, - cursor_state, - }, + state, qh: Arc::new(qh), } } @@ -265,6 +272,14 @@ impl Client for WaylandClient { let mut cursor_state = self.state.cursor_state.borrow_mut(); cursor_state.cursor_icon_name = cursor_icon_name; } + + fn get_clipboard(&self) -> Rc> { + self.state.clipboard.clone() + } + + fn get_primary(&self) -> Rc> { + self.state.primary.clone() + } } impl Dispatch for WaylandClientState { diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index dd01102276..53fbc8747f 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -6,6 +6,8 @@ use xcb::{x, Xid as _}; use xkbcommon::xkb; use collections::HashMap; +use copypasta::x11_clipboard::{Clipboard, Primary, X11ClipboardContext}; +use copypasta::ClipboardProvider; use crate::platform::linux::client::Client; use crate::platform::{LinuxPlatformInner, PlatformWindow}; @@ -28,6 +30,8 @@ struct WindowRef { struct X11ClientState { windows: HashMap, xkb: xkbcommon::xkb::State, + clipboard: Rc>>, + primary: Rc>>, } pub(crate) struct X11Client { @@ -70,6 +74,9 @@ impl X11Client { xkb::x11::state_new_from_device(&xkb_keymap, &xcb_connection, xkb_device_id) }; + let clipboard = X11ClipboardContext::::new().unwrap(); + let primary = X11ClipboardContext::::new().unwrap(); + let client: Rc = Rc::new(Self { platform_inner: inner.clone(), xcb_connection: Rc::clone(&xcb_connection), @@ -78,6 +85,8 @@ impl X11Client { state: RefCell::new(X11ClientState { windows: HashMap::default(), xkb: xkb_state, + clipboard: Rc::new(RefCell::new(clipboard)), + primary: Rc::new(RefCell::new(primary)), }), }); @@ -354,6 +363,14 @@ impl Client for X11Client { //todo!(linux) fn set_cursor_style(&self, _style: CursorStyle) {} + + fn get_clipboard(&self) -> Rc> { + self.state.borrow().clipboard.clone() + } + + fn get_primary(&self) -> Rc> { + self.state.borrow().primary.clone() + } } // Adatpted from: From a860530a2ef99d9a0b29f9cfaa72ee64e95acf94 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 4 Mar 2024 11:19:32 -0500 Subject: [PATCH 070/121] Assign OpenAI model based on Azure OpenAI deployment ID (#8835) Following up on #8646, this PR makes it so we select an OpenAI model based on the deployment ID when using Azure OpenAI. Release Notes: - N/A --- crates/assistant/src/assistant_settings.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/crates/assistant/src/assistant_settings.rs b/crates/assistant/src/assistant_settings.rs index 4b8f3feb85..007e994389 100644 --- a/crates/assistant/src/assistant_settings.rs +++ b/crates/assistant/src/assistant_settings.rs @@ -111,9 +111,23 @@ impl AssistantSettings { AiProviderSettings::OpenAi(settings) => { Ok(settings.default_model.unwrap_or(OpenAiModel::FourTurbo)) } - AiProviderSettings::AzureOpenAi(_settings) => { - // TODO: We need to use an Azure OpenAI model here. - Ok(OpenAiModel::FourTurbo) + AiProviderSettings::AzureOpenAi(settings) => { + let deployment_id = settings + .deployment_id + .as_deref() + .ok_or_else(|| anyhow!("no Azure OpenAI deployment ID"))?; + + match deployment_id { + // https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#gpt-4-and-gpt-4-turbo-preview + "gpt-4" | "gpt-4-32k" => Ok(OpenAiModel::Four), + // https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#gpt-35 + "gpt-35-turbo" | "gpt-35-turbo-16k" | "gpt-35-turbo-instruct" => { + Ok(OpenAiModel::ThreePointFiveTurbo) + } + _ => Err(anyhow!( + "no matching OpenAI model found for deployment ID: '{deployment_id}'" + )), + } } } } From 12980dd88fb951d26c26a80c251b29a921a3cf21 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 4 Mar 2024 11:47:07 -0500 Subject: [PATCH 071/121] Enable `clippy::derivable_impls` (#8836) This PR enables the [`clippy::derivable_impls`](https://rust-lang.github.io/rust-clippy/master/index.html#/derivable_impls) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/ai/src/test.rs | 9 +-------- tooling/xtask/src/main.rs | 1 - 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/crates/ai/src/test.rs b/crates/ai/src/test.rs index 89edc71b0b..f10ca4f5fa 100644 --- a/crates/ai/src/test.rs +++ b/crates/ai/src/test.rs @@ -54,6 +54,7 @@ impl LanguageModel for FakeLanguageModel { } } +#[derive(Default)] pub struct FakeEmbeddingProvider { pub embedding_count: AtomicUsize, } @@ -66,14 +67,6 @@ impl Clone for FakeEmbeddingProvider { } } -impl Default for FakeEmbeddingProvider { - fn default() -> Self { - FakeEmbeddingProvider { - embedding_count: AtomicUsize::default(), - } - } -} - impl FakeEmbeddingProvider { pub fn embedding_count(&self) -> usize { self.embedding_count.load(atomic::Ordering::SeqCst) diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index e40a92c0a0..1f075c59d4 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -85,7 +85,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::cast_abs_to_unsigned", "clippy::cmp_owned", "clippy::crate_in_macro_def", - "clippy::derivable_impls", "clippy::derive_ord_xor_partial_ord", "clippy::eq_op", "clippy::implied_bounds_in_impls", From 09760340ca5f2fede3546c4acd6b0274cc3e05c0 Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Mon, 4 Mar 2024 16:57:56 +0000 Subject: [PATCH 072/121] linux: remove todo for credential implementation (#8834) As commented [here](https://github.com/zed-industries/zed/pull/8035#issuecomment-1976894590), this is already done. Release Notes: - N/A --- crates/gpui/src/platform/linux/platform.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 016282b444..414c1507dc 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -371,7 +371,6 @@ impl Platform for LinuxPlatform { } } - //todo!(linux) fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task> { let url = url.to_string(); let username = username.to_string(); @@ -393,7 +392,6 @@ impl Platform for LinuxPlatform { //todo!(linux): add trait methods for accessing the primary selection - //todo!(linux) fn read_credentials(&self, url: &str) -> Task)>>> { let url = url.to_string(); self.background_executor().spawn(async move { @@ -421,7 +419,6 @@ impl Platform for LinuxPlatform { }) } - //todo!(linux) fn delete_credentials(&self, url: &str) -> Task> { let url = url.to_string(); self.background_executor().spawn(async move { From 9c6a0d98eda255ec934cff1855f5ad1353eb0c86 Mon Sep 17 00:00:00 2001 From: Thorsten Ball Date: Mon, 4 Mar 2024 18:06:55 +0100 Subject: [PATCH 073/121] Set working directory for `project` to project path (#8837) This fixes #8823 by setting the current working directory we use when launching our own `prettier` process via `node` to the project path. Why does this fix it? We already *did* read the correct configuration options for `prettier` from any configuration files, we also correctly inferred which `prettier` plugins to use, but somehow when running ./node_modules/.bin/prettier my-file.tsx produced different results compared to `prettier` in Zed. But we *do* pass the right options to `prettier.format` when calling it here: https://github.com/zed-industries/zed/blob/996f1036fcc86dc95f6402311175929ed3c00909/crates/prettier/src/prettier_server.js#L177-L190 I checked those against the `prettier --loglevel=debug` output: they're the same. Turns out that the difference is we launch our `prettier_server.js` (a JavaScript shim that wraps `prettier`-the-library in a language server interface) not in the project path. So somewhere inside `prettier.format` something is `require`d and fails because we're not in that project directory. But when you run `./node_modules/.bin/prettier` you are. With the fix here, `prettier` now correctly picks up the tailwind plugin that didn't seem to work in #8823. It probably fixes a bunch of other oddities that folks reported with `prettier` too. Release Notes: - Fixed `prettier` integration not correctly picking up `prettier` plugins, because it didn't run in the project's root path when invoked. ([#8823](https://github.com/zed-industries/zed/issues/8823)). --- crates/prettier/src/prettier.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/prettier/src/prettier.rs b/crates/prettier/src/prettier.rs index c9e94cdb24..676ed6d1ac 100644 --- a/crates/prettier/src/prettier.rs +++ b/crates/prettier/src/prettier.rs @@ -197,7 +197,7 @@ impl Prettier { arguments: vec![prettier_server.into(), prettier_dir.as_path().into()], env: None, }, - Path::new("/"), + &prettier_dir, None, cx.clone(), ) From 9ea50ed6496c9a5452b44b21e44bfa0622f8a97e Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 4 Mar 2024 12:22:11 -0500 Subject: [PATCH 074/121] Enable `clippy::iter_overeager_cloned` (#8839) This PR enables the [`clippy::iter_overeager_cloned`](https://rust-lang.github.io/rust-clippy/master/index.html#/iter_overeager_cloned) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/language/src/syntax_map.rs | 9 ++++++--- tooling/xtask/src/main.rs | 1 - 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index e517a9f092..926c4a36ed 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -614,9 +614,12 @@ impl SyntaxSnapshot { Some(old_tree.clone()), ); changed_ranges = join_ranges( - invalidated_ranges.iter().cloned().filter(|range| { - range.start <= step_end_byte && range.end >= step_start_byte - }), + invalidated_ranges + .iter() + .filter(|&range| { + range.start <= step_end_byte && range.end >= step_start_byte + }) + .cloned(), old_tree.changed_ranges(&tree).map(|r| { step_start_byte + r.start_byte..step_start_byte + r.end_byte }), diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 1f075c59d4..69b6bc95bb 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -88,7 +88,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::derive_ord_xor_partial_ord", "clippy::eq_op", "clippy::implied_bounds_in_impls", - "clippy::iter_overeager_cloned", "clippy::let_underscore_future", "clippy::map_entry", "clippy::never_loop", From 1dd4c1b057b1797a447a0494eacd65d9c0f0864c Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 4 Mar 2024 12:38:18 -0500 Subject: [PATCH 075/121] Enable `clippy::redundant_closure_call` (#8840) This PR enables the [`clippy::redundant_closure_call`](https://rust-lang.github.io/rust-clippy/master/index.html#/redundant_closure_call) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/languages/src/astro.rs | 6 +++--- crates/languages/src/elm.rs | 6 +++--- tooling/xtask/src/main.rs | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/languages/src/astro.rs b/crates/languages/src/astro.rs index 220b294e8e..2ed3853e07 100644 --- a/crates/languages/src/astro.rs +++ b/crates/languages/src/astro.rs @@ -12,7 +12,7 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use util::ResultExt; +use util::{async_maybe, ResultExt}; const SERVER_PATH: &str = "node_modules/@astrojs/language-server/bin/nodeServer.js"; @@ -105,7 +105,7 @@ async fn get_cached_server_binary( container_dir: PathBuf, node: &dyn NodeRuntime, ) -> Option { - (|| async move { + async_maybe!({ let mut last_version_dir = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { @@ -128,7 +128,7 @@ async fn get_cached_server_binary( last_version_dir )) } - })() + }) .await .log_err() } diff --git a/crates/languages/src/elm.rs b/crates/languages/src/elm.rs index 27b9c6d409..37b156db91 100644 --- a/crates/languages/src/elm.rs +++ b/crates/languages/src/elm.rs @@ -15,7 +15,7 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use util::ResultExt; +use util::{async_maybe, ResultExt}; const SERVER_NAME: &str = "elm-language-server"; const SERVER_PATH: &str = "node_modules/@elm-tooling/elm-language-server/out/node/index.js"; @@ -117,7 +117,7 @@ async fn get_cached_server_binary( container_dir: PathBuf, node: &dyn NodeRuntime, ) -> Option { - (|| async move { + async_maybe!({ let mut last_version_dir = None; let mut entries = fs::read_dir(&container_dir).await?; while let Some(entry) = entries.next().await { @@ -140,7 +140,7 @@ async fn get_cached_server_binary( last_version_dir )) } - })() + }) .await .log_err() } diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 69b6bc95bb..d93d2b7e5e 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -93,7 +93,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::never_loop", "clippy::non_canonical_clone_impl", "clippy::non_canonical_partial_ord_impl", - "clippy::redundant_closure_call", "clippy::reversed_empty_ranges", "clippy::single_range_in_vec_init", "clippy::suspicious_to_owned", From 78fa596839bcf3b367b042d0c7eeb8350850c12c Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 4 Mar 2024 13:37:23 -0500 Subject: [PATCH 076/121] Enable `clippy::crate_in_macro_def` (#8845) This PR enables the [`clippy::crate_in_macro_def`](https://rust-lang.github.io/rust-clippy/master/index.html#/crate_in_macro_def) rule and fixes the outstanding violations. Release Notes: - N/A --- crates/collab/src/db/tests.rs | 4 ++-- tooling/xtask/src/main.rs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/collab/src/db/tests.rs b/crates/collab/src/db/tests.rs index b07f7a84e1..35da659e54 100644 --- a/crates/collab/src/db/tests.rs +++ b/crates/collab/src/db/tests.rs @@ -109,13 +109,13 @@ macro_rules! test_both_dbs { ($test_name:ident, $postgres_test_name:ident, $sqlite_test_name:ident) => { #[gpui::test] async fn $postgres_test_name(cx: &mut gpui::TestAppContext) { - let test_db = crate::db::TestDb::postgres(cx.executor().clone()); + let test_db = $crate::db::TestDb::postgres(cx.executor().clone()); $test_name(test_db.db()).await; } #[gpui::test] async fn $sqlite_test_name(cx: &mut gpui::TestAppContext) { - let test_db = crate::db::TestDb::sqlite(cx.executor().clone()); + let test_db = $crate::db::TestDb::sqlite(cx.executor().clone()); $test_name(test_db.db()).await; } }; diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index d93d2b7e5e..44947a4367 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -84,7 +84,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { "clippy::borrowed_box", "clippy::cast_abs_to_unsigned", "clippy::cmp_owned", - "clippy::crate_in_macro_def", "clippy::derive_ord_xor_partial_ord", "clippy::eq_op", "clippy::implied_bounds_in_impls", From d7b5c883fec670539ceb40f3baa792bc1204f676 Mon Sep 17 00:00:00 2001 From: Andrew Lygin Date: Mon, 4 Mar 2024 21:56:17 +0300 Subject: [PATCH 077/121] Optimize project panel subscriptions (#8846) The project panel now both observes all the project updates and subscribes to project events it's interested in. The observing handler updates the list of visible entries on any notification, which looks pretty excessive. This PR removes the observer completely, and adds missing event handlers to the subscription, thus removing unnecessary work. Release Notes: - N/A --- crates/project_panel/src/project_panel.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index a065ad4b80..481086a8bb 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -166,13 +166,7 @@ impl ProjectPanel { fn new(workspace: &mut Workspace, cx: &mut ViewContext) -> View { let project = workspace.project().clone(); let project_panel = cx.new_view(|cx: &mut ViewContext| { - cx.observe(&project, |this, _, cx| { - this.update_visible_entries(None, cx); - cx.notify(); - }) - .detach(); let focus_handle = cx.focus_handle(); - cx.on_focus(&focus_handle, Self::focus_in).detach(); cx.subscribe(&project, |this, project, event, cx| match event { @@ -193,6 +187,10 @@ impl ProjectPanel { this.update_visible_entries(None, cx); cx.notify(); } + project::Event::WorktreeUpdatedEntries(_, _) | project::Event::WorktreeAdded => { + this.update_visible_entries(None, cx); + cx.notify(); + } _ => {} }) .detach(); From 95e532c56dc948d84487f141a5234a3b3006c4e5 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Mon, 4 Mar 2024 14:38:28 -0500 Subject: [PATCH 078/121] Add option to sign in to copilot from welcome screen (#8853) Fixes: https://github.com/zed-industries/zed/issues/8851 https://github.com/zed-industries/zed/assets/19867440/5d391289-34e8-4abc-9337-b7e253f4e513 Release Notes: - Added GitHub Copilot sign in on welcome screen ([#8851](https://github.com/zed-industries/zed/issues/8851)). --- Cargo.lock | 1 + crates/copilot_ui/src/copilot_button.rs | 2 +- crates/copilot_ui/src/copilot_ui.rs | 2 +- crates/welcome/Cargo.toml | 1 + crates/welcome/src/welcome.rs | 11 +++++++++++ 5 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5e7864f9b9..37684379a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11937,6 +11937,7 @@ version = "0.1.0" dependencies = [ "anyhow", "client", + "copilot_ui", "db", "editor", "fuzzy", diff --git a/crates/copilot_ui/src/copilot_button.rs b/crates/copilot_ui/src/copilot_button.rs index dca7411388..afc39c3d19 100644 --- a/crates/copilot_ui/src/copilot_button.rs +++ b/crates/copilot_ui/src/copilot_button.rs @@ -332,7 +332,7 @@ fn hide_copilot(fs: Arc, cx: &mut AppContext) { }); } -fn initiate_sign_in(cx: &mut WindowContext) { +pub fn initiate_sign_in(cx: &mut WindowContext) { let Some(copilot) = Copilot::global(cx) else { return; }; diff --git a/crates/copilot_ui/src/copilot_ui.rs b/crates/copilot_ui/src/copilot_ui.rs index 64dd068d5a..f55090ebcb 100644 --- a/crates/copilot_ui/src/copilot_ui.rs +++ b/crates/copilot_ui/src/copilot_ui.rs @@ -1,4 +1,4 @@ -mod copilot_button; +pub mod copilot_button; mod sign_in; pub use copilot_button::*; diff --git a/crates/welcome/Cargo.toml b/crates/welcome/Cargo.toml index e958bf26ff..2200db8bbd 100644 --- a/crates/welcome/Cargo.toml +++ b/crates/welcome/Cargo.toml @@ -14,6 +14,7 @@ test-support = [] [dependencies] anyhow.workspace = true client.workspace = true +copilot_ui.workspace = true db.workspace = true fuzzy.workspace = true gpui.workspace = true diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index 9a97fde6c3..01ffe2a166 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -2,6 +2,7 @@ mod base_keymap_picker; mod base_keymap_setting; use client::{telemetry::Telemetry, TelemetrySettings}; +use copilot_ui; use db::kvp::KEY_VALUE_STORE; use gpui::{ svg, AnyElement, AppContext, EventEmitter, FocusHandle, FocusableView, InteractiveElement, @@ -134,6 +135,16 @@ impl Render for WelcomePage { }) .detach_and_log_err(cx); })), + ) + .child( + Button::new("sign-in-to-copilot", "Sign in to GitHub Copilot") + .full_width() + .on_click(cx.listener(|this, _, cx| { + this.telemetry.report_app_event( + "welcome page: sign in to copilot".to_string(), + ); + copilot_ui::initiate_sign_in(cx); + })), ), ) .child( From b2f18cfe7192a32ddfa87ef461555fd1bd9a9867 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 4 Mar 2024 12:39:08 -0700 Subject: [PATCH 079/121] Ensure followed cursors are always visible (#8849) Before this change they would disappear if you blurred the pane. Release Notes: - Fixed an issue where the followed users' cursor would disappear if you blurred the pane. --- crates/editor/src/element.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 43a9c8f746..d46f0f2a31 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -2091,7 +2091,7 @@ impl EditorElement { editor.cursor_shape, &snapshot.display_snapshot, is_newest, - true, + editor.leader_peer_id.is_none(), None, ); if is_newest { From 2201b9b1167a603116a31ea96368a3df45146eb6 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 4 Mar 2024 21:04:53 +0100 Subject: [PATCH 080/121] task: Add task contexts (#8675) This PR supplements tasks with additional environment variables; ideally we'll be able to write a task like: `cargo test -p $ZED_CURRENT_PACKAGE -- $ZED_CURRENT_FUNCTION` - [x] Flesh out multibuffer interactions - [x] Add ZED_SYMBOL detection based on tree-sitter queries - [ ] Add release note and demo - [x] Figure out a solution for rerun dilemma - should `task: rerun` reevaluate contexts for tasks? This PR introduced the following variables: - ZED_COLUMN - current line column - ZED_ROW - current line row and the following, which are available for buffers with associated files: - ZED_WORKTREE_ROOT - absolute path to the root of the current worktree. - ZED_FILE - absolute path to the file - ZED_SYMBOL - currently selected symbol; should match the last symbol shown in a symbol breadcrumb (e.g. `mod tests > fn test_task_contexts` should be equal to ZED_SYMBOL of `test_task_contexts`). Note that this isn't necessarily a test function or a function at all. Also, you can use them in `cwd` field of definitions (note though that we're using https://docs.rs/subst/latest/subst/#features for that, so don't expect a full shell functionality to work); the syntax should match up with your typical Unix shell. Release Notes: - Added task contexts, which are additional environment variables set by Zed for task execution; task content is dependent on the state of the editor at the time the task is spawned. --------- Co-authored-by: Anthony --- Cargo.lock | 13 + crates/extension/src/extension_store.rs | 1 + crates/language/src/language.rs | 54 ++++ crates/language/src/language_registry.rs | 14 +- crates/languages/src/lib.rs | 237 ++++++++++------- crates/project/src/task_inventory.rs | 59 ++-- crates/task/Cargo.toml | 1 + crates/task/src/lib.rs | 13 +- crates/task/src/oneshot_source.rs | 9 +- crates/task/src/static_source.rs | 25 +- crates/tasks_ui/Cargo.toml | 5 + crates/tasks_ui/src/lib.rs | 325 +++++++++++++++++++++-- crates/tasks_ui/src/modal.rs | 57 ++-- 13 files changed, 623 insertions(+), 190 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 37684379a9..47cf6cfed6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9467,6 +9467,16 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "subst" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca1318e5d6716d6541696727c88d9b8dfc8cfe6afd6908e186546fd4af7f5b98" +dependencies = [ + "memchr", + "unicode-width", +] + [[package]] name = "subtle" version = "2.5.0" @@ -9698,6 +9708,7 @@ dependencies = [ "schemars", "serde", "serde_json_lenient", + "subst", "util", ] @@ -9716,6 +9727,8 @@ dependencies = [ "serde", "serde_json", "task", + "tree-sitter-rust", + "tree-sitter-typescript", "ui", "util", "workspace", diff --git a/crates/extension/src/extension_store.rs b/crates/extension/src/extension_store.rs index 55fd214e3e..e80ac6f5a4 100644 --- a/crates/extension/src/extension_store.rs +++ b/crates/extension/src/extension_store.rs @@ -555,6 +555,7 @@ impl ExtensionStore { language_name.clone(), language.grammar.clone(), language.matcher.clone(), + None, move || { let config = std::fs::read_to_string(language_path.join("config.toml"))?; let config: LanguageConfig = ::toml::from_str(&config)?; diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 38d64ccf0c..ee137abe43 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -120,6 +120,46 @@ pub struct Location { pub range: Range, } +pub struct LanguageContext { + pub package: Option, + pub symbol: Option, +} + +pub trait LanguageContextProvider: Send + Sync { + fn build_context(&self, location: Location, cx: &mut AppContext) -> Result; +} + +/// A context provider that fills out LanguageContext without inspecting the contents. +pub struct DefaultContextProvider; + +impl LanguageContextProvider for DefaultContextProvider { + fn build_context( + &self, + location: Location, + cx: &mut AppContext, + ) -> gpui::Result { + let symbols = location + .buffer + .read(cx) + .snapshot() + .symbols_containing(location.range.start, None); + let symbol = symbols.and_then(|symbols| { + symbols.last().map(|symbol| { + let range = symbol + .name_ranges + .last() + .cloned() + .unwrap_or(0..symbol.text.len()); + symbol.text[range].to_string() + }) + }); + Ok(LanguageContext { + package: None, + symbol, + }) + } +} + /// Represents a Language Server, with certain cached sync properties. /// Uses [`LspAdapter`] under the hood, but calls all 'static' methods /// once at startup, and caches the results. @@ -727,6 +767,7 @@ pub struct Language { pub(crate) id: LanguageId, pub(crate) config: LanguageConfig, pub(crate) grammar: Option>, + pub(crate) context_provider: Option>, } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] @@ -841,9 +882,18 @@ impl Language { highlight_map: Default::default(), }) }), + context_provider: None, } } + pub fn with_context_provider( + mut self, + provider: Option>, + ) -> Self { + self.context_provider = provider; + self + } + pub fn with_queries(mut self, queries: LanguageQueries) -> Result { if let Some(query) = queries.highlights { self = self @@ -1139,6 +1189,10 @@ impl Language { self.config.name.clone() } + pub fn context_provider(&self) -> Option> { + self.context_provider.clone() + } + pub fn highlight_text<'a>( self: &'a Arc, text: &'a Rope, diff --git a/crates/language/src/language_registry.rs b/crates/language/src/language_registry.rs index 8bc29f943f..d32b0f3346 100644 --- a/crates/language/src/language_registry.rs +++ b/crates/language/src/language_registry.rs @@ -1,6 +1,6 @@ use crate::{ - CachedLspAdapter, Language, LanguageConfig, LanguageId, LanguageMatcher, LanguageServerName, - LspAdapter, LspAdapterDelegate, PARSER, PLAIN_TEXT, + CachedLspAdapter, Language, LanguageConfig, LanguageContextProvider, LanguageId, + LanguageMatcher, LanguageServerName, LspAdapter, LspAdapterDelegate, PARSER, PLAIN_TEXT, }; use anyhow::{anyhow, Context as _, Result}; use collections::{hash_map, HashMap}; @@ -78,6 +78,7 @@ struct AvailableLanguage { matcher: LanguageMatcher, load: Arc Result<(LanguageConfig, LanguageQueries)> + 'static + Send + Sync>, loaded: bool, + context_provider: Option>, } enum AvailableGrammar { @@ -188,6 +189,7 @@ impl LanguageRegistry { config.name.clone(), config.grammar.clone(), config.matcher.clone(), + None, move || Ok((config.clone(), Default::default())), ) } @@ -237,6 +239,7 @@ impl LanguageRegistry { name: Arc, grammar_name: Option>, matcher: LanguageMatcher, + context_provider: Option>, load: impl Fn() -> Result<(LanguageConfig, LanguageQueries)> + 'static + Send + Sync, ) { let load = Arc::new(load); @@ -257,6 +260,8 @@ impl LanguageRegistry { grammar: grammar_name, matcher, load, + + context_provider, loaded: false, }); state.version += 1; @@ -422,6 +427,7 @@ impl LanguageRegistry { .spawn(async move { let id = language.id; let name = language.name.clone(); + let provider = language.context_provider.clone(); let language = async { let (config, queries) = (language.load)()?; @@ -431,7 +437,9 @@ impl LanguageRegistry { None }; - Language::new_with_id(id, config, grammar).with_queries(queries) + Language::new_with_id(id, config, grammar) + .with_context_provider(provider) + .with_queries(queries) } .await; diff --git a/crates/languages/src/lib.rs b/crates/languages/src/lib.rs index f47bf33214..6aef9f6d16 100644 --- a/crates/languages/src/lib.rs +++ b/crates/languages/src/lib.rs @@ -122,212 +122,245 @@ pub fn init( ("dart", tree_sitter_dart::language()), ]); - let language = |asset_dir_name: &'static str, adapters: Vec>| { - let config = load_config(asset_dir_name); - for adapter in adapters { - languages.register_lsp_adapter(config.name.clone(), adapter); - } - languages.register_language( - config.name.clone(), - config.grammar.clone(), - config.matcher.clone(), - move || Ok((config.clone(), load_queries(asset_dir_name))), - ); - }; - - language( + macro_rules! language { + ($name:literal) => { + let config = load_config($name); + languages.register_language( + config.name.clone(), + config.grammar.clone(), + config.matcher.clone(), + Some(Arc::new(language::DefaultContextProvider)), + move || Ok((config.clone(), load_queries($name))), + ); + }; + ($name:literal, $adapters:expr) => { + let config = load_config($name); + // typeck helper + let adapters: Vec> = $adapters; + for adapter in adapters { + languages.register_lsp_adapter(config.name.clone(), adapter); + } + languages.register_language( + config.name.clone(), + config.grammar.clone(), + config.matcher.clone(), + Some(Arc::new(language::DefaultContextProvider)), + move || Ok((config.clone(), load_queries($name))), + ); + }; + ($name:literal, $adapters:expr, $context_provider:expr) => { + let config = load_config($name); + // typeck helper + let adapters: Vec> = $adapters; + for adapter in $adapters { + languages.register_lsp_adapter(config.name.clone(), adapter); + } + languages.register_language( + config.name.clone(), + config.grammar.clone(), + config.matcher.clone(), + Some(Arc::new($context_provider)), + move || Ok((config.clone(), load_queries($name))), + ); + }; + } + language!( "astro", vec![ Arc::new(astro::AstroLspAdapter::new(node_runtime.clone())), Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), - ], + ] ); - language("bash", vec![]); - language("c", vec![Arc::new(c::CLspAdapter) as Arc]); - language("clojure", vec![Arc::new(clojure::ClojureLspAdapter)]); - language("cpp", vec![Arc::new(c::CLspAdapter)]); - language("csharp", vec![Arc::new(csharp::OmniSharpAdapter {})]); - language( + language!("bash"); + language!("c", vec![Arc::new(c::CLspAdapter) as Arc]); + language!("clojure", vec![Arc::new(clojure::ClojureLspAdapter)]); + language!("cpp", vec![Arc::new(c::CLspAdapter)]); + language!("csharp", vec![Arc::new(csharp::OmniSharpAdapter {})]); + language!( "css", vec![ Arc::new(css::CssLspAdapter::new(node_runtime.clone())), Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), - ], + ] ); - language( + language!( "dockerfile", vec![Arc::new(dockerfile::DockerfileLspAdapter::new( node_runtime.clone(), - ))], + ))] ); match &ElixirSettings::get(None, cx).lsp { - elixir::ElixirLspSetting::ElixirLs => language( - "elixir", - vec![ - Arc::new(elixir::ElixirLspAdapter), - Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), - ], - ), - elixir::ElixirLspSetting::NextLs => { - language("elixir", vec![Arc::new(elixir::NextLspAdapter)]) + elixir::ElixirLspSetting::ElixirLs => { + language!( + "elixir", + vec![ + Arc::new(elixir::ElixirLspAdapter), + Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), + ] + ); + } + elixir::ElixirLspSetting::NextLs => { + language!("elixir", vec![Arc::new(elixir::NextLspAdapter)]); + } + elixir::ElixirLspSetting::Local { path, arguments } => { + language!( + "elixir", + vec![Arc::new(elixir::LocalLspAdapter { + path: path.clone(), + arguments: arguments.clone(), + })] + ); } - elixir::ElixirLspSetting::Local { path, arguments } => language( - "elixir", - vec![Arc::new(elixir::LocalLspAdapter { - path: path.clone(), - arguments: arguments.clone(), - })], - ), } - language("gitcommit", vec![]); - language("erlang", vec![Arc::new(erlang::ErlangLspAdapter)]); + language!("gitcommit"); + language!("erlang", vec![Arc::new(erlang::ErlangLspAdapter)]); - language("gleam", vec![Arc::new(gleam::GleamLspAdapter)]); - language("go", vec![Arc::new(go::GoLspAdapter)]); - language("gomod", vec![]); - language("gowork", vec![]); - language("zig", vec![Arc::new(zig::ZlsAdapter)]); - language( + language!("gleam", vec![Arc::new(gleam::GleamLspAdapter)]); + language!("go", vec![Arc::new(go::GoLspAdapter)]); + language!("gomod"); + language!("gowork"); + language!("zig", vec![Arc::new(zig::ZlsAdapter)]); + language!( "heex", vec![ Arc::new(elixir::ElixirLspAdapter), Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), - ], + ] ); - language( + language!( "json", vec![Arc::new(json::JsonLspAdapter::new( node_runtime.clone(), languages.clone(), - ))], + ))] ); - language("markdown", vec![]); - language( + language!("markdown"); + language!( "python", vec![Arc::new(python::PythonLspAdapter::new( node_runtime.clone(), - ))], + ))] ); - language("rust", vec![Arc::new(rust::RustLspAdapter)]); - language("toml", vec![Arc::new(toml::TaploLspAdapter)]); + language!("rust", vec![Arc::new(rust::RustLspAdapter)]); + language!("toml", vec![Arc::new(toml::TaploLspAdapter)]); match &DenoSettings::get(None, cx).enable { true => { - language( + language!( "tsx", vec![ Arc::new(deno::DenoLspAdapter::new()), Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), - ], + ] ); - language("typescript", vec![Arc::new(deno::DenoLspAdapter::new())]); - language( + language!("typescript", vec![Arc::new(deno::DenoLspAdapter::new())]); + language!( "javascript", vec![ Arc::new(deno::DenoLspAdapter::new()), Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), - ], + ] ); } false => { - language( + language!( "tsx", vec![ Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())), Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())), Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), - ], + ] ); - language( + language!( "typescript", vec![ Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())), Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())), - ], + ] ); - language( + language!( "javascript", vec![ Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())), Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())), Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), - ], + ] ); } } - language("haskell", vec![Arc::new(haskell::HaskellLanguageServer {})]); - language( + language!("haskell", vec![Arc::new(haskell::HaskellLanguageServer {})]); + language!( "html", vec![ Arc::new(html::HtmlLspAdapter::new(node_runtime.clone())), Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), - ], + ] ); - language("ruby", vec![Arc::new(ruby::RubyLanguageServer)]); - language( + language!("ruby", vec![Arc::new(ruby::RubyLanguageServer)]); + language!( "erb", vec![ Arc::new(ruby::RubyLanguageServer), Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), - ], + ] ); - language("scheme", vec![]); - language("racket", vec![]); - language("lua", vec![Arc::new(lua::LuaLspAdapter)]); - language( + language!("scheme"); + language!("racket"); + language!("lua", vec![Arc::new(lua::LuaLspAdapter)]); + language!( "yaml", - vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))], + vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))] ); - language( + language!( "svelte", vec![ Arc::new(svelte::SvelteLspAdapter::new(node_runtime.clone())), Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), - ], + ] ); - language( + language!( "php", vec![ Arc::new(php::IntelephenseLspAdapter::new(node_runtime.clone())), Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), - ], + ] ); - language( + language!( "purescript", vec![Arc::new(purescript::PurescriptLspAdapter::new( node_runtime.clone(), - ))], + ))] ); - language( + language!( "elm", - vec![Arc::new(elm::ElmLspAdapter::new(node_runtime.clone()))], + vec![Arc::new(elm::ElmLspAdapter::new(node_runtime.clone()))] ); - language("glsl", vec![]); - language("nix", vec![]); - language("nu", vec![Arc::new(nu::NuLanguageServer {})]); - language("ocaml", vec![Arc::new(ocaml::OCamlLspAdapter)]); - language("ocaml-interface", vec![Arc::new(ocaml::OCamlLspAdapter)]); - language( + language!("glsl"); + language!("nix"); + language!("nu", vec![Arc::new(nu::NuLanguageServer {})]); + language!("ocaml", vec![Arc::new(ocaml::OCamlLspAdapter)]); + language!("ocaml-interface", vec![Arc::new(ocaml::OCamlLspAdapter)]); + language!( "vue", - vec![Arc::new(vue::VueLspAdapter::new(node_runtime.clone()))], + vec![Arc::new(vue::VueLspAdapter::new(node_runtime.clone()))] ); - language("uiua", vec![Arc::new(uiua::UiuaLanguageServer {})]); - language("proto", vec![]); - language("terraform", vec![Arc::new(terraform::TerraformLspAdapter)]); - language( + language!("uiua", vec![Arc::new(uiua::UiuaLanguageServer {})]); + language!("proto"); + language!("terraform", vec![Arc::new(terraform::TerraformLspAdapter)]); + language!( "terraform-vars", - vec![Arc::new(terraform::TerraformLspAdapter)], + vec![Arc::new(terraform::TerraformLspAdapter)] ); - language("hcl", vec![]); - language( + language!("hcl", vec![]); + language!( "prisma", vec![Arc::new(prisma::PrismaLspAdapter::new( node_runtime.clone(), - ))], + ))] ); - language("dart", vec![Arc::new(dart::DartLanguageServer {})]); + language!("dart", vec![Arc::new(dart::DartLanguageServer {})]); } #[cfg(any(test, feature = "test-support"))] diff --git a/crates/project/src/task_inventory.rs b/crates/project/src/task_inventory.rs index 54b3e80a8b..b51cdf2ba4 100644 --- a/crates/project/src/task_inventory.rs +++ b/crates/project/src/task_inventory.rs @@ -10,13 +10,13 @@ use collections::{HashMap, VecDeque}; use gpui::{AppContext, Context, Model, ModelContext, Subscription}; use itertools::Itertools; use project_core::worktree::WorktreeId; -use task::{Task, TaskId, TaskSource}; +use task::{Task, TaskContext, TaskId, TaskSource}; use util::{post_inc, NumericPrefixWithSuffix}; /// Inventory tracks available tasks for a given project. pub struct Inventory { sources: Vec, - last_scheduled_tasks: VecDeque, + last_scheduled_tasks: VecDeque<(TaskId, TaskContext)>, } struct SourceInInventory { @@ -133,17 +133,20 @@ impl Inventory { ) -> Vec<(TaskSourceKind, Arc)> { let mut lru_score = 0_u32; let tasks_by_usage = if lru { - self.last_scheduled_tasks - .iter() - .rev() - .fold(HashMap::default(), |mut tasks, id| { - tasks.entry(id).or_insert_with(|| post_inc(&mut lru_score)); + self.last_scheduled_tasks.iter().rev().fold( + HashMap::default(), + |mut tasks, (id, context)| { tasks - }) + .entry(id) + .or_insert_with(|| (post_inc(&mut lru_score), Some(context))); + tasks + }, + ) } else { HashMap::default() }; - let not_used_score = post_inc(&mut lru_score); + let not_used_task_context = None; + let not_used_score = (post_inc(&mut lru_score), not_used_task_context); self.sources .iter() .filter(|source| { @@ -171,7 +174,8 @@ impl Inventory { .sorted_unstable_by( |((kind_a, task_a), usages_a), ((kind_b, task_b), usages_b)| { usages_a - .cmp(usages_b) + .0 + .cmp(&usages_b.0) .then( kind_a .worktree() @@ -200,19 +204,21 @@ impl Inventory { } /// Returns the last scheduled task, if any of the sources contains one with the matching id. - pub fn last_scheduled_task(&self, cx: &mut AppContext) -> Option> { - self.last_scheduled_tasks.back().and_then(|id| { - // TODO straighten the `Path` story to understand what has to be passed here: or it will break in the future. - self.list_tasks(None, None, false, cx) - .into_iter() - .find(|(_, task)| task.id() == id) - .map(|(_, task)| task) - }) + pub fn last_scheduled_task(&self, cx: &mut AppContext) -> Option<(Arc, TaskContext)> { + self.last_scheduled_tasks + .back() + .and_then(|(id, task_context)| { + // TODO straighten the `Path` story to understand what has to be passed here: or it will break in the future. + self.list_tasks(None, None, false, cx) + .into_iter() + .find(|(_, task)| task.id() == id) + .map(|(_, task)| (task, task_context.clone())) + }) } /// Registers task "usage" as being scheduled – to be used for LRU sorting when listing all tasks. - pub fn task_scheduled(&mut self, id: TaskId) { - self.last_scheduled_tasks.push_back(id); + pub fn task_scheduled(&mut self, id: TaskId, task_context: TaskContext) { + self.last_scheduled_tasks.push_back((id, task_context)); if self.last_scheduled_tasks.len() > 5_000 { self.last_scheduled_tasks.pop_front(); } @@ -221,14 +227,11 @@ impl Inventory { #[cfg(any(test, feature = "test-support"))] pub mod test_inventory { - use std::{ - path::{Path, PathBuf}, - sync::Arc, - }; + use std::{path::Path, sync::Arc}; use gpui::{AppContext, Context as _, Model, ModelContext, TestAppContext}; use project_core::worktree::WorktreeId; - use task::{Task, TaskId, TaskSource}; + use task::{Task, TaskContext, TaskId, TaskSource}; use crate::Inventory; @@ -249,11 +252,11 @@ pub mod test_inventory { &self.name } - fn cwd(&self) -> Option<&Path> { + fn cwd(&self) -> Option<&str> { None } - fn exec(&self, _cwd: Option) -> Option { + fn exec(&self, _cwd: TaskContext) -> Option { None } } @@ -327,7 +330,7 @@ pub mod test_inventory { .into_iter() .find(|(_, task)| task.name() == task_name) .unwrap_or_else(|| panic!("Failed to find task with name {task_name}")); - inventory.task_scheduled(task.1.id().clone()); + inventory.task_scheduled(task.1.id().clone(), TaskContext::default()); }); } diff --git a/crates/task/Cargo.toml b/crates/task/Cargo.toml index aeafaba3f0..47f3170c74 100644 --- a/crates/task/Cargo.toml +++ b/crates/task/Cargo.toml @@ -13,6 +13,7 @@ gpui.workspace = true schemars.workspace = true serde.workspace = true serde_json_lenient.workspace = true +subst = "0.3.0" util.workspace = true [dev-dependencies] diff --git a/crates/task/src/lib.rs b/crates/task/src/lib.rs index 66e4249c7e..37107e2569 100644 --- a/crates/task/src/lib.rs +++ b/crates/task/src/lib.rs @@ -36,6 +36,15 @@ pub struct SpawnInTerminal { pub allow_concurrent_runs: bool, } +/// Keeps track of the file associated with a task and context of tasks execution (i.e. current file or current function) +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct TaskContext { + /// A path to a directory in which the task should be executed. + pub cwd: Option, + /// Additional environment variables associated with a given task. + pub env: HashMap, +} + /// Represents a short lived recipe of a task, whose main purpose /// is to get spawned. pub trait Task { @@ -44,10 +53,10 @@ pub trait Task { /// Human readable name of the task to display in the UI. fn name(&self) -> &str; /// Task's current working directory. If `None`, current project's root will be used. - fn cwd(&self) -> Option<&Path>; + fn cwd(&self) -> Option<&str>; /// Sets up everything needed to spawn the task in the given directory (`cwd`). /// If a task is intended to be spawned in the terminal, it should return the corresponding struct filled with the data necessary. - fn exec(&self, cwd: Option) -> Option; + fn exec(&self, cx: TaskContext) -> Option; } /// [`Source`] produces tasks that can be scheduled. diff --git a/crates/task/src/oneshot_source.rs b/crates/task/src/oneshot_source.rs index bda14f894e..85257bee54 100644 --- a/crates/task/src/oneshot_source.rs +++ b/crates/task/src/oneshot_source.rs @@ -2,7 +2,7 @@ use std::sync::Arc; -use crate::{SpawnInTerminal, Task, TaskId, TaskSource}; +use crate::{SpawnInTerminal, Task, TaskContext, TaskId, TaskSource}; use gpui::{AppContext, Context, Model}; /// A storage and source of tasks generated out of user command prompt inputs. @@ -30,21 +30,22 @@ impl Task for OneshotTask { &self.id.0 } - fn cwd(&self) -> Option<&std::path::Path> { + fn cwd(&self) -> Option<&str> { None } - fn exec(&self, cwd: Option) -> Option { + fn exec(&self, cx: TaskContext) -> Option { if self.id().0.is_empty() { return None; } + let TaskContext { cwd, env } = cx; Some(SpawnInTerminal { id: self.id().clone(), label: self.name().to_owned(), command: self.id().0.clone(), args: vec![], cwd, - env: Default::default(), + env, use_new_terminal: Default::default(), allow_concurrent_runs: Default::default(), }) diff --git a/crates/task/src/static_source.rs b/crates/task/src/static_source.rs index 9b022fdb0f..dedf5a6384 100644 --- a/crates/task/src/static_source.rs +++ b/crates/task/src/static_source.rs @@ -1,10 +1,6 @@ //! A source of tasks, based on a static configuration, deserialized from the tasks config file, and related infrastructure for tracking changes to the file. -use std::{ - borrow::Cow, - path::{Path, PathBuf}, - sync::Arc, -}; +use std::{borrow::Cow, path::Path, sync::Arc}; use collections::HashMap; use futures::StreamExt; @@ -13,7 +9,7 @@ use schemars::{gen::SchemaSettings, JsonSchema}; use serde::{Deserialize, Serialize}; use util::ResultExt; -use crate::{SpawnInTerminal, Task, TaskId, TaskSource}; +use crate::{SpawnInTerminal, Task, TaskContext, TaskId, TaskSource}; use futures::channel::mpsc::UnboundedReceiver; /// A single config file entry with the deserialized task definition. @@ -24,7 +20,16 @@ struct StaticTask { } impl Task for StaticTask { - fn exec(&self, cwd: Option) -> Option { + fn exec(&self, cx: TaskContext) -> Option { + let TaskContext { cwd, env } = cx; + let cwd = self + .definition + .cwd + .clone() + .and_then(|path| subst::substitute(&path, &env).map(Into::into).ok()) + .or(cwd); + let mut definition_env = self.definition.env.clone(); + definition_env.extend(env); Some(SpawnInTerminal { id: self.id.clone(), cwd, @@ -33,7 +38,7 @@ impl Task for StaticTask { label: self.definition.label.clone(), command: self.definition.command.clone(), args: self.definition.args.clone(), - env: self.definition.env.clone(), + env: definition_env, }) } @@ -45,7 +50,7 @@ impl Task for StaticTask { &self.id } - fn cwd(&self) -> Option<&Path> { + fn cwd(&self) -> Option<&str> { self.definition.cwd.as_deref() } } @@ -72,7 +77,7 @@ pub(crate) struct Definition { pub env: HashMap, /// Current working directory to spawn the command into, defaults to current project root. #[serde(default)] - pub cwd: Option, + pub cwd: Option, /// Whether to use a new terminal tab or reuse the existing one to spawn the process. #[serde(default)] pub use_new_terminal: bool, diff --git a/crates/tasks_ui/Cargo.toml b/crates/tasks_ui/Cargo.toml index 6c350ff931..cbf5280ef6 100644 --- a/crates/tasks_ui/Cargo.toml +++ b/crates/tasks_ui/Cargo.toml @@ -7,6 +7,7 @@ license = "GPL-3.0-or-later" [dependencies] anyhow.workspace = true +editor.workspace = true fuzzy.workspace = true gpui.workspace = true menu.workspace = true @@ -17,10 +18,14 @@ serde.workspace = true ui.workspace = true util.workspace = true workspace.workspace = true +language.workspace = true [dev-dependencies] editor = { workspace = true, features = ["test-support"] } +gpui = { workspace = true, features = ["test-support"] } language = { workspace = true, features = ["test-support"] } project = { workspace = true, features = ["test-support"] } serde_json.workspace = true +tree-sitter-rust.workspace = true +tree-sitter-typescript.workspace = true workspace = { workspace = true, features = ["test-support"] } diff --git a/crates/tasks_ui/src/lib.rs b/crates/tasks_ui/src/lib.rs index 5f517fdedf..278d01ef39 100644 --- a/crates/tasks_ui/src/lib.rs +++ b/crates/tasks_ui/src/lib.rs @@ -1,8 +1,11 @@ -use std::path::PathBuf; +use std::{collections::HashMap, path::PathBuf}; +use editor::Editor; use gpui::{AppContext, ViewContext, WindowContext}; +use language::Point; use modal::TasksModal; -use task::Task; +use project::{Location, WorktreeId}; +use task::{Task, TaskContext}; use util::ResultExt; use workspace::Workspace; @@ -15,16 +18,28 @@ pub fn init(cx: &mut AppContext) { .register_action(|workspace, _: &modal::Spawn, cx| { let inventory = workspace.project().read(cx).task_inventory().clone(); let workspace_handle = workspace.weak_handle(); - workspace - .toggle_modal(cx, |cx| TasksModal::new(inventory, workspace_handle, cx)) + let cwd = task_cwd(workspace, cx).log_err().flatten(); + let task_context = task_context(workspace, cwd, cx); + workspace.toggle_modal(cx, |cx| { + TasksModal::new(inventory, task_context, workspace_handle, cx) + }) }) - .register_action(move |workspace, _: &modal::Rerun, cx| { - if let Some(task) = workspace.project().update(cx, |project, cx| { - project - .task_inventory() - .update(cx, |inventory, cx| inventory.last_scheduled_task(cx)) - }) { - schedule_task(workspace, task.as_ref(), cx) + .register_action(move |workspace, action: &modal::Rerun, cx| { + if let Some((task, old_context)) = + workspace.project().update(cx, |project, cx| { + project + .task_inventory() + .update(cx, |inventory, cx| inventory.last_scheduled_task(cx)) + }) + { + let task_context = if action.reevaluate_context { + let cwd = task_cwd(workspace, cx).log_err().flatten(); + task_context(workspace, cwd, cx) + } else { + old_context + }; + + schedule_task(workspace, task.as_ref(), task_context, cx) }; }); }, @@ -32,16 +47,117 @@ pub fn init(cx: &mut AppContext) { .detach(); } -fn schedule_task(workspace: &Workspace, task: &dyn Task, cx: &mut ViewContext<'_, Workspace>) { - let cwd = match task.cwd() { - Some(cwd) => Some(cwd.to_path_buf()), - None => task_cwd(workspace, cx).log_err().flatten(), - }; - let spawn_in_terminal = task.exec(cwd); +fn task_context( + workspace: &Workspace, + cwd: Option, + cx: &mut WindowContext<'_>, +) -> TaskContext { + let current_editor = workspace + .active_item(cx) + .and_then(|item| item.act_as::(cx)) + .clone(); + if let Some(current_editor) = current_editor { + (|| { + let editor = current_editor.read(cx); + let selection = editor.selections.newest::(cx); + let (buffer, _, _) = editor + .buffer() + .read(cx) + .point_to_buffer_offset(selection.start, cx)?; + + current_editor.update(cx, |editor, cx| { + let snapshot = editor.snapshot(cx); + let selection_range = selection.range(); + let start = snapshot + .display_snapshot + .buffer_snapshot + .anchor_after(selection_range.start) + .text_anchor; + let end = snapshot + .display_snapshot + .buffer_snapshot + .anchor_after(selection_range.end) + .text_anchor; + let Point { row, column } = snapshot + .display_snapshot + .buffer_snapshot + .offset_to_point(selection_range.start); + let row = row + 1; + let column = column + 1; + let location = Location { + buffer: buffer.clone(), + range: start..end, + }; + + let current_file = location + .buffer + .read(cx) + .file() + .map(|file| file.path().to_string_lossy().to_string()); + let worktree_id = location + .buffer + .read(cx) + .file() + .map(|file| WorktreeId::from_usize(file.worktree_id())); + let context = buffer + .read(cx) + .language() + .and_then(|language| language.context_provider()) + .and_then(|provider| provider.build_context(location, cx).ok()); + + let worktree_path = worktree_id.and_then(|worktree_id| { + workspace + .project() + .read(cx) + .worktree_for_id(worktree_id, cx) + .map(|worktree| worktree.read(cx).abs_path().to_string_lossy().to_string()) + }); + + let mut env = HashMap::from_iter([ + ("ZED_ROW".into(), row.to_string()), + ("ZED_COLUMN".into(), column.to_string()), + ]); + if let Some(path) = current_file { + env.insert("ZED_FILE".into(), path); + } + if let Some(worktree_path) = worktree_path { + env.insert("ZED_WORKTREE_ROOT".into(), worktree_path); + } + if let Some(language_context) = context { + if let Some(symbol) = language_context.symbol { + env.insert("ZED_SYMBOL".into(), symbol); + } + } + + Some(TaskContext { + cwd: cwd.clone(), + env, + }) + }) + })() + .unwrap_or_else(|| TaskContext { + cwd, + env: Default::default(), + }) + } else { + TaskContext { + cwd, + env: Default::default(), + } + } +} + +fn schedule_task( + workspace: &Workspace, + task: &dyn Task, + task_cx: TaskContext, + cx: &mut ViewContext<'_, Workspace>, +) { + let spawn_in_terminal = task.exec(task_cx.clone()); if let Some(spawn_in_terminal) = spawn_in_terminal { workspace.project().update(cx, |project, cx| { project.task_inventory().update(cx, |inventory, _| { - inventory.task_scheduled(task.id().clone()); + inventory.task_scheduled(task.id().clone(), task_cx); }) }); cx.emit(workspace::Event::SpawnTask(spawn_in_terminal)); @@ -82,3 +198,176 @@ fn task_cwd(workspace: &Workspace, cx: &mut WindowContext) -> anyhow::Result Arc { + cx.update(|cx| { + let state = AppState::test(cx); + language::init(cx); + crate::init(cx); + editor::init(cx); + workspace::init_settings(cx); + Project::init_settings(cx); + state + }) + } +} diff --git a/crates/tasks_ui/src/modal.rs b/crates/tasks_ui/src/modal.rs index dc308de1b2..491ed05971 100644 --- a/crates/tasks_ui/src/modal.rs +++ b/crates/tasks_ui/src/modal.rs @@ -2,23 +2,36 @@ use std::{path::PathBuf, sync::Arc}; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ - actions, rems, AppContext, DismissEvent, EventEmitter, FocusableView, InteractiveElement, - Model, ParentElement, Render, SharedString, Styled, Subscription, View, ViewContext, - VisualContext, WeakView, + actions, impl_actions, rems, AppContext, DismissEvent, EventEmitter, FocusableView, + InteractiveElement, Model, ParentElement, Render, SharedString, Styled, Subscription, View, + ViewContext, VisualContext, WeakView, }; use picker::{ highlighted_match_with_paths::{HighlightedMatchWithPaths, HighlightedText}, Picker, PickerDelegate, }; use project::{Inventory, ProjectPath, TaskSourceKind}; -use task::{oneshot_source::OneshotSource, Task}; +use task::{oneshot_source::OneshotSource, Task, TaskContext}; use ui::{v_flex, ListItem, ListItemSpacing, RenderOnce, Selectable, WindowContext}; use util::{paths::PathExt, ResultExt}; use workspace::{ModalView, Workspace}; use crate::schedule_task; +use serde::Deserialize; +actions!(task, [Spawn]); -actions!(task, [Spawn, Rerun]); +/// Rerun last task +#[derive(PartialEq, Clone, Deserialize, Default)] +pub struct Rerun { + #[serde(default)] + /// Controls whether the task context is reevaluated prior to execution of a task. + /// If it is not, environment variables such as ZED_COLUMN, ZED_FILE are gonna be the same as in the last execution of a task + /// If it is, these variables will be updated to reflect current state of editor at the time task::Rerun is executed. + /// default: false + pub reevaluate_context: bool, +} + +impl_actions!(task, [Rerun]); /// A modal used to spawn new tasks. pub(crate) struct TasksModalDelegate { @@ -28,10 +41,15 @@ pub(crate) struct TasksModalDelegate { selected_index: usize, workspace: WeakView, prompt: String, + task_context: TaskContext, } impl TasksModalDelegate { - fn new(inventory: Model, workspace: WeakView) -> Self { + fn new( + inventory: Model, + task_context: TaskContext, + workspace: WeakView, + ) -> Self { Self { inventory, workspace, @@ -39,6 +57,7 @@ impl TasksModalDelegate { matches: Vec::new(), selected_index: 0, prompt: String::default(), + task_context, } } @@ -79,11 +98,16 @@ pub(crate) struct TasksModal { impl TasksModal { pub(crate) fn new( inventory: Model, + task_context: TaskContext, workspace: WeakView, cx: &mut ViewContext, ) -> Self { - let picker = cx - .new_view(|cx| Picker::uniform_list(TasksModalDelegate::new(inventory, workspace), cx)); + let picker = cx.new_view(|cx| { + Picker::uniform_list( + TasksModalDelegate::new(inventory, task_context, workspace), + cx, + ) + }); let _subscription = cx.subscribe(&picker, |_, _, _, cx| { cx.emit(DismissEvent); }); @@ -223,7 +247,7 @@ impl PickerDelegate for TasksModalDelegate { self.workspace .update(cx, |workspace, cx| { - schedule_task(workspace, task.as_ref(), cx); + schedule_task(workspace, task.as_ref(), self.task_context.clone(), cx); }) .ok(); cx.emit(DismissEvent); @@ -279,13 +303,12 @@ mod tests { use gpui::{TestAppContext, VisualTestContext}; use project::{FakeFs, Project}; use serde_json::json; - use workspace::AppState; use super::*; #[gpui::test] async fn test_spawn_tasks_modal_query_reuse(cx: &mut TestAppContext) { - init_test(cx); + crate::tests::init_test(cx); let fs = FakeFs::new(cx.executor()); fs.insert_tree( "/dir", @@ -431,16 +454,4 @@ mod tests { .collect::>() }) } - - fn init_test(cx: &mut TestAppContext) -> Arc { - cx.update(|cx| { - let state = AppState::test(cx); - language::init(cx); - crate::init(cx); - editor::init(cx); - workspace::init_settings(cx); - Project::init_settings(cx); - state - }) - } } From f53823c840cdb80c65e9c7fc5dd7a416d72d3ead Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 4 Mar 2024 16:08:47 -0700 Subject: [PATCH 081/121] Remove release channel from Zed URLs (#8863) Also adds a new command `cli: Register Zed Scheme` that will cause URLs to be opened in the current zed version, and we call this implicitly if you install the CLI Also add some status reporting to install cli Fixes: #8857 Release Notes: - Added success/error reporting to `cli: Install Cli` ([#8857](https://github.com/zed-industries/zed/issues/8857)). - Removed `zed-{preview,nightly,dev}:` url schemes (used by channel links) - Added `cli: Register Zed Scheme` to control which zed handles the `zed://` scheme (defaults to the most recently installed, or the version that you last used `cli: Install Cli` with) --- Cargo.lock | 1 - crates/channel/src/channel_store.rs | 21 ++++--- crates/client/src/client.rs | 45 +++++++------- crates/collab_ui/src/channel_view.rs | 2 +- crates/collab_ui/src/collab_panel.rs | 4 +- .../src/collab_panel/channel_modal.rs | 2 +- crates/command_palette/Cargo.toml | 1 - crates/command_palette/src/command_palette.rs | 6 +- crates/gpui/src/app.rs | 8 +++ crates/gpui/src/platform.rs | 2 + crates/gpui/src/platform/linux/platform.rs | 4 ++ crates/gpui/src/platform/mac/platform.rs | 43 +++++++++++++ crates/gpui/src/platform/test/platform.rs | 4 ++ crates/gpui/src/platform/windows/platform.rs | 4 ++ crates/install_cli/src/install_cli.rs | 12 ++-- crates/release_channel/src/lib.rs | 41 ------------ crates/zed/Cargo.toml | 6 +- crates/zed/src/main.rs | 16 ++--- crates/zed/src/open_listener.rs | 6 +- crates/zed/src/zed.rs | 62 ++++++++++++++++--- script/bundle | 1 + 21 files changed, 178 insertions(+), 113 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 47cf6cfed6..aa6eb75f2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2390,7 +2390,6 @@ dependencies = [ "picker", "postage", "project", - "release_channel", "serde", "serde_json", "settings", diff --git a/crates/channel/src/channel_store.rs b/crates/channel/src/channel_store.rs index dd7751c70a..0afd7e41b9 100644 --- a/crates/channel/src/channel_store.rs +++ b/crates/channel/src/channel_store.rs @@ -3,7 +3,7 @@ mod channel_index; use crate::{channel_buffer::ChannelBuffer, channel_chat::ChannelChat, ChannelMessage}; use anyhow::{anyhow, Result}; use channel_index::ChannelIndex; -use client::{ChannelId, Client, Subscription, User, UserId, UserStore}; +use client::{ChannelId, Client, ClientSettings, Subscription, User, UserId, UserStore}; use collections::{hash_map, HashMap, HashSet}; use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt}; use gpui::{ @@ -11,11 +11,11 @@ use gpui::{ Task, WeakModel, }; use language::Capability; -use release_channel::RELEASE_CHANNEL; use rpc::{ proto::{self, ChannelRole, ChannelVisibility}, TypedEnvelope, }; +use settings::Settings; use std::{mem, sync::Arc, time::Duration}; use util::{async_maybe, maybe, ResultExt}; @@ -93,16 +93,17 @@ pub struct ChannelState { } impl Channel { - pub fn link(&self) -> String { - RELEASE_CHANNEL.link_prefix().to_owned() - + "channel/" - + &Self::slug(&self.name) - + "-" - + &self.id.to_string() + pub fn link(&self, cx: &AppContext) -> String { + format!( + "{}/channel/{}-{}", + ClientSettings::get_global(cx).server_url, + Self::slug(&self.name), + self.id + ) } - pub fn notes_link(&self, heading: Option) -> String { - self.link() + pub fn notes_link(&self, heading: Option, cx: &AppContext) -> String { + self.link(cx) + "/notes" + &heading .map(|h| format!("#{}", Self::slug(&h))) diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 0f60f439d0..754a47baa4 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -1437,21 +1437,29 @@ async fn delete_credentials_from_keychain(cx: &AsyncAppContext) -> Result<()> { .await } -const WORKTREE_URL_PREFIX: &str = "zed://worktrees/"; +/// prefix for the zed:// url scheme +pub static ZED_URL_SCHEME: &str = "zed"; -pub fn encode_worktree_url(id: u64, access_token: &str) -> String { - format!("{}{}/{}", WORKTREE_URL_PREFIX, id, access_token) -} - -pub fn decode_worktree_url(url: &str) -> Option<(u64, String)> { - let path = url.trim().strip_prefix(WORKTREE_URL_PREFIX)?; - let mut parts = path.split('/'); - let id = parts.next()?.parse::().ok()?; - let access_token = parts.next()?; - if access_token.is_empty() { - return None; +/// Parses the given link into a Zed link. +/// +/// Returns a [`Some`] containing the unprefixed link if the link is a Zed link. +/// Returns [`None`] otherwise. +pub fn parse_zed_link<'a>(link: &'a str, cx: &AppContext) -> Option<&'a str> { + let server_url = &ClientSettings::get_global(cx).server_url; + if let Some(stripped) = link + .strip_prefix(server_url) + .and_then(|result| result.strip_prefix('/')) + { + return Some(stripped); } - Some((id, access_token.to_string())) + if let Some(stripped) = link + .strip_prefix(ZED_URL_SCHEME) + .and_then(|result| result.strip_prefix("://")) + { + return Some(stripped); + } + + None } #[cfg(test)] @@ -1629,17 +1637,6 @@ mod tests { assert_eq!(*dropped_auth_count.lock(), 1); } - #[test] - fn test_encode_and_decode_worktree_url() { - let url = encode_worktree_url(5, "deadbeef"); - assert_eq!(decode_worktree_url(&url), Some((5, "deadbeef".to_string()))); - assert_eq!( - decode_worktree_url(&format!("\n {}\t", url)), - Some((5, "deadbeef".to_string())) - ); - assert_eq!(decode_worktree_url("not://the-right-format"), None); - } - #[gpui::test] async fn test_subscribing_to_entity(cx: &mut TestAppContext) { init_test(cx); diff --git a/crates/collab_ui/src/channel_view.rs b/crates/collab_ui/src/channel_view.rs index 1cc48591c7..ac0793715f 100644 --- a/crates/collab_ui/src/channel_view.rs +++ b/crates/collab_ui/src/channel_view.rs @@ -265,7 +265,7 @@ impl ChannelView { return; }; - let link = channel.notes_link(closest_heading.map(|heading| heading.text)); + let link = channel.notes_link(closest_heading.map(|heading| heading.text), cx); cx.write_to_clipboard(ClipboardItem::new(link)); self.workspace .update(cx, |workspace, cx| { diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 1df8e7e73b..500c7affcf 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -2023,7 +2023,7 @@ impl CollabPanel { let Some(channel) = channel_store.channel_for_id(channel_id) else { return; }; - let item = ClipboardItem::new(channel.link()); + let item = ClipboardItem::new(channel.link(cx)); cx.write_to_clipboard(item) } @@ -2206,7 +2206,7 @@ impl CollabPanel { let channel = self.channel_store.read(cx).channel_for_id(channel_id)?; - channel_link = Some(channel.link()); + channel_link = Some(channel.link(cx)); (channel_icon, channel_tooltip_text) = match channel.visibility { proto::ChannelVisibility::Public => { (Some("icons/public.svg"), Some("Copy public channel link.")) diff --git a/crates/collab_ui/src/collab_panel/channel_modal.rs b/crates/collab_ui/src/collab_panel/channel_modal.rs index 125e8c64f3..4f9b18198b 100644 --- a/crates/collab_ui/src/collab_panel/channel_modal.rs +++ b/crates/collab_ui/src/collab_panel/channel_modal.rs @@ -197,7 +197,7 @@ impl Render for ChannelModal { .read(cx) .channel_for_id(channel_id) { - let item = ClipboardItem::new(channel.link()); + let item = ClipboardItem::new(channel.link(cx)); cx.write_to_clipboard(item); } })), diff --git a/crates/command_palette/Cargo.toml b/crates/command_palette/Cargo.toml index 4d268545e8..565939e4a3 100644 --- a/crates/command_palette/Cargo.toml +++ b/crates/command_palette/Cargo.toml @@ -18,7 +18,6 @@ gpui.workspace = true picker.workspace = true postage.workspace = true project.workspace = true -release_channel.workspace = true serde.workspace = true settings.workspace = true theme.workspace = true diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 197ad90586..0cd7f581de 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -4,7 +4,7 @@ use std::{ time::Duration, }; -use client::telemetry::Telemetry; +use client::{parse_zed_link, telemetry::Telemetry}; use collections::HashMap; use command_palette_hooks::{ CommandInterceptResult, CommandPaletteFilter, CommandPaletteInterceptor, @@ -17,7 +17,6 @@ use gpui::{ use picker::{Picker, PickerDelegate}; use postage::{sink::Sink, stream::Stream}; -use release_channel::parse_zed_link; use ui::{h_flex, prelude::*, v_flex, HighlightedLabel, KeyBinding, ListItem, ListItemSpacing}; use util::ResultExt; use workspace::{ModalView, Workspace}; @@ -26,6 +25,7 @@ use zed_actions::OpenZedUrl; actions!(command_palette, [Toggle]); pub fn init(cx: &mut AppContext) { + client::init_settings(cx); cx.set_global(HitCounts::default()); cx.set_global(CommandPaletteFilter::default()); cx.observe_new_views(CommandPalette::register).detach(); @@ -192,7 +192,7 @@ impl CommandPaletteDelegate { None }; - if parse_zed_link(&query).is_some() { + if parse_zed_link(&query, cx).is_some() { intercept_result = Some(CommandInterceptResult { action: OpenZedUrl { url: query.clone() }.boxed_clone(), string: query.clone(), diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index f30e5264f1..9373ad66e8 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -566,6 +566,14 @@ impl AppContext { self.platform.open_url(url); } + /// register_url_scheme requests that the given scheme (e.g. `zed` for `zed://` urls) + /// is opened by the current app. + /// On some platforms (e.g. macOS) you may be able to register URL schemes as part of app + /// distribution, but this method exists to let you register schemes at runtime. + pub fn register_url_scheme(&self, scheme: &str) -> Task> { + self.platform.register_url_scheme(scheme) + } + /// Returns the full pathname of the current app bundle. /// If the app is not being run from a bundle, returns an error. pub fn app_path(&self) -> Result { diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 8222b88fe1..1e1b66fffe 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -101,6 +101,8 @@ pub(crate) trait Platform: 'static { fn open_url(&self, url: &str); fn on_open_urls(&self, callback: Box)>); + fn register_url_scheme(&self, url: &str) -> Task>; + fn prompt_for_paths( &self, options: PathPromptOptions, diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 414c1507dc..2a7b8bfb2e 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -441,6 +441,10 @@ impl Platform for LinuxPlatform { fn window_appearance(&self) -> crate::WindowAppearance { crate::WindowAppearance::Light } + + fn register_url_scheme(&self, _: &str) -> Task> { + Task::ready(Err(anyhow!("register_url_scheme unimplemented"))) + } } #[cfg(test)] diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 0a60fd5172..d293f4cf40 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -525,6 +525,49 @@ impl Platform for MacPlatform { } } + fn register_url_scheme(&self, scheme: &str) -> Task> { + // API only available post Monterey + // https://developer.apple.com/documentation/appkit/nsworkspace/3753004-setdefaultapplicationaturl + let (done_tx, done_rx) = oneshot::channel(); + if self.os_version().ok() < Some(SemanticVersion::new(12, 0, 0)) { + return Task::ready(Err(anyhow!( + "macOS 12.0 or later is required to register URL schemes" + ))); + } + + let bundle_id = unsafe { + let bundle: id = msg_send![class!(NSBundle), mainBundle]; + let bundle_id: id = msg_send![bundle, bundleIdentifier]; + if bundle_id == nil { + return Task::ready(Err(anyhow!("Can only register URL scheme in bundled apps"))); + } + bundle_id + }; + + unsafe { + let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace]; + let scheme: id = ns_string(scheme); + let app: id = msg_send![workspace, URLForApplicationWithBundleIdentifier: bundle_id]; + let done_tx = Cell::new(Some(done_tx)); + let block = ConcreteBlock::new(move |error: id| { + let result = if error == nil { + Ok(()) + } else { + let msg: id = msg_send![error, localizedDescription]; + Err(anyhow!("Failed to register: {:?}", msg)) + }; + + if let Some(done_tx) = done_tx.take() { + let _ = done_tx.send(result); + } + }); + let _: () = msg_send![workspace, setDefaultApplicationAtURL: app toOpenURLsWithScheme: scheme completionHandler: block]; + } + + self.background_executor() + .spawn(async { crate::Flatten::flatten(done_rx.await.map_err(|e| anyhow!(e))) }) + } + fn on_open_urls(&self, callback: Box)>) { self.0.lock().open_urls = Some(callback); } diff --git a/crates/gpui/src/platform/test/platform.rs b/crates/gpui/src/platform/test/platform.rs index d97a4fc5ab..5df416f9ab 100644 --- a/crates/gpui/src/platform/test/platform.rs +++ b/crates/gpui/src/platform/test/platform.rs @@ -298,4 +298,8 @@ impl Platform for TestPlatform { fn double_click_interval(&self) -> std::time::Duration { Duration::from_millis(500) } + + fn register_url_scheme(&self, _: &str) -> Task> { + unimplemented!() + } } diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index e7751579c9..be3d789813 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -314,4 +314,8 @@ impl Platform for WindowsPlatform { fn delete_credentials(&self, url: &str) -> Task> { Task::Ready(Some(Err(anyhow!("not implemented yet.")))) } + + fn register_url_scheme(&self, _: &str) -> Task> { + Task::ready(Err(anyhow!("register_url_scheme unimplemented"))) + } } diff --git a/crates/install_cli/src/install_cli.rs b/crates/install_cli/src/install_cli.rs index 61c5aa2fb9..506de309ef 100644 --- a/crates/install_cli/src/install_cli.rs +++ b/crates/install_cli/src/install_cli.rs @@ -1,18 +1,18 @@ use anyhow::{anyhow, Result}; use gpui::{actions, AsyncAppContext}; -use std::path::Path; +use std::path::{Path, PathBuf}; use util::ResultExt; -actions!(cli, [Install]); +actions!(cli, [Install, RegisterZedScheme]); -pub async fn install_cli(cx: &AsyncAppContext) -> Result<()> { +pub async fn install_cli(cx: &AsyncAppContext) -> Result { let cli_path = cx.update(|cx| cx.path_for_auxiliary_executable("cli"))??; let link_path = Path::new("/usr/local/bin/zed"); let bin_dir_path = link_path.parent().unwrap(); // Don't re-create symlink if it points to the same CLI binary. if smol::fs::read_link(link_path).await.ok().as_ref() == Some(&cli_path) { - return Ok(()); + return Ok(link_path.into()); } // If the symlink is not there or is outdated, first try replacing it @@ -26,7 +26,7 @@ pub async fn install_cli(cx: &AsyncAppContext) -> Result<()> { .log_err() .is_some() { - return Ok(()); + return Ok(link_path.into()); } } @@ -51,7 +51,7 @@ pub async fn install_cli(cx: &AsyncAppContext) -> Result<()> { .await? .status; if status.success() { - Ok(()) + Ok(link_path.into()) } else { Err(anyhow!("error running osascript")) } diff --git a/crates/release_channel/src/lib.rs b/crates/release_channel/src/lib.rs index 78b17ba997..864df387c0 100644 --- a/crates/release_channel/src/lib.rs +++ b/crates/release_channel/src/lib.rs @@ -139,26 +139,6 @@ impl ReleaseChannel { } } - /// Returns the URL scheme for this [`ReleaseChannel`]. - pub fn url_scheme(&self) -> &'static str { - match self { - ReleaseChannel::Dev => "zed-dev://", - ReleaseChannel::Nightly => "zed-nightly://", - ReleaseChannel::Preview => "zed-preview://", - ReleaseChannel::Stable => "zed://", - } - } - - /// Returns the link prefix for this [`ReleaseChannel`]. - pub fn link_prefix(&self) -> &'static str { - match self { - ReleaseChannel::Dev => "https://zed.dev/dev/", - ReleaseChannel::Nightly => "https://zed.dev/nightly/", - ReleaseChannel::Preview => "https://zed.dev/preview/", - ReleaseChannel::Stable => "https://zed.dev/", - } - } - /// Returns the query parameter for this [`ReleaseChannel`]. pub fn release_query_param(&self) -> Option<&'static str> { match self { @@ -169,24 +149,3 @@ impl ReleaseChannel { } } } - -/// Parses the given link into a Zed link. -/// -/// Returns a [`Some`] containing the unprefixed link if the link is a Zed link. -/// Returns [`None`] otherwise. -pub fn parse_zed_link(link: &str) -> Option<&str> { - for release in [ - ReleaseChannel::Dev, - ReleaseChannel::Nightly, - ReleaseChannel::Preview, - ReleaseChannel::Stable, - ] { - if let Some(stripped) = link.strip_prefix(release.link_prefix()) { - return Some(stripped); - } - if let Some(stripped) = link.strip_prefix(release.url_scheme()) { - return Some(stripped); - } - } - None -} diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 4ad8549e68..2ad94a8bae 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -107,7 +107,7 @@ identifier = "dev.zed.Zed-Dev" name = "Zed Dev" osx_minimum_system_version = "10.15.7" osx_info_plist_exts = ["resources/info/*"] -osx_url_schemes = ["zed-dev"] +osx_url_schemes = ["zed"] [package.metadata.bundle-nightly] icon = ["resources/app-icon-nightly@2x.png", "resources/app-icon-nightly.png"] @@ -115,7 +115,7 @@ identifier = "dev.zed.Zed-Nightly" name = "Zed Nightly" osx_minimum_system_version = "10.15.7" osx_info_plist_exts = ["resources/info/*"] -osx_url_schemes = ["zed-nightly"] +osx_url_schemes = ["zed"] [package.metadata.bundle-preview] icon = ["resources/app-icon-preview@2x.png", "resources/app-icon-preview.png"] @@ -123,7 +123,7 @@ identifier = "dev.zed.Zed-Preview" name = "Zed Preview" osx_minimum_system_version = "10.15.7" osx_info_plist_exts = ["resources/info/*"] -osx_url_schemes = ["zed-preview"] +osx_url_schemes = ["zed"] [package.metadata.bundle-stable] icon = ["resources/app-icon@2x.png", "resources/app-icon.png"] diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index c7356216a1..277d4dda03 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -5,7 +5,7 @@ use anyhow::{anyhow, Context as _, Result}; use backtrace::Backtrace; use chrono::Utc; use cli::FORCE_CLI_MODE_ENV_VAR_NAME; -use client::{Client, UserStore}; +use client::{parse_zed_link, Client, UserStore}; use collab_ui::channel_view::ChannelView; use db::kvp::KEY_VALUE_STORE; use editor::Editor; @@ -23,7 +23,7 @@ use assets::Assets; use mimalloc::MiMalloc; use node_runtime::RealNodeRuntime; use parking_lot::Mutex; -use release_channel::{parse_zed_link, AppCommitSha, ReleaseChannel, RELEASE_CHANNEL}; +use release_channel::{AppCommitSha, ReleaseChannel, RELEASE_CHANNEL}; use serde::{Deserialize, Serialize}; use settings::{ default_settings, handle_settings_file_changes, watch_config_file, Settings, SettingsStore, @@ -106,7 +106,7 @@ fn main() { let (listener, mut open_rx) = OpenListener::new(); let listener = Arc::new(listener); let open_listener = listener.clone(); - app.on_open_urls(move |urls, _| open_listener.open_urls(&urls)); + app.on_open_urls(move |urls, cx| open_listener.open_urls(&urls, cx)); app.on_reopen(move |cx| { if let Some(app_state) = AppState::try_global(cx).and_then(|app_state| app_state.upgrade()) { @@ -271,9 +271,9 @@ fn main() { #[cfg(not(target_os = "linux"))] upload_panics_and_crashes(http.clone(), cx); cx.activate(true); - let urls = collect_url_args(); + let urls = collect_url_args(cx); if !urls.is_empty() { - listener.open_urls(&urls) + listener.open_urls(&urls, cx) } } else { upload_panics_and_crashes(http.clone(), cx); @@ -282,7 +282,7 @@ fn main() { if std::env::var(FORCE_CLI_MODE_ENV_VAR_NAME).ok().is_some() && !listener.triggered.load(Ordering::Acquire) { - listener.open_urls(&collect_url_args()) + listener.open_urls(&collect_url_args(cx), cx) } } @@ -921,13 +921,13 @@ fn stdout_is_a_pty() -> bool { std::env::var(FORCE_CLI_MODE_ENV_VAR_NAME).ok().is_none() && std::io::stdout().is_terminal() } -fn collect_url_args() -> Vec { +fn collect_url_args(cx: &AppContext) -> Vec { env::args() .skip(1) .filter_map(|arg| match std::fs::canonicalize(Path::new(&arg)) { Ok(path) => Some(format!("file://{}", path.to_string_lossy())), Err(error) => { - if let Some(_) = parse_zed_link(&arg) { + if let Some(_) = parse_zed_link(&arg, cx) { Some(arg) } else { log::error!("error parsing path argument: {}", error); diff --git a/crates/zed/src/open_listener.rs b/crates/zed/src/open_listener.rs index 4d3630a846..41dfe7e432 100644 --- a/crates/zed/src/open_listener.rs +++ b/crates/zed/src/open_listener.rs @@ -1,6 +1,7 @@ use anyhow::{anyhow, Context, Result}; use cli::{ipc, IpcHandshake}; use cli::{ipc::IpcSender, CliRequest, CliResponse}; +use client::parse_zed_link; use collections::HashMap; use editor::scroll::Autoscroll; use editor::Editor; @@ -10,7 +11,6 @@ use futures::{FutureExt, SinkExt, StreamExt}; use gpui::{AppContext, AsyncAppContext, Global}; use itertools::Itertools; use language::{Bias, Point}; -use release_channel::parse_zed_link; use std::path::Path; use std::sync::atomic::Ordering; use std::sync::Arc; @@ -66,13 +66,13 @@ impl OpenListener { ) } - pub fn open_urls(&self, urls: &[String]) { + pub fn open_urls(&self, urls: &[String], cx: &AppContext) { self.triggered.store(true, Ordering::Release); let request = if let Some(server_name) = urls.first().and_then(|url| url.strip_prefix("zed-cli://")) { self.handle_cli_connection(server_name) - } else if let Some(request_path) = urls.first().and_then(|url| parse_zed_link(url)) { + } else if let Some(request_path) = urls.first().and_then(|url| parse_zed_link(url, cx)) { self.handle_zed_url_scheme(request_path) } else { self.handle_file_urls(urls) diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 7c47a6b6b5..ca28267472 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -5,11 +5,12 @@ mod open_listener; pub use app_menus::*; use assistant::AssistantPanel; use breadcrumbs::Breadcrumbs; +use client::ZED_URL_SCHEME; use collections::VecDeque; use editor::{Editor, MultiBuffer}; use gpui::{ - actions, point, px, AppContext, Context, FocusableView, PromptLevel, TitlebarOptions, View, - ViewContext, VisualContext, WindowBounds, WindowKind, WindowOptions, + actions, point, px, AppContext, AsyncAppContext, Context, FocusableView, PromptLevel, + TitlebarOptions, View, ViewContext, VisualContext, WindowBounds, WindowKind, WindowOptions, }; pub use only_instance::*; pub use open_listener::*; @@ -38,11 +39,11 @@ use util::{ use uuid::Uuid; use vim::VimModeSetting; use welcome::BaseKeymap; -use workspace::Pane; use workspace::{ create_and_open_local_file, notifications::simple_message_notification::MessageNotification, - open_new, AppState, NewFile, NewWindow, Workspace, WorkspaceSettings, + open_new, AppState, NewFile, NewWindow, Toast, Workspace, WorkspaceSettings, }; +use workspace::{notifications::DetachAndPromptErr, Pane}; use zed_actions::{OpenBrowser, OpenSettings, OpenZedUrl, Quit}; actions!( @@ -232,7 +233,7 @@ pub fn initialize_workspace(app_state: Arc, cx: &mut AppContext) { cx.toggle_full_screen(); }) .register_action(|_, action: &OpenZedUrl, cx| { - OpenListener::global(cx).open_urls(&[action.url.clone()]) + OpenListener::global(cx).open_urls(&[action.url.clone()], cx) }) .register_action(|_, action: &OpenBrowser, cx| cx.open_url(&action.url)) .register_action(move |_, _: &IncreaseBufferFontSize, cx| { @@ -243,12 +244,50 @@ pub fn initialize_workspace(app_state: Arc, cx: &mut AppContext) { }) .register_action(move |_, _: &ResetBufferFontSize, cx| theme::reset_font_size(cx)) .register_action(|_, _: &install_cli::Install, cx| { - cx.spawn(|_, cx| async move { - install_cli::install_cli(cx.deref()) + cx.spawn(|workspace, mut cx| async move { + let path = install_cli::install_cli(cx.deref()) .await - .context("error creating CLI symlink") + .context("error creating CLI symlink")?; + workspace.update(&mut cx, |workspace, cx| { + workspace.show_toast( + Toast::new( + 0, + format!( + "Installed `zed` to {}. You can launch {} from your terminal.", + path.to_string_lossy(), + ReleaseChannel::global(cx).display_name() + ), + ), + cx, + ) + })?; + register_zed_scheme(&cx).await.log_err(); + Ok(()) }) - .detach_and_log_err(cx); + .detach_and_prompt_err("Error installing zed cli", cx, |_, _| None); + }) + .register_action(|_, _: &install_cli::RegisterZedScheme, cx| { + cx.spawn(|workspace, mut cx| async move { + register_zed_scheme(&cx).await?; + workspace.update(&mut cx, |workspace, cx| { + workspace.show_toast( + Toast::new( + 0, + format!( + "zed:// links will now open in {}.", + ReleaseChannel::global(cx).display_name() + ), + ), + cx, + ) + })?; + Ok(()) + }) + .detach_and_prompt_err( + "Error registering zed:// scheme", + cx, + |_, _| None, + ); }) .register_action(|workspace, _: &OpenLog, cx| { open_log_file(workspace, cx); @@ -2881,3 +2920,8 @@ mod tests { } } } + +async fn register_zed_scheme(cx: &AsyncAppContext) -> anyhow::Result<()> { + cx.update(|cx| cx.register_url_scheme(ZED_URL_SCHEME))? + .await +} diff --git a/script/bundle b/script/bundle index a6414febcb..d90ed2a014 100755 --- a/script/bundle +++ b/script/bundle @@ -173,6 +173,7 @@ if [ "$local_arch" = false ]; then cp -R target/${local_target_triple}/${target_dir}/WebRTC.framework "${app_path}/Contents/Frameworks/" else cp -R target/${target_dir}/WebRTC.framework "${app_path}/Contents/Frameworks/" + cp -R target/${target_dir}/cli "${app_path}/Contents/MacOS/" fi # Note: The app identifier for our development builds is the same as the app identifier for nightly. From b742db65fe892961db83148c8ee0474d1f2fe849 Mon Sep 17 00:00:00 2001 From: snorkypie Date: Tue, 5 Mar 2024 01:34:42 +0200 Subject: [PATCH 082/121] vim: Support keybinding `z.` (#8702) `z.` is similar to zz but moves the cursor to the first non-blank character. From the documentation: ``` z. Redraw, line [count] at center of window (default cursor line). Put cursor at first non-blank in the line. zz Like "z.", but leave the cursor in the same column. ``` Release Notes: - Support the `z.` vim keybinding: Center cursor in window and put cursor at first non-blank --- assets/keymaps/vim.json | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index bfaa444a68..3bfc718ffa 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -218,6 +218,7 @@ // z commands "z t": "editor::ScrollCursorTop", "z z": "editor::ScrollCursorCenter", + "z .": ["workspace::SendKeystrokes", "z z ^"], "z b": "editor::ScrollCursorBottom", "z c": "editor::Fold", "z o": "editor::UnfoldLines", From d223fe446d83a8070722911f0da483b333a804a1 Mon Sep 17 00:00:00 2001 From: Noritada Kobayashi Date: Tue, 5 Mar 2024 08:39:02 +0900 Subject: [PATCH 083/121] vim: Add support for `ap` and `ip` paragraph text objects (#7687) This PR adds support for `ap`/`ip` text objects in Vim mode and allows users to perform paragraph-based operations. Cases where compatibility with Neovim's behavior is checked, cases where there are known differences in behavior with Neovim (cases where the landing position is other than the beginning of the line), and cases where the Neovim behavior in the test suite seems strange are separated in the test code so that they can be identified. Release Notes: - Added support for `ap` and `ip` paragraph text objects in Vim mode ([#7359](https://github.com/zed-industries/zed/issues/7359)). --- assets/keymaps/vim.json | 1 + crates/vim/src/normal/delete.rs | 48 +- crates/vim/src/object.rs | 266 ++++++++++- crates/vim/src/visual.rs | 21 +- .../test_change_paragraph_object.json | 430 ++++++++++++++++++ .../test_delete_paragraph_object.json | 430 ++++++++++++++++++ ...ng_positions_not_at_beginning_of_line.json | 0 .../test_visual_paragraph_object.json | 80 ++++ 8 files changed, 1265 insertions(+), 11 deletions(-) create mode 100644 crates/vim/test_data/test_change_paragraph_object.json create mode 100644 crates/vim/test_data/test_delete_paragraph_object.json create mode 100644 crates/vim/test_data/test_paragraph_object_with_landing_positions_not_at_beginning_of_line.json create mode 100644 crates/vim/test_data/test_visual_paragraph_object.json diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index 3bfc718ffa..c68f209917 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -393,6 +393,7 @@ ], "t": "vim::Tag", "s": "vim::Sentence", + "p": "vim::Paragraph", "'": "vim::Quotes", "`": "vim::BackQuotes", "\"": "vim::DoubleQuotes", diff --git a/crates/vim/src/normal/delete.rs b/crates/vim/src/normal/delete.rs index 80e87ccaf9..b81c0b16a5 100644 --- a/crates/vim/src/normal/delete.rs +++ b/crates/vim/src/normal/delete.rs @@ -1,8 +1,12 @@ use crate::{motion::Motion, object::Object, utils::copy_selections_content, Vim}; use collections::{HashMap, HashSet}; -use editor::{display_map::ToDisplayPoint, scroll::Autoscroll, Bias}; +use editor::{ + display_map::{DisplaySnapshot, ToDisplayPoint}, + scroll::Autoscroll, + Bias, DisplayPoint, +}; use gpui::WindowContext; -use language::Point; +use language::{Point, Selection}; pub fn delete_motion(vim: &mut Vim, motion: Motion, times: Option, cx: &mut WindowContext) { vim.stop_recording(); @@ -72,6 +76,14 @@ pub fn delete_object(vim: &mut Vim, object: Object, around: bool, cx: &mut Windo s.move_with(|map, selection| { object.expand_selection(map, selection, around); let offset_range = selection.map(|p| p.to_offset(map, Bias::Left)).range(); + let mut move_selection_start_to_previous_line = + |map: &DisplaySnapshot, selection: &mut Selection| { + let start = selection.start.to_offset(map, Bias::Left); + if selection.start.row() > 0 { + should_move_to_start.insert(selection.id); + selection.start = (start - '\n'.len_utf8()).to_display_point(map); + } + }; let contains_only_newlines = map .chars_at(selection.start) .take_while(|(_, p)| p < &selection.end) @@ -88,12 +100,23 @@ pub fn delete_object(vim: &mut Vim, object: Object, around: bool, cx: &mut Windo // at the end or start if (around || object == Object::Sentence) && contains_only_newlines { if end_at_newline { - selection.end = - (offset_range.end + '\n'.len_utf8()).to_display_point(map); - } else if selection.start.row() > 0 { - should_move_to_start.insert(selection.id); - selection.start = - (offset_range.start - '\n'.len_utf8()).to_display_point(map); + move_selection_end_to_next_line(map, selection); + } else { + move_selection_start_to_previous_line(map, selection); + } + } + + // Does post-processing for the trailing newline and EOF + // when not cancelled. + let cancelled = around && selection.start == selection.end; + if object == Object::Paragraph && !cancelled { + // EOF check should be done before including a trailing newline. + if ends_at_eof(map, selection) { + move_selection_start_to_previous_line(map, selection); + } + + if end_at_newline { + move_selection_end_to_next_line(map, selection); } } }); @@ -117,6 +140,15 @@ pub fn delete_object(vim: &mut Vim, object: Object, around: bool, cx: &mut Windo }); } +fn move_selection_end_to_next_line(map: &DisplaySnapshot, selection: &mut Selection) { + let end = selection.end.to_offset(map, Bias::Left); + selection.end = (end + '\n'.len_utf8()).to_display_point(map); +} + +fn ends_at_eof(map: &DisplaySnapshot, selection: &mut Selection) -> bool { + selection.end.to_point(map) == map.buffer_snapshot.max_point() +} + #[cfg(test)] mod test { use indoc::indoc; diff --git a/crates/vim/src/object.rs b/crates/vim/src/object.rs index 6abe66c1fd..56b9499594 100644 --- a/crates/vim/src/object.rs +++ b/crates/vim/src/object.rs @@ -10,7 +10,7 @@ use editor::{ Bias, DisplayPoint, }; use gpui::{actions, impl_actions, ViewContext, WindowContext}; -use language::{char_kind, BufferSnapshot, CharKind, Selection}; +use language::{char_kind, BufferSnapshot, CharKind, Point, Selection}; use serde::Deserialize; use workspace::Workspace; @@ -18,6 +18,7 @@ use workspace::Workspace; pub enum Object { Word { ignore_punctuation: bool }, Sentence, + Paragraph, Quotes, BackQuotes, DoubleQuotes, @@ -43,6 +44,7 @@ actions!( vim, [ Sentence, + Paragraph, Quotes, BackQuotes, DoubleQuotes, @@ -65,6 +67,8 @@ pub fn register(workspace: &mut Workspace, _: &mut ViewContext) { workspace.register_action(|_: &mut Workspace, _: &Tag, cx: _| object(Object::Tag, cx)); workspace .register_action(|_: &mut Workspace, _: &Sentence, cx: _| object(Object::Sentence, cx)); + workspace + .register_action(|_: &mut Workspace, _: &Paragraph, cx: _| object(Object::Paragraph, cx)); workspace.register_action(|_: &mut Workspace, _: &Quotes, cx: _| object(Object::Quotes, cx)); workspace .register_action(|_: &mut Workspace, _: &BackQuotes, cx: _| object(Object::BackQuotes, cx)); @@ -109,6 +113,7 @@ impl Object { | Object::VerticalBars | Object::DoubleQuotes => false, Object::Sentence + | Object::Paragraph | Object::Parentheses | Object::Tag | Object::AngleBrackets @@ -120,7 +125,7 @@ impl Object { pub fn always_expands_both_ways(self) -> bool { match self { - Object::Word { .. } | Object::Sentence | Object::Argument => false, + Object::Word { .. } | Object::Sentence | Object::Paragraph | Object::Argument => false, Object::Quotes | Object::BackQuotes | Object::DoubleQuotes @@ -153,6 +158,7 @@ impl Object { | Object::VerticalBars | Object::Tag | Object::Argument => Mode::Visual, + Object::Paragraph => Mode::VisualLine, } } @@ -171,6 +177,7 @@ impl Object { } } Object::Sentence => sentence(map, relative_to, around), + Object::Paragraph => paragraph(map, relative_to, around), Object::Quotes => { surrounding_markers(map, relative_to, around, self.is_multiline(), '\'', '\'') } @@ -684,6 +691,99 @@ fn expand_to_include_whitespace( range } +/// If not `around` (i.e. inner), returns a range that surrounds the paragraph +/// where `relative_to` is in. If `around`, principally returns the range ending +/// at the end of the next paragraph. +/// +/// Here, the "paragraph" is defined as a block of non-blank lines or a block of +/// blank lines. If the paragraph ends with a trailing newline (i.e. not with +/// EOF), the returned range ends at the trailing newline of the paragraph (i.e. +/// the trailing newline is not subject to subsequent operations). +/// +/// Edge cases: +/// - If `around` and if the current paragraph is the last paragraph of the +/// file and is blank, then the selection results in an error. +/// - If `around` and if the current paragraph is the last paragraph of the +/// file and is not blank, then the returned range starts at the start of the +/// previous paragraph, if it exists. +fn paragraph( + map: &DisplaySnapshot, + relative_to: DisplayPoint, + around: bool, +) -> Option> { + let mut paragraph_start = start_of_paragraph(map, relative_to); + let mut paragraph_end = end_of_paragraph(map, relative_to); + + let paragraph_end_row = paragraph_end.row(); + let paragraph_ends_with_eof = paragraph_end_row == map.max_point().row(); + let point = relative_to.to_point(map); + let current_line_is_empty = map.buffer_snapshot.is_line_blank(point.row); + + if around { + if paragraph_ends_with_eof { + if current_line_is_empty { + return None; + } + + let paragraph_start_row = paragraph_start.row(); + if paragraph_start_row != 0 { + let previous_paragraph_last_line_start = + Point::new(paragraph_start_row - 1, 0).to_display_point(map); + paragraph_start = start_of_paragraph(map, previous_paragraph_last_line_start); + } + } else { + let next_paragraph_start = Point::new(paragraph_end_row + 1, 0).to_display_point(map); + paragraph_end = end_of_paragraph(map, next_paragraph_start); + } + } + + let range = paragraph_start..paragraph_end; + Some(range) +} + +/// Returns a position of the start of the current paragraph, where a paragraph +/// is defined as a run of non-blank lines or a run of blank lines. +pub fn start_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint { + let point = display_point.to_point(map); + if point.row == 0 { + return DisplayPoint::zero(); + } + + let is_current_line_blank = map.buffer_snapshot.is_line_blank(point.row); + + for row in (0..point.row).rev() { + let blank = map.buffer_snapshot.is_line_blank(row); + if blank != is_current_line_blank { + return Point::new(row + 1, 0).to_display_point(map); + } + } + + DisplayPoint::zero() +} + +/// Returns a position of the end of the current paragraph, where a paragraph +/// is defined as a run of non-blank lines or a run of blank lines. +/// The trailing newline is excluded from the paragraph. +pub fn end_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint { + let point = display_point.to_point(map); + if point.row == map.max_buffer_row() { + return map.max_point(); + } + + let is_current_line_blank = map.buffer_snapshot.is_line_blank(point.row); + + for row in point.row + 1..map.max_buffer_row() + 1 { + let blank = map.buffer_snapshot.is_line_blank(row); + if blank != is_current_line_blank { + let previous_row = row - 1; + return Point::new(previous_row, map.buffer_snapshot.line_len(previous_row)) + .to_display_point(map); + } + } + + map.max_point() +} + fn surrounding_markers( map: &DisplaySnapshot, relative_to: DisplayPoint, @@ -1047,6 +1147,168 @@ mod test { } } + const PARAGRAPH_EXAMPLES: &[&'static str] = &[ + // Single line + "ˇThe quick brown fox jumpˇs over the lazy dogˇ.ˇ", + // Multiple lines without empty lines + indoc! {" + ˇThe quick brownˇ + ˇfox jumps overˇ + the lazy dog.ˇ + "}, + // Heading blank paragraph and trailing normal paragraph + indoc! {" + ˇ + ˇ + ˇThe quick brown fox jumps + ˇover the lazy dog. + ˇ + ˇ + ˇThe quick brown fox jumpsˇ + ˇover the lazy dog.ˇ + "}, + // Inserted blank paragraph and trailing blank paragraph + indoc! {" + ˇThe quick brown fox jumps + ˇover the lazy dog. + ˇ + ˇ + ˇ + ˇThe quick brown fox jumpsˇ + ˇover the lazy dog.ˇ + ˇ + ˇ + ˇ + "}, + // "Blank" paragraph with whitespace characters + indoc! {" + ˇThe quick brown fox jumps + over the lazy dog. + + ˇ \t + + ˇThe quick brown fox jumps + over the lazy dog.ˇ + ˇ + ˇ \t + \t \t + "}, + // Single line "paragraphs", where selection size might be zero. + indoc! {" + ˇThe quick brown fox jumps over the lazy dog. + ˇ + ˇThe quick brown fox jumpˇs over the lazy dog.ˇ + ˇ + "}, + ]; + + #[gpui::test] + async fn test_change_paragraph_object(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + for paragraph_example in PARAGRAPH_EXAMPLES { + cx.assert_binding_matches_all(["c", "i", "p"], paragraph_example) + .await; + cx.assert_binding_matches_all(["c", "a", "p"], paragraph_example) + .await; + } + } + + #[gpui::test] + async fn test_delete_paragraph_object(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + for paragraph_example in PARAGRAPH_EXAMPLES { + cx.assert_binding_matches_all(["d", "i", "p"], paragraph_example) + .await; + cx.assert_binding_matches_all(["d", "a", "p"], paragraph_example) + .await; + } + } + + #[gpui::test] + async fn test_paragraph_object_with_landing_positions_not_at_beginning_of_line( + cx: &mut gpui::TestAppContext, + ) { + // Landing position not at the beginning of the line + const PARAGRAPH_LANDING_POSITION_EXAMPLE: &'static str = indoc! {" + The quick brown fox jumpsˇ + over the lazy dog.ˇ + ˇ ˇ\tˇ + ˇ ˇ + ˇ\tˇ ˇ\tˇ + ˇThe quick brown fox jumpsˇ + ˇover the lazy dog.ˇ + ˇ ˇ\tˇ + ˇ + ˇ ˇ\tˇ + ˇ\tˇ ˇ\tˇ + "}; + + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.assert_binding_matches_all_exempted( + ["c", "i", "p"], + PARAGRAPH_LANDING_POSITION_EXAMPLE, + ExemptionFeatures::IncorrectLandingPosition, + ) + .await; + cx.assert_binding_matches_all_exempted( + ["c", "a", "p"], + PARAGRAPH_LANDING_POSITION_EXAMPLE, + ExemptionFeatures::IncorrectLandingPosition, + ) + .await; + cx.assert_binding_matches_all_exempted( + ["d", "i", "p"], + PARAGRAPH_LANDING_POSITION_EXAMPLE, + ExemptionFeatures::IncorrectLandingPosition, + ) + .await; + cx.assert_binding_matches_all_exempted( + ["d", "a", "p"], + PARAGRAPH_LANDING_POSITION_EXAMPLE, + ExemptionFeatures::IncorrectLandingPosition, + ) + .await; + } + + #[gpui::test] + async fn test_visual_paragraph_object(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + const EXAMPLES: &[&'static str] = &[ + indoc! {" + ˇThe quick brown + fox jumps over + the lazy dog. + "}, + indoc! {" + ˇ + + ˇThe quick brown fox jumps + over the lazy dog. + ˇ + + ˇThe quick brown fox jumps + over the lazy dog. + "}, + indoc! {" + ˇThe quick brown fox jumps over the lazy dog. + ˇ + ˇThe quick brown fox jumps over the lazy dog. + + "}, + ]; + + for paragraph_example in EXAMPLES { + cx.assert_binding_matches_all(["v", "i", "p"], paragraph_example) + .await; + cx.assert_binding_matches_all(["v", "a", "p"], paragraph_example) + .await; + } + } + // Test string with "`" for opening surrounders and "'" for closing surrounders const SURROUNDING_MARKER_STRING: &str = indoc! {" ˇTh'ˇe ˇ`ˇ'ˇquˇi`ˇck broˇ'wn` diff --git a/crates/vim/src/visual.rs b/crates/vim/src/visual.rs index 3f68ddce75..c316340058 100644 --- a/crates/vim/src/visual.rs +++ b/crates/vim/src/visual.rs @@ -9,7 +9,7 @@ use editor::{ Bias, DisplayPoint, Editor, }; use gpui::{actions, ViewContext, WindowContext}; -use language::{Selection, SelectionGoal}; +use language::{Point, Selection, SelectionGoal}; use workspace::Workspace; use crate::{ @@ -279,6 +279,25 @@ pub fn visual_object(object: Object, cx: &mut WindowContext) { selection.end = range.end; } } + + // In the visual selection result of a paragraph object, the cursor is + // placed at the start of the last line. And in the visual mode, the + // selection end is located after the end character. So, adjustment of + // selection end is needed. + // + // We don't do this adjustment for a one-line blank paragraph since the + // trailing newline is included in its selection from the beginning. + if object == Object::Paragraph && range.start != range.end { + let row_of_selection_end_line = selection.end.to_point(map).row; + let new_selection_end = + if map.buffer_snapshot.line_len(row_of_selection_end_line) == 0 + { + Point::new(row_of_selection_end_line + 1, 0) + } else { + Point::new(row_of_selection_end_line, 1) + }; + selection.end = new_selection_end.to_display_point(map); + } } }); }); diff --git a/crates/vim/test_data/test_change_paragraph_object.json b/crates/vim/test_data/test_change_paragraph_object.json new file mode 100644 index 0000000000..7de16dac5b --- /dev/null +++ b/crates/vim/test_data/test_change_paragraph_object.json @@ -0,0 +1,430 @@ +{"Put":{"state":"ˇThe quick brown fox jumps over the lazy dog."}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumpˇs over the lazy dog."}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dogˇ."}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.ˇ"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Insert"}} +{"Put":{"state":"ˇThe quick brown fox jumps over the lazy dog."}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumpˇs over the lazy dog."}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dogˇ."}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.ˇ"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Insert"}} +{"Put":{"state":"ˇThe quick brown\nfox jumps over\nthe lazy dog.\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ\n","mode":"Insert"}} +{"Put":{"state":"The quick brownˇ\nfox jumps over\nthe lazy dog.\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ\n","mode":"Insert"}} +{"Put":{"state":"The quick brown\nˇfox jumps over\nthe lazy dog.\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ\n","mode":"Insert"}} +{"Put":{"state":"The quick brown\nfox jumps overˇ\nthe lazy dog.\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ\n","mode":"Insert"}} +{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy dog.ˇ\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ\n","mode":"Insert"}} +{"Put":{"state":"ˇThe quick brown\nfox jumps over\nthe lazy dog.\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Insert"}} +{"Put":{"state":"The quick brownˇ\nfox jumps over\nthe lazy dog.\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown\nˇfox jumps over\nthe lazy dog.\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown\nfox jumps overˇ\nthe lazy dog.\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy dog.ˇ\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Insert"}} +{"Put":{"state":"ˇ\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n","mode":"Insert"}} +{"Put":{"state":"\nˇ\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n","mode":"Insert"}} +{"Put":{"state":"\n\nˇThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"\n\nˇ\n\n\nThe quick brown fox jumps\nover the lazy dog.\n","mode":"Insert"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nˇover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"\n\nˇ\n\n\nThe quick brown fox jumps\nover the lazy dog.\n","mode":"Insert"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\nThe quick brown fox jumps\nover the lazy dog.\n","mode":"Insert"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\nˇ\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\nThe quick brown fox jumps\nover the lazy dog.\n","mode":"Insert"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇ\n","mode":"Insert"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumpsˇ\nover the lazy dog.\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇ\n","mode":"Insert"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nˇover the lazy dog.\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇ\n","mode":"Insert"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.ˇ\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇ\n","mode":"Insert"}} +{"Put":{"state":"ˇ\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ\n\n\nThe quick brown fox jumps\nover the lazy dog.\n","mode":"Insert"}} +{"Put":{"state":"\nˇ\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ\n\n\nThe quick brown fox jumps\nover the lazy dog.\n","mode":"Insert"}} +{"Put":{"state":"\n\nˇThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"\n\nˇ\nThe quick brown fox jumps\nover the lazy dog.\n","mode":"Insert"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nˇover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"\n\nˇ\nThe quick brown fox jumps\nover the lazy dog.\n","mode":"Insert"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\n","mode":"Insert"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\nˇ\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\n","mode":"Insert"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇ","mode":"Insert"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumpsˇ\nover the lazy dog.\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇ","mode":"Insert"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nˇover the lazy dog.\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇ","mode":"Insert"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.ˇ\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇ","mode":"Insert"}} +{"Put":{"state":"ˇThe quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nˇover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\nˇ\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\nˇ\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\nˇ\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\nˇ\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\nˇ\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\nˇ\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nˇThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nˇ\n\n\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumpsˇ\nover the lazy dog.\n\n\n\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nˇ\n\n\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nˇover the lazy dog.\n\n\n\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nˇ\n\n\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.ˇ\n\n\n\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nˇ\n\n\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\n\n\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\nˇ\n\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇ\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ","mode":"Insert"}} +{"Put":{"state":"ˇThe quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nˇover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\nˇ\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\nˇ\n\n\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\nˇ\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\nˇ\n\n\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\nˇ\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\nˇ\n\n\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nˇThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumpsˇ\nover the lazy dog.\n\n\n\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nˇover the lazy dog.\n\n\n\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.ˇ\n\n\n\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\n\n\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\n\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\nˇ\n\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\nˇ\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇ\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇ\n","mode":"Normal"}} +{"Put":{"state":"ˇThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\t \t\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\t \t\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\nˇ \t\n\nThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\t \t\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\nˇ\nThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\t \t\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nˇThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\t \t\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nˇ\n\n \t\n\t \t\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.ˇ\n\n \t\n\t \t\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nˇ\n\n \t\n\t \t\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\n \t\n\t \t\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.\n\nˇ \t\n\t \t\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ","mode":"Insert"}} +{"Put":{"state":"ˇThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\t \t\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ\nThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\t \t\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\nˇ \t\n\nThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\t \t\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\nˇ\n\n \t\n\t \t\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nˇThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\t \t\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.ˇ\n\n \t\n\t \t\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\n \t\n\t \t\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\n \t\n\t \t\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.\n\nˇ \t\n\t \t\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.\n\nˇ \t\n\t \t\n","mode":"Normal"}} +{"Put":{"state":"ˇThe quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumps over the lazy dog.\n\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ\n\nThe quick brown fox jumps over the lazy dog.\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\nˇ\nThe quick brown fox jumps over the lazy dog.\n\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\nˇ\nThe quick brown fox jumps over the lazy dog.\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\n\nˇThe quick brown fox jumps over the lazy dog.\n\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\n\nˇ\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumpˇs over the lazy dog.\n\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\n\nˇ\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumps over the lazy dog.ˇ\n\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\n\nˇ\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumps over the lazy dog.\nˇ\n"}} +{"Key":"c"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumps over the lazy dog.\nˇ","mode":"Insert"}} +{"Put":{"state":"ˇThe quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumps over the lazy dog.\n\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ\nThe quick brown fox jumps over the lazy dog.\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\nˇ\nThe quick brown fox jumps over the lazy dog.\n\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\nˇ\n\n","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\n\nˇThe quick brown fox jumps over the lazy dog.\n\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\n\nˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumpˇs over the lazy dog.\n\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\n\nˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumps over the lazy dog.ˇ\n\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\n\nˇ","mode":"Insert"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumps over the lazy dog.\nˇ\n"}} +{"Key":"c"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumps over the lazy dog.\nˇ\n","mode":"Normal"}} diff --git a/crates/vim/test_data/test_delete_paragraph_object.json b/crates/vim/test_data/test_delete_paragraph_object.json new file mode 100644 index 0000000000..2cf1402ae3 --- /dev/null +++ b/crates/vim/test_data/test_delete_paragraph_object.json @@ -0,0 +1,430 @@ +{"Put":{"state":"ˇThe quick brown fox jumps over the lazy dog."}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumpˇs over the lazy dog."}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dogˇ."}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.ˇ"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Normal"}} +{"Put":{"state":"ˇThe quick brown fox jumps over the lazy dog."}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumpˇs over the lazy dog."}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dogˇ."}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.ˇ"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Normal"}} +{"Put":{"state":"ˇThe quick brown\nfox jumps over\nthe lazy dog.\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Normal"}} +{"Put":{"state":"The quick brownˇ\nfox jumps over\nthe lazy dog.\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown\nˇfox jumps over\nthe lazy dog.\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown\nfox jumps overˇ\nthe lazy dog.\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy dog.ˇ\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Normal"}} +{"Put":{"state":"ˇThe quick brown\nfox jumps over\nthe lazy dog.\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Normal"}} +{"Put":{"state":"The quick brownˇ\nfox jumps over\nthe lazy dog.\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown\nˇfox jumps over\nthe lazy dog.\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown\nfox jumps overˇ\nthe lazy dog.\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy dog.ˇ\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ","mode":"Normal"}} +{"Put":{"state":"ˇ\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n","mode":"Normal"}} +{"Put":{"state":"\nˇ\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n","mode":"Normal"}} +{"Put":{"state":"\n\nˇThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"\n\nˇ\n\nThe quick brown fox jumps\nover the lazy dog.\n","mode":"Normal"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nˇover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"\n\nˇ\n\nThe quick brown fox jumps\nover the lazy dog.\n","mode":"Normal"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\nˇThe quick brown fox jumps\nover the lazy dog.\n","mode":"Normal"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\nˇ\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\nˇThe quick brown fox jumps\nover the lazy dog.\n","mode":"Normal"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇ","mode":"Normal"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumpsˇ\nover the lazy dog.\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇ","mode":"Normal"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nˇover the lazy dog.\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇ","mode":"Normal"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.ˇ\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇ","mode":"Normal"}} +{"Put":{"state":"ˇ\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ\n\nThe quick brown fox jumps\nover the lazy dog.\n","mode":"Normal"}} +{"Put":{"state":"\nˇ\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇ\n\nThe quick brown fox jumps\nover the lazy dog.\n","mode":"Normal"}} +{"Put":{"state":"\n\nˇThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"\n\nˇThe quick brown fox jumps\nover the lazy dog.\n","mode":"Normal"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nˇover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"\n\nˇThe quick brown fox jumps\nover the lazy dog.\n","mode":"Normal"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ","mode":"Normal"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\nˇ\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ","mode":"Normal"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\nˇ","mode":"Normal"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumpsˇ\nover the lazy dog.\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\nˇ","mode":"Normal"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nˇover the lazy dog.\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\nˇ","mode":"Normal"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.ˇ\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\nˇ","mode":"Normal"}} +{"Put":{"state":"ˇThe quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nˇover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\nˇ\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\nˇThe quick brown fox jumps\nover the lazy dog.\n\n\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\nˇ\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\nˇThe quick brown fox jumps\nover the lazy dog.\n\n\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\nˇ\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\nˇThe quick brown fox jumps\nover the lazy dog.\n\n\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nˇThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nˇ\n\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumpsˇ\nover the lazy dog.\n\n\n\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nˇ\n\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nˇover the lazy dog.\n\n\n\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nˇ\n\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.ˇ\n\n\n\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nˇ\n\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\n\n\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nˇover the lazy dog.","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\nˇ\n\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nˇover the lazy dog.","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇ\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nˇover the lazy dog.","mode":"Normal"}} +{"Put":{"state":"ˇThe quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇThe quick brown fox jumps\nover the lazy dog.\n\n\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nˇover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇThe quick brown fox jumps\nover the lazy dog.\n\n\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\nˇ\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\nˇ\n\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\nˇ\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\nˇ\n\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\nˇ\nThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\nˇ\n\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nˇThe quick brown fox jumps\nover the lazy dog.\n\n\n\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\nˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumpsˇ\nover the lazy dog.\n\n\n\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\nˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nˇover the lazy dog.\n\n\n\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\nˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.ˇ\n\n\n\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\nˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\n\n\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\n\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\nˇ\n\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\nˇ\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇ\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇ\n","mode":"Normal"}} +{"Put":{"state":"ˇThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\t \t\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ\n \t\n\nThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\t \t\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\nˇ \t\n\nThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\t \t\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\nˇThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\t \t\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nˇThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\t \t\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nˇ\n \t\n\t \t\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.ˇ\n\n \t\n\t \t\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nˇ\n \t\n\t \t\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\n \t\n\t \t\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nˇover the lazy dog.","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.\n\nˇ \t\n\t \t\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nˇover the lazy dog.","mode":"Normal"}} +{"Put":{"state":"ˇThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\t \t\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\t \t\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\nˇ \t\n\nThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\t \t\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\nˇ\n \t\n\t \t\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nˇThe quick brown fox jumps\nover the lazy dog.\n\n \t\n\t \t\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\nˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.ˇ\n\n \t\n\t \t\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\nˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\n \t\n\t \t\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\n \t\n\t \t\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.\n\nˇ \t\n\t \t\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps\nover the lazy dog.\n\n \t\n\nThe quick brown fox jumps\nover the lazy dog.\n\nˇ \t\n\t \t\n","mode":"Normal"}} +{"Put":{"state":"ˇThe quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumps over the lazy dog.\n\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"ˇ\nThe quick brown fox jumps over the lazy dog.\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\nˇ\nThe quick brown fox jumps over the lazy dog.\n\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\nˇThe quick brown fox jumps over the lazy dog.\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\n\nˇThe quick brown fox jumps over the lazy dog.\n\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\n\nˇ\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumpˇs over the lazy dog.\n\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\n\nˇ\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumps over the lazy dog.ˇ\n\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\n\nˇ\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumps over the lazy dog.\nˇ\n"}} +{"Key":"d"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\n\nˇThe quick brown fox jumps over the lazy dog.","mode":"Normal"}} +{"Put":{"state":"ˇThe quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumps over the lazy dog.\n\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"ˇThe quick brown fox jumps over the lazy dog.\n\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\nˇ\nThe quick brown fox jumps over the lazy dog.\n\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\nˇ\n","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\n\nˇThe quick brown fox jumps over the lazy dog.\n\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\nˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumpˇs over the lazy dog.\n\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\nˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumps over the lazy dog.ˇ\n\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\nˇ","mode":"Normal"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumps over the lazy dog.\nˇ\n"}} +{"Key":"d"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumps over the lazy dog.\nˇ\n","mode":"Normal"}} diff --git a/crates/vim/test_data/test_paragraph_object_with_landing_positions_not_at_beginning_of_line.json b/crates/vim/test_data/test_paragraph_object_with_landing_positions_not_at_beginning_of_line.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crates/vim/test_data/test_visual_paragraph_object.json b/crates/vim/test_data/test_visual_paragraph_object.json new file mode 100644 index 0000000000..604d6dc93f --- /dev/null +++ b/crates/vim/test_data/test_visual_paragraph_object.json @@ -0,0 +1,80 @@ +{"Put":{"state":"ˇThe quick brown\nfox jumps over\nthe lazy dog.\n"}} +{"Key":"v"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"«The quick brown\nfox jumps over\ntˇ»he lazy dog.\n","mode":"VisualLine"}} +{"Put":{"state":"ˇThe quick brown\nfox jumps over\nthe lazy dog.\n"}} +{"Key":"v"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"«The quick brown\nfox jumps over\nthe lazy dog.\nˇ»","mode":"VisualLine"}} +{"Put":{"state":"ˇ\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"v"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"«\n\nˇ»The quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n","mode":"VisualLine"}} +{"Put":{"state":"\n\nˇThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"v"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"\n\n«The quick brown fox jumps\noˇ»ver the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n","mode":"VisualLine"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"v"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n«\n\nˇ»The quick brown fox jumps\nover the lazy dog.\n","mode":"VisualLine"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"v"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n«The quick brown fox jumps\noˇ»ver the lazy dog.\n","mode":"VisualLine"}} +{"Put":{"state":"ˇ\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"v"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"«\n\nThe quick brown fox jumps\noˇ»ver the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n","mode":"VisualLine"}} +{"Put":{"state":"\n\nˇThe quick brown fox jumps\nover the lazy dog.\n\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"v"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"\n\n«The quick brown fox jumps\nover the lazy dog.\n\n\nˇ»The quick brown fox jumps\nover the lazy dog.\n","mode":"VisualLine"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\nˇ\n\nThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"v"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n«\n\nThe quick brown fox jumps\noˇ»ver the lazy dog.\n","mode":"VisualLine"}} +{"Put":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\nˇThe quick brown fox jumps\nover the lazy dog.\n"}} +{"Key":"v"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"\n\nThe quick brown fox jumps\nover the lazy dog.\n\n\n«The quick brown fox jumps\nover the lazy dog.\nˇ»","mode":"VisualLine"}} +{"Put":{"state":"ˇThe quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumps over the lazy dog.\n\n"}} +{"Key":"v"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"«Tˇ»he quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumps over the lazy dog.\n\n","mode":"VisualLine"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\nˇ\nThe quick brown fox jumps over the lazy dog.\n\n"}} +{"Key":"v"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\n«\nˇ»The quick brown fox jumps over the lazy dog.\n\n","mode":"VisualLine"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\n\nˇThe quick brown fox jumps over the lazy dog.\n\n"}} +{"Key":"v"} +{"Key":"i"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\n\n«Tˇ»he quick brown fox jumps over the lazy dog.\n\n","mode":"VisualLine"}} +{"Put":{"state":"ˇThe quick brown fox jumps over the lazy dog.\n\nThe quick brown fox jumps over the lazy dog.\n\n"}} +{"Key":"v"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"«The quick brown fox jumps over the lazy dog.\n\nˇ»The quick brown fox jumps over the lazy dog.\n\n","mode":"VisualLine"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\nˇ\nThe quick brown fox jumps over the lazy dog.\n\n"}} +{"Key":"v"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\n«\nTˇ»he quick brown fox jumps over the lazy dog.\n\n","mode":"VisualLine"}} +{"Put":{"state":"The quick brown fox jumps over the lazy dog.\n\nˇThe quick brown fox jumps over the lazy dog.\n\n"}} +{"Key":"v"} +{"Key":"a"} +{"Key":"p"} +{"Get":{"state":"The quick brown fox jumps over the lazy dog.\n\n«The quick brown fox jumps over the lazy dog.\n\nˇ»","mode":"VisualLine"}} From 4167c66b86714ad09d4fd463bc92b7b8c03c55c5 Mon Sep 17 00:00:00 2001 From: "Quadri A. Adekunle" Date: Tue, 5 Mar 2024 11:08:29 +1100 Subject: [PATCH 084/121] macOS: Fix center window with fixed bounds size (#8475) This PR fixes window showing up in the center of the monitor when `center: true` option is provided. The idea is to set the `window_wize` when creating the `window` using `native_window.initWithContentRect_styleMask_backing_defer_screen_()` Before: SCR-20240227-qokf After: SCR-20240227-qlmg Release Notes: - N/A --- crates/gpui/examples/hello_world.rs | 10 +++++- crates/gpui/src/platform/mac/window.rs | 44 ++++++++++++++------------ 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/crates/gpui/examples/hello_world.rs b/crates/gpui/examples/hello_world.rs index d0578e6681..9361cbf437 100644 --- a/crates/gpui/examples/hello_world.rs +++ b/crates/gpui/examples/hello_world.rs @@ -23,7 +23,15 @@ impl Render for HelloWorld { fn main() { App::new().run(|cx: &mut AppContext| { - cx.open_window(WindowOptions::default(), |cx| { + let options = WindowOptions { + bounds: WindowBounds::Fixed(Bounds { + size: size(px(600.0), px(600.0)).into(), + origin: Default::default(), + }), + center: true, + ..Default::default() + }; + cx.open_window(options, |cx| { cx.new_view(|_cx| HelloWorld { text: "World".into(), }) diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index bdfd41a46f..8e48cbef30 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -530,8 +530,27 @@ impl MacWindow { } } + let window_rect = match options.bounds { + WindowBounds::Fullscreen => { + // Set a temporary size as we will asynchronously resize the window + NSRect::new(NSPoint::new(0., 0.), NSSize::new(1024., 768.)) + } + WindowBounds::Maximized => { + let display_bounds = display.bounds(); + global_bounds_to_ns_rect(display_bounds) + } + WindowBounds::Fixed(bounds) => { + let display_bounds = display.bounds(); + if bounds.intersects(&display_bounds) { + global_bounds_to_ns_rect(bounds) + } else { + global_bounds_to_ns_rect(display_bounds) + } + } + }; + let native_window = native_window.initWithContentRect_styleMask_backing_defer_screen_( - NSRect::new(NSPoint::new(0., 0.), NSSize::new(1024., 768.)), + window_rect, style_mask, NSBackingStoreBuffered, NO, @@ -685,25 +704,10 @@ impl MacWindow { native_window.orderFront_(nil); } - let screen = native_window.screen(); - match options.bounds { - WindowBounds::Fullscreen => { - // We need to toggle full screen asynchronously as doing so may - // call back into the platform handlers. - window.toggle_full_screen() - } - WindowBounds::Maximized => { - native_window.setFrame_display_(screen.visibleFrame(), YES); - } - WindowBounds::Fixed(bounds) => { - let display_bounds = display.bounds(); - let frame = if bounds.intersects(&display_bounds) { - global_bounds_to_ns_rect(bounds) - } else { - global_bounds_to_ns_rect(display_bounds) - }; - native_window.setFrame_display_(frame, YES); - } + if options.bounds == WindowBounds::Fullscreen { + // We need to toggle full screen asynchronously as doing so may + // call back into the platform handlers. + window.toggle_full_screen(); } window.0.lock().move_traffic_light(); From 27c53437072750bedc0712c3fadd47ce40a9eb3d Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 4 Mar 2024 19:17:40 -0700 Subject: [PATCH 085/121] hosted projects (#8627) - **Allow joining a hosted project** You can't yet do anything in a hosted project, but you can join it and look how empty it is. Release Notes: - N/A --- crates/call/src/room.rs | 13 +- crates/channel/src/channel.rs | 2 +- crates/channel/src/channel_store.rs | 7 +- crates/client/src/user.rs | 3 + .../20221109000000_test_schema.sql | 5 +- ...0227215556_hosted_projects_in_projects.sql | 3 + crates/collab/src/db.rs | 4 +- .../collab/src/db/queries/hosted_projects.rs | 42 +- crates/collab/src/db/queries/projects.rs | 439 +++++++++++------- crates/collab/src/db/queries/rooms.rs | 4 +- crates/collab/src/db/tables/project.rs | 7 +- crates/collab/src/rpc.rs | 70 ++- crates/collab/src/tests/following_tests.rs | 2 +- crates/collab/src/tests/integration_tests.rs | 2 - crates/collab_ui/src/collab_panel.rs | 19 +- .../incoming_call_notification.rs | 2 +- .../project_shared_notification.rs | 2 +- crates/project/src/project.rs | 53 ++- crates/rpc/proto/zed.proto | 9 + crates/rpc/src/proto.rs | 2 + crates/workspace/src/pane_group.rs | 2 +- crates/workspace/src/workspace.rs | 59 ++- 22 files changed, 519 insertions(+), 232 deletions(-) create mode 100644 crates/collab/migrations/20240227215556_hosted_projects_in_projects.sql diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 3e524f5b3e..5599c15b6d 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -1182,19 +1182,10 @@ impl Room { ) -> Task>> { let client = self.client.clone(); let user_store = self.user_store.clone(); - let role = self.local_participant.role; cx.emit(Event::RemoteProjectJoined { project_id: id }); cx.spawn(move |this, mut cx| async move { - let project = Project::remote( - id, - client, - user_store, - language_registry, - fs, - role, - cx.clone(), - ) - .await?; + let project = + Project::remote(id, client, user_store, language_registry, fs, cx.clone()).await?; this.update(&mut cx, |this, cx| { this.joined_projects.retain(|project| { diff --git a/crates/channel/src/channel.rs b/crates/channel/src/channel.rs index 1dbf502252..aee92d0f6c 100644 --- a/crates/channel/src/channel.rs +++ b/crates/channel/src/channel.rs @@ -11,7 +11,7 @@ pub use channel_chat::{ mentions_to_proto, ChannelChat, ChannelChatEvent, ChannelMessage, ChannelMessageId, MessageParams, }; -pub use channel_store::{Channel, ChannelEvent, ChannelMembership, ChannelStore, HostedProjectId}; +pub use channel_store::{Channel, ChannelEvent, ChannelMembership, ChannelStore}; #[cfg(test)] mod channel_store_tests; diff --git a/crates/channel/src/channel_store.rs b/crates/channel/src/channel_store.rs index 0afd7e41b9..3197bc1bd3 100644 --- a/crates/channel/src/channel_store.rs +++ b/crates/channel/src/channel_store.rs @@ -3,7 +3,9 @@ mod channel_index; use crate::{channel_buffer::ChannelBuffer, channel_chat::ChannelChat, ChannelMessage}; use anyhow::{anyhow, Result}; use channel_index::ChannelIndex; -use client::{ChannelId, Client, ClientSettings, Subscription, User, UserId, UserStore}; +use client::{ + ChannelId, Client, ClientSettings, HostedProjectId, Subscription, User, UserId, UserStore, +}; use collections::{hash_map, HashMap, HashSet}; use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt}; use gpui::{ @@ -27,9 +29,6 @@ pub fn init(client: &Arc, user_store: Model, cx: &mut AppCont cx.set_global(GlobalChannelStore(channel_store)); } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] -pub struct HostedProjectId(pub u64); - #[derive(Debug, Clone, Default)] struct NotesVersion { epoch: u64, diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 9b7ef1dbb1..0c10e50ac5 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -24,6 +24,9 @@ impl std::fmt::Display for ChannelId { } } +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] +pub struct HostedProjectId(pub u64); + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ParticipantIndex(pub u32); diff --git a/crates/collab/migrations.sqlite/20221109000000_test_schema.sql b/crates/collab/migrations.sqlite/20221109000000_test_schema.sql index 20658e95cb..b7b427a6b7 100644 --- a/crates/collab/migrations.sqlite/20221109000000_test_schema.sql +++ b/crates/collab/migrations.sqlite/20221109000000_test_schema.sql @@ -46,10 +46,11 @@ CREATE UNIQUE INDEX "index_rooms_on_channel_id" ON "rooms" ("channel_id"); CREATE TABLE "projects" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT, "room_id" INTEGER REFERENCES rooms (id) ON DELETE CASCADE NOT NULL, - "host_user_id" INTEGER REFERENCES users (id) NOT NULL, + "host_user_id" INTEGER REFERENCES users (id), "host_connection_id" INTEGER, "host_connection_server_id" INTEGER REFERENCES servers (id) ON DELETE CASCADE, - "unregistered" BOOLEAN NOT NULL DEFAULT FALSE + "unregistered" BOOLEAN NOT NULL DEFAULT FALSE, + "hosted_project_id" INTEGER REFERENCES hosted_projects (id) ); CREATE INDEX "index_projects_on_host_connection_server_id" ON "projects" ("host_connection_server_id"); CREATE INDEX "index_projects_on_host_connection_id_and_host_connection_server_id" ON "projects" ("host_connection_id", "host_connection_server_id"); diff --git a/crates/collab/migrations/20240227215556_hosted_projects_in_projects.sql b/crates/collab/migrations/20240227215556_hosted_projects_in_projects.sql new file mode 100644 index 0000000000..69905d12f6 --- /dev/null +++ b/crates/collab/migrations/20240227215556_hosted_projects_in_projects.sql @@ -0,0 +1,3 @@ +-- Add migration script here +ALTER TABLE projects ALTER COLUMN host_user_id DROP NOT NULL; +ALTER TABLE projects ADD COLUMN hosted_project_id INTEGER REFERENCES hosted_projects(id) UNIQUE NULL; diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index 4a3d4f5656..caf8a83d50 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -670,6 +670,8 @@ pub struct RefreshedChannelBuffer { } pub struct Project { + pub id: ProjectId, + pub role: ChannelRole, pub collaborators: Vec, pub worktrees: BTreeMap, pub language_servers: Vec, @@ -695,7 +697,7 @@ impl ProjectCollaborator { #[derive(Debug)] pub struct LeftProject { pub id: ProjectId, - pub host_user_id: UserId, + pub host_user_id: Option, pub host_connection_id: Option, pub connection_ids: Vec, } diff --git a/crates/collab/src/db/queries/hosted_projects.rs b/crates/collab/src/db/queries/hosted_projects.rs index fd9a991906..394f1055c6 100644 --- a/crates/collab/src/db/queries/hosted_projects.rs +++ b/crates/collab/src/db/queries/hosted_projects.rs @@ -1,4 +1,4 @@ -use rpc::proto; +use rpc::{proto, ErrorCode}; use super::*; @@ -39,4 +39,44 @@ impl Database { }) .collect()) } + + pub async fn get_hosted_project( + &self, + hosted_project_id: HostedProjectId, + user_id: UserId, + tx: &DatabaseTransaction, + ) -> Result<(hosted_project::Model, ChannelRole)> { + let project = hosted_project::Entity::find_by_id(hosted_project_id) + .one(&*tx) + .await? + .ok_or_else(|| anyhow!(ErrorCode::NoSuchProject))?; + let channel = channel::Entity::find_by_id(project.channel_id) + .one(&*tx) + .await? + .ok_or_else(|| anyhow!(ErrorCode::NoSuchChannel))?; + + let role = match project.visibility { + ChannelVisibility::Public => { + self.check_user_is_channel_participant(&channel, user_id, tx) + .await? + } + ChannelVisibility::Members => { + self.check_user_is_channel_member(&channel, user_id, tx) + .await? + } + }; + + Ok((project, role)) + } + + pub async fn is_hosted_project(&self, project_id: ProjectId) -> Result { + self.transaction(|tx| async move { + Ok(project::Entity::find_by_id(project_id) + .one(&*tx) + .await? + .map(|project| project.hosted_project_id.is_some()) + .ok_or_else(|| anyhow!(ErrorCode::NoSuchProject))?) + }) + .await + } } diff --git a/crates/collab/src/db/queries/projects.rs b/crates/collab/src/db/queries/projects.rs index ed489169c9..190f854bfa 100644 --- a/crates/collab/src/db/queries/projects.rs +++ b/crates/collab/src/db/queries/projects.rs @@ -57,13 +57,14 @@ impl Database { } let project = project::ActiveModel { - room_id: ActiveValue::set(participant.room_id), - host_user_id: ActiveValue::set(participant.user_id), + room_id: ActiveValue::set(Some(participant.room_id)), + host_user_id: ActiveValue::set(Some(participant.user_id)), host_connection_id: ActiveValue::set(Some(connection.id as i32)), host_connection_server_id: ActiveValue::set(Some(ServerId( connection.owner_id as i32, ))), - ..Default::default() + id: ActiveValue::NotSet, + hosted_project_id: ActiveValue::Set(None), } .insert(&*tx) .await?; @@ -153,8 +154,12 @@ impl Database { self.update_project_worktrees(project.id, worktrees, &tx) .await?; + let room_id = project + .room_id + .ok_or_else(|| anyhow!("project not in a room"))?; + let guest_connection_ids = self.project_guest_connection_ids(project.id, &tx).await?; - let room = self.get_room(project.room_id, &tx).await?; + let room = self.get_room(room_id, &tx).await?; Ok((room, guest_connection_ids)) }) .await @@ -504,8 +509,30 @@ impl Database { .await } - /// Adds the given connection to the specified project. - pub async fn join_project( + /// Adds the given connection to the specified hosted project + pub async fn join_hosted_project( + &self, + id: HostedProjectId, + user_id: UserId, + connection: ConnectionId, + ) -> Result<(Project, ReplicaId)> { + self.transaction(|tx| async move { + let (hosted_project, role) = self.get_hosted_project(id, user_id, &tx).await?; + let project = project::Entity::find() + .filter(project::Column::HostedProjectId.eq(hosted_project.id)) + .one(&*tx) + .await? + .ok_or_else(|| anyhow!("hosted project is no longer shared"))?; + + self.join_project_internal(project, user_id, connection, role, &tx) + .await + }) + .await + } + + /// Adds the given connection to the specified project + /// in the current room. + pub async fn join_project_in_room( &self, project_id: ProjectId, connection: ConnectionId, @@ -532,180 +559,240 @@ impl Database { .one(&*tx) .await? .ok_or_else(|| anyhow!("no such project"))?; - if project.room_id != participant.room_id { + if project.room_id != Some(participant.room_id) { return Err(anyhow!("no such project"))?; } + self.join_project_internal( + project, + participant.user_id, + connection, + participant.role.unwrap_or(ChannelRole::Member), + &tx, + ) + .await + }) + .await + } - let mut collaborators = project + async fn join_project_internal( + &self, + project: project::Model, + user_id: UserId, + connection: ConnectionId, + role: ChannelRole, + tx: &DatabaseTransaction, + ) -> Result<(Project, ReplicaId)> { + let mut collaborators = project + .find_related(project_collaborator::Entity) + .all(&*tx) + .await?; + let replica_ids = collaborators + .iter() + .map(|c| c.replica_id) + .collect::>(); + let mut replica_id = ReplicaId(1); + while replica_ids.contains(&replica_id) { + replica_id.0 += 1; + } + let new_collaborator = project_collaborator::ActiveModel { + project_id: ActiveValue::set(project.id), + connection_id: ActiveValue::set(connection.id as i32), + connection_server_id: ActiveValue::set(ServerId(connection.owner_id as i32)), + user_id: ActiveValue::set(user_id), + replica_id: ActiveValue::set(replica_id), + is_host: ActiveValue::set(false), + ..Default::default() + } + .insert(&*tx) + .await?; + collaborators.push(new_collaborator); + + let db_worktrees = project.find_related(worktree::Entity).all(&*tx).await?; + let mut worktrees = db_worktrees + .into_iter() + .map(|db_worktree| { + ( + db_worktree.id as u64, + Worktree { + id: db_worktree.id as u64, + abs_path: db_worktree.abs_path, + root_name: db_worktree.root_name, + visible: db_worktree.visible, + entries: Default::default(), + repository_entries: Default::default(), + diagnostic_summaries: Default::default(), + settings_files: Default::default(), + scan_id: db_worktree.scan_id as u64, + completed_scan_id: db_worktree.completed_scan_id as u64, + }, + ) + }) + .collect::>(); + + // Populate worktree entries. + { + let mut db_entries = worktree_entry::Entity::find() + .filter( + Condition::all() + .add(worktree_entry::Column::ProjectId.eq(project.id)) + .add(worktree_entry::Column::IsDeleted.eq(false)), + ) + .stream(&*tx) + .await?; + while let Some(db_entry) = db_entries.next().await { + let db_entry = db_entry?; + if let Some(worktree) = worktrees.get_mut(&(db_entry.worktree_id as u64)) { + worktree.entries.push(proto::Entry { + id: db_entry.id as u64, + is_dir: db_entry.is_dir, + path: db_entry.path, + inode: db_entry.inode as u64, + mtime: Some(proto::Timestamp { + seconds: db_entry.mtime_seconds as u64, + nanos: db_entry.mtime_nanos as u32, + }), + is_symlink: db_entry.is_symlink, + is_ignored: db_entry.is_ignored, + is_external: db_entry.is_external, + git_status: db_entry.git_status.map(|status| status as i32), + }); + } + } + } + + // Populate repository entries. + { + let mut db_repository_entries = worktree_repository::Entity::find() + .filter( + Condition::all() + .add(worktree_repository::Column::ProjectId.eq(project.id)) + .add(worktree_repository::Column::IsDeleted.eq(false)), + ) + .stream(&*tx) + .await?; + while let Some(db_repository_entry) = db_repository_entries.next().await { + let db_repository_entry = db_repository_entry?; + if let Some(worktree) = worktrees.get_mut(&(db_repository_entry.worktree_id as u64)) + { + worktree.repository_entries.insert( + db_repository_entry.work_directory_id as u64, + proto::RepositoryEntry { + work_directory_id: db_repository_entry.work_directory_id as u64, + branch: db_repository_entry.branch, + }, + ); + } + } + } + + // Populate worktree diagnostic summaries. + { + let mut db_summaries = worktree_diagnostic_summary::Entity::find() + .filter(worktree_diagnostic_summary::Column::ProjectId.eq(project.id)) + .stream(&*tx) + .await?; + while let Some(db_summary) = db_summaries.next().await { + let db_summary = db_summary?; + if let Some(worktree) = worktrees.get_mut(&(db_summary.worktree_id as u64)) { + worktree + .diagnostic_summaries + .push(proto::DiagnosticSummary { + path: db_summary.path, + language_server_id: db_summary.language_server_id as u64, + error_count: db_summary.error_count as u32, + warning_count: db_summary.warning_count as u32, + }); + } + } + } + + // Populate worktree settings files + { + let mut db_settings_files = worktree_settings_file::Entity::find() + .filter(worktree_settings_file::Column::ProjectId.eq(project.id)) + .stream(&*tx) + .await?; + while let Some(db_settings_file) = db_settings_files.next().await { + let db_settings_file = db_settings_file?; + if let Some(worktree) = worktrees.get_mut(&(db_settings_file.worktree_id as u64)) { + worktree.settings_files.push(WorktreeSettingsFile { + path: db_settings_file.path, + content: db_settings_file.content, + }); + } + } + } + + // Populate language servers. + let language_servers = project + .find_related(language_server::Entity) + .all(&*tx) + .await?; + + let project = Project { + id: project.id, + role, + collaborators: collaborators + .into_iter() + .map(|collaborator| ProjectCollaborator { + connection_id: collaborator.connection(), + user_id: collaborator.user_id, + replica_id: collaborator.replica_id, + is_host: collaborator.is_host, + }) + .collect(), + worktrees, + language_servers: language_servers + .into_iter() + .map(|language_server| proto::LanguageServer { + id: language_server.id as u64, + name: language_server.name, + }) + .collect(), + }; + Ok((project, replica_id as ReplicaId)) + } + + pub async fn leave_hosted_project( + &self, + project_id: ProjectId, + connection: ConnectionId, + ) -> Result { + self.transaction(|tx| async move { + let result = project_collaborator::Entity::delete_many() + .filter( + Condition::all() + .add(project_collaborator::Column::ProjectId.eq(project_id)) + .add(project_collaborator::Column::ConnectionId.eq(connection.id as i32)) + .add( + project_collaborator::Column::ConnectionServerId + .eq(connection.owner_id as i32), + ), + ) + .exec(&*tx) + .await?; + if result.rows_affected == 0 { + return Err(anyhow!("not in the project"))?; + } + + let project = project::Entity::find_by_id(project_id) + .one(&*tx) + .await? + .ok_or_else(|| anyhow!("no such project"))?; + let collaborators = project .find_related(project_collaborator::Entity) .all(&*tx) .await?; - let replica_ids = collaborators - .iter() - .map(|c| c.replica_id) - .collect::>(); - let mut replica_id = ReplicaId(1); - while replica_ids.contains(&replica_id) { - replica_id.0 += 1; - } - let new_collaborator = project_collaborator::ActiveModel { - project_id: ActiveValue::set(project_id), - connection_id: ActiveValue::set(connection.id as i32), - connection_server_id: ActiveValue::set(ServerId(connection.owner_id as i32)), - user_id: ActiveValue::set(participant.user_id), - replica_id: ActiveValue::set(replica_id), - is_host: ActiveValue::set(false), - ..Default::default() - } - .insert(&*tx) - .await?; - collaborators.push(new_collaborator); - - let db_worktrees = project.find_related(worktree::Entity).all(&*tx).await?; - let mut worktrees = db_worktrees + let connection_ids = collaborators .into_iter() - .map(|db_worktree| { - ( - db_worktree.id as u64, - Worktree { - id: db_worktree.id as u64, - abs_path: db_worktree.abs_path, - root_name: db_worktree.root_name, - visible: db_worktree.visible, - entries: Default::default(), - repository_entries: Default::default(), - diagnostic_summaries: Default::default(), - settings_files: Default::default(), - scan_id: db_worktree.scan_id as u64, - completed_scan_id: db_worktree.completed_scan_id as u64, - }, - ) - }) - .collect::>(); - - // Populate worktree entries. - { - let mut db_entries = worktree_entry::Entity::find() - .filter( - Condition::all() - .add(worktree_entry::Column::ProjectId.eq(project_id)) - .add(worktree_entry::Column::IsDeleted.eq(false)), - ) - .stream(&*tx) - .await?; - while let Some(db_entry) = db_entries.next().await { - let db_entry = db_entry?; - if let Some(worktree) = worktrees.get_mut(&(db_entry.worktree_id as u64)) { - worktree.entries.push(proto::Entry { - id: db_entry.id as u64, - is_dir: db_entry.is_dir, - path: db_entry.path, - inode: db_entry.inode as u64, - mtime: Some(proto::Timestamp { - seconds: db_entry.mtime_seconds as u64, - nanos: db_entry.mtime_nanos as u32, - }), - is_symlink: db_entry.is_symlink, - is_ignored: db_entry.is_ignored, - is_external: db_entry.is_external, - git_status: db_entry.git_status.map(|status| status as i32), - }); - } - } - } - - // Populate repository entries. - { - let mut db_repository_entries = worktree_repository::Entity::find() - .filter( - Condition::all() - .add(worktree_repository::Column::ProjectId.eq(project_id)) - .add(worktree_repository::Column::IsDeleted.eq(false)), - ) - .stream(&*tx) - .await?; - while let Some(db_repository_entry) = db_repository_entries.next().await { - let db_repository_entry = db_repository_entry?; - if let Some(worktree) = - worktrees.get_mut(&(db_repository_entry.worktree_id as u64)) - { - worktree.repository_entries.insert( - db_repository_entry.work_directory_id as u64, - proto::RepositoryEntry { - work_directory_id: db_repository_entry.work_directory_id as u64, - branch: db_repository_entry.branch, - }, - ); - } - } - } - - // Populate worktree diagnostic summaries. - { - let mut db_summaries = worktree_diagnostic_summary::Entity::find() - .filter(worktree_diagnostic_summary::Column::ProjectId.eq(project_id)) - .stream(&*tx) - .await?; - while let Some(db_summary) = db_summaries.next().await { - let db_summary = db_summary?; - if let Some(worktree) = worktrees.get_mut(&(db_summary.worktree_id as u64)) { - worktree - .diagnostic_summaries - .push(proto::DiagnosticSummary { - path: db_summary.path, - language_server_id: db_summary.language_server_id as u64, - error_count: db_summary.error_count as u32, - warning_count: db_summary.warning_count as u32, - }); - } - } - } - - // Populate worktree settings files - { - let mut db_settings_files = worktree_settings_file::Entity::find() - .filter(worktree_settings_file::Column::ProjectId.eq(project_id)) - .stream(&*tx) - .await?; - while let Some(db_settings_file) = db_settings_files.next().await { - let db_settings_file = db_settings_file?; - if let Some(worktree) = - worktrees.get_mut(&(db_settings_file.worktree_id as u64)) - { - worktree.settings_files.push(WorktreeSettingsFile { - path: db_settings_file.path, - content: db_settings_file.content, - }); - } - } - } - - // Populate language servers. - let language_servers = project - .find_related(language_server::Entity) - .all(&*tx) - .await?; - - let project = Project { - collaborators: collaborators - .into_iter() - .map(|collaborator| ProjectCollaborator { - connection_id: collaborator.connection(), - user_id: collaborator.user_id, - replica_id: collaborator.replica_id, - is_host: collaborator.is_host, - }) - .collect(), - worktrees, - language_servers: language_servers - .into_iter() - .map(|language_server| proto::LanguageServer { - id: language_server.id as u64, - name: language_server.name, - }) - .collect(), - }; - Ok((project, replica_id as ReplicaId)) + .map(|collaborator| collaborator.connection()) + .collect(); + Ok(LeftProject { + id: project.id, + connection_ids, + host_user_id: None, + host_connection_id: None, + }) }) .await } @@ -772,7 +859,7 @@ impl Database { .exec(&*tx) .await?; - let room = self.get_room(project.room_id, &tx).await?; + let room = self.get_room(room_id, &tx).await?; let left_project = LeftProject { id: project_id, host_user_id: project.host_user_id, @@ -996,7 +1083,9 @@ impl Database { .one(&*tx) .await? .ok_or_else(|| anyhow!("project {} not found", project_id))?; - Ok(project.room_id) + Ok(project + .room_id + .ok_or_else(|| anyhow!("project not in room"))?) }) .await } diff --git a/crates/collab/src/db/queries/rooms.rs b/crates/collab/src/db/queries/rooms.rs index cd960ec5a7..13707d6f47 100644 --- a/crates/collab/src/db/queries/rooms.rs +++ b/crates/collab/src/db/queries/rooms.rs @@ -491,7 +491,7 @@ impl Database { .one(&*tx) .await? .ok_or_else(|| anyhow!("project does not exist"))?; - if project.host_user_id != user_id { + if project.host_user_id != Some(user_id) { return Err(anyhow!("no such project"))?; } @@ -851,7 +851,7 @@ impl Database { } if collaborator.is_host { - left_project.host_user_id = collaborator.user_id; + left_project.host_user_id = Some(collaborator.user_id); left_project.host_connection_id = Some(collaborator_connection_id); } } diff --git a/crates/collab/src/db/tables/project.rs b/crates/collab/src/db/tables/project.rs index 8c26836046..550f8415d7 100644 --- a/crates/collab/src/db/tables/project.rs +++ b/crates/collab/src/db/tables/project.rs @@ -1,4 +1,4 @@ -use crate::db::{ProjectId, Result, RoomId, ServerId, UserId}; +use crate::db::{HostedProjectId, ProjectId, Result, RoomId, ServerId, UserId}; use anyhow::anyhow; use rpc::ConnectionId; use sea_orm::entity::prelude::*; @@ -8,10 +8,11 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: ProjectId, - pub room_id: RoomId, - pub host_user_id: UserId, + pub room_id: Option, + pub host_user_id: Option, pub host_connection_id: Option, pub host_connection_server_id: Option, + pub hosted_project_id: Option, } impl Model { diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index d6a0ba8e89..4fda8b9959 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -4,8 +4,9 @@ use crate::{ auth::{self, Impersonator}, db::{ self, BufferId, ChannelId, ChannelRole, ChannelsForUser, CreatedChannelMessage, Database, - InviteMemberResult, MembershipUpdated, MessageId, NotificationId, ProjectId, - RemoveChannelMemberResult, RespondToChannelInvite, RoomId, ServerId, User, UserId, + HostedProjectId, InviteMemberResult, MembershipUpdated, MessageId, NotificationId, Project, + ProjectId, RemoveChannelMemberResult, ReplicaId, RespondToChannelInvite, RoomId, ServerId, + User, UserId, }, executor::Executor, AppState, Error, Result, @@ -197,6 +198,7 @@ impl Server { .add_request_handler(share_project) .add_message_handler(unshare_project) .add_request_handler(join_project) + .add_request_handler(join_hosted_project) .add_message_handler(leave_project) .add_request_handler(update_project) .add_request_handler(update_worktree) @@ -1584,22 +1586,46 @@ async fn join_project( session: Session, ) -> Result<()> { let project_id = ProjectId::from_proto(request.project_id); - let guest_user_id = session.user_id; tracing::info!(%project_id, "join project"); let (project, replica_id) = &mut *session .db() .await - .join_project(project_id, session.connection_id) + .join_project_in_room(project_id, session.connection_id) .await?; + join_project_internal(response, session, project, replica_id) +} + +trait JoinProjectInternalResponse { + fn send(self, result: proto::JoinProjectResponse) -> Result<()>; +} +impl JoinProjectInternalResponse for Response { + fn send(self, result: proto::JoinProjectResponse) -> Result<()> { + Response::::send(self, result) + } +} +impl JoinProjectInternalResponse for Response { + fn send(self, result: proto::JoinProjectResponse) -> Result<()> { + Response::::send(self, result) + } +} + +fn join_project_internal( + response: impl JoinProjectInternalResponse, + session: Session, + project: &mut Project, + replica_id: &ReplicaId, +) -> Result<()> { let collaborators = project .collaborators .iter() .filter(|collaborator| collaborator.connection_id != session.connection_id) .map(|collaborator| collaborator.to_proto()) .collect::>(); + let project_id = project.id; + let guest_user_id = session.user_id; let worktrees = project .worktrees @@ -1631,10 +1657,12 @@ async fn join_project( // First, we send the metadata associated with each worktree. response.send(proto::JoinProjectResponse { + project_id: project.id.0 as u64, worktrees: worktrees.clone(), replica_id: replica_id.0 as u32, collaborators: collaborators.clone(), language_servers: project.language_servers.clone(), + role: project.role.into(), // todo })?; for (worktree_id, worktree) in mem::take(&mut project.worktrees) { @@ -1707,15 +1735,17 @@ async fn join_project( async fn leave_project(request: proto::LeaveProject, session: Session) -> Result<()> { let sender_id = session.connection_id; let project_id = ProjectId::from_proto(request.project_id); + let db = session.db().await; + if db.is_hosted_project(project_id).await? { + let project = db.leave_hosted_project(project_id, sender_id).await?; + project_left(&project, &session); + return Ok(()); + } - let (room, project) = &*session - .db() - .await - .leave_project(project_id, sender_id) - .await?; + let (room, project) = &*db.leave_project(project_id, sender_id).await?; tracing::info!( %project_id, - host_user_id = %project.host_user_id, + host_user_id = ?project.host_user_id, host_connection_id = ?project.host_connection_id, "leave project" ); @@ -1726,6 +1756,24 @@ async fn leave_project(request: proto::LeaveProject, session: Session) -> Result Ok(()) } +async fn join_hosted_project( + request: proto::JoinHostedProject, + response: Response, + session: Session, +) -> Result<()> { + let (mut project, replica_id) = session + .db() + .await + .join_hosted_project( + HostedProjectId(request.id as i32), + session.user_id, + session.connection_id, + ) + .await?; + + join_project_internal(response, session, &mut project, &replica_id) +} + /// Updates other participants with changes to the project async fn update_project( request: proto::UpdateProject, @@ -3624,7 +3672,7 @@ async fn leave_channel_buffers_for_session(session: &Session) -> Result<()> { fn project_left(project: &db::LeftProject, session: &Session) { for connection_id in &project.connection_ids { - if project.host_user_id == session.user_id { + if project.host_user_id == Some(session.user_id) { session .peer .send( diff --git a/crates/collab/src/tests/following_tests.rs b/crates/collab/src/tests/following_tests.rs index ff53c339e6..57e5388045 100644 --- a/crates/collab/src/tests/following_tests.rs +++ b/crates/collab/src/tests/following_tests.rs @@ -1534,7 +1534,7 @@ async fn test_following_across_workspaces(cx_a: &mut TestAppContext, cx_b: &mut executor.run_until_parked(); assert_eq!(visible_push_notifications(cx_a).len(), 1); cx_a.update(|cx| { - workspace::join_remote_project( + workspace::join_in_room_project( project_b_id, client_b.user_id().unwrap(), client_a.app_state.clone(), diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 08e811adcf..443617bbe3 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -22,7 +22,6 @@ use project::{ search::SearchQuery, DiagnosticSummary, FormatTrigger, HoverBlockKind, Project, ProjectPath, }; use rand::prelude::*; -use rpc::proto::ChannelRole; use serde_json::json; use settings::SettingsStore; use std::{ @@ -3742,7 +3741,6 @@ async fn test_leaving_project( client_b.user_store().clone(), client_b.language_registry().clone(), FakeFs::new(cx.background_executor().clone()), - ChannelRole::Member, cx, ) }) diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 500c7affcf..4f242d68bb 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -7,8 +7,8 @@ use crate::{ CollaborationPanelSettings, }; use call::ActiveCall; -use channel::{Channel, ChannelEvent, ChannelStore, HostedProjectId}; -use client::{ChannelId, Client, Contact, User, UserStore}; +use channel::{Channel, ChannelEvent, ChannelStore}; +use client::{ChannelId, Client, Contact, HostedProjectId, User, UserStore}; use contact_finder::ContactFinder; use db::kvp::KEY_VALUE_STORE; use editor::{Editor, EditorElement, EditorStyle}; @@ -911,7 +911,7 @@ impl CollabPanel { this.workspace .update(cx, |workspace, cx| { let app_state = workspace.app_state().clone(); - workspace::join_remote_project(project_id, host_user_id, app_state, cx) + workspace::join_in_room_project(project_id, host_user_id, app_state, cx) .detach_and_prompt_err("Failed to join project", cx, |_, _| None); }) .ok(); @@ -1047,8 +1047,15 @@ impl CollabPanel { .indent_level(2) .indent_step_size(px(20.)) .selected(is_selected) - .on_click(cx.listener(move |_this, _, _cx| { - // todo() + .on_click(cx.listener(move |this, _, cx| { + if let Some(workspace) = this.workspace.upgrade() { + let app_state = workspace.read(cx).app_state().clone(); + workspace::join_hosted_project(id, app_state, cx).detach_and_prompt_err( + "Failed to open project", + cx, + |_, _| None, + ) + } })) .start_slot( h_flex() @@ -1461,7 +1468,7 @@ impl CollabPanel { } => { if let Some(workspace) = self.workspace.upgrade() { let app_state = workspace.read(cx).app_state().clone(); - workspace::join_remote_project( + workspace::join_in_room_project( *project_id, *host_user_id, app_state, diff --git a/crates/collab_ui/src/notifications/incoming_call_notification.rs b/crates/collab_ui/src/notifications/incoming_call_notification.rs index b6ba2f9507..a8ba20c1e5 100644 --- a/crates/collab_ui/src/notifications/incoming_call_notification.rs +++ b/crates/collab_ui/src/notifications/incoming_call_notification.rs @@ -82,7 +82,7 @@ impl IncomingCallNotificationState { if let Some(project_id) = initial_project_id { cx.update(|cx| { if let Some(app_state) = app_state.upgrade() { - workspace::join_remote_project( + workspace::join_in_room_project( project_id, caller_user_id, app_state, diff --git a/crates/collab_ui/src/notifications/project_shared_notification.rs b/crates/collab_ui/src/notifications/project_shared_notification.rs index 08141d0c2e..46c5c8ce8a 100644 --- a/crates/collab_ui/src/notifications/project_shared_notification.rs +++ b/crates/collab_ui/src/notifications/project_shared_notification.rs @@ -98,7 +98,7 @@ impl ProjectSharedNotification { fn join(&mut self, cx: &mut ViewContext) { if let Some(app_state) = self.app_state.upgrade() { - workspace::join_remote_project(self.project_id, self.owner.id, app_state, cx) + workspace::join_in_room_project(self.project_id, self.owner.id, app_state, cx) .detach_and_log_err(cx); } } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index aee2f5e6c2..8661ceada0 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -11,7 +11,7 @@ mod project_tests; use anyhow::{anyhow, bail, Context as _, Result}; use async_trait::async_trait; -use client::{proto, Client, Collaborator, TypedEnvelope, UserStore}; +use client::{proto, Client, Collaborator, HostedProjectId, TypedEnvelope, UserStore}; use clock::ReplicaId; use collections::{hash_map, BTreeMap, HashMap, HashSet, VecDeque}; use copilot::Copilot; @@ -167,6 +167,7 @@ pub struct Project { prettiers_per_worktree: HashMap>>, prettier_instances: HashMap, tasks: Model, + hosted_project_id: Option, } pub enum LanguageServerToQuery { @@ -605,6 +606,7 @@ impl Project { prettiers_per_worktree: HashMap::default(), prettier_instances: HashMap::default(), tasks, + hosted_project_id: None, } }) } @@ -615,17 +617,30 @@ impl Project { user_store: Model, languages: Arc, fs: Arc, - role: proto::ChannelRole, - mut cx: AsyncAppContext, + cx: AsyncAppContext, ) -> Result> { client.authenticate_and_connect(true, &cx).await?; - let subscription = client.subscribe_to_entity(remote_id)?; let response = client .request_envelope(proto::JoinProject { project_id: remote_id, }) .await?; + Self::from_join_project_response(response, None, client, user_store, languages, fs, cx) + .await + } + async fn from_join_project_response( + response: TypedEnvelope, + hosted_project_id: Option, + client: Arc, + user_store: Model, + languages: Arc, + fs: Arc, + mut cx: AsyncAppContext, + ) -> Result> { + let remote_id = response.payload.project_id; + let role = response.payload.role(); + let subscription = client.subscribe_to_entity(remote_id)?; let this = cx.new_model(|cx| { let replica_id = response.payload.replica_id as ReplicaId; let tasks = Inventory::new(cx); @@ -714,6 +729,7 @@ impl Project { prettiers_per_worktree: HashMap::default(), prettier_instances: HashMap::default(), tasks, + hosted_project_id, }; this.set_role(role, cx); for worktree in worktrees { @@ -742,6 +758,31 @@ impl Project { Ok(this) } + pub async fn hosted( + hosted_project_id: HostedProjectId, + user_store: Model, + client: Arc, + languages: Arc, + fs: Arc, + cx: AsyncAppContext, + ) -> Result> { + let response = client + .request_envelope(proto::JoinHostedProject { + id: hosted_project_id.0, + }) + .await?; + Self::from_join_project_response( + response, + Some(hosted_project_id), + client, + user_store, + languages, + fs, + cx, + ) + .await + } + fn release(&mut self, cx: &mut AppContext) { match &self.client_state { ProjectClientState::Local => {} @@ -987,6 +1028,10 @@ impl Project { } } + pub fn hosted_project_id(&self) -> Option { + self.hosted_project_id + } + pub fn replica_id(&self) -> ReplicaId { match self.client_state { ProjectClientState::Remote { replica_id, .. } => replica_id, diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 6a986ff1f3..a45e7a5e21 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -196,6 +196,8 @@ message Envelope { GetImplementation get_implementation = 162; GetImplementationResponse get_implementation_response = 163; + + JoinHostedProject join_hosted_project = 164; } reserved 158 to 161; @@ -230,6 +232,7 @@ enum ErrorCode { CircularNesting = 10; WrongMoveTarget = 11; UnsharedItem = 12; + NoSuchProject = 13; reserved 6; } @@ -404,11 +407,17 @@ message JoinProject { uint64 project_id = 1; } +message JoinHostedProject { + uint64 id = 1; +} + message JoinProjectResponse { + uint64 project_id = 5; uint32 replica_id = 1; repeated WorktreeMetadata worktrees = 2; repeated Collaborator collaborators = 3; repeated LanguageServer language_servers = 4; + ChannelRole role = 6; } message LeaveProject { diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 8e112ab56a..38e80103ca 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -206,6 +206,7 @@ messages!( (JoinChannelChat, Foreground), (JoinChannelChatResponse, Foreground), (JoinProject, Foreground), + (JoinHostedProject, Foreground), (JoinProjectResponse, Foreground), (JoinRoom, Foreground), (JoinRoomResponse, Foreground), @@ -329,6 +330,7 @@ request_messages!( (JoinChannel, JoinRoomResponse), (JoinChannelBuffer, JoinChannelBufferResponse), (JoinChannelChat, JoinChannelChatResponse), + (JoinHostedProject, JoinProjectResponse), (JoinProject, JoinProjectResponse), (JoinRoom, JoinRoomResponse), (LeaveChannelBuffer, Ack), diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index 40c636c26f..0c3f9b9dc0 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -268,7 +268,7 @@ impl Member { this.cursor_pointer().on_mouse_down( MouseButton::Left, cx.listener(move |this, _, cx| { - crate::join_remote_project( + crate::join_in_room_project( leader_project_id, leader_user_id, this.app_state().clone(), diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 390b36a7f0..b4cb71c45a 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -15,7 +15,7 @@ use anyhow::{anyhow, Context as _, Result}; use call::{call_settings::CallSettings, ActiveCall}; use client::{ proto::{self, ErrorCode, PeerId}, - ChannelId, Client, ErrorExt, Status, TypedEnvelope, UserStore, + ChannelId, Client, ErrorExt, HostedProjectId, Status, TypedEnvelope, UserStore, }; use collections::{hash_map, HashMap, HashSet}; use derive_more::{Deref, DerefMut}; @@ -2635,7 +2635,7 @@ impl Workspace { // if they are active in another project, follow there. if let Some(project_id) = other_project_id { let app_state = self.app_state.clone(); - crate::join_remote_project(project_id, remote_participant.user.id, app_state, cx) + crate::join_in_room_project(project_id, remote_participant.user.id, app_state, cx) .detach_and_log_err(cx); } @@ -4158,7 +4158,7 @@ async fn join_channel_internal( if let Some(room) = open_room { let task = room.update(cx, |room, cx| { if let Some((project, host)) = room.most_active_project(cx) { - return Some(join_remote_project(project, host, app_state.clone(), cx)); + return Some(join_in_room_project(project, host, app_state.clone(), cx)); } None @@ -4229,7 +4229,7 @@ async fn join_channel_internal( let task = room.update(cx, |room, cx| { if let Some((project, host)) = room.most_active_project(cx) { - return Some(join_remote_project(project, host, app_state.clone(), cx)); + return Some(join_in_room_project(project, host, app_state.clone(), cx)); } // if you are the first to join a channel, share your project @@ -4464,7 +4464,56 @@ pub fn create_and_open_local_file( }) } -pub fn join_remote_project( +pub fn join_hosted_project( + hosted_project_id: HostedProjectId, + app_state: Arc, + cx: &mut AppContext, +) -> Task> { + cx.spawn(|mut cx| async move { + let existing_window = cx.update(|cx| { + cx.windows().into_iter().find_map(|window| { + let workspace = window.downcast::()?; + workspace + .read(cx) + .is_ok_and(|workspace| { + workspace.project().read(cx).hosted_project_id() == Some(hosted_project_id) + }) + .then(|| workspace) + }) + })?; + + let workspace = if let Some(existing_window) = existing_window { + existing_window + } else { + let project = Project::hosted( + hosted_project_id, + app_state.user_store.clone(), + app_state.client.clone(), + app_state.languages.clone(), + app_state.fs.clone(), + cx.clone(), + ) + .await?; + + let window_bounds_override = window_bounds_env_override(&cx); + cx.update(|cx| { + let options = (app_state.build_window_options)(window_bounds_override, None, cx); + cx.open_window(options, |cx| { + cx.new_view(|cx| Workspace::new(0, project, app_state.clone(), cx)) + }) + })? + }; + + workspace.update(&mut cx, |_, cx| { + cx.activate(true); + cx.activate_window(); + })?; + + Ok(()) + }) +} + +pub fn join_in_room_project( project_id: u64, follow_user_id: u64, app_state: Arc, From e0c66b30c85c7f846ee208391340ff5f4a3ac01a Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 4 Mar 2024 21:27:01 -0700 Subject: [PATCH 086/121] Fix panic in enclosing bracket ranges (#8870) This function was operating in the wrong co-ordinate space (c.f. #8081) Release Notes: - Fixed a panic on `ctrl-m` in a multibuffer --- crates/multi_buffer/src/multi_buffer.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 61af2d366c..60b8af4a53 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -195,7 +195,7 @@ struct Excerpt { /// /// Contains methods for getting the [`Buffer`] of the excerpt, /// as well as mapping offsets to/from buffer and multibuffer coordinates. -#[derive(Copy, Clone)] +#[derive(Clone)] pub struct MultiBufferExcerpt<'a> { excerpt: &'a Excerpt, excerpt_offset: usize, @@ -2963,7 +2963,16 @@ impl MultiBufferSnapshot { excerpt .buffer() .enclosing_bracket_ranges(excerpt.map_range_to_buffer(range)) - .filter(move |(open, close)| excerpt.contains_buffer_range(open.start..close.end)), + .filter_map(move |(open, close)| { + if excerpt.contains_buffer_range(open.start..close.end) { + Some(( + excerpt.map_range_from_buffer(open), + excerpt.map_range_from_buffer(close), + )) + } else { + None + } + }), ) } From 4b2e7745945bbffb5e1e9b7c5825c693d8034607 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 5 Mar 2024 10:43:02 +0200 Subject: [PATCH 087/121] Fix license generation and Closure LSP repo link (#8876) Release Notes: - N/A --- docs/src/languages/clojure.md | 2 +- script/generate-licenses | 4 ++-- script/licenses/zed-licenses.toml | 7 +++++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/src/languages/clojure.md b/docs/src/languages/clojure.md index 58ff9790e0..b89e122a56 100644 --- a/docs/src/languages/clojure.md +++ b/docs/src/languages/clojure.md @@ -1,4 +1,4 @@ # Clojure -- Tree Sitter: [tree-sitter-clojure](https://github.com/sogaiu/tree-sitter-clojure) +- Tree Sitter: [tree-sitter-clojure](https://github.com/prcastro/tree-sitter-clojure) - Language Server: [clojure-lsp](https://github.com/clojure-lsp/clojure-lsp) diff --git a/script/generate-licenses b/script/generate-licenses index 107da5a0da..10d85f60dc 100755 --- a/script/generate-licenses +++ b/script/generate-licenses @@ -1,6 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash -set -e +set -euo pipefail OUTPUT_FILE=$(pwd)/assets/licenses.md diff --git a/script/licenses/zed-licenses.toml b/script/licenses/zed-licenses.toml index d338e7ab0b..c4557a8c20 100644 --- a/script/licenses/zed-licenses.toml +++ b/script/licenses/zed-licenses.toml @@ -12,6 +12,7 @@ accepted = [ "Unicode-DFS-2016", "OpenSSL", "Zlib", + "BSL-1.0" ] workarounds = [ "ring", @@ -35,3 +36,9 @@ license = "BSD-3-Clause" [[fuchsia-cprng.clarify.files]] path = 'LICENSE' checksum = '03b114f53e6587a398931762ee11e2395bfdba252a329940e2c8c9e81813845b' + +[tree-sitter-hcl.clarify] +license = "Apache-2.0" +[[tree-sitter-hcl.clarify.files]] +path = 'LICENSE' +checksum = 'c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4' From 6a268e959f486a9e3e52b5a4cb0770229b9393f2 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Tue, 5 Mar 2024 17:19:52 +0800 Subject: [PATCH 088/121] Improve VCS Menu header top margin. (#8879) ## Before ![SCR-20240305-nga](https://github.com/zed-industries/zed/assets/5518/e814d770-a5b1-4289-acc4-808bf7d96690) ![SCR-20240305-nge](https://github.com/zed-industries/zed/assets/5518/abba4f7c-7e6c-447f-8e4f-fbecdf802f62) ## After ![SCR-20240305-nfp](https://github.com/zed-industries/zed/assets/5518/5539b15c-c67d-466b-9c46-44e488788b04) ![SCR-20240305-nfu](https://github.com/zed-industries/zed/assets/5518/5cfdf861-b55c-4f1e-a41f-dcd42a449488) Release Notes: - Improve VCS Menu header top margin, and use `TextMuted` color for matched count label. --- crates/vcs_menu/src/lib.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/vcs_menu/src/lib.rs b/crates/vcs_menu/src/lib.rs index d193efa18d..bd27a51bc0 100644 --- a/crates/vcs_menu/src/lib.rs +++ b/crates/vcs_menu/src/lib.rs @@ -9,7 +9,7 @@ use gpui::{ use picker::{Picker, PickerDelegate}; use std::{ops::Not, sync::Arc}; use ui::{ - h_flex, v_flex, Button, ButtonCommon, Clickable, HighlightedLabel, Label, LabelCommon, + h_flex, v_flex, Button, ButtonCommon, Clickable, Color, HighlightedLabel, Label, LabelCommon, LabelSize, ListItem, ListItemSpacing, Selectable, }; use util::ResultExt; @@ -293,11 +293,13 @@ impl PickerDelegate for BranchListDelegate { let label = if self.last_query.is_empty() { h_flex() .ml_3() - .child(Label::new("Recent branches").size(LabelSize::Small)) + .child(Label::new("Recent Branches").size(LabelSize::Small)) } else { let match_label = self.matches.is_empty().not().then(|| { let suffix = if self.matches.len() == 1 { "" } else { "es" }; - Label::new(format!("{} match{}", self.matches.len(), suffix)).size(LabelSize::Small) + Label::new(format!("{} match{}", self.matches.len(), suffix)) + .color(Color::Muted) + .size(LabelSize::Small) }); h_flex() .px_3() @@ -306,7 +308,7 @@ impl PickerDelegate for BranchListDelegate { .child(Label::new("Branches").size(LabelSize::Small)) .children(match_label) }; - Some(label.into_any()) + Some(label.mt_1().into_any()) } fn render_footer(&self, cx: &mut ViewContext>) -> Option { if self.last_query.is_empty() { From fae5e83d93c9dfc794e0d80e4f9df95b78a576b8 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Tue, 5 Mar 2024 17:32:57 +0800 Subject: [PATCH 089/121] Fix all Picker Item cursor to use `Pointer`. (#8877) Before: https://github.com/zed-industries/zed/assets/5518/84874858-7847-4fa4-b7a3-41ecc65a2f7d After: https://github.com/zed-industries/zed/assets/5518/d395ea96-aa26-4de1-8bfc-73cc43ee75cf Release Notes: - Made picker items to use `Pointer` cursor style --- crates/picker/src/picker.rs | 1 + crates/recent_projects/src/recent_projects.rs | 1 - crates/vcs_menu/src/lib.rs | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index 033f6c7661..11c2458ee1 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -325,6 +325,7 @@ impl Picker { fn render_element(&self, cx: &mut ViewContext, ix: usize) -> impl IntoElement { div() .id(("item", ix)) + .cursor_pointer() .on_click(cx.listener(move |this, event: &ClickEvent, cx| { this.handle_click(ix, event.down.modifiers.command, cx) })) diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index f2d0ad0f84..76d42c2780 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -133,7 +133,6 @@ impl Render for RecentProjects { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { v_flex() .w(rems(self.rem_width)) - .cursor_pointer() .child(self.picker.clone()) .on_mouse_down_out(cx.listener(|this, _, cx| { this.picker.update(cx, |this, cx| { diff --git a/crates/vcs_menu/src/lib.rs b/crates/vcs_menu/src/lib.rs index bd27a51bc0..2f88328084 100644 --- a/crates/vcs_menu/src/lib.rs +++ b/crates/vcs_menu/src/lib.rs @@ -67,7 +67,6 @@ impl Render for BranchList { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { v_flex() .w(rems(self.rem_width)) - .cursor_pointer() .child(self.picker.clone()) .on_mouse_down_out(cx.listener(|this, _, cx| { this.picker.update(cx, |this, cx| { From 537d92533ceeeb9f3e12e694a56cc2076c866856 Mon Sep 17 00:00:00 2001 From: Thorsten Ball Date: Tue, 5 Mar 2024 11:06:49 +0100 Subject: [PATCH 090/121] Backport `code_actions_on_format` docs and update them (#8881) Release Notes: - N/A --- docs/src/configuring_zed.md | 64 +++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/docs/src/configuring_zed.md b/docs/src/configuring_zed.md index 3323ea8c04..b2ae9ab86a 100644 --- a/docs/src/configuring_zed.md +++ b/docs/src/configuring_zed.md @@ -306,6 +306,70 @@ To override settings for a language, add an entry for that language server's nam } ``` +## Code Actions On Format + +- Description: The code actions to perform with the primary language server when formatting the buffer. +- Setting: `code_actions_on_format` +- Default: `{}`, except for Go it's `{ "source.organizeImports": true }` + +**Examples** + +1. Organize imports on format in TypeScript and TSX buffers: + +```json +{ + "languages": { + "TypeScript": { + "code_actions_on_format": { + "source.organizeImports": true + } + }, + "TSX": { + "code_actions_on_format": { + "source.organizeImports": true + } + } + } +} +``` + +2. Run ESLint `fixAll` code action when formatting (requires Zed `0.125.0`): + +```json +{ + "languages": { + "JavaScript": { + "code_actions_on_format": { + "source.fixAll.eslint": true + } + } + } +} +``` + +3. Run only a single ESLint rule when using `fixAll` (requires Zed `0.125.0`): + +```json +{ + "languages": { + "JavaScript": { + "code_actions_on_format": { + "source.fixAll.eslint": true + } + } + }, + "lsp": { + "eslint": { + "settings": { + "codeActionOnSave": { + "rules": ["import/order"] + } + } + } + } +} +``` + ## Auto close - Description: Whether or not to automatically type closing characters for you. From 0b34b1de7b74cf14d5aa03e8bb71272adad98e40 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Tue, 5 Mar 2024 21:54:48 +0800 Subject: [PATCH 091/121] Fix first/last item margin on scroll (#8880) ![output](https://github.com/zed-industries/zed/assets/5518/e39a3600-99c4-4d3c-baee-efd53a474f38) Before: https://github.com/zed-industries/zed/assets/5518/f7a4563a-504a-4a41-bfd4-21e9439cd02b After: https://github.com/zed-industries/zed/assets/5518/0ba41527-46fd-404f-8207-1b8c5cf37434 Release Notes: - Fixed first and last item margin when scroll view has padding --- crates/command_palette/src/command_palette.rs | 1 + crates/file_finder/src/file_finder.rs | 1 + crates/gpui/src/elements/list.rs | 2 +- crates/gpui/src/elements/uniform_list.rs | 8 ++++---- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 0cd7f581de..c8a98962c9 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -396,6 +396,7 @@ impl PickerDelegate for CommandPaletteDelegate { .child( h_flex() .w_full() + .py_px() .justify_between() .child(HighlightedLabel::new( command.name.clone(), diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index a8349a6335..5f2c874457 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -880,6 +880,7 @@ impl PickerDelegate for FileFinderDelegate { .child( h_flex() .gap_2() + .py_px() .child(HighlightedLabel::new(file_name, file_name_positions)) .child( HighlightedLabel::new(full_path, full_path_positions) diff --git a/crates/gpui/src/elements/list.rs b/crates/gpui/src/elements/list.rs index f449862668..90dd8da0c2 100644 --- a/crates/gpui/src/elements/list.rs +++ b/crates/gpui/src/elements/list.rs @@ -241,7 +241,7 @@ impl ListState { let mut cursor = state.items.cursor::(); cursor.seek(&Count(ix + 1), Bias::Right, &()); let bottom = cursor.start().height + padding.top; - let goal_top = px(0.).max(bottom - height); + let goal_top = px(0.).max(bottom - height + padding.bottom); cursor.seek(&Height(goal_top), Bias::Left, &()); let start_ix = cursor.start().count; diff --git a/crates/gpui/src/elements/uniform_list.rs b/crates/gpui/src/elements/uniform_list.rs index 8a6651524b..2d708f270e 100644 --- a/crates/gpui/src/elements/uniform_list.rs +++ b/crates/gpui/src/elements/uniform_list.rs @@ -221,10 +221,10 @@ impl Element for UniformList { let item_top = item_height * ix + padding.top; let item_bottom = item_top + item_height; let scroll_top = -updated_scroll_offset.y; - if item_top < scroll_top { - updated_scroll_offset.y = -item_top; - } else if item_bottom > scroll_top + list_height { - updated_scroll_offset.y = -(item_bottom - list_height); + if item_top < scroll_top + padding.top { + updated_scroll_offset.y = -(item_top) + padding.top; + } else if item_bottom > scroll_top + list_height - padding.bottom { + updated_scroll_offset.y = -(item_bottom - list_height) - padding.bottom; } scroll_offset = *updated_scroll_offset; } From d286c56ebb9d25a948867f103015d7c42ca0f4c8 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Tue, 5 Mar 2024 06:37:28 -0800 Subject: [PATCH 092/121] Optimize rustybuzz and ttf-parser in Dev (#8873) This PR improves the `draw()` time from hundreds to about 30ms, so roughly 10x. It makes Zed quite usable in Dev profile. Release Notes: - N/A --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 682908e2f6..5ca534a9ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -337,6 +337,8 @@ debug = "full" [profile.dev.package] taffy = { opt-level = 3 } cranelift-codegen = { opt-level = 3 } +rustybuzz = { opt-level = 3 } +ttf-parser = { opt-level = 3 } wasmtime-cranelift = { opt-level = 3 } [profile.release] From 2b8b913b6b0008f47145f5652b91515d81e32887 Mon Sep 17 00:00:00 2001 From: Thorsten Ball Date: Tue, 5 Mar 2024 16:59:00 +0100 Subject: [PATCH 093/121] Use correct worktree when getting permalink to line (#8888) Previously this code would call `project.visible_worktrees(cx).next` which might not necessarily return the worktree matching the currently open file. What this change does is it adds `get_repo` method on `Project` that allows us to get the `GitRepository` for the current buffer. Release Notes: - Fixed `open permalink to line` not working when multiple folders are added to the project. Co-authored-by: Mikayla --- crates/editor/src/editor.rs | 38 ++++++++++++----------------- crates/project/src/project.rs | 13 ++++++++++ crates/project_core/src/worktree.rs | 5 ++++ 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index ce15b36f6a..34291fbc33 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -89,6 +89,7 @@ pub use multi_buffer::{ use ordered_float::OrderedFloat; use parking_lot::{Mutex, RwLock}; use project::project_settings::{GitGutterSetting, ProjectSettings}; +use project::Item; use project::{FormatTrigger, Location, Project, ProjectPath, ProjectTransaction}; use rand::prelude::*; use rpc::proto::*; @@ -8657,22 +8658,23 @@ impl Editor { fn get_permalink_to_line(&mut self, cx: &mut ViewContext) -> Result { use git::permalink::{build_permalink, BuildPermalinkParams}; - let project = self.project.clone().ok_or_else(|| anyhow!("no project"))?; - let project = project.read(cx); - - let worktree = project - .visible_worktrees(cx) - .next() - .ok_or_else(|| anyhow!("no worktree"))?; - - let mut cwd = worktree.read(cx).abs_path().to_path_buf(); - cwd.push(".git"); + let (path, repo) = maybe!({ + let project_handle = self.project.as_ref()?.clone(); + let project = project_handle.read(cx); + let buffer = self.buffer().read(cx).as_singleton()?; + let path = buffer + .read(cx) + .file()? + .as_local()? + .path() + .to_str()? + .to_string(); + let repo = project.get_repo(&buffer.read(cx).project_path(cx)?, cx)?; + Some((path, repo)) + }) + .ok_or_else(|| anyhow!("unable to open git repository"))?; const REMOTE_NAME: &str = "origin"; - let repo = project - .fs() - .open_repo(&cwd) - .ok_or_else(|| anyhow!("no Git repo"))?; let origin_url = repo .lock() .remote_url(REMOTE_NAME) @@ -8681,14 +8683,6 @@ impl Editor { .lock() .head_sha() .ok_or_else(|| anyhow!("failed to read HEAD SHA"))?; - - let path = maybe!({ - let buffer = self.buffer().read(cx).as_singleton()?; - let file = buffer.read(cx).file().and_then(|f| f.as_local())?; - file.path().to_str().map(|path| path.to_string()) - }) - .ok_or_else(|| anyhow!("failed to determine file path"))?; - let selections = self.selections.all::(cx); let selection = selections.iter().peekable().next(); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 8661ceada0..aff690b348 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -16,6 +16,7 @@ use clock::ReplicaId; use collections::{hash_map, BTreeMap, HashMap, HashSet, VecDeque}; use copilot::Copilot; use debounced_delay::DebouncedDelay; +use fs::repository::GitRepository; use futures::{ channel::mpsc::{self, UnboundedReceiver}, future::{try_join_all, Shared}, @@ -7302,6 +7303,18 @@ impl Project { }) } + pub fn get_repo( + &self, + project_path: &ProjectPath, + cx: &AppContext, + ) -> Option>> { + self.worktree_for_id(project_path.worktree_id, cx)? + .read(cx) + .as_local()? + .snapshot() + .local_git_repo(&project_path.path) + } + // RPC message handlers async fn handle_unshare_project( diff --git a/crates/project_core/src/worktree.rs b/crates/project_core/src/worktree.rs index e5d50557f9..1f9e9a8e15 100644 --- a/crates/project_core/src/worktree.rs +++ b/crates/project_core/src/worktree.rs @@ -2116,6 +2116,11 @@ impl LocalSnapshot { Some((path, self.git_repositories.get(&repo.work_directory_id())?)) } + pub fn local_git_repo(&self, path: &Path) -> Option>> { + self.local_repo_for_path(path) + .map(|(_, entry)| entry.repo_ptr.clone()) + } + fn build_update( &self, project_id: u64, From 7c9f680b1b6babf2aa1ebd14883e40f38413f7be Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 5 Mar 2024 09:15:08 -0700 Subject: [PATCH 094/121] Request more resources for collab pods on Kubernetes (#8890) Worried that if we don't do this, they don't give us enough. We're maxing out the pod's CPU but the node is barely sweating. Release Notes: - N/A --- crates/collab/k8s/collab.template.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/collab/k8s/collab.template.yml b/crates/collab/k8s/collab.template.yml index 4915c6c97c..42590255cc 100644 --- a/crates/collab/k8s/collab.template.yml +++ b/crates/collab/k8s/collab.template.yml @@ -76,6 +76,10 @@ spec: port: 8080 initialDelaySeconds: 1 periodSeconds: 1 + resources: + requests: + cpu: "46" + memory: "90Gi" env: - name: HTTP_PORT value: "8080" From 36c48318065d61812f4c7d682cdaf63b9ebd6cf7 Mon Sep 17 00:00:00 2001 From: Ezekiel Warren Date: Tue, 5 Mar 2024 08:35:07 -0800 Subject: [PATCH 095/121] windows: mouse and keyboard (#8791) Windows mouse and keyboard working! I also tweaked the message loop so that it didn't get stuck. The peek message loop was almost never returning for me during testing. Release Notes: - Added windows mouse and keyboard support ![windows-mouse-and-keyboard](https://github.com/zed-industries/zed/assets/1284289/08578fbf-0cb2-4e44-bab1-3c4f0291ea4b) --- Cargo.toml | 4 + crates/gpui/src/platform/windows/platform.rs | 148 ++++- crates/gpui/src/platform/windows/util.rs | 18 + crates/gpui/src/platform/windows/window.rs | 534 ++++++++++++++++--- 4 files changed, 612 insertions(+), 92 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5ca534a9ee..e0887458f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -314,8 +314,12 @@ version = "0.53.0" features = [ "Win32_Graphics_Gdi", "Win32_UI_WindowsAndMessaging", + "Win32_UI_Input_KeyboardAndMouse", + "Win32_System_SystemServices", "Win32_Security", "Win32_System_Threading", + "Win32_System_DataExchange", + "Win32_System_Ole", ] diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index be3d789813..26fda0be37 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -4,6 +4,7 @@ use std::{ cell::RefCell, collections::HashSet, + ffi::{c_uint, c_void}, path::{Path, PathBuf}, rc::Rc, sync::Arc, @@ -15,27 +16,39 @@ use async_task::Runnable; use futures::channel::oneshot::Receiver; use parking_lot::Mutex; use time::UtcOffset; -use util::SemanticVersion; +use util::{ResultExt, SemanticVersion}; use windows::Win32::{ - Foundation::{CloseHandle, HANDLE, HWND}, + Foundation::{CloseHandle, GetLastError, HANDLE, HWND, WAIT_EVENT}, System::Threading::{CreateEventW, INFINITE}, UI::WindowsAndMessaging::{ - DispatchMessageW, MsgWaitForMultipleObjects, PeekMessageW, PostQuitMessage, - TranslateMessage, MSG, PM_REMOVE, QS_ALLINPUT, WM_QUIT, + DispatchMessageW, GetMessageW, MsgWaitForMultipleObjects, PostQuitMessage, + SystemParametersInfoW, TranslateMessage, MSG, QS_ALLINPUT, SPI_GETWHEELSCROLLCHARS, + SPI_GETWHEELSCROLLLINES, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, WM_QUIT, WM_SETTINGCHANGE, }, }; use crate::{ - Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, ForegroundExecutor, - Keymap, Menu, PathPromptOptions, Platform, PlatformDisplay, PlatformInput, PlatformTextSystem, - PlatformWindow, Task, WindowAppearance, WindowOptions, WindowsDispatcher, WindowsDisplay, - WindowsTextSystem, WindowsWindow, + try_get_window_inner, Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, + ForegroundExecutor, Keymap, Menu, PathPromptOptions, Platform, PlatformDisplay, PlatformInput, + PlatformTextSystem, PlatformWindow, Task, WindowAppearance, WindowOptions, WindowsDispatcher, + WindowsDisplay, WindowsTextSystem, WindowsWindow, }; pub(crate) struct WindowsPlatform { inner: Rc, } +/// Windows settings pulled from SystemParametersInfo +/// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow +#[derive(Default, Debug)] +pub(crate) struct WindowsPlatformSystemSettings { + /// SEE: SPI_GETWHEELSCROLLCHARS + pub(crate) wheel_scroll_chars: u32, + + /// SEE: SPI_GETWHEELSCROLLLINES + pub(crate) wheel_scroll_lines: u32, +} + pub(crate) struct WindowsPlatformInner { background_executor: BackgroundExecutor, pub(crate) foreground_executor: ForegroundExecutor, @@ -44,6 +57,7 @@ pub(crate) struct WindowsPlatformInner { callbacks: Mutex, pub(crate) window_handles: RefCell>, pub(crate) event: HANDLE, + pub(crate) settings: RefCell, } impl Drop for WindowsPlatformInner { @@ -65,6 +79,57 @@ struct Callbacks { validate_app_menu_command: Option bool>>, } +enum WindowsMessageWaitResult { + ForegroundExecution, + WindowsMessage(MSG), + Error, +} + +impl WindowsPlatformSystemSettings { + fn new() -> Self { + let mut settings = Self::default(); + settings.update_all(); + settings + } + + pub(crate) fn update_all(&mut self) { + self.update_wheel_scroll_lines(); + self.update_wheel_scroll_chars(); + } + + pub(crate) fn update_wheel_scroll_lines(&mut self) { + let mut value = c_uint::default(); + let result = unsafe { + SystemParametersInfoW( + SPI_GETWHEELSCROLLLINES, + 0, + Some((&mut value) as *mut c_uint as *mut c_void), + SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS::default(), + ) + }; + + if result.log_err() != None { + self.wheel_scroll_lines = value; + } + } + + pub(crate) fn update_wheel_scroll_chars(&mut self) { + let mut value = c_uint::default(); + let result = unsafe { + SystemParametersInfoW( + SPI_GETWHEELSCROLLCHARS, + 0, + Some((&mut value) as *mut c_uint as *mut c_void), + SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS::default(), + ) + }; + + if result.log_err() != None { + self.wheel_scroll_chars = value; + } + } +} + impl WindowsPlatform { pub(crate) fn new() -> Self { let (main_sender, main_receiver) = flume::unbounded::(); @@ -75,6 +140,7 @@ impl WindowsPlatform { let text_system = Arc::new(WindowsTextSystem::new()); let callbacks = Mutex::new(Callbacks::default()); let window_handles = RefCell::new(HashSet::new()); + let settings = RefCell::new(WindowsPlatformSystemSettings::new()); let inner = Rc::new(WindowsPlatformInner { background_executor, foreground_executor, @@ -83,9 +149,44 @@ impl WindowsPlatform { callbacks, window_handles, event, + settings, }); Self { inner } } + + /// runs message handlers that should be processed before dispatching to prevent translating unnecessary messages + /// returns true if message is handled and should not dispatch + fn run_immediate_msg_handlers(&self, msg: &MSG) -> bool { + if msg.message == WM_SETTINGCHANGE { + self.inner.settings.borrow_mut().update_all(); + return true; + } + + if let Some(inner) = try_get_window_inner(msg.hwnd) { + inner.handle_immediate_msg(msg.message, msg.wParam, msg.lParam) + } else { + false + } + } + + fn wait_message(&self) -> WindowsMessageWaitResult { + let wait_result = unsafe { + MsgWaitForMultipleObjects(Some(&[self.inner.event]), false, INFINITE, QS_ALLINPUT) + }; + + match wait_result { + WAIT_EVENT(0) => WindowsMessageWaitResult::ForegroundExecution, + WAIT_EVENT(1) => { + let mut msg = MSG::default(); + unsafe { GetMessageW(&mut msg, HWND::default(), 0, 0) }; + WindowsMessageWaitResult::WindowsMessage(msg) + } + _ => { + log::error!("unhandled windows wait message: {}", wait_result.0); + WindowsMessageWaitResult::Error + } + } + } } impl Platform for WindowsPlatform { @@ -103,22 +204,27 @@ impl Platform for WindowsPlatform { fn run(&self, on_finish_launching: Box) { on_finish_launching(); - 'a: loop { - unsafe { - MsgWaitForMultipleObjects(Some(&[self.inner.event]), false, INFINITE, QS_ALLINPUT) - }; - let mut msg = MSG::default(); - while unsafe { PeekMessageW(&mut msg, HWND::default(), 0, 0, PM_REMOVE) }.as_bool() { - if msg.message == WM_QUIT { - break 'a; + loop { + match self.wait_message() { + WindowsMessageWaitResult::ForegroundExecution => { + for runnable in self.inner.main_receiver.drain() { + runnable.run(); + } } - unsafe { TranslateMessage(&msg) }; - unsafe { DispatchMessageW(&msg) }; - } - while let Ok(runnable) = self.inner.main_receiver.try_recv() { - runnable.run(); + WindowsMessageWaitResult::WindowsMessage(msg) => { + if msg.message == WM_QUIT { + break; + } + + if !self.run_immediate_msg_handlers(&msg) { + unsafe { TranslateMessage(&msg) }; + unsafe { DispatchMessageW(&msg) }; + } + } + WindowsMessageWaitResult::Error => {} } } + let mut callbacks = self.inner.callbacks.lock(); if let Some(callback) = callbacks.quit.as_mut() { callback() diff --git a/crates/gpui/src/platform/windows/util.rs b/crates/gpui/src/platform/windows/util.rs index bba6f132ab..3a3ebccb64 100644 --- a/crates/gpui/src/platform/windows/util.rs +++ b/crates/gpui/src/platform/windows/util.rs @@ -3,6 +3,8 @@ use windows::Win32::Foundation::{LPARAM, WPARAM}; pub(crate) trait HiLoWord { fn hiword(&self) -> u16; fn loword(&self) -> u16; + fn signed_hiword(&self) -> i16; + fn signed_loword(&self) -> i16; } impl HiLoWord for WPARAM { @@ -13,6 +15,14 @@ impl HiLoWord for WPARAM { fn loword(&self) -> u16 { (self.0 & 0xFFFF) as u16 } + + fn signed_hiword(&self) -> i16 { + ((self.0 >> 16) & 0xFFFF) as i16 + } + + fn signed_loword(&self) -> i16 { + (self.0 & 0xFFFF) as i16 + } } impl HiLoWord for LPARAM { @@ -23,4 +33,12 @@ impl HiLoWord for LPARAM { fn loword(&self) -> u16 { (self.0 & 0xFFFF) as u16 } + + fn signed_hiword(&self) -> i16 { + ((self.0 >> 16) & 0xFFFF) as i16 + } + + fn signed_loword(&self) -> i16 { + (self.0 & 0xFFFF) as i16 + } } diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index f43ac7cec8..162db74a31 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -18,24 +18,58 @@ use windows::{ core::{w, HSTRING, PCWSTR}, Win32::{ Foundation::{HINSTANCE, HWND, LPARAM, LRESULT, WPARAM}, - UI::WindowsAndMessaging::{ - CreateWindowExW, DefWindowProcW, GetWindowLongPtrW, LoadCursorW, PostQuitMessage, - RegisterClassW, SetWindowLongPtrW, SetWindowTextW, ShowWindow, CREATESTRUCTW, - CW_USEDEFAULT, GWLP_USERDATA, HMENU, IDC_ARROW, SW_MAXIMIZE, SW_SHOW, WINDOW_EX_STYLE, - WINDOW_LONG_PTR_INDEX, WM_CLOSE, WM_DESTROY, WM_MOVE, WM_NCCREATE, WM_NCDESTROY, - WM_PAINT, WM_SIZE, WNDCLASSW, WS_OVERLAPPEDWINDOW, WS_VISIBLE, + System::SystemServices::{ + MK_LBUTTON, MK_MBUTTON, MK_RBUTTON, MK_XBUTTON1, MK_XBUTTON2, MODIFIERKEYS_FLAGS, + }, + UI::{ + Input::KeyboardAndMouse::{ + GetKeyState, VIRTUAL_KEY, VK_BACK, VK_CONTROL, VK_DOWN, VK_END, VK_ESCAPE, VK_F1, + VK_F24, VK_HOME, VK_INSERT, VK_LEFT, VK_LWIN, VK_MENU, VK_NEXT, VK_PRIOR, + VK_RETURN, VK_RIGHT, VK_RWIN, VK_SHIFT, VK_SPACE, VK_TAB, VK_UP, + }, + WindowsAndMessaging::{ + CreateWindowExW, DefWindowProcW, GetWindowLongPtrW, LoadCursorW, PostQuitMessage, + RegisterClassW, SetWindowLongPtrW, SetWindowTextW, ShowWindow, CREATESTRUCTW, + CW_USEDEFAULT, GWLP_USERDATA, HMENU, IDC_ARROW, SW_MAXIMIZE, SW_SHOW, WHEEL_DELTA, + WINDOW_EX_STYLE, WINDOW_LONG_PTR_INDEX, WM_CHAR, WM_CLOSE, WM_DESTROY, WM_KEYDOWN, + WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, + WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_MOVE, WM_NCCREATE, WM_NCDESTROY, + WM_PAINT, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SIZE, WM_SYSCHAR, WM_SYSKEYDOWN, + WM_SYSKEYUP, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSW, WS_OVERLAPPEDWINDOW, + WS_VISIBLE, XBUTTON1, XBUTTON2, + }, }, }, }; use crate::{ - platform::blade::BladeRenderer, AnyWindowHandle, Bounds, GlobalPixels, HiLoWord, Modifiers, - Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, - Point, PromptLevel, Scene, Size, WindowAppearance, WindowBounds, WindowOptions, WindowsDisplay, - WindowsPlatformInner, + platform::blade::BladeRenderer, AnyWindowHandle, Bounds, GlobalPixels, HiLoWord, KeyDownEvent, + KeyUpEvent, Keystroke, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, + NavigationDirection, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, + PlatformInputHandler, PlatformWindow, Point, PromptLevel, Scene, ScrollDelta, Size, TouchPhase, + WindowAppearance, WindowBounds, WindowOptions, WindowsDisplay, WindowsPlatformInner, }; -struct WindowsWindowInner { +#[derive(PartialEq)] +pub(crate) enum CallbackResult { + /// handled by system or user callback + Handled { + /// `true` if user callback handled event + by_callback: bool, + }, + Unhandled, +} + +impl CallbackResult { + pub fn is_handled(&self) -> bool { + match self { + Self::Handled { by_callback: _ } => true, + _ => false, + } + } +} + +pub(crate) struct WindowsWindowInner { hwnd: HWND, origin: Cell>, size: Cell>, @@ -109,76 +143,424 @@ impl WindowsWindowInner { } } + fn is_virtual_key_pressed(&self, vkey: VIRTUAL_KEY) -> bool { + unsafe { GetKeyState(vkey.0 as i32) < 0 } + } + + fn current_modifiers(&self) -> Modifiers { + Modifiers { + control: self.is_virtual_key_pressed(VK_CONTROL), + alt: self.is_virtual_key_pressed(VK_MENU), + shift: self.is_virtual_key_pressed(VK_SHIFT), + command: self.is_virtual_key_pressed(VK_LWIN) || self.is_virtual_key_pressed(VK_RWIN), + function: false, + } + } + + /// returns true if message is handled and should not dispatch + pub(crate) fn handle_immediate_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> bool { + match msg { + WM_KEYDOWN | WM_SYSKEYDOWN => self.handle_keydown_msg(wparam).is_handled(), + WM_KEYUP | WM_SYSKEYUP => self.handle_keyup_msg(wparam).is_handled(), + _ => false, + } + } + fn handle_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT { log::debug!("msg: {msg}, wparam: {}, lparam: {}", wparam.0, lparam.0); match msg { - WM_MOVE => { - let x = lparam.loword() as f64; - let y = lparam.hiword() as f64; - self.origin.set(Point::new(x.into(), y.into())); - let mut callbacks = self.callbacks.borrow_mut(); - if let Some(callback) = callbacks.moved.as_mut() { - callback() + WM_MOVE => self.handle_move_msg(lparam), + WM_SIZE => self.handle_size_msg(lparam), + WM_PAINT => self.handle_paint_msg(), + WM_CLOSE => self.handle_close_msg(msg, wparam, lparam), + WM_DESTROY => self.handle_destroy_msg(), + WM_MOUSEMOVE => self.handle_mouse_move_msg(lparam, wparam), + WM_LBUTTONDOWN => self.handle_mouse_down_msg(MouseButton::Left, lparam), + WM_RBUTTONDOWN => self.handle_mouse_down_msg(MouseButton::Right, lparam), + WM_MBUTTONDOWN => self.handle_mouse_down_msg(MouseButton::Middle, lparam), + WM_XBUTTONDOWN => { + let nav_dir = match wparam.hiword() { + XBUTTON1 => Some(NavigationDirection::Forward), + XBUTTON2 => Some(NavigationDirection::Back), + _ => None, + }; + + if let Some(nav_dir) = nav_dir { + self.handle_mouse_down_msg(MouseButton::Navigate(nav_dir), lparam) + } else { + LRESULT(1) } } - WM_SIZE => { - // todo!("windows"): handle maximized or minimized - let width = lparam.loword().max(1) as f64; - let height = lparam.hiword().max(1) as f64; - self.renderer - .borrow_mut() - .update_drawable_size(Size { width, height }); - let width = width.into(); - let height = height.into(); - self.size.set(Size { width, height }); - let mut callbacks = self.callbacks.borrow_mut(); - if let Some(callback) = callbacks.resize.as_mut() { - callback( - Size { - width: Pixels(width.0), - height: Pixels(height.0), - }, - 1.0, - ) + WM_LBUTTONUP => self.handle_mouse_up_msg(MouseButton::Left, lparam), + WM_RBUTTONUP => self.handle_mouse_up_msg(MouseButton::Right, lparam), + WM_MBUTTONUP => self.handle_mouse_up_msg(MouseButton::Middle, lparam), + WM_XBUTTONUP => { + let nav_dir = match wparam.hiword() { + XBUTTON1 => Some(NavigationDirection::Back), + XBUTTON2 => Some(NavigationDirection::Forward), + _ => None, + }; + + if let Some(nav_dir) = nav_dir { + self.handle_mouse_up_msg(MouseButton::Navigate(nav_dir), lparam) + } else { + LRESULT(1) } } - WM_PAINT => { - let mut callbacks = self.callbacks.borrow_mut(); - if let Some(callback) = callbacks.request_frame.as_mut() { - callback() - } - } - WM_CLOSE => { - let mut callbacks: std::cell::RefMut<'_, Callbacks> = self.callbacks.borrow_mut(); - if let Some(callback) = callbacks.should_close.as_mut() { - if callback() { - return LRESULT(0); - } - } - drop(callbacks); - return unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) }; - } - WM_DESTROY => { - let mut callbacks: std::cell::RefMut<'_, Callbacks> = self.callbacks.borrow_mut(); - if let Some(callback) = callbacks.close.take() { - callback() - } - let mut window_handles = self.platform_inner.window_handles.borrow_mut(); - window_handles.remove(&self.handle); - if window_handles.is_empty() { - self.platform_inner - .foreground_executor - .spawn(async { - unsafe { PostQuitMessage(0) }; - }) - .detach(); - } - return LRESULT(1); - } - _ => return unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) }, + WM_MOUSEWHEEL => self.handle_mouse_wheel_msg(wparam, lparam), + WM_MOUSEHWHEEL => self.handle_mouse_horizontal_wheel_msg(wparam, lparam), + WM_CHAR | WM_SYSCHAR => self.handle_char_msg(wparam), + // These events are handled by the immediate handler + WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP => LRESULT(0), + _ => unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) }, + } + } + + fn handle_move_msg(&self, lparam: LPARAM) -> LRESULT { + let x = lparam.signed_loword() as f64; + let y = lparam.signed_hiword() as f64; + self.origin.set(Point::new(x.into(), y.into())); + let mut callbacks = self.callbacks.borrow_mut(); + if let Some(callback) = callbacks.moved.as_mut() { + callback() } LRESULT(0) } + + fn handle_size_msg(&self, lparam: LPARAM) -> LRESULT { + let width = lparam.loword().max(1) as f64; + let height = lparam.hiword().max(1) as f64; + self.renderer + .borrow_mut() + .update_drawable_size(Size { width, height }); + let width = width.into(); + let height = height.into(); + self.size.set(Size { width, height }); + let mut callbacks = self.callbacks.borrow_mut(); + if let Some(callback) = callbacks.resize.as_mut() { + callback( + Size { + width: Pixels(width.0), + height: Pixels(height.0), + }, + 1.0, + ) + } + LRESULT(0) + } + + fn handle_paint_msg(&self) -> LRESULT { + let mut callbacks = self.callbacks.borrow_mut(); + if let Some(callback) = callbacks.request_frame.as_mut() { + callback() + } + LRESULT(0) + } + + fn handle_close_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT { + let mut callbacks = self.callbacks.borrow_mut(); + if let Some(callback) = callbacks.should_close.as_mut() { + if callback() { + return LRESULT(0); + } + } + drop(callbacks); + unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) } + } + + fn handle_destroy_msg(&self) -> LRESULT { + let mut callbacks = self.callbacks.borrow_mut(); + if let Some(callback) = callbacks.close.take() { + callback() + } + let mut window_handles = self.platform_inner.window_handles.borrow_mut(); + window_handles.remove(&self.handle); + if window_handles.is_empty() { + self.platform_inner + .foreground_executor + .spawn(async { + unsafe { PostQuitMessage(0) }; + }) + .detach(); + } + LRESULT(1) + } + + fn handle_mouse_move_msg(&self, lparam: LPARAM, wparam: WPARAM) -> LRESULT { + let x = Pixels::from(lparam.signed_loword() as f32); + let y = Pixels::from(lparam.signed_hiword() as f32); + self.mouse_position.set(Point { x, y }); + let mut callbacks = self.callbacks.borrow_mut(); + if let Some(callback) = callbacks.input.as_mut() { + let pressed_button = match MODIFIERKEYS_FLAGS(wparam.loword() as u32) { + flags if flags.contains(MK_LBUTTON) => Some(MouseButton::Left), + flags if flags.contains(MK_RBUTTON) => Some(MouseButton::Right), + flags if flags.contains(MK_MBUTTON) => Some(MouseButton::Middle), + flags if flags.contains(MK_XBUTTON1) => { + Some(MouseButton::Navigate(NavigationDirection::Back)) + } + flags if flags.contains(MK_XBUTTON2) => { + Some(MouseButton::Navigate(NavigationDirection::Forward)) + } + _ => None, + }; + let event = MouseMoveEvent { + position: Point { x, y }, + pressed_button, + modifiers: self.current_modifiers(), + }; + if callback(PlatformInput::MouseMove(event)) { + return LRESULT(0); + } + } + LRESULT(1) + } + + fn parse_key_msg_keystroke(&self, wparam: WPARAM) -> Option { + let vk_code = wparam.loword(); + + // 0-9 https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes + if vk_code >= 0x30 && vk_code <= 0x39 { + let modifiers = self.current_modifiers(); + + if modifiers.shift { + return None; + } + + let digit_char = (b'0' + ((vk_code - 0x30) as u8)) as char; + return Some(Keystroke { + modifiers, + key: digit_char.to_string(), + ime_key: Some(digit_char.to_string()), + }); + } + + // A-Z https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes + if vk_code >= 0x41 && vk_code <= 0x5A { + let offset = (vk_code - 0x41) as u8; + let alpha_char = (b'a' + offset) as char; + let alpha_char_upper = (b'A' + offset) as char; + let modifiers = self.current_modifiers(); + return Some(Keystroke { + modifiers, + key: alpha_char.to_string(), + ime_key: Some(if modifiers.shift { + alpha_char_upper.to_string() + } else { + alpha_char.to_string() + }), + }); + } + + if vk_code >= VK_F1.0 && vk_code <= VK_F24.0 { + let offset = vk_code - VK_F1.0; + return Some(Keystroke { + modifiers: self.current_modifiers(), + key: format!("f{}", offset + 1), + ime_key: None, + }); + } + + let key = match VIRTUAL_KEY(vk_code) { + VK_SPACE => Some(("space", Some(" "))), + VK_TAB => Some(("tab", Some("\t"))), + VK_BACK => Some(("backspace", None)), + VK_RETURN => Some(("enter", None)), + VK_UP => Some(("up", None)), + VK_DOWN => Some(("down", None)), + VK_RIGHT => Some(("right", None)), + VK_LEFT => Some(("left", None)), + VK_HOME => Some(("home", None)), + VK_END => Some(("end", None)), + VK_PRIOR => Some(("pageup", None)), + VK_NEXT => Some(("pagedown", None)), + VK_ESCAPE => Some(("escape", None)), + VK_INSERT => Some(("insert", None)), + _ => None, + }; + + if let Some((key, ime_key)) = key { + Some(Keystroke { + modifiers: self.current_modifiers(), + key: key.to_string(), + ime_key: ime_key.map(|k| k.to_string()), + }) + } else { + None + } + } + + fn handle_keydown_msg(&self, wparam: WPARAM) -> CallbackResult { + let mut callbacks = self.callbacks.borrow_mut(); + let keystroke = self.parse_key_msg_keystroke(wparam); + if let Some(keystroke) = keystroke { + if let Some(callback) = callbacks.input.as_mut() { + let ime_key = keystroke.ime_key.clone(); + let event = KeyDownEvent { + keystroke, + is_held: true, + }; + + if callback(PlatformInput::KeyDown(event)) { + if let Some(request_frame) = callbacks.request_frame.as_mut() { + request_frame(); + } + CallbackResult::Handled { by_callback: true } + } else if let Some(mut input_handler) = self.input_handler.take() { + if let Some(ime_key) = ime_key { + input_handler.replace_text_in_range(None, &ime_key); + } + self.input_handler.set(Some(input_handler)); + if let Some(request_frame) = callbacks.request_frame.as_mut() { + request_frame(); + } + CallbackResult::Handled { by_callback: true } + } else { + CallbackResult::Handled { by_callback: false } + } + } else { + CallbackResult::Handled { by_callback: false } + } + } else { + CallbackResult::Unhandled + } + } + + fn handle_keyup_msg(&self, wparam: WPARAM) -> CallbackResult { + let mut callbacks = self.callbacks.borrow_mut(); + let keystroke = self.parse_key_msg_keystroke(wparam); + if let Some(keystroke) = keystroke { + if let Some(callback) = callbacks.input.as_mut() { + let event = KeyUpEvent { keystroke }; + CallbackResult::Handled { + by_callback: callback(PlatformInput::KeyUp(event)), + } + } else { + CallbackResult::Handled { by_callback: false } + } + } else { + CallbackResult::Unhandled + } + } + + fn handle_char_msg(&self, wparam: WPARAM) -> LRESULT { + let mut callbacks = self.callbacks.borrow_mut(); + if let Some(callback) = callbacks.input.as_mut() { + let modifiers = self.current_modifiers(); + let msg_char = wparam.0 as u8 as char; + let keystroke = Keystroke { + modifiers, + key: msg_char.to_string(), + ime_key: Some(msg_char.to_string()), + }; + let ime_key = keystroke.ime_key.clone(); + let event = KeyDownEvent { + keystroke, + is_held: false, + }; + + if callback(PlatformInput::KeyDown(event)) { + return LRESULT(0); + } + + if let Some(mut input_handler) = self.input_handler.take() { + if let Some(ime_key) = ime_key { + input_handler.replace_text_in_range(None, &ime_key); + } + self.input_handler.set(Some(input_handler)); + return LRESULT(0); + } + } + return LRESULT(1); + } + + fn handle_mouse_down_msg(&self, button: MouseButton, lparam: LPARAM) -> LRESULT { + let mut callbacks = self.callbacks.borrow_mut(); + if let Some(callback) = callbacks.input.as_mut() { + let x = Pixels::from(lparam.signed_loword() as f32); + let y = Pixels::from(lparam.signed_hiword() as f32); + let event = MouseDownEvent { + button, + position: Point { x, y }, + modifiers: self.current_modifiers(), + click_count: 1, + }; + if callback(PlatformInput::MouseDown(event)) { + return LRESULT(0); + } + } + LRESULT(1) + } + + fn handle_mouse_up_msg(&self, button: MouseButton, lparam: LPARAM) -> LRESULT { + let mut callbacks = self.callbacks.borrow_mut(); + if let Some(callback) = callbacks.input.as_mut() { + let x = Pixels::from(lparam.signed_loword() as f32); + let y = Pixels::from(lparam.signed_hiword() as f32); + let event = MouseUpEvent { + button, + position: Point { x, y }, + modifiers: self.current_modifiers(), + click_count: 1, + }; + if callback(PlatformInput::MouseUp(event)) { + return LRESULT(0); + } + } + LRESULT(1) + } + + fn handle_mouse_wheel_msg(&self, wparam: WPARAM, lparam: LPARAM) -> LRESULT { + let mut callbacks = self.callbacks.borrow_mut(); + if let Some(callback) = callbacks.input.as_mut() { + let x = Pixels::from(lparam.signed_loword() as f32); + let y = Pixels::from(lparam.signed_hiword() as f32); + let wheel_distance = (wparam.signed_hiword() as f32 / WHEEL_DELTA as f32) + * self.platform_inner.settings.borrow().wheel_scroll_lines as f32; + let event = crate::ScrollWheelEvent { + position: Point { x, y }, + delta: ScrollDelta::Lines(Point { + x: 0.0, + y: wheel_distance, + }), + modifiers: self.current_modifiers(), + touch_phase: TouchPhase::Moved, + }; + if callback(PlatformInput::ScrollWheel(event)) { + if let Some(request_frame) = callbacks.request_frame.as_mut() { + request_frame(); + } + return LRESULT(0); + } + } + LRESULT(1) + } + + fn handle_mouse_horizontal_wheel_msg(&self, wparam: WPARAM, lparam: LPARAM) -> LRESULT { + let mut callbacks = self.callbacks.borrow_mut(); + if let Some(callback) = callbacks.input.as_mut() { + let x = Pixels::from(lparam.signed_loword() as f32); + let y = Pixels::from(lparam.signed_hiword() as f32); + let wheel_distance = (wparam.signed_hiword() as f32 / WHEEL_DELTA as f32) + * self.platform_inner.settings.borrow().wheel_scroll_chars as f32; + let event = crate::ScrollWheelEvent { + position: Point { x, y }, + delta: ScrollDelta::Lines(Point { + x: wheel_distance, + y: 0.0, + }), + modifiers: self.current_modifiers(), + touch_phase: TouchPhase::Moved, + }; + if callback(PlatformInput::ScrollWheel(event)) { + if let Some(request_frame) = callbacks.request_frame.as_mut() { + request_frame(); + } + return LRESULT(0); + } + } + LRESULT(1) + } } #[derive(Default)] @@ -512,6 +894,16 @@ unsafe extern "system" fn wnd_proc( r } +pub(crate) fn try_get_window_inner(hwnd: HWND) -> Option> { + let ptr = unsafe { get_window_long(hwnd, GWLP_USERDATA) } as *mut Weak; + if !ptr.is_null() { + let inner = unsafe { &*ptr }; + inner.upgrade() + } else { + None + } +} + unsafe fn get_window_long(hwnd: HWND, nindex: WINDOW_LONG_PTR_INDEX) -> isize { #[cfg(target_pointer_width = "64")] unsafe { From 52f750b2164b6c7b94da82f28ce79b1cb647b4cd Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Tue, 5 Mar 2024 08:48:34 -0800 Subject: [PATCH 096/121] Update blade to latest: work around Intel+NVidia driver bug (#8811) Picks up https://github.com/kvark/blade/pull/92 Should unblock some of the unhappy users. Upstream bug - https://gitlab.freedesktop.org/mesa/mesa/-/issues/4688 Release Notes: - N/A --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- crates/gpui/src/platform/blade/blade_renderer.rs | 1 + crates/gpui/src/platform/linux/wayland/window.rs | 1 + crates/gpui/src/platform/linux/x11/window.rs | 1 + crates/gpui/src/platform/windows/window.rs | 1 + 6 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa6eb75f2c..3bbc622c37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1451,7 +1451,7 @@ dependencies = [ [[package]] name = "blade-graphics" version = "0.3.0" -source = "git+https://github.com/kvark/blade?rev=e9d93a4d41f3946a03ffb76136290d6ccf7f2b80#e9d93a4d41f3946a03ffb76136290d6ccf7f2b80" +source = "git+https://github.com/kvark/blade?rev=43721bf42d298b7cbee2195ee66f73a5f1c7b2fc#43721bf42d298b7cbee2195ee66f73a5f1c7b2fc" dependencies = [ "ash", "ash-window", @@ -1481,7 +1481,7 @@ dependencies = [ [[package]] name = "blade-macros" version = "0.2.1" -source = "git+https://github.com/kvark/blade?rev=e9d93a4d41f3946a03ffb76136290d6ccf7f2b80#e9d93a4d41f3946a03ffb76136290d6ccf7f2b80" +source = "git+https://github.com/kvark/blade?rev=43721bf42d298b7cbee2195ee66f73a5f1c7b2fc#43721bf42d298b7cbee2195ee66f73a5f1c7b2fc" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index e0887458f4..40364b9528 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -197,8 +197,8 @@ async-compression = { version = "0.4", features = ["gzip", "futures-io"] } async-tar = "0.4.2" async-trait = "0.1" bitflags = "2.4.2" -blade-graphics = { git = "https://github.com/kvark/blade", rev = "e9d93a4d41f3946a03ffb76136290d6ccf7f2b80" } -blade-macros = { git = "https://github.com/kvark/blade", rev = "e9d93a4d41f3946a03ffb76136290d6ccf7f2b80" } +blade-graphics = { git = "https://github.com/kvark/blade", rev = "43721bf42d298b7cbee2195ee66f73a5f1c7b2fc" } +blade-macros = { git = "https://github.com/kvark/blade", rev = "43721bf42d298b7cbee2195ee66f73a5f1c7b2fc" } blade-rwh = { package = "raw-window-handle", version = "0.5" } chrono = { version = "0.4", features = ["serde"] } clap = "4.4" diff --git a/crates/gpui/src/platform/blade/blade_renderer.rs b/crates/gpui/src/platform/blade/blade_renderer.rs index 554479ef1c..192f487d77 100644 --- a/crates/gpui/src/platform/blade/blade_renderer.rs +++ b/crates/gpui/src/platform/blade/blade_renderer.rs @@ -60,6 +60,7 @@ pub unsafe fn new_renderer( gpu::ContextDesc { validation: cfg!(debug_assertions), capture: false, + overlay: false, }, ) .unwrap(), diff --git a/crates/gpui/src/platform/linux/wayland/window.rs b/crates/gpui/src/platform/linux/wayland/window.rs index 11e1743b03..9af7aa1ed8 100644 --- a/crates/gpui/src/platform/linux/wayland/window.rs +++ b/crates/gpui/src/platform/linux/wayland/window.rs @@ -83,6 +83,7 @@ impl WaylandWindowInner { gpu::ContextDesc { validation: false, capture: false, + overlay: false, }, ) } diff --git a/crates/gpui/src/platform/linux/x11/window.rs b/crates/gpui/src/platform/linux/x11/window.rs index f60f7f19b9..7c719c9ac7 100644 --- a/crates/gpui/src/platform/linux/x11/window.rs +++ b/crates/gpui/src/platform/linux/x11/window.rs @@ -237,6 +237,7 @@ impl X11WindowState { gpu::ContextDesc { validation: false, capture: false, + overlay: false, }, ) } diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 162db74a31..8747311d8b 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -118,6 +118,7 @@ impl WindowsWindowInner { gpu::ContextDesc { validation: false, capture: false, + overlay: false, }, ) } From 22fe03913ca11259b12eb250119dfb091bd70800 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Tue, 5 Mar 2024 12:01:17 -0500 Subject: [PATCH 097/121] Move Clippy configuration to the workspace level (#8891) This PR moves the Clippy configuration up to the workspace level. We're using the [`lints` table](https://doc.rust-lang.org/cargo/reference/workspaces.html#the-lints-table) to configure the Clippy ruleset in the workspace's `Cargo.toml`. Each crate in the workspace now has the following in their own `Cargo.toml` to inherit the lints from the workspace: ```toml [lints] workspace = true ``` This allows for configuring rust-analyzer to show Clippy lints in the editor by using the following configuration in your Zed `settings.json`: ```json { "lsp": { "rust-analyzer": { "initialization_options": { "check": { "command": "clippy" } } } } ``` Release Notes: - N/A --- Cargo.lock | 2 +- Cargo.toml | 43 +++++++++++++++- crates/activity_indicator/Cargo.toml | 3 ++ crates/ai/Cargo.toml | 3 ++ crates/assets/Cargo.toml | 3 ++ crates/assistant/Cargo.toml | 3 ++ crates/audio/Cargo.toml | 3 ++ crates/auto_update/Cargo.toml | 3 ++ crates/breadcrumbs/Cargo.toml | 3 ++ crates/call/Cargo.toml | 3 ++ crates/channel/Cargo.toml | 3 ++ crates/cli/Cargo.toml | 3 ++ crates/client/Cargo.toml | 3 ++ crates/clock/Cargo.toml | 3 ++ crates/collab/Cargo.toml | 3 ++ crates/collab_ui/Cargo.toml | 3 ++ crates/collections/Cargo.toml | 3 ++ crates/color/Cargo.toml | 3 ++ crates/command_palette/Cargo.toml | 3 ++ crates/command_palette_hooks/Cargo.toml | 3 ++ crates/copilot/Cargo.toml | 3 ++ crates/copilot_ui/Cargo.toml | 3 ++ crates/db/Cargo.toml | 3 ++ crates/diagnostics/Cargo.toml | 3 ++ crates/editor/Cargo.toml | 3 ++ crates/extension/Cargo.toml | 3 ++ crates/extension_api/Cargo.toml | 3 ++ crates/extensions_ui/Cargo.toml | 3 ++ crates/feature_flags/Cargo.toml | 3 ++ crates/feedback/Cargo.toml | 3 ++ crates/file_finder/Cargo.toml | 3 ++ crates/fs/Cargo.toml | 3 ++ crates/fsevent/Cargo.toml | 6 ++- crates/fuzzy/Cargo.toml | 3 ++ crates/git/Cargo.toml | 3 ++ crates/go_to_line/Cargo.toml | 3 ++ crates/gpui/Cargo.toml | 3 ++ crates/gpui_macros/Cargo.toml | 3 ++ crates/install_cli/Cargo.toml | 3 ++ crates/journal/Cargo.toml | 3 ++ crates/language/Cargo.toml | 3 ++ crates/language_selector/Cargo.toml | 3 ++ crates/language_tools/Cargo.toml | 3 ++ crates/languages/Cargo.toml | 3 ++ crates/live_kit_client/Cargo.toml | 3 ++ crates/live_kit_server/Cargo.toml | 3 ++ crates/lsp/Cargo.toml | 3 ++ crates/markdown_preview/Cargo.toml | 3 ++ crates/media/Cargo.toml | 3 ++ crates/menu/Cargo.toml | 3 ++ crates/multi_buffer/Cargo.toml | 3 ++ crates/node_runtime/Cargo.toml | 3 ++ crates/notifications/Cargo.toml | 3 ++ crates/outline/Cargo.toml | 3 ++ crates/picker/Cargo.toml | 3 ++ crates/prettier/Cargo.toml | 3 ++ crates/project/Cargo.toml | 3 ++ crates/project_core/Cargo.toml | 3 ++ crates/project_panel/Cargo.toml | 3 ++ crates/project_symbols/Cargo.toml | 3 ++ crates/quick_action_bar/Cargo.toml | 3 ++ crates/recent_projects/Cargo.toml | 3 ++ crates/refineable/Cargo.toml | 3 ++ .../refineable/derive_refineable/Cargo.toml | 3 ++ crates/release_channel/Cargo.toml | 3 ++ crates/rich_text/Cargo.toml | 3 ++ crates/rope/Cargo.toml | 3 ++ crates/rpc/Cargo.toml | 3 ++ crates/search/Cargo.toml | 3 ++ crates/semantic_index/Cargo.toml | 3 ++ crates/settings/Cargo.toml | 3 ++ crates/snippet/Cargo.toml | 3 ++ crates/sqlez/Cargo.toml | 3 ++ crates/sqlez_macros/Cargo.toml | 3 ++ crates/story/Cargo.toml | 3 ++ crates/storybook/Cargo.toml | 3 ++ crates/sum_tree/Cargo.toml | 3 ++ crates/task/Cargo.toml | 3 ++ crates/tasks_ui/Cargo.toml | 3 ++ crates/telemetry_events/Cargo.toml | 3 ++ crates/terminal/Cargo.toml | 3 ++ crates/terminal_view/Cargo.toml | 3 ++ crates/text/Cargo.toml | 3 ++ crates/theme/Cargo.toml | 3 ++ crates/theme_importer/Cargo.toml | 3 ++ crates/theme_selector/Cargo.toml | 3 ++ crates/time_format/Cargo.toml | 3 ++ crates/ui/Cargo.toml | 3 ++ crates/util/Cargo.toml | 3 ++ crates/vcs_menu/Cargo.toml | 3 ++ crates/vim/Cargo.toml | 3 ++ crates/welcome/Cargo.toml | 3 ++ crates/workspace/Cargo.toml | 3 ++ crates/zed/Cargo.toml | 3 ++ crates/zed_actions/Cargo.toml | 3 ++ extensions/gleam/Cargo.toml | 3 ++ tooling/xtask/Cargo.toml | 3 ++ tooling/xtask/src/main.rs | 51 ------------------- 98 files changed, 328 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3bbc622c37..9e19a6c5a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3927,7 +3927,7 @@ dependencies = [ [[package]] name = "fsevent" -version = "2.0.2" +version = "0.1.0" dependencies = [ "bitflags 2.4.2", "fsevent-sys 3.1.0", diff --git a/Cargo.toml b/Cargo.toml index 40364b9528..ae3df6cf95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -322,8 +322,6 @@ features = [ "Win32_System_Ole", ] - - [patch.crates-io] tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "e4a23971ec3071a09c1e84816954c98f96e98e52" } # Workaround for a broken nightly build of gpui: See #7644 and revisit once 0.5.3 is released. @@ -350,5 +348,46 @@ debug = "limited" lto = "thin" codegen-units = 1 +[workspace.lints.clippy] +dbg_macro = "deny" +todo = "deny" + +# These are all of the rules that currently have violations in the Zed +# codebase. +# +# We'll want to drive this list down by either: +# 1. fixing violations of the rule and begin enforcing it +# 2. deciding we want to allow the rule permanently, at which point +# we should codify that separately above. +# +# This list shouldn't be added to; it should only get shorter. +# ============================================================================= + +# There are a bunch of rules currently failing in the `style` group, so +# allow all of those, for now. +style = "allow" + +# Individual rules that have violations in the codebase: +almost_complete_range = "allow" +arc_with_non_send_sync = "allow" +await_holding_lock = "allow" +borrow_deref_ref = "allow" +borrowed_box = "allow" +cast_abs_to_unsigned = "allow" +cmp_owned = "allow" +derive_ord_xor_partial_ord = "allow" +eq_op = "allow" +implied_bounds_in_impls = "allow" +let_underscore_future = "allow" +map_entry = "allow" +never_loop = "allow" +non_canonical_clone_impl = "allow" +non_canonical_partial_ord_impl = "allow" +reversed_empty_ranges = "allow" +single_range_in_vec_init = "allow" +suspicious_to_owned = "allow" +type_complexity = "allow" +unnecessary_to_owned = "allow" + [workspace.metadata.cargo-machete] ignored = ["bindgen", "cbindgen", "prost_build", "serde"] diff --git a/crates/activity_indicator/Cargo.toml b/crates/activity_indicator/Cargo.toml index 1513377e7d..ff0ae5c6a1 100644 --- a/crates/activity_indicator/Cargo.toml +++ b/crates/activity_indicator/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/activity_indicator.rs" doctest = false diff --git a/crates/ai/Cargo.toml b/crates/ai/Cargo.toml index 726c7329dc..69c3b88e62 100644 --- a/crates/ai/Cargo.toml +++ b/crates/ai/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/ai.rs" doctest = false diff --git a/crates/assets/Cargo.toml b/crates/assets/Cargo.toml index 19eef955dc..8fcb1f9cfe 100644 --- a/crates/assets/Cargo.toml +++ b/crates/assets/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [dependencies] anyhow.workspace = true gpui.workspace = true diff --git a/crates/assistant/Cargo.toml b/crates/assistant/Cargo.toml index 3f24babef6..d84075a632 100644 --- a/crates/assistant/Cargo.toml +++ b/crates/assistant/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/assistant.rs" doctest = false diff --git a/crates/audio/Cargo.toml b/crates/audio/Cargo.toml index d66df11f9f..bfe22de1f0 100644 --- a/crates/audio/Cargo.toml +++ b/crates/audio/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/audio.rs" doctest = false diff --git a/crates/auto_update/Cargo.toml b/crates/auto_update/Cargo.toml index 8135d5b795..6d4de08d04 100644 --- a/crates/auto_update/Cargo.toml +++ b/crates/auto_update/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/auto_update.rs" doctest = false diff --git a/crates/breadcrumbs/Cargo.toml b/crates/breadcrumbs/Cargo.toml index 24f3a70fd3..45d0f09f66 100644 --- a/crates/breadcrumbs/Cargo.toml +++ b/crates/breadcrumbs/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/breadcrumbs.rs" doctest = false diff --git a/crates/call/Cargo.toml b/crates/call/Cargo.toml index a0acf544b5..569026d006 100644 --- a/crates/call/Cargo.toml +++ b/crates/call/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/call.rs" doctest = false diff --git a/crates/channel/Cargo.toml b/crates/channel/Cargo.toml index ccd690059f..49132a4b7b 100644 --- a/crates/channel/Cargo.toml +++ b/crates/channel/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/channel.rs" doctest = false diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 60285628e8..6316c64bdc 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/cli.rs" doctest = false diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index 4dc8c38ef4..2b45efec85 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/client.rs" doctest = false diff --git a/crates/clock/Cargo.toml b/crates/clock/Cargo.toml index adef338cc7..d1fb21747b 100644 --- a/crates/clock/Cargo.toml +++ b/crates/clock/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/clock.rs" doctest = false diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 7cd499513a..35e1a47baf 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -7,6 +7,9 @@ version = "0.44.0" publish = false license = "AGPL-3.0-or-later" +[lints] +workspace = true + [[bin]] name = "collab" diff --git a/crates/collab_ui/Cargo.toml b/crates/collab_ui/Cargo.toml index e88d191af5..ab8b85321e 100644 --- a/crates/collab_ui/Cargo.toml +++ b/crates/collab_ui/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/collab_ui.rs" doctest = false diff --git a/crates/collections/Cargo.toml b/crates/collections/Cargo.toml index 85cee90e12..b16b4c1300 100644 --- a/crates/collections/Cargo.toml +++ b/crates/collections/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "Apache-2.0" +[lints] +workspace = true + [lib] path = "src/collections.rs" doctest = false diff --git a/crates/color/Cargo.toml b/crates/color/Cargo.toml index d3e4a125f4..a68a5fb518 100644 --- a/crates/color/Cargo.toml +++ b/crates/color/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [features] default = [] diff --git a/crates/command_palette/Cargo.toml b/crates/command_palette/Cargo.toml index 565939e4a3..eecd80b6e1 100644 --- a/crates/command_palette/Cargo.toml +++ b/crates/command_palette/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/command_palette.rs" doctest = false diff --git a/crates/command_palette_hooks/Cargo.toml b/crates/command_palette_hooks/Cargo.toml index 8dd8b7bf69..cc4f396bf8 100644 --- a/crates/command_palette_hooks/Cargo.toml +++ b/crates/command_palette_hooks/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/command_palette_hooks.rs" doctest = false diff --git a/crates/copilot/Cargo.toml b/crates/copilot/Cargo.toml index eca15d3c56..609bd0e3a8 100644 --- a/crates/copilot/Cargo.toml +++ b/crates/copilot/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/copilot.rs" doctest = false diff --git a/crates/copilot_ui/Cargo.toml b/crates/copilot_ui/Cargo.toml index cc83e09aa6..cc184b2dfc 100644 --- a/crates/copilot_ui/Cargo.toml +++ b/crates/copilot_ui/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/copilot_ui.rs" doctest = false diff --git a/crates/db/Cargo.toml b/crates/db/Cargo.toml index 0b01e691b1..f31609277d 100644 --- a/crates/db/Cargo.toml +++ b/crates/db/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/db.rs" doctest = false diff --git a/crates/diagnostics/Cargo.toml b/crates/diagnostics/Cargo.toml index e4e408b665..fcaacfd62a 100644 --- a/crates/diagnostics/Cargo.toml +++ b/crates/diagnostics/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/diagnostics.rs" doctest = false diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index 8266e12ba7..78aeae8ca9 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/editor.rs" doctest = false diff --git a/crates/extension/Cargo.toml b/crates/extension/Cargo.toml index 7c0e2d7afa..d5ceeeff4b 100644 --- a/crates/extension/Cargo.toml +++ b/crates/extension/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/extension_store.rs" diff --git a/crates/extension_api/Cargo.toml b/crates/extension_api/Cargo.toml index 1adbd0c0ee..84d2064eb5 100644 --- a/crates/extension_api/Cargo.toml +++ b/crates/extension_api/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" edition = "2021" license = "Apache-2.0" +[lints] +workspace = true + [lib] path = "src/extension_api.rs" diff --git a/crates/extensions_ui/Cargo.toml b/crates/extensions_ui/Cargo.toml index df55a5091a..7c2320717f 100644 --- a/crates/extensions_ui/Cargo.toml +++ b/crates/extensions_ui/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/extensions_ui.rs" diff --git a/crates/feature_flags/Cargo.toml b/crates/feature_flags/Cargo.toml index cf0f9475af..101e90c646 100644 --- a/crates/feature_flags/Cargo.toml +++ b/crates/feature_flags/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/feature_flags.rs" diff --git a/crates/feedback/Cargo.toml b/crates/feedback/Cargo.toml index b3c89e2c54..05465ba95b 100644 --- a/crates/feedback/Cargo.toml +++ b/crates/feedback/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/feedback.rs" diff --git a/crates/file_finder/Cargo.toml b/crates/file_finder/Cargo.toml index 377bc2dc2a..283b7c5495 100644 --- a/crates/file_finder/Cargo.toml +++ b/crates/file_finder/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/file_finder.rs" doctest = false diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml index 4b48dbc2bf..4a26a13e5f 100644 --- a/crates/fs/Cargo.toml +++ b/crates/fs/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/fs.rs" diff --git a/crates/fsevent/Cargo.toml b/crates/fsevent/Cargo.toml index 23490e8fa5..6a5a01843d 100644 --- a/crates/fsevent/Cargo.toml +++ b/crates/fsevent/Cargo.toml @@ -1,10 +1,12 @@ [package] name = "fsevent" -version = "2.0.2" -license = "MIT" +version = "0.1.0" edition = "2021" publish = false +license = "GPL-3.0-or-later" +[lints] +workspace = true [lib] path = "src/fsevent.rs" diff --git a/crates/fuzzy/Cargo.toml b/crates/fuzzy/Cargo.toml index 3b323afdaa..e3a016c98b 100644 --- a/crates/fuzzy/Cargo.toml +++ b/crates/fuzzy/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/fuzzy.rs" doctest = false diff --git a/crates/git/Cargo.toml b/crates/git/Cargo.toml index 720a0cdd32..4a47b74d59 100644 --- a/crates/git/Cargo.toml +++ b/crates/git/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/git.rs" diff --git a/crates/go_to_line/Cargo.toml b/crates/go_to_line/Cargo.toml index a009e27547..921434a407 100644 --- a/crates/go_to_line/Cargo.toml +++ b/crates/go_to_line/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/go_to_line.rs" doctest = false diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index de83868b35..c4a2432ece 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -7,6 +7,9 @@ description = "Zed's GPU-accelerated UI framework" publish = false license = "Apache-2.0" +[lints] +workspace = true + [features] test-support = [ "backtrace", diff --git a/crates/gpui_macros/Cargo.toml b/crates/gpui_macros/Cargo.toml index f5a7589139..b48da0429e 100644 --- a/crates/gpui_macros/Cargo.toml +++ b/crates/gpui_macros/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "Apache-2.0" +[lints] +workspace = true + [lib] path = "src/gpui_macros.rs" proc-macro = true diff --git a/crates/install_cli/Cargo.toml b/crates/install_cli/Cargo.toml index 8afccceaaf..4edeb01dcd 100644 --- a/crates/install_cli/Cargo.toml +++ b/crates/install_cli/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/install_cli.rs" diff --git a/crates/journal/Cargo.toml b/crates/journal/Cargo.toml index 325e6b5297..e5dd558fa8 100644 --- a/crates/journal/Cargo.toml +++ b/crates/journal/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/journal.rs" doctest = false diff --git a/crates/language/Cargo.toml b/crates/language/Cargo.toml index 77abad1ecf..e6f2a45ea5 100644 --- a/crates/language/Cargo.toml +++ b/crates/language/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/language.rs" doctest = false diff --git a/crates/language_selector/Cargo.toml b/crates/language_selector/Cargo.toml index 5365be61a9..b864ffc31f 100644 --- a/crates/language_selector/Cargo.toml +++ b/crates/language_selector/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/language_selector.rs" doctest = false diff --git a/crates/language_tools/Cargo.toml b/crates/language_tools/Cargo.toml index 2afad658c8..6d0a1199b3 100644 --- a/crates/language_tools/Cargo.toml +++ b/crates/language_tools/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/language_tools.rs" doctest = false diff --git a/crates/languages/Cargo.toml b/crates/languages/Cargo.toml index 8dd4078abc..122607791e 100644 --- a/crates/languages/Cargo.toml +++ b/crates/languages/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [dependencies] anyhow.workspace = true async-compression.workspace = true diff --git a/crates/live_kit_client/Cargo.toml b/crates/live_kit_client/Cargo.toml index a18199fe5a..708fd61d41 100644 --- a/crates/live_kit_client/Cargo.toml +++ b/crates/live_kit_client/Cargo.toml @@ -6,6 +6,9 @@ description = "Bindings to LiveKit Swift client SDK" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/live_kit_client.rs" doctest = false diff --git a/crates/live_kit_server/Cargo.toml b/crates/live_kit_server/Cargo.toml index 3c742111c4..bdac22a85c 100644 --- a/crates/live_kit_server/Cargo.toml +++ b/crates/live_kit_server/Cargo.toml @@ -6,6 +6,9 @@ description = "SDK for the LiveKit server API" publish = false license = "AGPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/live_kit_server.rs" doctest = false diff --git a/crates/lsp/Cargo.toml b/crates/lsp/Cargo.toml index 902105341b..23b2f204ef 100644 --- a/crates/lsp/Cargo.toml +++ b/crates/lsp/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/lsp.rs" doctest = false diff --git a/crates/markdown_preview/Cargo.toml b/crates/markdown_preview/Cargo.toml index 5ac5ae3cb5..f3192a79ca 100644 --- a/crates/markdown_preview/Cargo.toml +++ b/crates/markdown_preview/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/markdown_preview.rs" diff --git a/crates/media/Cargo.toml b/crates/media/Cargo.toml index 784139f129..ac22eccde6 100644 --- a/crates/media/Cargo.toml +++ b/crates/media/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "Apache-2.0" +[lints] +workspace = true + [lib] path = "src/media.rs" doctest = false diff --git a/crates/menu/Cargo.toml b/crates/menu/Cargo.toml index cf17727242..ce12455710 100644 --- a/crates/menu/Cargo.toml +++ b/crates/menu/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/menu.rs" doctest = false diff --git a/crates/multi_buffer/Cargo.toml b/crates/multi_buffer/Cargo.toml index 322ff38a5a..f866217da8 100644 --- a/crates/multi_buffer/Cargo.toml +++ b/crates/multi_buffer/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/multi_buffer.rs" doctest = false diff --git a/crates/node_runtime/Cargo.toml b/crates/node_runtime/Cargo.toml index 1c608b703f..7e713a3e2d 100644 --- a/crates/node_runtime/Cargo.toml +++ b/crates/node_runtime/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/node_runtime.rs" doctest = false diff --git a/crates/notifications/Cargo.toml b/crates/notifications/Cargo.toml index 40d61087fa..3e1c856e40 100644 --- a/crates/notifications/Cargo.toml +++ b/crates/notifications/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/notification_store.rs" doctest = false diff --git a/crates/outline/Cargo.toml b/crates/outline/Cargo.toml index 28dc5d6a1b..540e979ab3 100644 --- a/crates/outline/Cargo.toml +++ b/crates/outline/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/outline.rs" doctest = false diff --git a/crates/picker/Cargo.toml b/crates/picker/Cargo.toml index 510ce14a59..79a8ed70d8 100644 --- a/crates/picker/Cargo.toml +++ b/crates/picker/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/picker.rs" doctest = false diff --git a/crates/prettier/Cargo.toml b/crates/prettier/Cargo.toml index 242a65ba7b..586ff0770f 100644 --- a/crates/prettier/Cargo.toml +++ b/crates/prettier/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/prettier.rs" doctest = false diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index c76424144f..875ebb2de4 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/project.rs" doctest = false diff --git a/crates/project_core/Cargo.toml b/crates/project_core/Cargo.toml index c72fe909da..b82e9a26eb 100644 --- a/crates/project_core/Cargo.toml +++ b/crates/project_core/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] test-support = [ diff --git a/crates/project_panel/Cargo.toml b/crates/project_panel/Cargo.toml index 093d47e124..2772fc18a2 100644 --- a/crates/project_panel/Cargo.toml +++ b/crates/project_panel/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/project_panel.rs" doctest = false diff --git a/crates/project_symbols/Cargo.toml b/crates/project_symbols/Cargo.toml index 93b122eaf0..cadbdd83aa 100644 --- a/crates/project_symbols/Cargo.toml +++ b/crates/project_symbols/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/project_symbols.rs" doctest = false diff --git a/crates/quick_action_bar/Cargo.toml b/crates/quick_action_bar/Cargo.toml index e1f8c66d10..3d5d56a318 100644 --- a/crates/quick_action_bar/Cargo.toml +++ b/crates/quick_action_bar/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/quick_action_bar.rs" doctest = false diff --git a/crates/recent_projects/Cargo.toml b/crates/recent_projects/Cargo.toml index f96dfbdd7d..3e8f63d133 100644 --- a/crates/recent_projects/Cargo.toml +++ b/crates/recent_projects/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/recent_projects.rs" doctest = false diff --git a/crates/refineable/Cargo.toml b/crates/refineable/Cargo.toml index 97f03f1f2f..bda47e7a8a 100644 --- a/crates/refineable/Cargo.toml +++ b/crates/refineable/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "Apache-2.0" +[lints] +workspace = true + [lib] path = "src/refineable.rs" doctest = false diff --git a/crates/refineable/derive_refineable/Cargo.toml b/crates/refineable/derive_refineable/Cargo.toml index f0d6d6d2dc..00502ec19a 100644 --- a/crates/refineable/derive_refineable/Cargo.toml +++ b/crates/refineable/derive_refineable/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "Apache-2.0" +[lints] +workspace = true + [lib] path = "src/derive_refineable.rs" proc-macro = true diff --git a/crates/release_channel/Cargo.toml b/crates/release_channel/Cargo.toml index b40ce36346..acea552d63 100644 --- a/crates/release_channel/Cargo.toml +++ b/crates/release_channel/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [dependencies] gpui.workspace = true once_cell = "1.19.0" diff --git a/crates/rich_text/Cargo.toml b/crates/rich_text/Cargo.toml index 5a12d0e56f..146fdcbd8a 100644 --- a/crates/rich_text/Cargo.toml +++ b/crates/rich_text/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/rich_text.rs" doctest = false diff --git a/crates/rope/Cargo.toml b/crates/rope/Cargo.toml index 9190341f53..b1ce369416 100644 --- a/crates/rope/Cargo.toml +++ b/crates/rope/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/rope.rs" diff --git a/crates/rpc/Cargo.toml b/crates/rpc/Cargo.toml index 7f6750bce5..a4af21df11 100644 --- a/crates/rpc/Cargo.toml +++ b/crates/rpc/Cargo.toml @@ -6,6 +6,9 @@ version = "0.1.0" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/rpc.rs" doctest = false diff --git a/crates/search/Cargo.toml b/crates/search/Cargo.toml index 8daa5e743e..e54b5e0338 100644 --- a/crates/search/Cargo.toml +++ b/crates/search/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/search.rs" doctest = false diff --git a/crates/semantic_index/Cargo.toml b/crates/semantic_index/Cargo.toml index 5c922a503a..957a5e3cdf 100644 --- a/crates/semantic_index/Cargo.toml +++ b/crates/semantic_index/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/semantic_index.rs" doctest = false diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml index d9ad9d3f9a..3177716196 100644 --- a/crates/settings/Cargo.toml +++ b/crates/settings/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/settings.rs" doctest = false diff --git a/crates/snippet/Cargo.toml b/crates/snippet/Cargo.toml index c2c62cc01e..f12c7dded3 100644 --- a/crates/snippet/Cargo.toml +++ b/crates/snippet/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/snippet.rs" doctest = false diff --git a/crates/sqlez/Cargo.toml b/crates/sqlez/Cargo.toml index 71c67af095..98b05a06e4 100644 --- a/crates/sqlez/Cargo.toml +++ b/crates/sqlez/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [dependencies] anyhow.workspace = true collections.workspace = true diff --git a/crates/sqlez_macros/Cargo.toml b/crates/sqlez_macros/Cargo.toml index aab2596ddd..969fe26c88 100644 --- a/crates/sqlez_macros/Cargo.toml +++ b/crates/sqlez_macros/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/sqlez_macros.rs" proc-macro = true diff --git a/crates/story/Cargo.toml b/crates/story/Cargo.toml index fc1754c253..419a447c13 100644 --- a/crates/story/Cargo.toml +++ b/crates/story/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [dependencies] gpui.workspace = true itertools = { package = "itertools", version = "0.10" } diff --git a/crates/storybook/Cargo.toml b/crates/storybook/Cargo.toml index caf66c4c4f..38cc3ddc98 100644 --- a/crates/storybook/Cargo.toml +++ b/crates/storybook/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [[bin]] name = "storybook" path = "src/storybook.rs" diff --git a/crates/sum_tree/Cargo.toml b/crates/sum_tree/Cargo.toml index ac24e0d298..b370e6df18 100644 --- a/crates/sum_tree/Cargo.toml +++ b/crates/sum_tree/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "Apache-2.0" +[lints] +workspace = true + [lib] path = "src/sum_tree.rs" doctest = false diff --git a/crates/task/Cargo.toml b/crates/task/Cargo.toml index 47f3170c74..8f4da57a63 100644 --- a/crates/task/Cargo.toml +++ b/crates/task/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [dependencies] anyhow.workspace = true collections.workspace = true diff --git a/crates/tasks_ui/Cargo.toml b/crates/tasks_ui/Cargo.toml index cbf5280ef6..446179890c 100644 --- a/crates/tasks_ui/Cargo.toml +++ b/crates/tasks_ui/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [dependencies] anyhow.workspace = true editor.workspace = true diff --git a/crates/telemetry_events/Cargo.toml b/crates/telemetry_events/Cargo.toml index 6893e7c183..b202a60b25 100644 --- a/crates/telemetry_events/Cargo.toml +++ b/crates/telemetry_events/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/telemetry_events.rs" diff --git a/crates/terminal/Cargo.toml b/crates/terminal/Cargo.toml index 884b754aac..4686266557 100644 --- a/crates/terminal/Cargo.toml +++ b/crates/terminal/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/terminal.rs" doctest = false diff --git a/crates/terminal_view/Cargo.toml b/crates/terminal_view/Cargo.toml index 8daf4b3f02..d9e3606d4d 100644 --- a/crates/terminal_view/Cargo.toml +++ b/crates/terminal_view/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/terminal_view.rs" doctest = false diff --git a/crates/text/Cargo.toml b/crates/text/Cargo.toml index 798b506b5e..4e57a78407 100644 --- a/crates/text/Cargo.toml +++ b/crates/text/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/text.rs" doctest = false diff --git a/crates/theme/Cargo.toml b/crates/theme/Cargo.toml index fff134af84..0ead51ba41 100644 --- a/crates/theme/Cargo.toml +++ b/crates/theme/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [features] default = [] stories = ["dep:story"] diff --git a/crates/theme_importer/Cargo.toml b/crates/theme_importer/Cargo.toml index ddff83f385..07ed1695be 100644 --- a/crates/theme_importer/Cargo.toml +++ b/crates/theme_importer/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [dependencies] anyhow.workspace = true clap = { workspace = true, features = ["derive"] } diff --git a/crates/theme_selector/Cargo.toml b/crates/theme_selector/Cargo.toml index 7e7e6fe9b6..3f9c9e6205 100644 --- a/crates/theme_selector/Cargo.toml +++ b/crates/theme_selector/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/theme_selector.rs" doctest = false diff --git a/crates/time_format/Cargo.toml b/crates/time_format/Cargo.toml index ea1fb336d5..1b5d36eb6c 100644 --- a/crates/time_format/Cargo.toml +++ b/crates/time_format/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/time_format.rs" doctest = false diff --git a/crates/ui/Cargo.toml b/crates/ui/Cargo.toml index 1776f151d3..3acae49d95 100644 --- a/crates/ui/Cargo.toml +++ b/crates/ui/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] name = "ui" path = "src/ui.rs" diff --git a/crates/util/Cargo.toml b/crates/util/Cargo.toml index 9725320507..191d0beb56 100644 --- a/crates/util/Cargo.toml +++ b/crates/util/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "Apache-2.0" +[lints] +workspace = true + [lib] path = "src/util.rs" doctest = true diff --git a/crates/vcs_menu/Cargo.toml b/crates/vcs_menu/Cargo.toml index 3258c229dd..786e2ee6fc 100644 --- a/crates/vcs_menu/Cargo.toml +++ b/crates/vcs_menu/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [dependencies] anyhow.workspace = true fs.workspace = true diff --git a/crates/vim/Cargo.toml b/crates/vim/Cargo.toml index 071421e74e..4a78547d1e 100644 --- a/crates/vim/Cargo.toml +++ b/crates/vim/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/vim.rs" doctest = false diff --git a/crates/welcome/Cargo.toml b/crates/welcome/Cargo.toml index 2200db8bbd..6fd65c795e 100644 --- a/crates/welcome/Cargo.toml +++ b/crates/welcome/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/welcome.rs" diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index 44c6a98859..db8d004b9e 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] path = "src/workspace.rs" doctest = false diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 2ad94a8bae..fe50da4884 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -6,6 +6,9 @@ version = "0.126.0" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [lib] name = "zed" path = "src/zed.rs" diff --git a/crates/zed_actions/Cargo.toml b/crates/zed_actions/Cargo.toml index 19c9415514..ee279cde65 100644 --- a/crates/zed_actions/Cargo.toml +++ b/crates/zed_actions/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [dependencies] gpui.workspace = true serde.workspace = true diff --git a/extensions/gleam/Cargo.toml b/extensions/gleam/Cargo.toml index b4167dd1ca..881978c90b 100644 --- a/extensions/gleam/Cargo.toml +++ b/extensions/gleam/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "Apache-2.0" +[lints] +workspace = true + [dependencies] zed_extension_api = { path = "../../crates/extension_api" } diff --git a/tooling/xtask/Cargo.toml b/tooling/xtask/Cargo.toml index fac3b54e5e..11f0036c74 100644 --- a/tooling/xtask/Cargo.toml +++ b/tooling/xtask/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" publish = false license = "GPL-3.0-or-later" +[lints] +workspace = true + [dependencies] anyhow.workspace = true clap = { workspace = true, features = ["derive"] } diff --git a/tooling/xtask/src/main.rs b/tooling/xtask/src/main.rs index 44947a4367..e4feb2bdb1 100644 --- a/tooling/xtask/src/main.rs +++ b/tooling/xtask/src/main.rs @@ -63,57 +63,6 @@ fn run_clippy(args: ClippyArgs) -> Result<()> { #[cfg(not(target_os = "windows"))] clippy_command.args(["--deny", "warnings"]); - /// These are all of the rules that currently have violations in the Zed - /// codebase. - /// - /// We'll want to drive this list down by either: - /// 1. fixing violations of the rule and begin enforcing it - /// 2. deciding we want to allow the rule permanently, at which point - /// we should codify that separately in this task. - /// - /// This list shouldn't be added to; it should only get shorter. - const MIGRATORY_RULES_TO_ALLOW: &[&str] = &[ - // There are a bunch of rules currently failing in the `style` group, so - // allow all of those, for now. - "clippy::style", - // Individual rules that have violations in the codebase: - "clippy::almost_complete_range", - "clippy::arc_with_non_send_sync", - "clippy::await_holding_lock", - "clippy::borrow_deref_ref", - "clippy::borrowed_box", - "clippy::cast_abs_to_unsigned", - "clippy::cmp_owned", - "clippy::derive_ord_xor_partial_ord", - "clippy::eq_op", - "clippy::implied_bounds_in_impls", - "clippy::let_underscore_future", - "clippy::map_entry", - "clippy::never_loop", - "clippy::non_canonical_clone_impl", - "clippy::non_canonical_partial_ord_impl", - "clippy::reversed_empty_ranges", - "clippy::single_range_in_vec_init", - "clippy::suspicious_to_owned", - "clippy::type_complexity", - "clippy::unnecessary_to_owned", - ]; - - // When fixing violations automatically for a single package we don't care - // about the rules we're already violating, since it may be possible to - // have them fixed automatically. - let ignore_suppressed_rules = args.fix && args.package.is_some(); - if !ignore_suppressed_rules { - for rule in MIGRATORY_RULES_TO_ALLOW { - clippy_command.args(["--allow", rule]); - } - } - - // Deny `dbg!` and `todo!`s. - clippy_command - .args(["--deny", "clippy::dbg_macro"]) - .args(["--deny", "clippy::todo"]); - eprintln!( "running: {cargo} {}", clippy_command From a25edcc5a894c4fea13e4684329aff84821cc5e9 Mon Sep 17 00:00:00 2001 From: Owen Law <81528246+someone13574@users.noreply.github.com> Date: Tue, 5 Mar 2024 12:16:08 -0500 Subject: [PATCH 098/121] Add libxcb-devel build dep to void linux script (#8872) Added missing libxcb-devel to build dependency script for void-linux. --- script/linux | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/script/linux b/script/linux index a35918ce73..9f4e0402c2 100755 --- a/script/linux +++ b/script/linux @@ -74,10 +74,11 @@ if [[ -n $xbps ]]; then deps=( alsa-lib-devel fontconfig-devel - wayland-devel + libxcb-devel libxkbcommon-devel - openssl-devel libzstd-devel + openssl-devel + wayland-devel ) $maysudo "$xbps" -Syu "${deps[@]}" exit 0 From b6af393e6deaaf8d479f5f332dad9e5a91a1098f Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Tue, 5 Mar 2024 12:24:54 -0500 Subject: [PATCH 099/121] Enable `clippy::borrow_deref_ref` (#8894) This PR enables the [`clippy::borrow_deref_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#/borrow_deref_ref) rule and fixes the outstanding violations. Release Notes: - N/A --- Cargo.toml | 1 - crates/collab/src/db/queries/buffers.rs | 22 ++++++------ crates/collab/src/db/queries/channels.rs | 36 +++++++++---------- .../collab/src/db/queries/hosted_projects.rs | 6 ++-- crates/collab/src/db/queries/messages.rs | 8 ++--- crates/collab/src/db/queries/notifications.rs | 6 ++-- crates/collab/src/db/queries/projects.rs | 20 +++++------ crates/collab/src/db/queries/rooms.rs | 22 ++++++------ crates/collab/src/db/queries/servers.rs | 2 +- crates/collab/src/db/queries/users.rs | 2 +- .../random_project_collaboration_tests.rs | 4 +-- crates/editor/src/items.rs | 4 +-- crates/workspace/src/pane.rs | 2 +- 13 files changed, 67 insertions(+), 68 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ae3df6cf95..55f8980115 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -371,7 +371,6 @@ style = "allow" almost_complete_range = "allow" arc_with_non_send_sync = "allow" await_holding_lock = "allow" -borrow_deref_ref = "allow" borrowed_box = "allow" cast_abs_to_unsigned = "allow" cmp_owned = "allow" diff --git a/crates/collab/src/db/queries/buffers.rs b/crates/collab/src/db/queries/buffers.rs index 4ebca6cebb..eddde98fd9 100644 --- a/crates/collab/src/db/queries/buffers.rs +++ b/crates/collab/src/db/queries/buffers.rs @@ -308,7 +308,7 @@ impl Database { connection_lost: ActiveValue::set(true), ..Default::default() }) - .exec(&*tx) + .exec(tx) .await?; Ok(()) } @@ -363,7 +363,7 @@ impl Database { .eq(connection.owner_id as i32), ), ) - .exec(&*tx) + .exec(tx) .await?; if result.rows_affected == 0 { Err(anyhow!("not a collaborator on this project"))?; @@ -375,7 +375,7 @@ impl Database { .filter( Condition::all().add(channel_buffer_collaborator::Column::ChannelId.eq(channel_id)), ) - .stream(&*tx) + .stream(tx) .await?; while let Some(row) = rows.next().await { let row = row?; @@ -429,7 +429,7 @@ impl Database { Condition::all().add(channel_buffer_collaborator::Column::ChannelId.eq(channel_id)), ) .into_values::<_, QueryUserIds>() - .all(&*tx) + .all(tx) .await?; Ok(users) @@ -602,7 +602,7 @@ impl Database { .select_only() .column(buffer_snapshot::Column::OperationSerializationVersion) .into_values::<_, QueryOperationSerializationVersion>() - .one(&*tx) + .one(tx) .await? .ok_or_else(|| anyhow!("missing buffer snapshot"))?) } @@ -617,7 +617,7 @@ impl Database { ..Default::default() } .find_related(buffer::Entity) - .one(&*tx) + .one(tx) .await? .ok_or_else(|| anyhow!("no such buffer"))?) } @@ -639,7 +639,7 @@ impl Database { .eq(id) .and(buffer_snapshot::Column::Epoch.eq(buffer.epoch)), ) - .one(&*tx) + .one(tx) .await? .ok_or_else(|| anyhow!("no such snapshot"))?; @@ -657,7 +657,7 @@ impl Database { ) .order_by_asc(buffer_operation::Column::LamportTimestamp) .order_by_asc(buffer_operation::Column::ReplicaId) - .stream(&*tx) + .stream(tx) .await?; let mut operations = Vec::new(); @@ -751,7 +751,7 @@ impl Database { tx: &DatabaseTransaction, ) -> Result> { let latest_operations = self - .get_latest_operations_for_buffers(channel_ids_by_buffer_id.keys().copied(), &*tx) + .get_latest_operations_for_buffers(channel_ids_by_buffer_id.keys().copied(), tx) .await?; Ok(latest_operations @@ -781,7 +781,7 @@ impl Database { observed_buffer_edits::Column::BufferId .is_in(channel_ids_by_buffer_id.keys().copied()), ) - .all(&*tx) + .all(tx) .await?; Ok(observed_operations @@ -844,7 +844,7 @@ impl Database { let stmt = Statement::from_string(self.pool.get_database_backend(), sql); Ok(buffer_operation::Entity::find() .from_raw_sql(stmt) - .all(&*tx) + .all(tx) .await?) } } diff --git a/crates/collab/src/db/queries/channels.rs b/crates/collab/src/db/queries/channels.rs index 376a44b6eb..2d622a94d8 100644 --- a/crates/collab/src/db/queries/channels.rs +++ b/crates/collab/src/db/queries/channels.rs @@ -441,9 +441,9 @@ impl Database { user_id: UserId, tx: &DatabaseTransaction, ) -> Result { - let new_channels = self.get_user_channels(user_id, Some(channel), &*tx).await?; + let new_channels = self.get_user_channels(user_id, Some(channel), tx).await?; let removed_channels = self - .get_channel_descendants_excluding_self([channel], &*tx) + .get_channel_descendants_excluding_self([channel], tx) .await? .into_iter() .map(|channel| channel.id) @@ -564,16 +564,16 @@ impl Database { let channel_memberships = channel_member::Entity::find() .filter(filter) - .all(&*tx) + .all(tx) .await?; let channels = channel::Entity::find() .filter(channel::Column::Id.is_in(channel_memberships.iter().map(|m| m.channel_id))) - .all(&*tx) + .all(tx) .await?; let mut descendants = self - .get_channel_descendants_excluding_self(channels.iter(), &*tx) + .get_channel_descendants_excluding_self(channels.iter(), tx) .await?; for channel in channels { @@ -614,7 +614,7 @@ impl Database { .column(room::Column::ChannelId) .column(room_participant::Column::UserId) .into_values::<_, QueryUserIdsAndChannelIds>() - .stream(&*tx) + .stream(tx) .await?; while let Some(row) = rows.next().await { let row: (ChannelId, UserId) = row?; @@ -627,7 +627,7 @@ impl Database { let mut channel_ids_by_buffer_id = HashMap::default(); let mut rows = buffer::Entity::find() .filter(buffer::Column::ChannelId.is_in(channel_ids.iter().copied())) - .stream(&*tx) + .stream(tx) .await?; while let Some(row) = rows.next().await { let row = row?; @@ -636,21 +636,21 @@ impl Database { drop(rows); let latest_buffer_versions = self - .latest_channel_buffer_changes(&channel_ids_by_buffer_id, &*tx) + .latest_channel_buffer_changes(&channel_ids_by_buffer_id, tx) .await?; - let latest_channel_messages = self.latest_channel_messages(&channel_ids, &*tx).await?; + let latest_channel_messages = self.latest_channel_messages(&channel_ids, tx).await?; let observed_buffer_versions = self - .observed_channel_buffer_changes(&channel_ids_by_buffer_id, user_id, &*tx) + .observed_channel_buffer_changes(&channel_ids_by_buffer_id, user_id, tx) .await?; let observed_channel_messages = self - .observed_channel_messages(&channel_ids, user_id, &*tx) + .observed_channel_messages(&channel_ids, user_id, tx) .await?; let hosted_projects = self - .get_hosted_projects(&channel_ids, &roles_by_channel_id, &*tx) + .get_hosted_projects(&channel_ids, &roles_by_channel_id, tx) .await?; Ok(ChannelsForUser { @@ -778,7 +778,7 @@ impl Database { tx: &DatabaseTransaction, ) -> Result> { let participants = self - .get_channel_participant_details_internal(channel, &*tx) + .get_channel_participant_details_internal(channel, tx) .await?; Ok(participants .into_iter() @@ -855,7 +855,7 @@ impl Database { .filter(channel_member::Column::ChannelId.eq(channel.root_id())) .filter(channel_member::Column::UserId.eq(user_id)) .filter(channel_member::Column::Accepted.eq(false)) - .one(&*tx) + .one(tx) .await?; Ok(row) @@ -875,7 +875,7 @@ impl Database { .and(channel_member::Column::UserId.eq(user_id)) .and(channel_member::Column::Accepted.eq(true)), ) - .one(&*tx) + .one(tx) .await?; let Some(membership) = membership else { @@ -930,7 +930,7 @@ impl Database { tx: &DatabaseTransaction, ) -> Result { Ok(channel::Entity::find_by_id(channel_id) - .one(&*tx) + .one(tx) .await? .ok_or_else(|| proto::ErrorCode::NoSuchChannel.anyhow())?) } @@ -943,7 +943,7 @@ impl Database { ) -> Result { let room = room::Entity::find() .filter(room::Column::ChannelId.eq(channel_id)) - .one(&*tx) + .one(tx) .await?; let room_id = if let Some(room) = room { @@ -954,7 +954,7 @@ impl Database { live_kit_room: ActiveValue::Set(live_kit_room.to_string()), ..Default::default() }) - .exec(&*tx) + .exec(tx) .await?; result.last_insert_id diff --git a/crates/collab/src/db/queries/hosted_projects.rs b/crates/collab/src/db/queries/hosted_projects.rs index 394f1055c6..ca91e2822e 100644 --- a/crates/collab/src/db/queries/hosted_projects.rs +++ b/crates/collab/src/db/queries/hosted_projects.rs @@ -11,7 +11,7 @@ impl Database { ) -> Result> { Ok(hosted_project::Entity::find() .filter(hosted_project::Column::ChannelId.is_in(channel_ids.iter().map(|id| id.0))) - .all(&*tx) + .all(tx) .await? .into_iter() .flat_map(|project| { @@ -47,11 +47,11 @@ impl Database { tx: &DatabaseTransaction, ) -> Result<(hosted_project::Model, ChannelRole)> { let project = hosted_project::Entity::find_by_id(hosted_project_id) - .one(&*tx) + .one(tx) .await? .ok_or_else(|| anyhow!(ErrorCode::NoSuchProject))?; let channel = channel::Entity::find_by_id(project.channel_id) - .one(&*tx) + .one(tx) .await? .ok_or_else(|| anyhow!(ErrorCode::NoSuchChannel))?; diff --git a/crates/collab/src/db/queries/messages.rs b/crates/collab/src/db/queries/messages.rs index 83796d4512..663e1d0f83 100644 --- a/crates/collab/src/db/queries/messages.rs +++ b/crates/collab/src/db/queries/messages.rs @@ -171,7 +171,7 @@ impl Database { .filter(channel_message_mention::Column::MessageId.is_in(messages.iter().map(|m| m.id))) .order_by_asc(channel_message_mention::Column::MessageId) .order_by_asc(channel_message_mention::Column::StartOffset) - .stream(&*tx) + .stream(tx) .await?; let mut message_ix = 0; @@ -384,7 +384,7 @@ impl Database { .to_owned(), ) // TODO: Try to upgrade SeaORM so we don't have to do this hack around their bug - .exec_without_returning(&*tx) + .exec_without_returning(tx) .await?; Ok(()) } @@ -401,7 +401,7 @@ impl Database { observed_channel_messages::Column::ChannelId .is_in(channel_ids.iter().map(|id| id.0)), ) - .all(&*tx) + .all(tx) .await?; Ok(rows @@ -452,7 +452,7 @@ impl Database { let stmt = Statement::from_string(self.pool.get_database_backend(), sql); let mut last_messages = channel_message::Model::find_by_statement(stmt) - .stream(&*tx) + .stream(tx) .await?; let mut results = Vec::new(); diff --git a/crates/collab/src/db/queries/notifications.rs b/crates/collab/src/db/queries/notifications.rs index ccdda65342..5a44f62a53 100644 --- a/crates/collab/src/db/queries/notifications.rs +++ b/crates/collab/src/db/queries/notifications.rs @@ -95,7 +95,7 @@ impl Database { content: ActiveValue::Set(proto.content.clone()), ..Default::default() } - .save(&*tx) + .save(tx) .await?; Ok(Some(( @@ -184,7 +184,7 @@ impl Database { tx: &DatabaseTransaction, ) -> Result> { if let Some(id) = self - .find_notification(recipient_id, notification, &*tx) + .find_notification(recipient_id, notification, tx) .await? { let row = notification::Entity::update(notification::ActiveModel { @@ -236,7 +236,7 @@ impl Database { }), ) .into_values::<_, QueryIds>() - .one(&*tx) + .one(tx) .await?) } } diff --git a/crates/collab/src/db/queries/projects.rs b/crates/collab/src/db/queries/projects.rs index 190f854bfa..c051882d95 100644 --- a/crates/collab/src/db/queries/projects.rs +++ b/crates/collab/src/db/queries/projects.rs @@ -186,7 +186,7 @@ impl Database { .update_column(worktree::Column::RootName) .to_owned(), ) - .exec(&*tx) + .exec(tx) .await?; } @@ -194,7 +194,7 @@ impl Database { .filter(worktree::Column::ProjectId.eq(project_id).and( worktree::Column::Id.is_not_in(worktrees.iter().map(|worktree| worktree.id as i64)), )) - .exec(&*tx) + .exec(tx) .await?; Ok(()) @@ -584,7 +584,7 @@ impl Database { ) -> Result<(Project, ReplicaId)> { let mut collaborators = project .find_related(project_collaborator::Entity) - .all(&*tx) + .all(tx) .await?; let replica_ids = collaborators .iter() @@ -603,11 +603,11 @@ impl Database { is_host: ActiveValue::set(false), ..Default::default() } - .insert(&*tx) + .insert(tx) .await?; collaborators.push(new_collaborator); - let db_worktrees = project.find_related(worktree::Entity).all(&*tx).await?; + let db_worktrees = project.find_related(worktree::Entity).all(tx).await?; let mut worktrees = db_worktrees .into_iter() .map(|db_worktree| { @@ -637,7 +637,7 @@ impl Database { .add(worktree_entry::Column::ProjectId.eq(project.id)) .add(worktree_entry::Column::IsDeleted.eq(false)), ) - .stream(&*tx) + .stream(tx) .await?; while let Some(db_entry) = db_entries.next().await { let db_entry = db_entry?; @@ -668,7 +668,7 @@ impl Database { .add(worktree_repository::Column::ProjectId.eq(project.id)) .add(worktree_repository::Column::IsDeleted.eq(false)), ) - .stream(&*tx) + .stream(tx) .await?; while let Some(db_repository_entry) = db_repository_entries.next().await { let db_repository_entry = db_repository_entry?; @@ -689,7 +689,7 @@ impl Database { { let mut db_summaries = worktree_diagnostic_summary::Entity::find() .filter(worktree_diagnostic_summary::Column::ProjectId.eq(project.id)) - .stream(&*tx) + .stream(tx) .await?; while let Some(db_summary) = db_summaries.next().await { let db_summary = db_summary?; @@ -710,7 +710,7 @@ impl Database { { let mut db_settings_files = worktree_settings_file::Entity::find() .filter(worktree_settings_file::Column::ProjectId.eq(project.id)) - .stream(&*tx) + .stream(tx) .await?; while let Some(db_settings_file) = db_settings_files.next().await { let db_settings_file = db_settings_file?; @@ -726,7 +726,7 @@ impl Database { // Populate language servers. let language_servers = project .find_related(language_server::Entity) - .all(&*tx) + .all(tx) .await?; let project = Project { diff --git a/crates/collab/src/db/queries/rooms.rs b/crates/collab/src/db/queries/rooms.rs index 13707d6f47..dcb31266df 100644 --- a/crates/collab/src/db/queries/rooms.rs +++ b/crates/collab/src/db/queries/rooms.rs @@ -374,7 +374,7 @@ impl Database { .select_only() .column(room_participant::Column::ParticipantIndex) .into_values::<_, QueryParticipantIndices>() - .all(&*tx) + .all(tx) .await?; let mut participant_index = 0; @@ -407,7 +407,7 @@ impl Database { tx: &DatabaseTransaction, ) -> Result { let participant_index = self - .get_next_participant_index_internal(room_id, &*tx) + .get_next_participant_index_internal(room_id, tx) .await?; room_participant::Entity::insert_many([room_participant::ActiveModel { @@ -441,12 +441,12 @@ impl Database { ]) .to_owned(), ) - .exec(&*tx) + .exec(tx) .await?; let (channel, room) = self.get_channel_room(room_id, &tx).await?; let channel = channel.ok_or_else(|| anyhow!("no channel for room"))?; - let channel_members = self.get_channel_participants(&channel, &*tx).await?; + let channel_members = self.get_channel_participants(&channel, tx).await?; Ok(JoinRoom { room, channel_id: Some(channel.id), @@ -1042,11 +1042,11 @@ impl Database { tx: &DatabaseTransaction, ) -> Result<()> { let channel = room::Entity::find_by_id(room_id) - .one(&*tx) + .one(tx) .await? .ok_or_else(|| anyhow!("could not find room"))? .find_related(channel::Entity) - .one(&*tx) + .one(tx) .await?; if let Some(channel) = channel { @@ -1057,13 +1057,13 @@ impl Database { .is_in(channel.ancestors()) .and(channel::Column::RequiresZedCla.eq(true)), ) - .count(&*tx) + .count(tx) .await? > 0; if requires_zed_cla { if contributor::Entity::find() .filter(contributor::Column::UserId.eq(user_id)) - .one(&*tx) + .one(tx) .await? .is_none() { @@ -1098,7 +1098,7 @@ impl Database { .eq(connection.owner_id as i32), ), ) - .one(&*tx) + .one(tx) .await?; if let Some(participant) = participant { @@ -1106,7 +1106,7 @@ impl Database { answering_connection_lost: ActiveValue::set(true), ..participant.into_active_model() }) - .exec(&*tx) + .exec(tx) .await?; } Ok(()) @@ -1295,7 +1295,7 @@ impl Database { drop(db_followers); let channel = if let Some(channel_id) = db_room.channel_id { - Some(self.get_channel_internal(channel_id, &*tx).await?) + Some(self.get_channel_internal(channel_id, tx).await?) } else { None }; diff --git a/crates/collab/src/db/queries/servers.rs b/crates/collab/src/db/queries/servers.rs index c79b00eee8..f4e01beba1 100644 --- a/crates/collab/src/db/queries/servers.rs +++ b/crates/collab/src/db/queries/servers.rs @@ -98,7 +98,7 @@ impl Database { .add(server::Column::Environment.eq(environment)) .add(server::Column::Id.ne(new_server_id)), ) - .all(&*tx) + .all(tx) .await?; Ok(stale_servers.into_iter().map(|server| server.id).collect()) } diff --git a/crates/collab/src/db/queries/users.rs b/crates/collab/src/db/queries/users.rs index e60ff63385..f17b43bb98 100644 --- a/crates/collab/src/db/queries/users.rs +++ b/crates/collab/src/db/queries/users.rs @@ -122,7 +122,7 @@ impl Database { metrics_id: ActiveValue::set(Uuid::new_v4()), ..Default::default() }) - .exec_with_returning(&*tx) + .exec_with_returning(tx) .await?; Ok(user) } diff --git a/crates/collab/src/tests/random_project_collaboration_tests.rs b/crates/collab/src/tests/random_project_collaboration_tests.rs index 3cb270e6ef..c1705be8ec 100644 --- a/crates/collab/src/tests/random_project_collaboration_tests.rs +++ b/crates/collab/src/tests/random_project_collaboration_tests.rs @@ -1483,10 +1483,10 @@ fn project_for_root_name( root_name: &str, cx: &TestAppContext, ) -> Option> { - if let Some(ix) = project_ix_for_root_name(&*client.local_projects().deref(), root_name, cx) { + if let Some(ix) = project_ix_for_root_name(client.local_projects().deref(), root_name, cx) { return Some(client.local_projects()[ix].clone()); } - if let Some(ix) = project_ix_for_root_name(&*client.remote_projects().deref(), root_name, cx) { + if let Some(ix) = project_ix_for_root_name(client.remote_projects().deref(), root_name, cx) { return Some(client.remote_projects()[ix].clone()); } None diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index d1324adad0..07ef6a9d84 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -1186,9 +1186,9 @@ pub fn active_match_index( None } else { match ranges.binary_search_by(|probe| { - if probe.end.cmp(cursor, &*buffer).is_lt() { + if probe.end.cmp(cursor, buffer).is_lt() { Ordering::Less - } else if probe.start.cmp(cursor, &*buffer).is_gt() { + } else if probe.start.cmp(cursor, buffer).is_gt() { Ordering::Greater } else { Ordering::Equal diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index e99bb3f193..5248671ad2 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1164,7 +1164,7 @@ impl Pane { matches!( WorkspaceSettings::get_global(cx).autosave, AutosaveSetting::OnFocusChange | AutosaveSetting::OnWindowChange - ) && Self::can_autosave_item(&*item, cx) + ) && Self::can_autosave_item(item, cx) })?; if !will_autosave { let answer = pane.update(cx, |pane, cx| { From 9a2ed4bf1abd48a95db0ac1c9cb9e7596b68e59b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E5=B1=B1=E9=A2=A8=E9=9C=B2?= Date: Wed, 6 Mar 2024 02:48:27 +0900 Subject: [PATCH 100/121] Windows: use folders under AppData (#8828) To be honest, I am not sure how to use these directories. But since it is difficult to change these later, if we are going to change them, I think it is time to do. Release Notes: - N/A --- crates/util/src/paths.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/crates/util/src/paths.rs b/crates/util/src/paths.rs index 27306bcfb7..107f852c91 100644 --- a/crates/util/src/paths.rs +++ b/crates/util/src/paths.rs @@ -8,17 +8,31 @@ use serde::{Deserialize, Serialize}; lazy_static::lazy_static! { pub static ref HOME: PathBuf = dirs::home_dir().expect("failed to determine home directory"); - pub static ref CONFIG_DIR: PathBuf = HOME.join(".config").join("zed"); + pub static ref CONFIG_DIR: PathBuf = if cfg!(target_os = "windows") { + dirs::config_dir() + .expect("failed to determine RoamingAppData directory") + .join("Zed") + } else { + HOME.join(".config").join("zed") + }; pub static ref CONVERSATIONS_DIR: PathBuf = CONFIG_DIR.join("conversations"); pub static ref EMBEDDINGS_DIR: PathBuf = CONFIG_DIR.join("embeddings"); pub static ref THEMES_DIR: PathBuf = CONFIG_DIR.join("themes"); pub static ref LOGS_DIR: PathBuf = if cfg!(target_os = "macos") { HOME.join("Library/Logs/Zed") + } else if cfg!(target_os = "windows") { + dirs::data_local_dir() + .expect("failed to determine LocalAppData directory") + .join("Zed/logs") } else { CONFIG_DIR.join("logs") }; pub static ref SUPPORT_DIR: PathBuf = if cfg!(target_os = "macos") { HOME.join("Library/Application Support/Zed") + } else if cfg!(target_os = "windows") { + dirs::config_dir() + .expect("failed to determine RoamingAppData directory") + .join("Zed") } else { CONFIG_DIR.clone() }; @@ -29,6 +43,10 @@ lazy_static::lazy_static! { pub static ref DB_DIR: PathBuf = SUPPORT_DIR.join("db"); pub static ref CRASHES_DIR: PathBuf = if cfg!(target_os = "macos") { HOME.join("Library/Logs/DiagnosticReports") + } else if cfg!(target_os = "windows") { + dirs::data_local_dir() + .expect("failed to determine LocalAppData directory") + .join("Zed/crashes") } else { CONFIG_DIR.join("crashes") }; @@ -45,7 +63,13 @@ lazy_static::lazy_static! { pub static ref OLD_LOG: PathBuf = LOGS_DIR.join("Zed.log.old"); pub static ref LOCAL_SETTINGS_RELATIVE_PATH: &'static Path = Path::new(".zed/settings.json"); pub static ref LOCAL_TASKS_RELATIVE_PATH: &'static Path = Path::new(".zed/tasks.json"); - pub static ref TEMP_DIR: PathBuf = HOME.join(".cache").join("zed"); + pub static ref TEMP_DIR: PathBuf = if cfg!(target_os = "widows") { + dirs::data_local_dir() + .expect("failed to determine LocalAppData directory") + .join("Temp/Zed") + } else { + HOME.join(".cache").join("zed") + }; } pub trait PathExt { From cfffa29f9a17d3163691df5ccdaf5b8a6d738f95 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Tue, 5 Mar 2024 10:56:14 -0700 Subject: [PATCH 101/121] Enable tokio-console (#8897) Release Notes: - Added tokio-console in production --- .cargo/config.toml | 2 +- Cargo.lock | 196 +++++++++++++++++++++++++- crates/collab/Cargo.toml | 1 + crates/collab/k8s/collab.template.yml | 4 +- crates/collab/src/main.rs | 1 + 5 files changed, 197 insertions(+), 7 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 70a2137854..d73dead142 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,6 +1,6 @@ [build] # v0 mangling scheme provides more detailed backtraces around closures -rustflags = ["-C", "symbol-mangling-version=v0"] +rustflags = ["-C", "symbol-mangling-version=v0", "--cfg", "tokio_unstable"] [alias] xtask = "run --package xtask --" diff --git a/Cargo.lock b/Cargo.lock index 9e19a6c5a0..a3aecf8ed9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1229,7 +1229,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acee9fd5073ab6b045a275b3e709c163dd36c90685219cb21804a147b58dba43" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.2.9", "base64 0.13.1", "bitflags 1.3.2", "bytes 1.5.0", @@ -1239,7 +1239,7 @@ dependencies = [ "http-body", "hyper", "itoa", - "matchit", + "matchit 0.5.0", "memchr", "mime", "percent-encoding", @@ -1257,6 +1257,34 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core 0.3.4", + "bitflags 1.3.2", + "bytes 1.5.0", + "futures-util", + "http 0.2.9", + "http-body", + "hyper", + "itoa", + "matchit 0.7.3", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "axum-core" version = "0.2.9" @@ -1273,13 +1301,30 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes 1.5.0", + "futures-util", + "http 0.2.9", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + [[package]] name = "axum-extra" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69034b3b0fd97923eee2ce8a47540edb21e07f48f87f67d44bb4271cec622bdb" dependencies = [ - "axum", + "axum 0.5.17", "bytes 1.5.0", "futures-util", "http 0.2.9", @@ -2219,7 +2264,7 @@ dependencies = [ "audio", "aws-config", "aws-sdk-s3", - "axum", + "axum 0.5.17", "axum-extra", "call", "channel", @@ -2229,6 +2274,7 @@ dependencies = [ "clock", "collab_ui", "collections", + "console-subscriber", "ctor", "dashmap", "editor", @@ -2430,6 +2476,43 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "console-api" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd326812b3fd01da5bb1af7d340d0d555fd3d4b641e7f1dfcf5962a902952787" +dependencies = [ + "futures-core", + "prost 0.12.3", + "prost-types 0.12.3", + "tonic", + "tracing-core", +] + +[[package]] +name = "console-subscriber" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7481d4c57092cd1c19dd541b92bdce883de840df30aa5d03fd48a3935c01842e" +dependencies = [ + "console-api", + "crossbeam-channel", + "crossbeam-utils", + "futures-task", + "hdrhistogram", + "humantime", + "prost-types 0.12.3", + "serde", + "serde_json", + "thread_local", + "tokio", + "tokio-stream", + "tonic", + "tracing", + "tracing-core", + "tracing-subscriber", +] + [[package]] name = "const-cstr" version = "0.3.0" @@ -4501,6 +4584,19 @@ dependencies = [ "hashbrown 0.14.0", ] +[[package]] +name = "hdrhistogram" +version = "7.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" +dependencies = [ + "base64 0.21.4", + "byteorder", + "flate2", + "nom", + "num-traits", +] + [[package]] name = "headers" version = "0.3.9" @@ -4727,6 +4823,18 @@ dependencies = [ "tokio-rustls", ] +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -5703,6 +5811,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "matrixmultiply" version = "0.3.8" @@ -7365,6 +7479,16 @@ dependencies = [ "prost-derive 0.9.0", ] +[[package]] +name = "prost" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" +dependencies = [ + "bytes 1.5.0", + "prost-derive 0.12.3", +] + [[package]] name = "prost-build" version = "0.9.0" @@ -7411,6 +7535,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prost-derive" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" +dependencies = [ + "anyhow", + "itertools 0.11.0", + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "prost-types" version = "0.8.0" @@ -7431,6 +7568,15 @@ dependencies = [ "prost 0.9.0", ] +[[package]] +name = "prost-types" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" +dependencies = [ + "prost 0.12.3", +] + [[package]] name = "protobuf" version = "2.28.0" @@ -10077,6 +10223,7 @@ dependencies = [ "signal-hook-registry", "socket2 0.5.4", "tokio-macros", + "tracing", "windows-sys 0.48.0", ] @@ -10091,6 +10238,16 @@ dependencies = [ "log", ] +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-macros" version = "2.1.0" @@ -10225,6 +10382,33 @@ dependencies = [ "winnow 0.6.1", ] +[[package]] +name = "tonic" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" +dependencies = [ + "async-stream", + "async-trait", + "axum 0.6.20", + "base64 0.21.4", + "bytes 1.5.0", + "h2", + "http 0.2.9", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost 0.12.3", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower" version = "0.4.13" @@ -10233,9 +10417,13 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", + "indexmap 1.9.3", "pin-project", "pin-project-lite", + "rand 0.8.5", + "slab", "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 35e1a47baf..95ee83fd48 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -17,6 +17,7 @@ name = "collab" name = "seed" [dependencies] +console-subscriber = "0.2" anyhow.workspace = true async-tungstenite = "0.16" aws-config = { version = "1.1.5" } diff --git a/crates/collab/k8s/collab.template.yml b/crates/collab/k8s/collab.template.yml index 42590255cc..2ce3c3d34a 100644 --- a/crates/collab/k8s/collab.template.yml +++ b/crates/collab/k8s/collab.template.yml @@ -78,8 +78,8 @@ spec: periodSeconds: 1 resources: requests: - cpu: "46" - memory: "90Gi" + cpu: "20" + memory: "20Gi" env: - name: HTTP_PORT value: "8080" diff --git a/crates/collab/src/main.rs b/crates/collab/src/main.rs index add6bc47f8..489a86c4ba 100644 --- a/crates/collab/src/main.rs +++ b/crates/collab/src/main.rs @@ -25,6 +25,7 @@ const REVISION: Option<&'static str> = option_env!("GITHUB_SHA"); #[tokio::main] async fn main() -> Result<()> { + console_subscriber::init(); if let Err(error) = env::load_dotenv() { eprintln!( "error loading .env.toml (this is expected in production): {}", From ad0c5731e566045d2ee62af602692cce08ac27e4 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Tue, 5 Mar 2024 13:23:17 -0500 Subject: [PATCH 102/121] Update config.yml --- .github/ISSUE_TEMPLATE/config.yml | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 95d4064e8f..93511150ad 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,16 +1,17 @@ +blank_issues_enabled: false contact_links: - - name: Language Request - url: https://github.com/zed-industries/extensions/issues/new?assignees=&labels=language&projects=&template=1_language_request.yml&title=%3Cname_of_language%3E - about: Request a language in the extensions repository - - name: Theme Request - url: https://github.com/zed-industries/extensions/issues/new?assignees=&labels=theme&projects=&template=0_theme_request.yml&title=%3Cname_of_theme%3E+theme - about: Request a theme in the extensions repository - - name: Top-Ranking Issues - url: https://github.com/zed-industries/zed/issues/5393 - about: See an overview of the most popular Zed issues - - name: Platform Support - url: https://github.com/zed-industries/zed/issues/5391 - about: A quick note on platform support - - name: Positive Feedback - url: https://github.com/zed-industries/zed/discussions/5397 - about: A central location for kind words about Zed + - name: Language Request + url: https://github.com/zed-industries/extensions/issues/new?assignees=&labels=language&projects=&template=1_language_request.yml&title=%3Cname_of_language%3E + about: Request a language in the extensions repository + - name: Theme Request + url: https://github.com/zed-industries/extensions/issues/new?assignees=&labels=theme&projects=&template=0_theme_request.yml&title=%3Cname_of_theme%3E+theme + about: Request a theme in the extensions repository + - name: Top-Ranking Issues + url: https://github.com/zed-industries/zed/issues/5393 + about: See an overview of the most popular Zed issues + - name: Platform Support + url: https://github.com/zed-industries/zed/issues/5391 + about: A quick note on platform support + - name: Positive Feedback + url: https://github.com/zed-industries/zed/discussions/5397 + about: A central location for kind words about Zed From d98b61e3d6ca98b2ed6aa7e617c66ccf5f267001 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Tue, 5 Mar 2024 13:51:25 -0500 Subject: [PATCH 103/121] Improve wording on recent projects placeholder instructions --- crates/recent_projects/src/recent_projects.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index 76d42c2780..a9ceefee6d 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -183,7 +183,7 @@ impl PickerDelegate for RecentProjectsDelegate { ) }; Arc::from(format!( - "{reuse_window} reuses the window, {create_window} opens a new one", + "{reuse_window} reuses this window, {create_window} opens a new one", )) } From 9e66d48ccd3bb87ee97aec4e63484dc8b78e45ac Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Tue, 5 Mar 2024 14:36:53 -0500 Subject: [PATCH 104/121] Enable `clippy::cmp_owned` (#8899) This PR enables the [`clippy::cmp_owned`](https://rust-lang.github.io/rust-clippy/master/index.html#/cmp_owned) rule and fixes the outstanding violations. Release Notes: - N/A --- Cargo.toml | 1 - crates/assistant/src/assistant_panel.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 55f8980115..c1102dec92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -373,7 +373,6 @@ arc_with_non_send_sync = "allow" await_holding_lock = "allow" borrowed_box = "allow" cast_abs_to_unsigned = "allow" -cmp_owned = "allow" derive_ord_xor_partial_ord = "allow" eq_op = "allow" implied_bounds_in_impls = "allow" diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index 97f0bca083..28623ea8b1 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -652,7 +652,7 @@ impl AssistantPanel { // If Markdown or No Language is Known, increase the randomness for more creative output // If Code, decrease temperature to get more deterministic outputs let temperature = if let Some(language) = language_name.clone() { - if language.to_string() != "Markdown".to_string() { + if *language != *"Markdown" { 0.5 } else { 1.0 From d112bcfadfbb2495cea2c946b1e35b02f87c16ee Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Tue, 5 Mar 2024 13:34:25 -0700 Subject: [PATCH 105/121] Fix project subscription order (#8900) Co-Authored-By: Antonio Release Notes: - Fixed a bug that prevents project joining Co-authored-by: Antonio --- crates/project/src/project.rs | 66 +++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index aff690b348..519764eb61 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -11,7 +11,10 @@ mod project_tests; use anyhow::{anyhow, bail, Context as _, Result}; use async_trait::async_trait; -use client::{proto, Client, Collaborator, HostedProjectId, TypedEnvelope, UserStore}; +use client::{ + proto, Client, Collaborator, HostedProjectId, PendingEntitySubscription, TypedEnvelope, + UserStore, +}; use clock::ReplicaId; use collections::{hash_map, BTreeMap, HashMap, HashSet, VecDeque}; use copilot::Copilot; @@ -622,17 +625,26 @@ impl Project { ) -> Result> { client.authenticate_and_connect(true, &cx).await?; + let subscription = client.subscribe_to_entity(remote_id)?; let response = client .request_envelope(proto::JoinProject { project_id: remote_id, }) .await?; - Self::from_join_project_response(response, None, client, user_store, languages, fs, cx) - .await + Self::from_join_project_response( + response, + subscription, + client, + user_store, + languages, + fs, + cx, + ) + .await } async fn from_join_project_response( response: TypedEnvelope, - hosted_project_id: Option, + subscription: PendingEntitySubscription, client: Arc, user_store: Model, languages: Arc, @@ -641,7 +653,6 @@ impl Project { ) -> Result> { let remote_id = response.payload.project_id; let role = response.payload.role(); - let subscription = client.subscribe_to_entity(remote_id)?; let this = cx.new_model(|cx| { let replica_id = response.payload.replica_id as ReplicaId; let tasks = Inventory::new(cx); @@ -730,7 +741,7 @@ impl Project { prettiers_per_worktree: HashMap::default(), prettier_instances: HashMap::default(), tasks, - hosted_project_id, + hosted_project_id: None, }; this.set_role(role, cx); for worktree in worktrees { @@ -760,28 +771,29 @@ impl Project { } pub async fn hosted( - hosted_project_id: HostedProjectId, - user_store: Model, - client: Arc, - languages: Arc, - fs: Arc, - cx: AsyncAppContext, + _hosted_project_id: HostedProjectId, + _user_store: Model, + _client: Arc, + _languages: Arc, + _fs: Arc, + _cx: AsyncAppContext, ) -> Result> { - let response = client - .request_envelope(proto::JoinHostedProject { - id: hosted_project_id.0, - }) - .await?; - Self::from_join_project_response( - response, - Some(hosted_project_id), - client, - user_store, - languages, - fs, - cx, - ) - .await + // let response = client + // .request_envelope(proto::JoinHostedProject { + // id: hosted_project_id.0, + // }) + // .await?; + // Self::from_join_project_response( + // response, + // Some(hosted_project_id), + // client, + // user_store, + // languages, + // fs, + // cx, + // ) + // .await + Err(anyhow!("disabled")) } fn release(&mut self, cx: &mut AppContext) { From addfcdea8db96eaa0573419b47dba372fb3c5ed7 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Tue, 5 Mar 2024 16:04:55 -0500 Subject: [PATCH 106/121] Enable `clippy::implied_bounds_in_impls` (#8906) This PR enables the [`clippy::implied_bounds_in_impls`](https://rust-lang.github.io/rust-clippy/master/index.html#/implied_bounds_in_impls) rule and fixes the outstanding violations. Release Notes: - N/A --- Cargo.toml | 1 - crates/workspace/src/pane.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c1102dec92..c33252f8a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -375,7 +375,6 @@ borrowed_box = "allow" cast_abs_to_unsigned = "allow" derive_ord_xor_partial_ord = "allow" eq_op = "allow" -implied_bounds_in_impls = "allow" let_underscore_future = "allow" map_entry = "allow" never_loop = "allow" diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 5248671ad2..493a99dbe7 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -630,7 +630,7 @@ impl Pane { self.items.len() } - pub fn items(&self) -> impl Iterator> + DoubleEndedIterator { + pub fn items(&self) -> impl DoubleEndedIterator> { self.items.iter() } From 703c9655a026f38bb62d3359dde062bf91a99638 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 5 Mar 2024 23:42:12 +0200 Subject: [PATCH 107/121] Always resolve code action if needed (#8904) Follow-up of https://github.com/zed-industries/zed/pull/8874 and https://github.com/zed-industries/zed/pull/7635 Closes https://github.com/zed-industries/zed/issues/7609 * mentions all `lsp::CodeActions` properties in the Zed client resolve capabilities to remove more json out of general actions request potentially * removes odd `CodeActions.data` field checks, as that field is opaque and is intended to store data, needed by the langserver to resolve this code action * if any `CodeAction` lacks either `command` or `edits` fields, tries to resolve the action This all effectively causes Zed to always fire an action resolve request, since we update actions list (replacing the resolved actions with the new, unresolved ones) via `refresh_code_actions` https://github.com/zed-industries/zed/blob/9e66d48ccd3bb87ee97aec4e63484dc8b78e45ac/crates/editor/src/editor.rs#L3650 that is being called on selections change and the actions menu open. Yet, we do not query the resolve until the action is either applied (selected in the list), or called for formatting, so it seems to be fine to resolve them always, as it's not a frequent operation such as reacting to every keystroke. Release Notes: - Fixed certain code actions not being resolved properly ([7609](https://github.com/zed-industries/zed/issues/7609)) --------- Co-authored-by: Derrick Laird --- crates/lsp/src/lsp.rs | 9 ++++- crates/project/src/lsp_command.rs | 13 ++++++ crates/project/src/project.rs | 62 ++++++++++++++--------------- crates/project/src/project_tests.rs | 40 ++++++++++++++----- 4 files changed, 81 insertions(+), 43 deletions(-) diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 1652e34044..b1099cda5f 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -570,7 +570,14 @@ impl LanguageServer { }), data_support: Some(true), resolve_support: Some(CodeActionCapabilityResolveSupport { - properties: vec!["edit".to_string(), "command".to_string()], + properties: vec![ + "kind".to_string(), + "diagnostics".to_string(), + "isPreferred".to_string(), + "disabled".to_string(), + "edit".to_string(), + "command".to_string(), + ], }), ..Default::default() }), diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 2f8e409afa..8cbd83a50c 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -1834,6 +1834,19 @@ impl LspCommand for GetCodeActions { } } +impl GetCodeActions { + pub fn can_resolve_actions(capabilities: &ServerCapabilities) -> bool { + capabilities + .code_action_provider + .as_ref() + .and_then(|options| match options { + lsp::CodeActionProviderCapability::Simple(_is_supported) => None, + lsp::CodeActionProviderCapability::Options(options) => options.resolve_provider, + }) + .unwrap_or(false) + } +} + #[async_trait(?Send)] impl LspCommand for OnTypeFormatting { type Response = Option; diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 519764eb61..a82b156b03 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -40,11 +40,11 @@ use language::{ deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version, serialize_anchor, serialize_version, split_operations, }, - range_from_lsp, range_to_lsp, Bias, Buffer, BufferSnapshot, CachedLspAdapter, Capability, - CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff, - Documentation, Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName, - LocalFile, LspAdapterDelegate, OffsetRangeExt, Operation, Patch, PendingLanguageServer, - PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, Unclipped, + range_from_lsp, Bias, Buffer, BufferSnapshot, CachedLspAdapter, Capability, CodeAction, + CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff, Documentation, + Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName, LocalFile, + LspAdapterDelegate, Operation, Patch, PendingLanguageServer, PointUtf16, TextBufferSnapshot, + ToOffset, ToPointUtf16, Transaction, Unclipped, }; use log::error; use lsp::{ @@ -4360,7 +4360,10 @@ impl Project { })? .await?; - for action in actions { + for mut action in actions { + Self::try_resolve_code_action(&language_server, &mut action) + .await + .context("resolving a formatting code action")?; if let Some(edit) = action.lsp_action.edit { if edit.changes.is_none() && edit.document_changes.is_none() { continue; @@ -4380,6 +4383,7 @@ impl Project { project_transaction.0.extend(new.0); } + // TODO kb here too: if let Some(command) = action.lsp_action.command { project.update(&mut cx, |this, _| { this.last_workspace_edits_by_language_server @@ -5422,33 +5426,10 @@ impl Project { } else { return Task::ready(Ok(Default::default())); }; - let range = action.range.to_point_utf16(buffer); - cx.spawn(move |this, mut cx| async move { - if let Some(lsp_range) = action - .lsp_action - .data - .as_mut() - .and_then(|d| d.get_mut("codeActionParams")) - .and_then(|d| d.get_mut("range")) - { - *lsp_range = serde_json::to_value(&range_to_lsp(range)).unwrap(); - action.lsp_action = lang_server - .request::(action.lsp_action) - .await?; - } else { - let actions = this - .update(&mut cx, |this, cx| { - this.code_actions(&buffer_handle, action.range, cx) - })? - .await?; - action.lsp_action = actions - .into_iter() - .find(|a| a.lsp_action.title == action.lsp_action.title) - .ok_or_else(|| anyhow!("code action is outdated"))? - .lsp_action; - } - + Self::try_resolve_code_action(&lang_server, &mut action) + .await + .context("resolving a code action")?; if let Some(edit) = action.lsp_action.edit { if edit.changes.is_some() || edit.document_changes.is_some() { return Self::deserialize_workspace_edit( @@ -8322,6 +8303,23 @@ impl Project { }) } + async fn try_resolve_code_action( + lang_server: &LanguageServer, + action: &mut CodeAction, + ) -> anyhow::Result<()> { + if GetCodeActions::can_resolve_actions(&lang_server.capabilities()) { + if action.lsp_action.data.is_some() + && (action.lsp_action.command.is_none() || action.lsp_action.edit.is_none()) + { + action.lsp_action = lang_server + .request::(action.lsp_action.clone()) + .await?; + } + } + + anyhow::Ok(()) + } + async fn handle_refresh_inlay_hints( this: Model, _: TypedEnvelope, diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 1af8fca291..c513b9a2c4 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -2475,8 +2475,21 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) { let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(typescript_lang()); - let mut fake_language_servers = - language_registry.register_fake_lsp_adapter("TypeScript", Default::default()); + let mut fake_language_servers = language_registry.register_fake_lsp_adapter( + "TypeScript", + FakeLspAdapter { + capabilities: lsp::ServerCapabilities { + code_action_provider: Some(lsp::CodeActionProviderCapability::Options( + lsp::CodeActionOptions { + resolve_provider: Some(true), + ..lsp::CodeActionOptions::default() + }, + )), + ..lsp::ServerCapabilities::default() + }, + ..FakeLspAdapter::default() + }, + ); let buffer = project .update(cx, |p, cx| p.open_local_buffer("/dir/a.ts", cx)) @@ -2492,16 +2505,14 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) { Ok(Some(vec![ lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction { title: "The code action".into(), - command: Some(lsp::Command { - title: "The command".into(), - command: "_the/command".into(), - arguments: Some(vec![json!("the-argument")]), - }), - ..Default::default() + data: Some(serde_json::json!({ + "command": "_the/command", + })), + ..lsp::CodeAction::default() }), lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction { title: "two".into(), - ..Default::default() + ..lsp::CodeAction::default() }), ])) }) @@ -2516,7 +2527,16 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) { // Resolving the code action does not populate its edits. In absence of // edits, we must execute the given command. fake_server.handle_request::( - |action, _| async move { Ok(action) }, + |mut action, _| async move { + if action.data.is_some() { + action.command = Some(lsp::Command { + title: "The command".into(), + command: "_the/command".into(), + arguments: Some(vec![json!("the-argument")]), + }); + } + Ok(action) + }, ); // While executing the command, the language server sends the editor From b68a277b5e638926284c9927a189d3e77a7f4309 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 5 Mar 2024 14:11:33 -0800 Subject: [PATCH 108/121] Fix tracing subscriber after introducing Tokio-console (#8907) We've also upgraded `Axum` in order to avoid having two versions of that library in Collab (one due to Tokio-console). Release Notes: - N/A --------- Co-authored-by: Conrad --- Cargo.lock | 127 ++++++++------------------ crates/collab/Cargo.toml | 8 +- crates/collab/k8s/collab.template.yml | 2 + crates/collab/src/api.rs | 6 +- crates/collab/src/api/events.rs | 9 +- crates/collab/src/api/extensions.rs | 2 +- crates/collab/src/lib.rs | 4 +- crates/collab/src/main.rs | 31 ++++--- crates/collab/src/rpc.rs | 2 +- script/run-local-minio | 2 +- 10 files changed, 75 insertions(+), 118 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a3aecf8ed9..c012316eaa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1224,13 +1224,13 @@ dependencies = [ [[package]] name = "axum" -version = "0.5.17" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acee9fd5073ab6b045a275b3e709c163dd36c90685219cb21804a147b58dba43" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", - "axum-core 0.2.9", - "base64 0.13.1", + "axum-core", + "base64 0.21.4", "bitflags 1.3.2", "bytes 1.5.0", "futures-util", @@ -1239,68 +1239,25 @@ dependencies = [ "http-body", "hyper", "itoa", - "matchit 0.5.0", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "sha-1 0.10.1", - "sync_wrapper", - "tokio", - "tokio-tungstenite", - "tower", - "tower-http 0.3.5", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" -dependencies = [ - "async-trait", - "axum-core 0.3.4", - "bitflags 1.3.2", - "bytes 1.5.0", - "futures-util", - "http 0.2.9", - "http-body", - "hyper", - "itoa", - "matchit 0.7.3", + "matchit", "memchr", "mime", "percent-encoding", "pin-project-lite", "rustversion", "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", "sync_wrapper", + "tokio", + "tokio-tungstenite", "tower", "tower-layer", "tower-service", ] -[[package]] -name = "axum-core" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e5939e02c56fecd5c017c37df4238c0a839fa76b7f97acdd7efb804fd181cc" -dependencies = [ - "async-trait", - "bytes 1.5.0", - "futures-util", - "http 0.2.9", - "http-body", - "mime", - "tower-layer", - "tower-service", -] - [[package]] name = "axum-core" version = "0.3.4" @@ -1320,11 +1277,11 @@ dependencies = [ [[package]] name = "axum-extra" -version = "0.3.7" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69034b3b0fd97923eee2ce8a47540edb21e07f48f87f67d44bb4271cec622bdb" +checksum = "f9a320103719de37b7b4da4c8eb629d4573f6bcfd3dfe80d3208806895ccf81d" dependencies = [ - "axum 0.5.17", + "axum", "bytes 1.5.0", "futures-util", "http 0.2.9", @@ -2264,7 +2221,7 @@ dependencies = [ "audio", "aws-config", "aws-sdk-s3", - "axum 0.5.17", + "axum", "axum-extra", "call", "channel", @@ -2286,7 +2243,6 @@ dependencies = [ "git", "gpui", "hex", - "hyper", "indoc", "language", "lazy_static", @@ -2326,7 +2282,6 @@ dependencies = [ "tower", "tower-http 0.4.4", "tracing", - "tracing-log", "tracing-subscriber", "unindent", "util", @@ -3060,6 +3015,12 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" +[[package]] +name = "data-encoding" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" + [[package]] name = "data-url" version = "0.1.1" @@ -5805,12 +5766,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" -[[package]] -name = "matchit" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" - [[package]] name = "matchit" version = "0.7.3" @@ -8853,6 +8808,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd154a240de39fdebcf5775d2675c204d7c13cf39a4c697be6493c8e734337c" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_repr" version = "0.1.16" @@ -8924,17 +8889,6 @@ dependencies = [ "opaque-debug", ] -[[package]] -name = "sha-1" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.10.7", -] - [[package]] name = "sha1" version = "0.10.6" @@ -10292,14 +10246,14 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.17.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", "log", "tokio", - "tungstenite 0.17.3", + "tungstenite 0.20.1", ] [[package]] @@ -10390,7 +10344,7 @@ checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" dependencies = [ "async-stream", "async-trait", - "axum 0.6.20", + "axum", "base64 0.21.4", "bytes 1.5.0", "h2", @@ -10443,7 +10397,6 @@ dependencies = [ "http-body", "http-range-header", "pin-project-lite", - "tower", "tower-layer", "tower-service", ] @@ -11044,7 +10997,7 @@ dependencies = [ "log", "native-tls", "rand 0.8.5", - "sha-1 0.9.8", + "sha-1", "thiserror", "url", "utf-8", @@ -11052,18 +11005,18 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.17.3" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" dependencies = [ - "base64 0.13.1", "byteorder", "bytes 1.5.0", + "data-encoding", "http 0.2.9", "httparse", "log", "rand 0.8.5", - "sha-1 0.10.1", + "sha1", "thiserror", "url", "utf-8", diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 95ee83fd48..c32bb53237 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -22,8 +22,8 @@ anyhow.workspace = true async-tungstenite = "0.16" aws-config = { version = "1.1.5" } aws-sdk-s3 = { version = "1.15.0" } -axum = { version = "0.5", features = ["json", "headers", "ws"] } -axum-extra = { version = "0.3", features = ["erased-json"] } +axum = { version = "0.6", features = ["json", "headers", "ws"] } +axum-extra = { version = "0.4", features = ["erased-json"] } chrono.workspace = true clock.workspace = true clickhouse.workspace = true @@ -32,7 +32,6 @@ dashmap = "5.4" envy = "0.4.2" futures.workspace = true hex.workspace = true -hyper = "0.14" live_kit_server.workspace = true log.workspace = true nanoid = "0.4" @@ -59,8 +58,7 @@ toml.workspace = true tower = "0.4" tower-http = { workspace = true, features = ["trace"] } tracing = "0.1.34" -tracing-log = "0.1.3" -tracing-subscriber = { version = "0.3.11", features = ["env-filter", "json"] } +tracing-subscriber = { version = "0.3.11", features = ["env-filter", "json", "registry", "tracing-log"] } util.workspace = true uuid.workspace = true diff --git a/crates/collab/k8s/collab.template.yml b/crates/collab/k8s/collab.template.yml index 2ce3c3d34a..e1ec20b07c 100644 --- a/crates/collab/k8s/collab.template.yml +++ b/crates/collab/k8s/collab.template.yml @@ -63,6 +63,8 @@ spec: ports: - containerPort: 8080 protocol: TCP + - containerPort: 6669 + protocol: TCP livenessProbe: httpGet: path: /healthz diff --git a/crates/collab/src/api.rs b/crates/collab/src/api.rs index d227211fc1..15c07838ae 100644 --- a/crates/collab/src/api.rs +++ b/crates/collab/src/api.rs @@ -11,7 +11,7 @@ use crate::{ use anyhow::anyhow; use axum::{ body::Body, - extract::{Path, Query}, + extract::{self, Path, Query}, http::{self, Request, StatusCode}, middleware::{self, Next}, response::IntoResponse, @@ -26,7 +26,7 @@ use tower::ServiceBuilder; pub use extensions::fetch_extensions_from_blob_store_periodically; -pub fn routes(rpc_server: Option>, state: Arc) -> Router { +pub fn routes(rpc_server: Option>, state: Arc) -> Router<(), Body> { Router::new() .route("/user", get(get_authenticated_user)) .route("/users/:id/access_tokens", post(create_access_token)) @@ -176,8 +176,8 @@ async fn check_is_contributor( } async fn add_contributor( - Json(params): Json, Extension(app): Extension>, + extract::Json(params): extract::Json, ) -> Result<()> { app.db .add_contributor( diff --git a/crates/collab/src/api/events.rs b/crates/collab/src/api/events.rs index 40700a859b..102609f2d9 100644 --- a/crates/collab/src/api/events.rs +++ b/crates/collab/src/api/events.rs @@ -3,9 +3,12 @@ use std::sync::{Arc, OnceLock}; use anyhow::{anyhow, Context}; use aws_sdk_s3::primitives::ByteStream; use axum::{ - body::Bytes, headers::Header, http::HeaderName, routing::post, Extension, Router, TypedHeader, + body::Bytes, + headers::Header, + http::{HeaderMap, HeaderName, StatusCode}, + routing::post, + Extension, Router, TypedHeader, }; -use hyper::{HeaderMap, StatusCode}; use serde::{Serialize, Serializer}; use sha2::{Digest, Sha256}; use telemetry_events::{ @@ -81,8 +84,8 @@ impl Header for CloudflareIpCountryHeader { pub async fn post_crash( Extension(app): Extension>, - body: Bytes, headers: HeaderMap, + body: Bytes, ) -> Result<()> { static CRASH_REPORTS_BUCKET: &str = "zed-crash-reports"; diff --git a/crates/collab/src/api/extensions.rs b/crates/collab/src/api/extensions.rs index c276e219d1..34b5ae0d9f 100644 --- a/crates/collab/src/api/extensions.rs +++ b/crates/collab/src/api/extensions.rs @@ -7,12 +7,12 @@ use anyhow::{anyhow, Context as _}; use aws_sdk_s3::presigning::PresigningConfig; use axum::{ extract::{Path, Query}, + http::StatusCode, response::Redirect, routing::get, Extension, Json, Router, }; use collections::HashMap; -use hyper::StatusCode; use serde::{Deserialize, Serialize}; use std::{sync::Arc, time::Duration}; use time::PrimitiveDateTime; diff --git a/crates/collab/src/lib.rs b/crates/collab/src/lib.rs index 0be28c1358..616405edad 100644 --- a/crates/collab/src/lib.rs +++ b/crates/collab/src/lib.rs @@ -43,8 +43,8 @@ impl From for Error { } } -impl From for Error { - fn from(error: hyper::Error) -> Self { +impl From for Error { + fn from(error: axum::http::Error) -> Self { Self::Internal(error.into()) } } diff --git a/crates/collab/src/main.rs b/crates/collab/src/main.rs index 489a86c4ba..7f4cb8e0cb 100644 --- a/crates/collab/src/main.rs +++ b/crates/collab/src/main.rs @@ -1,11 +1,10 @@ use anyhow::anyhow; -use axum::{extract::MatchedPath, routing::get, Extension, Router}; +use axum::{extract::MatchedPath, http::Request, routing::get, Extension, Router}; use collab::{ api::fetch_extensions_from_blob_store_periodically, db, env, executor::Executor, AppState, Config, MigrateConfig, Result, }; use db::Database; -use hyper::Request; use std::{ env::args, net::{SocketAddr, TcpListener}, @@ -16,8 +15,9 @@ use std::{ use tokio::signal::unix::SignalKind; use tower_http::trace::{self, TraceLayer}; use tracing::Level; -use tracing_log::LogTracer; -use tracing_subscriber::{filter::EnvFilter, fmt::format::JsonFields, Layer}; +use tracing_subscriber::{ + filter::EnvFilter, fmt::format::JsonFields, util::SubscriberInitExt, Layer, +}; use util::ResultExt; const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -25,7 +25,6 @@ const REVISION: Option<&'static str> = option_env!("GITHUB_SHA"); #[tokio::main] async fn main() -> Result<()> { - console_subscriber::init(); if let Err(error) = env::load_dotenv() { eprintln!( "error loading .env.toml (this is expected in production): {}", @@ -112,7 +111,8 @@ async fn main() -> Result<()> { ); #[cfg(unix)] - axum::Server::from_tcp(listener)? + axum::Server::from_tcp(listener) + .map_err(|e| anyhow!(e))? .serve(app.into_make_service_with_connect_info::()) .with_graceful_shutdown(async move { let mut sigterm = tokio::signal::unix::signal(SignalKind::terminate()) @@ -129,7 +129,8 @@ async fn main() -> Result<()> { rpc_server.teardown(); } }) - .await?; + .await + .map_err(|e| anyhow!(e))?; // todo("windows") #[cfg(windows)] @@ -179,11 +180,11 @@ async fn handle_liveness_probe(Extension(state): Extension>) -> Re pub fn init_tracing(config: &Config) -> Option<()> { use std::str::FromStr; use tracing_subscriber::layer::SubscriberExt; - let rust_log = config.rust_log.clone()?; - LogTracer::init().log_err()?; + let filter = EnvFilter::from_str(config.rust_log.as_deref()?).log_err()?; - let subscriber = tracing_subscriber::Registry::default() + tracing_subscriber::registry() + .with(console_subscriber::spawn()) .with(if config.log_json.unwrap_or(false) { Box::new( tracing_subscriber::fmt::layer() @@ -193,17 +194,17 @@ pub fn init_tracing(config: &Config) -> Option<()> { .json() .flatten_event(true) .with_span_list(true), - ), + ) + .with_filter(filter), ) as Box + Send + Sync> } else { Box::new( tracing_subscriber::fmt::layer() - .event_format(tracing_subscriber::fmt::format().pretty()), + .event_format(tracing_subscriber::fmt::format().pretty()) + .with_filter(filter), ) }) - .with(EnvFilter::from_str(rust_log.as_str()).log_err()?); - - tracing::subscriber::set_global_default(subscriber).unwrap(); + .init(); None } diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 4fda8b9959..5362f80df2 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -844,7 +844,7 @@ impl Header for AppVersionHeader { } } -pub fn routes(server: Arc) -> Router { +pub fn routes(server: Arc) -> Router<(), Body> { Router::new() .route("/rpc", get(handle_websocket_request)) .layer( diff --git a/script/run-local-minio b/script/run-local-minio index 8d2d4877ff..292e676c9d 100755 --- a/script/run-local-minio +++ b/script/run-local-minio @@ -6,4 +6,4 @@ mkdir -p .blob_store/zed-crash-reports export MINIO_ROOT_USER=the-blob-store-access-key export MINIO_ROOT_PASSWORD=the-blob-store-secret-key -minio server --quiet .blob_store +exec minio server --quiet .blob_store From bca98caa075f4b93041a6f058a5550966d3cce03 Mon Sep 17 00:00:00 2001 From: Kirpal Grewal <45569241+KGrewal1@users.noreply.github.com> Date: Tue, 5 Mar 2024 22:28:58 +0000 Subject: [PATCH 109/121] Enable `clippy::unnecessary_to_owned` (#8908) lint for `unnecessary_to_owned` and fix the sole violation in the codebase --- Cargo.toml | 1 - crates/editor/src/editor.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c33252f8a4..68ce41c644 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -384,7 +384,6 @@ reversed_empty_ranges = "allow" single_range_in_vec_init = "allow" suspicious_to_owned = "allow" type_complexity = "allow" -unnecessary_to_owned = "allow" [workspace.metadata.cargo-machete] ignored = ["bindgen", "cbindgen", "prost_build", "serde"] diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 34291fbc33..deaae27678 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -8720,7 +8720,7 @@ impl Editor { match permalink { Ok(permalink) => { - cx.open_url(&permalink.to_string()); + cx.open_url(permalink.as_ref()); } Err(err) => { let message = format!("Failed to open permalink: {err}"); From 35c516fda91672b41343d587f9d6ff09326b2f89 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 5 Mar 2024 14:40:09 -0800 Subject: [PATCH 110/121] Log the time incoming RPC messages were queued (#8909) Release Notes: - N/A Co-authored-by: Conrad --- crates/collab/src/rpc.rs | 9 ++++--- crates/rpc/src/macros.rs | 3 ++- crates/rpc/src/peer.rs | 52 +++++++++++++++++++++++++--------------- crates/rpc/src/proto.rs | 10 ++++---- 4 files changed, 47 insertions(+), 27 deletions(-) diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 5362f80df2..b8f2c929b2 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -464,6 +464,7 @@ impl Server { TypeId::of::(), Box::new(move |envelope, session| { let envelope = envelope.into_any().downcast::>().unwrap(); + let received_at = envelope.received_at; let span = info_span!( "handle message", payload_type = envelope.payload_type_name() @@ -478,12 +479,14 @@ impl Server { let future = (handler)(*envelope, session); async move { let result = future.await; - let duration_ms = start_time.elapsed().as_micros() as f64 / 1000.0; + let total_duration_ms = received_at.elapsed().as_micros() as f64 / 1000.0; + let processing_duration_ms = start_time.elapsed().as_micros() as f64 / 1000.0; + let queue_duration_ms = processing_duration_ms - total_duration_ms; match result { Err(error) => { - tracing::error!(%error, ?duration_ms, "error handling message") + tracing::error!(%error, ?total_duration_ms, ?processing_duration_ms, ?queue_duration_ms, "error handling message") } - Ok(()) => tracing::info!(?duration_ms, "finished handling message"), + Ok(()) => tracing::info!(?total_duration_ms, ?processing_duration_ms, ?queue_duration_ms, "finished handling message"), } } .instrument(span) diff --git a/crates/rpc/src/macros.rs b/crates/rpc/src/macros.rs index 85e2b0cf87..f85889a97b 100644 --- a/crates/rpc/src/macros.rs +++ b/crates/rpc/src/macros.rs @@ -1,7 +1,7 @@ #[macro_export] macro_rules! messages { ($(($name:ident, $priority:ident)),* $(,)?) => { - pub fn build_typed_envelope(sender_id: ConnectionId, envelope: Envelope) -> Option> { + pub fn build_typed_envelope(sender_id: ConnectionId, received_at: Instant, envelope: Envelope) -> Option> { match envelope.payload { $(Some(envelope::Payload::$name(payload)) => { Some(Box::new(TypedEnvelope { @@ -12,6 +12,7 @@ macro_rules! messages { }), message_id: envelope.id, payload, + received_at, })) }, )* _ => None diff --git a/crates/rpc/src/peer.rs b/crates/rpc/src/peer.rs index c73f52a99e..486e758a3c 100644 --- a/crates/rpc/src/peer.rs +++ b/crates/rpc/src/peer.rs @@ -13,7 +13,7 @@ use futures::{ }; use parking_lot::{Mutex, RwLock}; use serde::{ser::SerializeStruct, Serialize}; -use std::{fmt, sync::atomic::Ordering::SeqCst}; +use std::{fmt, sync::atomic::Ordering::SeqCst, time::Instant}; use std::{ future::Future, marker::PhantomData, @@ -79,6 +79,7 @@ pub struct TypedEnvelope { pub original_sender_id: Option, pub message_id: u32, pub payload: T, + pub received_at: Instant, } impl TypedEnvelope { @@ -111,8 +112,16 @@ pub struct ConnectionState { next_message_id: Arc, #[allow(clippy::type_complexity)] #[serde(skip)] - response_channels: - Arc)>>>>>, + response_channels: Arc< + Mutex< + Option< + HashMap< + u32, + oneshot::Sender<(proto::Envelope, std::time::Instant, oneshot::Sender<()>)>, + >, + >, + >, + >, } const KEEPALIVE_INTERVAL: Duration = Duration::from_secs(1); @@ -154,7 +163,7 @@ impl Peer { #[cfg(any(test, feature = "test-support"))] const INCOMING_BUFFER_SIZE: usize = 1; #[cfg(not(any(test, feature = "test-support")))] - const INCOMING_BUFFER_SIZE: usize = 64; + const INCOMING_BUFFER_SIZE: usize = 256; let (mut incoming_tx, incoming_rx) = mpsc::channel(INCOMING_BUFFER_SIZE); let (outgoing_tx, mut outgoing_rx) = mpsc::unbounded(); @@ -238,10 +247,10 @@ impl Peer { tracing::trace!(%connection_id, "incoming rpc message: received"); tracing::trace!(%connection_id, "receive timeout: resetting"); receive_timeout.set(create_timer(RECEIVE_TIMEOUT).fuse()); - if let proto::Message::Envelope(incoming) = incoming { + if let (proto::Message::Envelope(incoming), received_at) = incoming { tracing::trace!(%connection_id, "incoming rpc message: processing"); futures::select_biased! { - result = incoming_tx.send(incoming).fuse() => match result { + result = incoming_tx.send((incoming, received_at)).fuse() => match result { Ok(_) => { tracing::trace!(%connection_id, "incoming rpc message: processed"); } @@ -272,7 +281,7 @@ impl Peer { .write() .insert(connection_id, connection_state); - let incoming_rx = incoming_rx.filter_map(move |incoming| { + let incoming_rx = incoming_rx.filter_map(move |(incoming, received_at)| { let response_channels = response_channels.clone(); async move { let message_id = incoming.id; @@ -291,7 +300,7 @@ impl Peer { let channel = response_channels.lock().as_mut()?.remove(&responding_to); if let Some(tx) = channel { let requester_resumed = oneshot::channel(); - if let Err(error) = tx.send((incoming, requester_resumed.0)) { + if let Err(error) = tx.send((incoming, received_at, requester_resumed.0)) { tracing::trace!( %connection_id, message_id, @@ -315,8 +324,9 @@ impl Peer { "incoming response: requester resumed" ); } else { - let message_type = proto::build_typed_envelope(connection_id, incoming) - .map(|p| p.payload_type_name()); + let message_type = + proto::build_typed_envelope(connection_id, received_at, incoming) + .map(|p| p.payload_type_name()); tracing::warn!( %connection_id, message_id, @@ -329,14 +339,16 @@ impl Peer { None } else { tracing::trace!(%connection_id, message_id, "incoming message: received"); - proto::build_typed_envelope(connection_id, incoming).or_else(|| { - tracing::error!( - %connection_id, - message_id, - "unable to construct a typed envelope" - ); - None - }) + proto::build_typed_envelope(connection_id, received_at, incoming).or_else( + || { + tracing::error!( + %connection_id, + message_id, + "unable to construct a typed envelope" + ); + None + }, + ) } } }); @@ -425,7 +437,8 @@ impl Peer { }); async move { send?; - let (response, _barrier) = rx.await.map_err(|_| anyhow!("connection was closed"))?; + let (response, received_at, _barrier) = + rx.await.map_err(|_| anyhow!("connection was closed"))?; if let Some(proto::envelope::Payload::Error(error)) = &response.payload { Err(RpcError::from_proto(&error, T::NAME)) @@ -436,6 +449,7 @@ impl Peer { original_sender_id: response.original_sender_id, payload: T::Response::from_envelope(response) .ok_or_else(|| anyhow!("received response of the wrong type"))?, + received_at, }) } } diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 38e80103ca..40d0d6e3c4 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -8,6 +8,7 @@ use futures::{SinkExt as _, StreamExt as _}; use prost::Message as _; use serde::Serialize; use std::any::{Any, TypeId}; +use std::time::Instant; use std::{ cmp, fmt::Debug, @@ -515,8 +516,9 @@ impl MessageStream where S: futures::Stream> + Unpin, { - pub async fn read(&mut self) -> Result { + pub async fn read(&mut self) -> Result<(Message, Instant), anyhow::Error> { while let Some(bytes) = self.stream.next().await { + let received_at = Instant::now(); match bytes? { WebSocketMessage::Binary(bytes) => { zstd::stream::copy_decode(bytes.as_slice(), &mut self.encoding_buffer).unwrap(); @@ -525,10 +527,10 @@ where self.encoding_buffer.clear(); self.encoding_buffer.shrink_to(MAX_BUFFER_LEN); - return Ok(Message::Envelope(envelope)); + return Ok((Message::Envelope(envelope), received_at)); } - WebSocketMessage::Ping(_) => return Ok(Message::Ping), - WebSocketMessage::Pong(_) => return Ok(Message::Pong), + WebSocketMessage::Ping(_) => return Ok((Message::Ping, received_at)), + WebSocketMessage::Pong(_) => return Ok((Message::Pong, received_at)), WebSocketMessage::Close(_) => break, _ => {} } From ce6bde5a242909834341998a49eeb7f95191ff75 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 5 Mar 2024 14:59:31 -0800 Subject: [PATCH 111/121] Install perf on collab image (#8910) Release Notes: - N/A --------- Co-authored-by: Conrad --- Dockerfile | 9 ++++++--- crates/collab/k8s/collab.template.yml | 4 ---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 704a00adb5..93eac61291 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax = docker/dockerfile:1.2 -FROM rust:1.76-bullseye as builder +FROM rust:1.76-bookworm as builder WORKDIR app COPY . . @@ -20,9 +20,12 @@ RUN --mount=type=cache,target=./target \ cp /app/target/release/collab /app/collab # Copy collab server binary to the runtime image -FROM debian:bullseye-slim as runtime +FROM debian:bookworm-slim as runtime RUN apt-get update; \ - apt-get install -y --no-install-recommends libcurl4-openssl-dev ca-certificates + apt-get install -y --no-install-recommends \ + libcurl4-openssl-dev \ + ca-certificates \ + linux-perf WORKDIR app COPY --from=builder /app/collab /app/collab COPY --from=builder /app/crates/collab/migrations /app/migrations diff --git a/crates/collab/k8s/collab.template.yml b/crates/collab/k8s/collab.template.yml index e1ec20b07c..f0484bf57e 100644 --- a/crates/collab/k8s/collab.template.yml +++ b/crates/collab/k8s/collab.template.yml @@ -78,10 +78,6 @@ spec: port: 8080 initialDelaySeconds: 1 periodSeconds: 1 - resources: - requests: - cpu: "20" - memory: "20Gi" env: - name: HTTP_PORT value: "8080" From 4de80688b9567796c270c2f2fddf41021ddd65f6 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 5 Mar 2024 15:22:41 -0800 Subject: [PATCH 112/121] Revert "Install perf on collab image (#8910)" Keep the removal of the collab resource requests. This reverts commit ce6bde5a242909834341998a49eeb7f95191ff75. --- Dockerfile | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 93eac61291..704a00adb5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax = docker/dockerfile:1.2 -FROM rust:1.76-bookworm as builder +FROM rust:1.76-bullseye as builder WORKDIR app COPY . . @@ -20,12 +20,9 @@ RUN --mount=type=cache,target=./target \ cp /app/target/release/collab /app/collab # Copy collab server binary to the runtime image -FROM debian:bookworm-slim as runtime +FROM debian:bullseye-slim as runtime RUN apt-get update; \ - apt-get install -y --no-install-recommends \ - libcurl4-openssl-dev \ - ca-certificates \ - linux-perf + apt-get install -y --no-install-recommends libcurl4-openssl-dev ca-certificates WORKDIR app COPY --from=builder /app/collab /app/collab COPY --from=builder /app/crates/collab/migrations /app/migrations From 01e5e4224a3630e5bbd38462f63bc5487048fbd6 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 5 Mar 2024 16:00:03 -0800 Subject: [PATCH 113/121] Fix numeric sign of queue duration in logs --- crates/collab/src/rpc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index b8f2c929b2..2cb69c264b 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -481,7 +481,7 @@ impl Server { let result = future.await; let total_duration_ms = received_at.elapsed().as_micros() as f64 / 1000.0; let processing_duration_ms = start_time.elapsed().as_micros() as f64 / 1000.0; - let queue_duration_ms = processing_duration_ms - total_duration_ms; + let queue_duration_ms = total_duration_ms - processing_duration_ms; match result { Err(error) => { tracing::error!(%error, ?total_duration_ms, ?processing_duration_ms, ?queue_duration_ms, "error handling message") From 6d538468248827608472c6ce3498b083104c55f7 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Tue, 5 Mar 2024 21:58:00 -0700 Subject: [PATCH 114/121] 0-downtime collab deploys? (#8926) Before this change Kubernetes would send a SIGTERM to the old server before the new one was ready. Now it will wait. From my reading it seems like startupProbe should not be necessary if we have a readinessProbe; but from testing it seems like without startupProbe we still drop requests when using `rollout restart` Release Notes: - Fixed connectivity issues during Zed deploys. --- crates/collab/k8s/collab.template.yml | 13 +++++++++++++ crates/collab/src/rpc.rs | 4 +++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/crates/collab/k8s/collab.template.yml b/crates/collab/k8s/collab.template.yml index f0484bf57e..41584e94a7 100644 --- a/crates/collab/k8s/collab.template.yml +++ b/crates/collab/k8s/collab.template.yml @@ -33,6 +33,11 @@ metadata: spec: replicas: 1 + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 selector: matchLabels: app: ${ZED_SERVICE_NAME} @@ -78,6 +83,13 @@ spec: port: 8080 initialDelaySeconds: 1 periodSeconds: 1 + startupProbe: + httpGet: + path: / + port: 8080 + initialDelaySeconds: 1 + periodSeconds: 1 + failureThreshold: 15 env: - name: HTTP_PORT value: "8080" @@ -173,6 +185,7 @@ spec: value: "true" - name: ZED_ENVIRONMENT value: ${ZED_ENVIRONMENT} + terminationGracePeriodSeconds: 10 securityContext: capabilities: # FIXME - Switch to the more restrictive `PERFMON` capability. diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 2cb69c264b..c3df8d12fd 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -67,7 +67,9 @@ use tracing::{field, info_span, instrument, Instrument}; use util::SemanticVersion; pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30); -pub const CLEANUP_TIMEOUT: Duration = Duration::from_secs(10); + +// kubernetes gives terminated pods 10s to shutdown gracefully. After they're gone, we can clean up old resources. +pub const CLEANUP_TIMEOUT: Duration = Duration::from_secs(15); const MESSAGE_COUNT_PER_PAGE: usize = 100; const MAX_MESSAGE_LEN: usize = 1024; From c58422cb3f22031abcbcb4c3f12f496f1a33839c Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Tue, 5 Mar 2024 22:11:58 -0700 Subject: [PATCH 115/121] Fix YAML indentation --- crates/collab/k8s/collab.template.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/collab/k8s/collab.template.yml b/crates/collab/k8s/collab.template.yml index 41584e94a7..7ade648adf 100644 --- a/crates/collab/k8s/collab.template.yml +++ b/crates/collab/k8s/collab.template.yml @@ -185,9 +185,9 @@ spec: value: "true" - name: ZED_ENVIRONMENT value: ${ZED_ENVIRONMENT} - terminationGracePeriodSeconds: 10 securityContext: capabilities: # FIXME - Switch to the more restrictive `PERFMON` capability. # This capability isn't yet available in a stable version of Debian. add: ["SYS_ADMIN"] + terminationGracePeriodSeconds: 10 From 6327f3ced845224de39176613e522eb780976a01 Mon Sep 17 00:00:00 2001 From: Hans Date: Wed, 6 Mar 2024 14:02:24 +0800 Subject: [PATCH 116/121] Modify the link to the latest (#8925) Fixed the link for Vue LSP server Release Notes: - N/A --- docs/src/languages/vue.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/languages/vue.md b/docs/src/languages/vue.md index 53b7207291..9cf0f58c35 100644 --- a/docs/src/languages/vue.md +++ b/docs/src/languages/vue.md @@ -1,4 +1,4 @@ # Vue -- Tree Sitter: [tree-sitter-vue](https://github.com/vuejs/language-tools/tree/master/packages/vue-language-server) +- Tree Sitter: [tree-sitter-vue](https://github.com/vuejs/language-tools/tree/master/packages/language-server) - Language Server: [vue](https://github.com/vuejs/language-tools) From e3a7192c1142b1aae9220dbca820fec89b5dcfab Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 6 Mar 2024 10:38:17 +0100 Subject: [PATCH 117/121] Give a name to load balancers and increase node count for production (#8939) Release Notes: - N/A Co-authored-by: Thorsten --- .github/workflows/deploy_collab.yml | 6 ++++++ crates/collab/k8s/collab.template.yml | 2 ++ crates/collab/k8s/postgrest.template.yml | 1 + 3 files changed, 9 insertions(+) diff --git a/.github/workflows/deploy_collab.yml b/.github/workflows/deploy_collab.yml index 220c4ca6f7..e0783d0825 100644 --- a/.github/workflows/deploy_collab.yml +++ b/.github/workflows/deploy_collab.yml @@ -104,8 +104,12 @@ jobs: set -eu if [[ $GITHUB_REF_NAME = "collab-production" ]]; then export ZED_KUBE_NAMESPACE=production + export ZED_COLLAB_LOAD_BALANCER_SIZE_UNIT=10 + export ZED_API_LOAD_BALANCER_SIZE_UNIT=2 elif [[ $GITHUB_REF_NAME = "collab-staging" ]]; then export ZED_KUBE_NAMESPACE=staging + export ZED_COLLAB_LOAD_BALANCER_SIZE_UNIT=1 + export ZED_API_LOAD_BALANCER_SIZE_UNIT=1 else echo "cowardly refusing to deploy from an unknown branch" exit 1 @@ -120,11 +124,13 @@ jobs: export ZED_IMAGE_ID="registry.digitalocean.com/zed/collab:${GITHUB_SHA}" export ZED_SERVICE_NAME=collab + export ZED_LOAD_BALANCER_SIZE_UNIT=$ZED_COLLAB_LOAD_BALANCER_SIZE_UNIT envsubst < crates/collab/k8s/collab.template.yml | kubectl apply -f - kubectl -n "$ZED_KUBE_NAMESPACE" rollout status deployment/$ZED_SERVICE_NAME --watch echo "deployed ${ZED_SERVICE_NAME} to ${ZED_KUBE_NAMESPACE}" export ZED_SERVICE_NAME=api + export ZED_LOAD_BALANCER_SIZE_UNIT=$ZED_API_LOAD_BALANCER_SIZE_UNIT envsubst < crates/collab/k8s/collab.template.yml | kubectl apply -f - kubectl -n "$ZED_KUBE_NAMESPACE" rollout status deployment/$ZED_SERVICE_NAME --watch echo "deployed ${ZED_SERVICE_NAME} to ${ZED_KUBE_NAMESPACE}" diff --git a/crates/collab/k8s/collab.template.yml b/crates/collab/k8s/collab.template.yml index 7ade648adf..38f09d2194 100644 --- a/crates/collab/k8s/collab.template.yml +++ b/crates/collab/k8s/collab.template.yml @@ -11,6 +11,8 @@ metadata: namespace: ${ZED_KUBE_NAMESPACE} name: ${ZED_SERVICE_NAME} annotations: + service.beta.kubernetes.io/do-loadbalancer-name: "${ZED_SERVICE_NAME}-${ZED_KUBE_NAMESPACE}" + service.beta.kubernetes.io/do-loadbalancer-size-unit: ${ZED_LOAD_BALANCER_SIZE_UNIT} service.beta.kubernetes.io/do-loadbalancer-tls-ports: "443" service.beta.kubernetes.io/do-loadbalancer-certificate-id: ${ZED_DO_CERTIFICATE_ID} service.beta.kubernetes.io/do-loadbalancer-disable-lets-encrypt-dns-records: "true" diff --git a/crates/collab/k8s/postgrest.template.yml b/crates/collab/k8s/postgrest.template.yml index f03c0ce33c..ff83880a95 100644 --- a/crates/collab/k8s/postgrest.template.yml +++ b/crates/collab/k8s/postgrest.template.yml @@ -5,6 +5,7 @@ metadata: namespace: ${ZED_KUBE_NAMESPACE} name: postgrest annotations: + service.beta.kubernetes.io/do-loadbalancer-name: "postgrest-${ZED_KUBE_NAMESPACE}" service.beta.kubernetes.io/do-loadbalancer-tls-ports: "443" service.beta.kubernetes.io/do-loadbalancer-certificate-id: ${ZED_DO_CERTIFICATE_ID} service.beta.kubernetes.io/do-loadbalancer-disable-lets-encrypt-dns-records: "true" From 334c3c670bdd4fa5b509d52aa321aaf11ec8221b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 6 Mar 2024 10:52:35 +0100 Subject: [PATCH 118/121] Use a string for ZED_LOAD_BALANCER_SIZE_UNIT Co-Authored-By: Thorsten --- .github/workflows/deploy_collab.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy_collab.yml b/.github/workflows/deploy_collab.yml index e0783d0825..c8f5999b0e 100644 --- a/.github/workflows/deploy_collab.yml +++ b/.github/workflows/deploy_collab.yml @@ -124,13 +124,13 @@ jobs: export ZED_IMAGE_ID="registry.digitalocean.com/zed/collab:${GITHUB_SHA}" export ZED_SERVICE_NAME=collab - export ZED_LOAD_BALANCER_SIZE_UNIT=$ZED_COLLAB_LOAD_BALANCER_SIZE_UNIT + export ZED_LOAD_BALANCER_SIZE_UNIT="$ZED_COLLAB_LOAD_BALANCER_SIZE_UNIT" envsubst < crates/collab/k8s/collab.template.yml | kubectl apply -f - kubectl -n "$ZED_KUBE_NAMESPACE" rollout status deployment/$ZED_SERVICE_NAME --watch echo "deployed ${ZED_SERVICE_NAME} to ${ZED_KUBE_NAMESPACE}" export ZED_SERVICE_NAME=api - export ZED_LOAD_BALANCER_SIZE_UNIT=$ZED_API_LOAD_BALANCER_SIZE_UNIT + export ZED_LOAD_BALANCER_SIZE_UNIT="$ZED_API_LOAD_BALANCER_SIZE_UNIT" envsubst < crates/collab/k8s/collab.template.yml | kubectl apply -f - kubectl -n "$ZED_KUBE_NAMESPACE" rollout status deployment/$ZED_SERVICE_NAME --watch echo "deployed ${ZED_SERVICE_NAME} to ${ZED_KUBE_NAMESPACE}" From c8383e3b18454032445d811b11ffba89b23e5f59 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 6 Mar 2024 11:03:14 +0100 Subject: [PATCH 119/121] Use a string for ZED_LOAD_BALANCER_SIZE_UNIT in k8s template Co-Authored-By: Thorsten --- .github/workflows/deploy_collab.yml | 4 ++-- crates/collab/k8s/collab.template.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy_collab.yml b/.github/workflows/deploy_collab.yml index c8f5999b0e..e0783d0825 100644 --- a/.github/workflows/deploy_collab.yml +++ b/.github/workflows/deploy_collab.yml @@ -124,13 +124,13 @@ jobs: export ZED_IMAGE_ID="registry.digitalocean.com/zed/collab:${GITHUB_SHA}" export ZED_SERVICE_NAME=collab - export ZED_LOAD_BALANCER_SIZE_UNIT="$ZED_COLLAB_LOAD_BALANCER_SIZE_UNIT" + export ZED_LOAD_BALANCER_SIZE_UNIT=$ZED_COLLAB_LOAD_BALANCER_SIZE_UNIT envsubst < crates/collab/k8s/collab.template.yml | kubectl apply -f - kubectl -n "$ZED_KUBE_NAMESPACE" rollout status deployment/$ZED_SERVICE_NAME --watch echo "deployed ${ZED_SERVICE_NAME} to ${ZED_KUBE_NAMESPACE}" export ZED_SERVICE_NAME=api - export ZED_LOAD_BALANCER_SIZE_UNIT="$ZED_API_LOAD_BALANCER_SIZE_UNIT" + export ZED_LOAD_BALANCER_SIZE_UNIT=$ZED_API_LOAD_BALANCER_SIZE_UNIT envsubst < crates/collab/k8s/collab.template.yml | kubectl apply -f - kubectl -n "$ZED_KUBE_NAMESPACE" rollout status deployment/$ZED_SERVICE_NAME --watch echo "deployed ${ZED_SERVICE_NAME} to ${ZED_KUBE_NAMESPACE}" diff --git a/crates/collab/k8s/collab.template.yml b/crates/collab/k8s/collab.template.yml index 38f09d2194..528479105b 100644 --- a/crates/collab/k8s/collab.template.yml +++ b/crates/collab/k8s/collab.template.yml @@ -12,7 +12,7 @@ metadata: name: ${ZED_SERVICE_NAME} annotations: service.beta.kubernetes.io/do-loadbalancer-name: "${ZED_SERVICE_NAME}-${ZED_KUBE_NAMESPACE}" - service.beta.kubernetes.io/do-loadbalancer-size-unit: ${ZED_LOAD_BALANCER_SIZE_UNIT} + service.beta.kubernetes.io/do-loadbalancer-size-unit: "${ZED_LOAD_BALANCER_SIZE_UNIT}" service.beta.kubernetes.io/do-loadbalancer-tls-ports: "443" service.beta.kubernetes.io/do-loadbalancer-certificate-id: ${ZED_DO_CERTIFICATE_ID} service.beta.kubernetes.io/do-loadbalancer-disable-lets-encrypt-dns-records: "true" From 6036830049422ce765535cc4b16374f71b8eaf33 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 6 Mar 2024 05:58:41 -0800 Subject: [PATCH 120/121] Throttle the sending of UpdateFollowers messages (#8918) ## Problem We're trying to figure out why we sometimes see high latency when collaborating, even though the collab server logs indicate that messages are not taking long to process. We think that high volumes of certain types of messages, including `UpdateFollowers` may cause a lot of messages to queue up, causing delays before collab sees certain messages. ## Fix This PR reduces the number of `UpdateFollowers` messages that clients send to collab when scrolling around or moving the cursor, using a time-based throttle. The downside of this change is that scrolling will not be as smooth when following someone. The advantage is that it will be much easier to keep up with the stream of updates, since they will be sent much less frequently. ## Release Notes: - Fixed slowness that could occur when collaborating due to excessive messages being sent to support following. --------- Co-authored-by: Nathan Co-authored-by: Conrad Co-authored-by: Antonio Scandurra Co-authored-by: Thorsten Co-authored-by: Thorsten Ball --- Cargo.lock | 1 + crates/collab/src/tests/following_tests.rs | 9 ++ crates/gpui/src/test.rs | 2 +- crates/picker/Cargo.toml | 1 + crates/picker/src/picker.rs | 41 +++++++-- crates/workspace/src/item.rs | 98 ++++++++++++++-------- crates/workspace/src/workspace.rs | 2 +- 7 files changed, 109 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c012316eaa..483b56d999 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6960,6 +6960,7 @@ dependencies = [ name = "picker" version = "0.1.0" dependencies = [ + "anyhow", "ctor", "editor", "env_logger", diff --git a/crates/collab/src/tests/following_tests.rs b/crates/collab/src/tests/following_tests.rs index 57e5388045..0dff43e41b 100644 --- a/crates/collab/src/tests/following_tests.rs +++ b/crates/collab/src/tests/following_tests.rs @@ -373,8 +373,10 @@ async fn test_basic_following( editor_a1.update(cx_a, |editor, cx| { editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2])); }); + executor.advance_clock(workspace::item::LEADER_UPDATE_THROTTLE); executor.run_until_parked(); cx_b.background_executor.run_until_parked(); + editor_b1.update(cx_b, |editor, cx| { assert_eq!(editor.selections.ranges(cx), &[1..1, 2..2]); }); @@ -387,6 +389,7 @@ async fn test_basic_following( editor.change_selections(None, cx, |s| s.select_ranges([3..3])); editor.set_scroll_position(point(0., 100.), cx); }); + executor.advance_clock(workspace::item::LEADER_UPDATE_THROTTLE); executor.run_until_parked(); editor_b1.update(cx_b, |editor, cx| { assert_eq!(editor.selections.ranges(cx), &[3..3]); @@ -1598,6 +1601,8 @@ async fn test_following_stops_on_unshare(cx_a: &mut TestAppContext, cx_b: &mut T editor_a.update(cx_a, |editor, cx| { editor.change_selections(None, cx, |s| s.select_ranges([1..1])) }); + cx_a.executor() + .advance_clock(workspace::item::LEADER_UPDATE_THROTTLE); cx_a.run_until_parked(); editor_b.update(cx_b, |editor, cx| { assert_eq!(editor.selections.ranges(cx), vec![1..1]) @@ -1616,6 +1621,8 @@ async fn test_following_stops_on_unshare(cx_a: &mut TestAppContext, cx_b: &mut T editor_a.update(cx_a, |editor, cx| { editor.change_selections(None, cx, |s| s.select_ranges([2..2])) }); + cx_a.executor() + .advance_clock(workspace::item::LEADER_UPDATE_THROTTLE); cx_a.run_until_parked(); editor_b.update(cx_b, |editor, cx| { assert_eq!(editor.selections.ranges(cx), vec![1..1]) @@ -1720,6 +1727,7 @@ async fn test_following_into_excluded_file( // When client B starts following client A, currently visible file is replicated workspace_b.update(cx_b, |workspace, cx| workspace.follow(peer_id_a, cx)); + executor.advance_clock(workspace::item::LEADER_UPDATE_THROTTLE); executor.run_until_parked(); let editor_for_excluded_b = workspace_b.update(cx_b, |workspace, cx| { @@ -1741,6 +1749,7 @@ async fn test_following_into_excluded_file( editor_for_excluded_a.update(cx_a, |editor, cx| { editor.select_right(&Default::default(), cx); }); + executor.advance_clock(workspace::item::LEADER_UPDATE_THROTTLE); executor.run_until_parked(); // Changes from B to the excluded file are replicated in A's editor diff --git a/crates/gpui/src/test.rs b/crates/gpui/src/test.rs index eab11e7b4c..3f267542f8 100644 --- a/crates/gpui/src/test.rs +++ b/crates/gpui/src/test.rs @@ -46,10 +46,10 @@ pub fn run_test( let starting_seed = env::var("SEED") .map(|seed| seed.parse().expect("invalid SEED variable")) .unwrap_or(0); - let is_randomized = num_iterations > 1; if let Ok(iterations) = env::var("ITERATIONS") { num_iterations = iterations.parse().expect("invalid ITERATIONS variable"); } + let is_randomized = num_iterations > 1; for seed in starting_seed..starting_seed + num_iterations { let mut retry = 0; diff --git a/crates/picker/Cargo.toml b/crates/picker/Cargo.toml index 79a8ed70d8..435e4459e9 100644 --- a/crates/picker/Cargo.toml +++ b/crates/picker/Cargo.toml @@ -13,6 +13,7 @@ path = "src/picker.rs" doctest = false [dependencies] +anyhow.workspace = true editor.workspace = true gpui.workspace = true menu.workspace = true diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index 11c2458ee1..6ff9f62e0f 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -1,3 +1,4 @@ +use anyhow::Result; use editor::Editor; use gpui::{ div, list, prelude::*, uniform_list, AnyElement, AppContext, ClickEvent, DismissEvent, @@ -15,11 +16,16 @@ enum ElementContainer { UniformList(UniformListScrollHandle), } +struct PendingUpdateMatches { + delegate_update_matches: Option>, + _task: Task>, +} + pub struct Picker { pub delegate: D, element_container: ElementContainer, editor: View, - pending_update_matches: Option>, + pending_update_matches: Option, confirm_on_update: Option, width: Option, max_height: Option, @@ -281,15 +287,32 @@ impl Picker { } pub fn update_matches(&mut self, query: String, cx: &mut ViewContext) { - let update = self.delegate.update_matches(query, cx); + let delegate_pending_update_matches = self.delegate.update_matches(query, cx); + self.matches_updated(cx); - self.pending_update_matches = Some(cx.spawn(|this, mut cx| async move { - update.await; - this.update(&mut cx, |this, cx| { - this.matches_updated(cx); - }) - .ok(); - })); + // This struct ensures that we can synchronously drop the task returned by the + // delegate's `update_matches` method and the task that the picker is spawning. + // If we simply capture the delegate's task into the picker's task, when the picker's + // task gets synchronously dropped, the delegate's task would keep running until + // the picker's task has a chance of being scheduled, because dropping a task happens + // asynchronously. + self.pending_update_matches = Some(PendingUpdateMatches { + delegate_update_matches: Some(delegate_pending_update_matches), + _task: cx.spawn(|this, mut cx| async move { + let delegate_pending_update_matches = this.update(&mut cx, |this, _| { + this.pending_update_matches + .as_mut() + .unwrap() + .delegate_update_matches + .take() + .unwrap() + })?; + delegate_pending_update_matches.await; + this.update(&mut cx, |this, cx| { + this.matches_updated(cx); + }) + }), + }); } fn matches_updated(&mut self, cx: &mut ViewContext) { diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index 25d9f5ed89..07ca2c06a7 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -11,6 +11,7 @@ use client::{ proto::{self, PeerId}, Client, }; +use futures::{channel::mpsc, StreamExt}; use gpui::{ AnyElement, AnyView, AppContext, Entity, EntityId, EventEmitter, FocusHandle, FocusableView, HighlightStyle, Model, Pixels, Point, SharedString, Task, View, ViewContext, WeakView, @@ -27,14 +28,13 @@ use std::{ ops::Range, path::PathBuf, rc::Rc, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, + sync::Arc, time::Duration, }; use theme::Theme; +pub const LEADER_UPDATE_THROTTLE: Duration = Duration::from_millis(200); + #[derive(Deserialize)] pub struct ItemSettings { pub git_status: bool, @@ -415,7 +415,7 @@ impl ItemHandle for View { followed_item.is_project_item(cx), proto::update_followers::Variant::CreateView(proto::View { id: followed_item - .remote_id(&workspace.app_state.client, cx) + .remote_id(&workspace.client(), cx) .map(|id| id.to_proto()), variant: Some(message), leader_id: workspace.leader_for_pane(&pane), @@ -431,8 +431,46 @@ impl ItemHandle for View { .is_none() { let mut pending_autosave = DelayedDebouncedEditAction::new(); + let (pending_update_tx, mut pending_update_rx) = mpsc::unbounded(); let pending_update = Rc::new(RefCell::new(None)); - let pending_update_scheduled = Arc::new(AtomicBool::new(false)); + + let mut send_follower_updates = None; + if let Some(item) = self.to_followable_item_handle(cx) { + let is_project_item = item.is_project_item(cx); + let item = item.downgrade(); + + send_follower_updates = Some(cx.spawn({ + let pending_update = pending_update.clone(); + |workspace, mut cx| async move { + while let Some(mut leader_id) = pending_update_rx.next().await { + while let Ok(Some(id)) = pending_update_rx.try_next() { + leader_id = id; + } + + workspace.update(&mut cx, |workspace, cx| { + let item = item.upgrade().expect( + "item to be alive, otherwise task would have been dropped", + ); + workspace.update_followers( + is_project_item, + proto::update_followers::Variant::UpdateView( + proto::UpdateView { + id: item + .remote_id(workspace.client(), cx) + .map(|id| id.to_proto()), + variant: pending_update.borrow_mut().take(), + leader_id, + }, + ), + cx, + ); + })?; + cx.background_executor().timer(LEADER_UPDATE_THROTTLE).await; + } + anyhow::Ok(()) + } + })); + } let mut event_subscription = Some(cx.subscribe(self, move |workspace, item, event, cx| { @@ -448,9 +486,7 @@ impl ItemHandle for View { }; if let Some(item) = item.to_followable_item_handle(cx) { - let is_project_item = item.is_project_item(cx); let leader_id = workspace.leader_for_pane(&pane); - let follow_event = item.to_follow_event(event); if leader_id.is_some() && matches!(follow_event, Some(FollowEvent::Unfollow)) @@ -458,35 +494,13 @@ impl ItemHandle for View { workspace.unfollow(&pane, cx); } - if item.focus_handle(cx).contains_focused(cx) - && item.add_event_to_update_proto( + if item.focus_handle(cx).contains_focused(cx) { + item.add_event_to_update_proto( event, &mut pending_update.borrow_mut(), cx, - ) - && !pending_update_scheduled.load(Ordering::SeqCst) - { - pending_update_scheduled.store(true, Ordering::SeqCst); - cx.defer({ - let pending_update = pending_update.clone(); - let pending_update_scheduled = pending_update_scheduled.clone(); - move |this, cx| { - pending_update_scheduled.store(false, Ordering::SeqCst); - this.update_followers( - is_project_item, - proto::update_followers::Variant::UpdateView( - proto::UpdateView { - id: item - .remote_id(&this.app_state.client, cx) - .map(|id| id.to_proto()), - variant: pending_update.borrow_mut().take(), - leader_id, - }, - ), - cx, - ); - } - }); + ); + pending_update_tx.unbounded_send(leader_id).ok(); } } @@ -535,6 +549,7 @@ impl ItemHandle for View { cx.observe_release(self, move |workspace, _, _| { workspace.panes_by_item.remove(&item_id); event_subscription.take(); + send_follower_updates.take(); }) .detach(); } @@ -715,6 +730,7 @@ pub trait FollowableItem: Item { pub trait FollowableItemHandle: ItemHandle { fn remote_id(&self, client: &Arc, cx: &WindowContext) -> Option; + fn downgrade(&self) -> Box; fn set_leader_peer_id(&self, leader_peer_id: Option, cx: &mut WindowContext); fn to_state_proto(&self, cx: &WindowContext) -> Option; fn add_event_to_update_proto( @@ -743,6 +759,10 @@ impl FollowableItemHandle for View { }) } + fn downgrade(&self) -> Box { + Box::new(self.downgrade()) + } + fn set_leader_peer_id(&self, leader_peer_id: Option, cx: &mut WindowContext) { self.update(cx, |this, cx| this.set_leader_peer_id(leader_peer_id, cx)) } @@ -782,6 +802,16 @@ impl FollowableItemHandle for View { } } +pub trait WeakFollowableItemHandle: Send + Sync { + fn upgrade(&self) -> Option>; +} + +impl WeakFollowableItemHandle for WeakView { + fn upgrade(&self) -> Option> { + Some(Box::new(self.upgrade()?)) + } +} + #[cfg(any(test, feature = "test-support"))] pub mod test { use super::{Item, ItemEvent}; diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index b4cb71c45a..50631ba736 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1108,7 +1108,7 @@ impl Workspace { ) } - pub fn client(&self) -> &Client { + pub fn client(&self) -> &Arc { &self.app_state.client } From 567fee421946e0adba6be5065292b75ee701f88a Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Wed, 6 Mar 2024 22:23:55 +0800 Subject: [PATCH 121/121] Update `Project search` to `Project Search`. (#8943) Release notes: - N/A --- crates/search/src/project_search.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index c5d5f667dc..b9b7d95218 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -493,7 +493,7 @@ impl Item for ProjectSearchView { }); let tab_name = last_query .filter(|query| !query.is_empty()) - .unwrap_or_else(|| "Project search".into()); + .unwrap_or_else(|| "Project Search".into()); h_flex() .gap_2() .child(Icon::new(IconName::MagnifyingGlass).color(if selected {