diff --git a/Cargo.lock b/Cargo.lock index 209560dcd3e39405b7ae1d1413a2ef904f4759c1..b65c9800a54ccb3cf263dd0458d28bf71f435e51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11474,6 +11474,7 @@ dependencies = [ "copilot2", "ctor", "db2", + "diagnostics2", "editor2", "env_logger 0.9.3", "feature_flags2", diff --git a/crates/command_palette2/src/command_palette.rs b/crates/command_palette2/src/command_palette.rs index 8138f025d3da4af5cfb285c48be0ea8b88879f8f..062d0bb7e5af1b922350389239efd05fa672b127 100644 --- a/crates/command_palette2/src/command_palette.rs +++ b/crates/command_palette2/src/command_palette.rs @@ -116,6 +116,7 @@ impl Clone for Command { } } } + /// Hit count for each command in the palette. /// We only account for commands triggered directly via command palette and not by e.g. keystrokes because /// if an user already knows a keystroke for a command, they are unlikely to use a command palette to look for it. diff --git a/crates/diagnostics2/src/diagnostics.rs b/crates/diagnostics2/src/diagnostics.rs index f684a279fe5dd9104444e3a6cd1f78f967db4a6c..789a3e6ece68fddaa036a9dadd9db1af86e289a6 100644 --- a/crates/diagnostics2/src/diagnostics.rs +++ b/crates/diagnostics2/src/diagnostics.rs @@ -14,8 +14,9 @@ use editor::{ use futures::future::try_join_all; use gpui::{ actions, div, AnyElement, AnyView, AppContext, Component, Context, Div, EventEmitter, - FocusHandle, InteractiveComponent, Model, ParentComponent, Render, SharedString, Styled, - Subscription, Task, View, ViewContext, VisualContext, WeakView, + FocusEvent, FocusHandle, Focusable, FocusableComponent, InteractiveComponent, Model, + ParentComponent, Render, SharedString, Styled, Subscription, Task, View, ViewContext, + VisualContext, WeakView, }; use language::{ Anchor, Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Point, Selection, @@ -48,9 +49,8 @@ const CONTEXT_LINE_COUNT: u32 = 1; pub fn init(cx: &mut AppContext) { ProjectDiagnosticsSettings::register(cx); - // todo!() - // cx.add_action(ProjectDiagnosticsEditor::deploy); - // cx.add_action(ProjectDiagnosticsEditor::toggle_warnings); + cx.observe_new_views(ProjectDiagnosticsEditor::register) + .detach(); } struct ProjectDiagnosticsEditor { @@ -91,39 +91,34 @@ struct DiagnosticGroupState { impl EventEmitter for ProjectDiagnosticsEditor {} impl Render for ProjectDiagnosticsEditor { - type Element = Div; + type Element = Focusable>; + + fn render(&mut self, _: &mut ViewContext) -> Self::Element { + let child = if self.path_states.is_empty() { + div() + .flex() + .items_center() + .justify_center() + .size_full() + .child(Label::new("No problems in workspace")) + } else { + div().size_full().child(self.editor.clone()) + }; - fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - div().size_full().bg(gpui::red()) + div() + .track_focus(&self.focus_handle) + .size_full() + .on_focus_in(Self::focus_in) + .on_action(Self::toggle_warnings) + .child(child) } } -// impl View for ProjectDiagnosticsEditor { -// fn render(&mut self, cx: &mut ViewContext) -> AnyElement { -// if self.path_states.is_empty() { -// let theme = &theme::current(cx).project_diagnostics; -// PaneBackdrop::new( -// cx.view_id(), -// Label::new("No problems in workspace", theme.empty_message.clone()) -// .aligned() -// .contained() -// .with_style(theme.container) -// .into_any(), -// ) -// .into_any() -// } else { -// ChildView::new(&self.editor, cx).into_any() -// } -// } - -// fn focus_in(&mut self, _: AnyView, cx: &mut ViewContext) { -// if cx.is_self_focused() && !self.path_states.is_empty() { -// cx.focus(&self.editor); -// } -// } -// } - impl ProjectDiagnosticsEditor { + fn register(workspace: &mut Workspace, _: &mut ViewContext) { + workspace.register_action(Self::deploy); + } + fn new( project_handle: Model, workspace: WeakView, @@ -240,6 +235,12 @@ impl ProjectDiagnosticsEditor { cx.notify(); } + fn focus_in(&mut self, _: &FocusEvent, cx: &mut ViewContext) { + if self.focus_handle.is_focused(cx) && !self.path_states.is_empty() { + self.editor.focus_handle(cx).focus(cx) + } + } + fn update_excerpts( &mut self, language_server_id: Option, diff --git a/crates/diagnostics2/src/items.rs b/crates/diagnostics2/src/items.rs index 33e3a52691fb823c6396b3b711af00b4581190b7..0fe7ac760e528c83a7a939b959385b90d2e27df0 100644 --- a/crates/diagnostics2/src/items.rs +++ b/crates/diagnostics2/src/items.rs @@ -1,20 +1,18 @@ use collections::HashSet; use editor::{Editor, GoToDiagnostic}; use gpui::{ - div, serde_json, AppContext, CursorStyle, Div, Entity, EventEmitter, MouseButton, Render, - Styled, Subscription, View, ViewContext, WeakView, + div, serde_json, svg, AppContext, CursorStyle, Div, Entity, EventEmitter, InteractiveComponent, + MouseButton, ParentComponent, Render, Stateful, Styled, Subscription, Svg, View, ViewContext, + WeakView, }; use language::Diagnostic; use lsp::LanguageServerId; +use theme::ActiveTheme; +use ui::{Icon, IconElement, Label, TextColor}; use workspace::{item::ItemHandle, StatusItemView, ToolbarItemEvent, Workspace}; use crate::ProjectDiagnosticsEditor; -// todo!() -// pub fn init(cx: &mut AppContext) { -// cx.add_action(DiagnosticIndicator::go_to_next_diagnostic); -// } - pub struct DiagnosticIndicator { summary: project::DiagnosticSummary, active_editor: Option>, @@ -25,10 +23,33 @@ pub struct DiagnosticIndicator { } impl Render for DiagnosticIndicator { - type Element = Div; + type Element = Stateful>; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - div().size_full().bg(gpui::red()) + let mut summary_row = div().flex().flex_row().size_full(); + + if self.summary.error_count > 0 { + summary_row = + summary_row.child(IconElement::new(Icon::XCircle).color(TextColor::Error)); + summary_row = summary_row.child(Label::new(self.summary.error_count.to_string())); + } + + if self.summary.warning_count > 0 { + summary_row = summary_row + .child(IconElement::new(Icon::ExclamationTriangle).color(TextColor::Warning)); + summary_row = summary_row.child(Label::new(self.summary.warning_count.to_string())); + } + + if self.summary.error_count == 0 && self.summary.warning_count == 0 { + summary_row = + summary_row.child(IconElement::new(Icon::Check).color(TextColor::Success)); + } + + div() + .id(cx.entity_id()) + .on_action(Self::go_to_next_diagnostic) + .size_full() + .child(summary_row) } } @@ -40,19 +61,23 @@ impl DiagnosticIndicator { this.in_progress_checks.insert(*language_server_id); cx.notify(); } + project::Event::DiskBasedDiagnosticsFinished { language_server_id } | project::Event::LanguageServerRemoved(language_server_id) => { this.summary = project.read(cx).diagnostic_summary(cx); this.in_progress_checks.remove(language_server_id); cx.notify(); } + project::Event::DiagnosticsUpdated { .. } => { this.summary = project.read(cx).diagnostic_summary(cx); cx.notify(); } + _ => {} }) .detach(); + Self { summary: project.read(cx).diagnostic_summary(cx), in_progress_checks: project diff --git a/crates/diagnostics2/src/toolbar_controls.rs b/crates/diagnostics2/src/toolbar_controls.rs index e60ded4016302518f43448270c080af26e7498fb..6720f43d6fc397f834d42b336c8d4d76550812c5 100644 --- a/crates/diagnostics2/src/toolbar_controls.rs +++ b/crates/diagnostics2/src/toolbar_controls.rs @@ -14,50 +14,35 @@ impl Render for ToolbarControls { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - div() - .h_flex() - .child(IconButton::new("toggle-warnings", Icon::Warning).on_click(|view, cx| todo!())) + let include_warnings = self + .editor + .as_ref() + .and_then(|editor| editor.upgrade()) + .map(|editor| editor.read(cx).include_warnings) + .unwrap_or(false); + + let tooltip = if include_warnings { + "Exclude Warnings" + } else { + "Include Warnings" + }; + + div().child( + IconButton::new("toggle-warnings", Icon::ExclamationTriangle) + .tooltip(tooltip) + .on_click(|this: &mut Self, cx| { + if let Some(editor) = this.editor.as_ref().and_then(|editor| editor.upgrade()) { + editor.update(cx, |editor, cx| { + editor.toggle_warnings(&Default::default(), cx); + }); + } + }), + ) } } impl EventEmitter for ToolbarControls {} -// 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, @@ -82,42 +67,3 @@ impl ToolbarControls { 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::