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