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