From e98be4600abd8e2c5d9603a495a051ac0e7204ab Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Wed, 27 Sep 2023 12:26:31 -0400 Subject: [PATCH 01/11] v0.106.x preview --- crates/zed/RELEASE_CHANNEL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/zed/RELEASE_CHANNEL b/crates/zed/RELEASE_CHANNEL index 90012116c0..4de2f126df 100644 --- a/crates/zed/RELEASE_CHANNEL +++ b/crates/zed/RELEASE_CHANNEL @@ -1 +1 @@ -dev \ No newline at end of file +preview \ No newline at end of file From 63db1a97ed705acbfe865935280cae553e17ef34 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Wed, 27 Sep 2023 19:20:08 -0600 Subject: [PATCH 02/11] Don't prompt to save unchanged files (#3053) - don't prompt to save a set of unchanged files when closing (preview-only) --- crates/vim/src/command.rs | 4 ++ crates/workspace/src/pane.rs | 83 +++++++++++++++++-------------- crates/workspace/src/workspace.rs | 2 +- 3 files changed, 50 insertions(+), 39 deletions(-) diff --git a/crates/vim/src/command.rs b/crates/vim/src/command.rs index 092d72c2fc..6c78de1844 100644 --- a/crates/vim/src/command.rs +++ b/crates/vim/src/command.rs @@ -430,5 +430,9 @@ mod test { cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 2)); cx.simulate_keystrokes([":", "q", "enter"]); cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 1)); + cx.simulate_keystrokes([":", "n", "e", "w", "enter"]); + cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 2)); + cx.simulate_keystrokes([":", "q", "a", "enter"]); + cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 0)); } } diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 0f717a0edf..ea70e4f7de 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -893,9 +893,13 @@ impl Pane { ) -> Task> { // Find the items to close. let mut items_to_close = Vec::new(); + let mut dirty_items = Vec::new(); for item in &self.items { if should_close(item.id()) { items_to_close.push(item.boxed_clone()); + if item.is_dirty(cx) { + dirty_items.push(item.boxed_clone()); + } } } @@ -907,13 +911,10 @@ impl Pane { let workspace = self.workspace.clone(); cx.spawn(|pane, mut cx| async move { - if save_intent == SaveIntent::Close && items_to_close.len() > 1 { + if save_intent == SaveIntent::Close && dirty_items.len() > 1 { let mut answer = pane.update(&mut cx, |_, cx| { - let prompt = Self::file_names_for_prompt( - &mut items_to_close.iter(), - items_to_close.len(), - cx, - ); + let prompt = + Self::file_names_for_prompt(&mut dirty_items.iter(), dirty_items.len(), cx); cx.prompt( PromptLevel::Warning, &prompt, @@ -921,7 +922,7 @@ impl Pane { ) })?; match answer.next().await { - Some(0) => save_intent = SaveIntent::Save, + Some(0) => save_intent = SaveIntent::SaveAll, Some(1) => save_intent = SaveIntent::Skip, _ => {} } @@ -2537,14 +2538,12 @@ mod tests { set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx); - let task = pane - .update(cx, |pane, cx| { - pane.close_inactive_items(&CloseInactiveItems, cx) - }) - .unwrap(); - cx.foreground().run_until_parked(); - window.simulate_prompt_answer(2, cx); - task.await.unwrap(); + pane.update(cx, |pane, cx| { + pane.close_inactive_items(&CloseInactiveItems, cx) + }) + .unwrap() + .await + .unwrap(); assert_item_labels(&pane, ["C*"], cx); } @@ -2565,12 +2564,10 @@ mod tests { add_labeled_item(&pane, "E", false, cx); assert_item_labels(&pane, ["A^", "B", "C^", "D", "E*"], cx); - let task = pane - .update(cx, |pane, cx| pane.close_clean_items(&CloseCleanItems, cx)) + pane.update(cx, |pane, cx| pane.close_clean_items(&CloseCleanItems, cx)) + .unwrap() + .await .unwrap(); - cx.foreground().run_until_parked(); - window.simulate_prompt_answer(2, cx); - task.await.unwrap(); assert_item_labels(&pane, ["A^", "C*^"], cx); } @@ -2586,14 +2583,12 @@ mod tests { set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx); - let task = pane - .update(cx, |pane, cx| { - pane.close_items_to_the_left(&CloseItemsToTheLeft, cx) - }) - .unwrap(); - cx.foreground().run_until_parked(); - window.simulate_prompt_answer(2, cx); - task.await.unwrap(); + pane.update(cx, |pane, cx| { + pane.close_items_to_the_left(&CloseItemsToTheLeft, cx) + }) + .unwrap() + .await + .unwrap(); assert_item_labels(&pane, ["C*", "D", "E"], cx); } @@ -2609,14 +2604,12 @@ mod tests { set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx); - let task = pane - .update(cx, |pane, cx| { - pane.close_items_to_the_right(&CloseItemsToTheRight, cx) - }) - .unwrap(); - cx.foreground().run_until_parked(); - window.simulate_prompt_answer(2, cx); - task.await.unwrap(); + pane.update(cx, |pane, cx| { + pane.close_items_to_the_right(&CloseItemsToTheRight, cx) + }) + .unwrap() + .await + .unwrap(); assert_item_labels(&pane, ["A", "B", "C*"], cx); } @@ -2635,14 +2628,28 @@ mod tests { add_labeled_item(&pane, "C", false, cx); assert_item_labels(&pane, ["A", "B", "C*"], cx); - let t = pane + pane.update(cx, |pane, cx| { + pane.close_all_items(&CloseAllItems { save_intent: None }, cx) + }) + .unwrap() + .await + .unwrap(); + assert_item_labels(&pane, [], cx); + + add_labeled_item(&pane, "A", true, cx); + add_labeled_item(&pane, "B", true, cx); + add_labeled_item(&pane, "C", true, cx); + assert_item_labels(&pane, ["A^", "B^", "C*^"], cx); + + let save = pane .update(cx, |pane, cx| { pane.close_all_items(&CloseAllItems { save_intent: None }, cx) }) .unwrap(); + cx.foreground().run_until_parked(); window.simulate_prompt_answer(2, cx); - t.await.unwrap(); + save.await.unwrap(); assert_item_labels(&pane, [], cx); } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 2f82ed3701..beb35d1654 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1419,7 +1419,7 @@ impl Workspace { ) })?; match answer.next().await { - Some(0) => save_intent = SaveIntent::Save, + Some(0) => save_intent = SaveIntent::SaveAll, Some(1) => save_intent = SaveIntent::Skip, _ => {} } From 098a6b1aae415e487faefccd4b33d6a184bda1e3 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Thu, 28 Sep 2023 17:24:00 -0400 Subject: [PATCH 03/11] Enable semantic_index by default (#3061) Release Notes: - Enabled the `semantic_index` setting by default. --- assets/settings/default.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/settings/default.json b/assets/settings/default.json index 1f8068d109..c314295c8d 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -370,7 +370,7 @@ }, // Difference settings for semantic_index "semantic_index": { - "enabled": false + "enabled": true }, // Settings specific to our elixir integration "elixir": { From 59f160a30affb0164dc345f2a4740f6036d540ce Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 29 Sep 2023 13:13:04 +0100 Subject: [PATCH 04/11] Introduce the ability to include or exclude warnings from project diagnostics (#3056) ![CleanShot 2023-09-27 at 18 09 37](https://github.com/zed-industries/zed/assets/482957/317d31e4-81f8-44d8-b94f-8ca7150d3fd2) Release Notes: - Added the ability to exclude warnings from project diagnostics. By default, they will be on but they can be disabled temporarily by clicking on the warnings icon. The default behavior can be changed by changing the new `diagnostics.include_warnings` setting. --- Cargo.lock | 3 + assets/settings/default.json | 5 + crates/diagnostics/Cargo.toml | 3 + crates/diagnostics/src/diagnostics.rs | 31 ++++- .../src/project_diagnostics_settings.rs | 28 +++++ crates/diagnostics/src/toolbar_controls.rs | 115 ++++++++++++++++++ crates/zed/src/zed.rs | 4 + 7 files changed, 186 insertions(+), 3 deletions(-) create mode 100644 crates/diagnostics/src/project_diagnostics_settings.rs create mode 100644 crates/diagnostics/src/toolbar_controls.rs diff --git a/Cargo.lock b/Cargo.lock index 3342bf39b8..7a44033d71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2210,6 +2210,9 @@ dependencies = [ "lsp", "postage", "project", + "schemars", + "serde", + "serde_derive", "serde_json", "settings", "smallvec", diff --git a/assets/settings/default.json b/assets/settings/default.json index c314295c8d..95f99a78e9 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -227,6 +227,11 @@ }, // Automatically update Zed "auto_update": true, + // Diagnostics configuration. + "diagnostics": { + // Whether to show warnings or not by default. + "include_warnings": true + }, // Git gutter behavior configuration. "git": { // Control whether the git gutter is shown. May take 2 values: diff --git a/crates/diagnostics/Cargo.toml b/crates/diagnostics/Cargo.toml index 4e898cca0a..b0b2450f05 100644 --- a/crates/diagnostics/Cargo.toml +++ b/crates/diagnostics/Cargo.toml @@ -21,6 +21,9 @@ util = { path = "../util" } workspace = { path = "../workspace" } anyhow.workspace = true +schemars.workspace = true +serde.workspace = true +serde_derive.workspace = true smallvec.workspace = true postage.workspace = true diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index ac45bcbb79..0b1c6f8470 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -1,4 +1,6 @@ pub mod items; +mod project_diagnostics_settings; +mod toolbar_controls; use anyhow::Result; use collections::{BTreeSet, HashSet}; @@ -19,6 +21,7 @@ use language::{ }; use lsp::LanguageServerId; use project::{DiagnosticSummary, Project, ProjectPath}; +use project_diagnostics_settings::ProjectDiagnosticsSettings; use serde_json::json; use smallvec::SmallVec; use std::{ @@ -30,18 +33,21 @@ use std::{ sync::Arc, }; use theme::ThemeSettings; +pub use toolbar_controls::ToolbarControls; use util::TryFutureExt; use workspace::{ item::{BreadcrumbText, Item, ItemEvent, ItemHandle}, ItemNavHistory, Pane, PaneBackdrop, ToolbarItemLocation, Workspace, }; -actions!(diagnostics, [Deploy]); +actions!(diagnostics, [Deploy, ToggleWarnings]); const CONTEXT_LINE_COUNT: u32 = 1; pub fn init(cx: &mut AppContext) { + settings::register::(cx); cx.add_action(ProjectDiagnosticsEditor::deploy); + cx.add_action(ProjectDiagnosticsEditor::toggle_warnings); items::init(cx); } @@ -55,6 +61,7 @@ struct ProjectDiagnosticsEditor { excerpts: ModelHandle, path_states: Vec, paths_to_update: BTreeSet<(ProjectPath, LanguageServerId)>, + include_warnings: bool, } struct PathState { @@ -187,6 +194,7 @@ impl ProjectDiagnosticsEditor { editor, path_states: Default::default(), paths_to_update, + include_warnings: settings::get::(cx).include_warnings, }; this.update_excerpts(None, cx); this @@ -204,6 +212,18 @@ impl ProjectDiagnosticsEditor { } } + fn toggle_warnings(&mut self, _: &ToggleWarnings, cx: &mut ViewContext) { + self.include_warnings = !self.include_warnings; + self.paths_to_update = self + .project + .read(cx) + .diagnostic_summaries(cx) + .map(|(path, server_id, _)| (path, server_id)) + .collect(); + self.update_excerpts(None, cx); + cx.notify(); + } + fn update_excerpts( &mut self, language_server_id: Option, @@ -277,14 +297,18 @@ impl ProjectDiagnosticsEditor { let mut blocks_to_add = Vec::new(); let mut blocks_to_remove = HashSet::default(); let mut first_excerpt_id = None; + let max_severity = if self.include_warnings { + DiagnosticSeverity::WARNING + } else { + DiagnosticSeverity::ERROR + }; let excerpts_snapshot = self.excerpts.update(cx, |excerpts, excerpts_cx| { let mut old_groups = path_state.diagnostic_groups.iter().enumerate().peekable(); let mut new_groups = snapshot .diagnostic_groups(language_server_id) .into_iter() .filter(|(_, group)| { - group.entries[group.primary_ix].diagnostic.severity - <= DiagnosticSeverity::WARNING + group.entries[group.primary_ix].diagnostic.severity <= max_severity }) .peekable(); loop { @@ -1501,6 +1525,7 @@ mod tests { client::init_settings(cx); workspace::init_settings(cx); Project::init_settings(cx); + crate::init(cx); }); } diff --git a/crates/diagnostics/src/project_diagnostics_settings.rs b/crates/diagnostics/src/project_diagnostics_settings.rs new file mode 100644 index 0000000000..1592d3c7f0 --- /dev/null +++ b/crates/diagnostics/src/project_diagnostics_settings.rs @@ -0,0 +1,28 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Debug)] +pub struct ProjectDiagnosticsSettings { + pub include_warnings: bool, +} + +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] +pub struct ProjectDiagnosticsSettingsContent { + include_warnings: Option, +} + +impl settings::Setting for ProjectDiagnosticsSettings { + const KEY: Option<&'static str> = Some("diagnostics"); + type FileContent = ProjectDiagnosticsSettingsContent; + + fn load( + default_value: &Self::FileContent, + user_values: &[&Self::FileContent], + _cx: &gpui::AppContext, + ) -> anyhow::Result + where + Self: Sized, + { + Self::load_via_json_merge(default_value, user_values) + } +} diff --git a/crates/diagnostics/src/toolbar_controls.rs b/crates/diagnostics/src/toolbar_controls.rs new file mode 100644 index 0000000000..421571eede --- /dev/null +++ b/crates/diagnostics/src/toolbar_controls.rs @@ -0,0 +1,115 @@ +use crate::{ProjectDiagnosticsEditor, ToggleWarnings}; +use gpui::{ + elements::*, + platform::{CursorStyle, MouseButton}, + Action, Entity, EventContext, View, ViewContext, WeakViewHandle, +}; +use workspace::{item::ItemHandle, ToolbarItemLocation, ToolbarItemView}; + +pub struct ToolbarControls { + editor: Option>, +} + +impl Entity for ToolbarControls { + type Event = (); +} + +impl View for ToolbarControls { + fn ui_name() -> &'static str { + "ToolbarControls" + } + + fn render(&mut self, cx: &mut ViewContext) -> AnyElement { + let include_warnings = self + .editor + .as_ref() + .and_then(|editor| editor.upgrade(cx)) + .map(|editor| editor.read(cx).include_warnings) + .unwrap_or(false); + let tooltip = if include_warnings { + "Exclude Warnings".into() + } else { + "Include Warnings".into() + }; + Flex::row() + .with_child(render_toggle_button( + 0, + "icons/warning.svg", + include_warnings, + (tooltip, Some(Box::new(ToggleWarnings))), + cx, + move |this, cx| { + if let Some(editor) = this.editor.and_then(|editor| editor.upgrade(cx)) { + editor.update(cx, |editor, cx| { + editor.toggle_warnings(&Default::default(), cx) + }); + } + }, + )) + .into_any() + } +} + +impl ToolbarItemView for ToolbarControls { + fn set_active_pane_item( + &mut self, + active_pane_item: Option<&dyn ItemHandle>, + _: &mut ViewContext, + ) -> ToolbarItemLocation { + if let Some(pane_item) = active_pane_item.as_ref() { + if let Some(editor) = pane_item.downcast::() { + self.editor = Some(editor.downgrade()); + ToolbarItemLocation::PrimaryRight { flex: None } + } else { + ToolbarItemLocation::Hidden + } + } else { + ToolbarItemLocation::Hidden + } + } +} + +impl ToolbarControls { + pub fn new() -> Self { + ToolbarControls { editor: None } + } +} + +fn render_toggle_button< + F: 'static + Fn(&mut ToolbarControls, &mut EventContext), +>( + index: usize, + icon: &'static str, + toggled: bool, + tooltip: (String, Option>), + cx: &mut ViewContext, + on_click: F, +) -> AnyElement { + enum Button {} + + let theme = theme::current(cx); + let (tooltip_text, action) = tooltip; + + MouseEventHandler::new::(index, cx, |mouse_state, _| { + let style = theme + .workspace + .toolbar + .toggleable_tool + .in_state(toggled) + .style_for(mouse_state); + Svg::new(icon) + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .constrained() + .with_width(style.button_width) + .with_height(style.button_width) + .contained() + .with_style(style.container) + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, view, cx| on_click(view, cx)) + .with_tooltip::