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