1use crate::{BufferDiagnosticsEditor, ProjectDiagnosticsEditor, ToggleDiagnosticsRefresh};
2use gpui::{Context, EventEmitter, ParentElement, Render, Window};
3use language::DiagnosticEntry;
4use text::{Anchor, BufferId};
5use ui::prelude::*;
6use ui::{IconButton, IconButtonShape, IconName, Tooltip};
7use workspace::{ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, item::ItemHandle};
8
9pub struct ToolbarControls {
10 editor: Option<Box<dyn DiagnosticsToolbarEditor>>,
11}
12
13pub(crate) trait DiagnosticsToolbarEditor: Send + Sync {
14 /// Informs the toolbar whether warnings are included in the diagnostics.
15 fn include_warnings(&self, cx: &App) -> bool;
16 /// Toggles whether warning diagnostics should be displayed by the
17 /// diagnostics editor.
18 fn toggle_warnings(&self, window: &mut Window, cx: &mut App);
19 /// Indicates whether any of the excerpts displayed by the diagnostics
20 /// editor are stale.
21 fn has_stale_excerpts(&self, cx: &App) -> bool;
22 /// Indicates whether the diagnostics editor is currently updating the
23 /// diagnostics.
24 fn is_updating(&self, cx: &App) -> bool;
25 /// Requests that the diagnostics editor stop updating the diagnostics.
26 fn stop_updating(&self, cx: &mut App);
27 /// Requests that the diagnostics editor updates the displayed diagnostics
28 /// with the latest information.
29 fn refresh_diagnostics(&self, window: &mut Window, cx: &mut App);
30 /// Returns a list of diagnostics for the provided buffer id.
31 fn get_diagnostics_for_buffer(
32 &self,
33 buffer_id: BufferId,
34 cx: &App,
35 ) -> Vec<DiagnosticEntry<Anchor>>;
36}
37
38impl Render for ToolbarControls {
39 fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
40 let mut has_stale_excerpts = false;
41 let mut include_warnings = false;
42 let mut is_updating = false;
43
44 match &self.editor {
45 Some(editor) => {
46 include_warnings = editor.include_warnings(cx);
47 has_stale_excerpts = editor.has_stale_excerpts(cx);
48 is_updating = editor.is_updating(cx);
49 }
50 None => {}
51 }
52
53 let warning_tooltip = if include_warnings {
54 "Exclude Warnings"
55 } else {
56 "Include Warnings"
57 };
58
59 let warning_color = if include_warnings {
60 Color::Warning
61 } else {
62 Color::Muted
63 };
64
65 h_flex()
66 .gap_1()
67 .map(|div| {
68 if is_updating {
69 div.child(
70 IconButton::new("stop-updating", IconName::Stop)
71 .icon_color(Color::Info)
72 .shape(IconButtonShape::Square)
73 .tooltip(Tooltip::for_action_title(
74 "Stop diagnostics update",
75 &ToggleDiagnosticsRefresh,
76 ))
77 .on_click(cx.listener(move |toolbar_controls, _, _, cx| {
78 match toolbar_controls.editor() {
79 Some(editor) => {
80 editor.stop_updating(cx);
81 cx.notify();
82 }
83 None => {}
84 }
85 })),
86 )
87 } else {
88 div.child(
89 IconButton::new("refresh-diagnostics", IconName::ArrowCircle)
90 .icon_color(Color::Info)
91 .shape(IconButtonShape::Square)
92 .disabled(!has_stale_excerpts)
93 .tooltip(Tooltip::for_action_title(
94 "Refresh diagnostics",
95 &ToggleDiagnosticsRefresh,
96 ))
97 .on_click(cx.listener({
98 move |toolbar_controls, _, window, cx| match toolbar_controls
99 .editor()
100 {
101 Some(editor) => editor.refresh_diagnostics(window, cx),
102 None => {}
103 }
104 })),
105 )
106 }
107 })
108 .child(
109 IconButton::new("toggle-warnings", IconName::Warning)
110 .icon_color(warning_color)
111 .shape(IconButtonShape::Square)
112 .tooltip(Tooltip::text(warning_tooltip))
113 .on_click(cx.listener(|this, _, window, cx| match &this.editor {
114 Some(editor) => editor.toggle_warnings(window, cx),
115 None => {}
116 })),
117 )
118 }
119}
120
121impl EventEmitter<ToolbarItemEvent> for ToolbarControls {}
122
123impl ToolbarItemView for ToolbarControls {
124 fn set_active_pane_item(
125 &mut self,
126 active_pane_item: Option<&dyn ItemHandle>,
127 _window: &mut Window,
128 _: &mut Context<Self>,
129 ) -> ToolbarItemLocation {
130 if let Some(pane_item) = active_pane_item.as_ref() {
131 if let Some(editor) = pane_item.downcast::<ProjectDiagnosticsEditor>() {
132 self.editor = Some(Box::new(editor.downgrade()));
133 ToolbarItemLocation::PrimaryRight
134 } else if let Some(editor) = pane_item.downcast::<BufferDiagnosticsEditor>() {
135 self.editor = Some(Box::new(editor.downgrade()));
136 ToolbarItemLocation::PrimaryRight
137 } else {
138 ToolbarItemLocation::Hidden
139 }
140 } else {
141 ToolbarItemLocation::Hidden
142 }
143 }
144}
145
146impl Default for ToolbarControls {
147 fn default() -> Self {
148 Self::new()
149 }
150}
151
152impl ToolbarControls {
153 pub fn new() -> Self {
154 ToolbarControls { editor: None }
155 }
156
157 fn editor(&self) -> Option<&dyn DiagnosticsToolbarEditor> {
158 self.editor.as_deref()
159 }
160}