Prior we were only updating the diagnostics pane when it is either unfocued, saved or when a disk based diagnostic run finishes (aka cargo check). The reason for this is simple, we do not want to take away the excerpt under the users cursor while they are typing if they manage to fix the diagnostic. Additionally we need to prevent dropping the changed buffer before it is saved. Delaying updates was a simple way to work around these kind of issues, but comes at a huge annoyance that the diagnostics pane is not actually reflecting the current state of the world but some snapshot of it instead making it less than ideal to work within it for languages that do not leverage disk based diagnostics (that is not rust-analyzer, and even for rust-analyzer its annoying). This PR changes this. We now always live update the view but take care to retain unsaved buffers as well as buffers that contain a cursor in them (as well as some other "checkpoint" properties). Release Notes: - Improved diagnostics pane to live update when editing within its editor
152 lines
5.6 KiB
Rust
152 lines
5.6 KiB
Rust
use crate::{BufferDiagnosticsEditor, ProjectDiagnosticsEditor, ToggleDiagnosticsRefresh};
|
|
use gpui::{Context, EventEmitter, ParentElement, Render, Window};
|
|
use language::DiagnosticEntry;
|
|
use text::{Anchor, BufferId};
|
|
use ui::prelude::*;
|
|
use ui::{IconButton, IconButtonShape, IconName, Tooltip};
|
|
use workspace::{ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, item::ItemHandle};
|
|
|
|
pub struct ToolbarControls {
|
|
editor: Option<Box<dyn DiagnosticsToolbarEditor>>,
|
|
}
|
|
|
|
pub(crate) trait DiagnosticsToolbarEditor: Send + Sync {
|
|
/// Informs the toolbar whether warnings are included in the diagnostics.
|
|
fn include_warnings(&self, cx: &App) -> bool;
|
|
/// Toggles whether warning diagnostics should be displayed by the
|
|
/// diagnostics editor.
|
|
fn toggle_warnings(&self, window: &mut Window, cx: &mut App);
|
|
/// Indicates whether the diagnostics editor is currently updating the
|
|
/// diagnostics.
|
|
fn is_updating(&self, cx: &App) -> bool;
|
|
/// Requests that the diagnostics editor stop updating the diagnostics.
|
|
fn stop_updating(&self, cx: &mut App);
|
|
/// Requests that the diagnostics editor updates the displayed diagnostics
|
|
/// with the latest information.
|
|
fn refresh_diagnostics(&self, window: &mut Window, cx: &mut App);
|
|
/// Returns a list of diagnostics for the provided buffer id.
|
|
fn get_diagnostics_for_buffer(
|
|
&self,
|
|
buffer_id: BufferId,
|
|
cx: &App,
|
|
) -> Vec<DiagnosticEntry<Anchor>>;
|
|
}
|
|
|
|
impl Render for ToolbarControls {
|
|
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
|
let mut include_warnings = false;
|
|
let mut is_updating = false;
|
|
|
|
match &self.editor {
|
|
Some(editor) => {
|
|
include_warnings = editor.include_warnings(cx);
|
|
is_updating = editor.is_updating(cx);
|
|
}
|
|
None => {}
|
|
}
|
|
|
|
let warning_tooltip = if include_warnings {
|
|
"Exclude Warnings"
|
|
} else {
|
|
"Include Warnings"
|
|
};
|
|
|
|
let warning_color = if include_warnings {
|
|
Color::Warning
|
|
} else {
|
|
Color::Muted
|
|
};
|
|
|
|
h_flex()
|
|
.gap_1()
|
|
.map(|div| {
|
|
if is_updating {
|
|
div.child(
|
|
IconButton::new("stop-updating", IconName::Stop)
|
|
.icon_color(Color::Info)
|
|
.shape(IconButtonShape::Square)
|
|
.tooltip(Tooltip::for_action_title(
|
|
"Stop diagnostics update",
|
|
&ToggleDiagnosticsRefresh,
|
|
))
|
|
.on_click(cx.listener(move |toolbar_controls, _, _, cx| {
|
|
if let Some(editor) = toolbar_controls.editor() {
|
|
editor.stop_updating(cx);
|
|
cx.notify();
|
|
}
|
|
})),
|
|
)
|
|
} else {
|
|
div.child(
|
|
IconButton::new("refresh-diagnostics", IconName::ArrowCircle)
|
|
.icon_color(Color::Info)
|
|
.shape(IconButtonShape::Square)
|
|
.tooltip(Tooltip::for_action_title(
|
|
"Refresh diagnostics",
|
|
&ToggleDiagnosticsRefresh,
|
|
))
|
|
.on_click(cx.listener({
|
|
move |toolbar_controls, _, window, cx| {
|
|
if let Some(editor) = toolbar_controls.editor() {
|
|
editor.refresh_diagnostics(window, cx)
|
|
}
|
|
}
|
|
})),
|
|
)
|
|
}
|
|
})
|
|
.child(
|
|
IconButton::new("toggle-warnings", IconName::Warning)
|
|
.icon_color(warning_color)
|
|
.shape(IconButtonShape::Square)
|
|
.tooltip(Tooltip::text(warning_tooltip))
|
|
.on_click(cx.listener(|this, _, window, cx| {
|
|
if let Some(editor) = &this.editor {
|
|
editor.toggle_warnings(window, cx)
|
|
}
|
|
})),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl EventEmitter<ToolbarItemEvent> for ToolbarControls {}
|
|
|
|
impl ToolbarItemView for ToolbarControls {
|
|
fn set_active_pane_item(
|
|
&mut self,
|
|
active_pane_item: Option<&dyn ItemHandle>,
|
|
_window: &mut Window,
|
|
_: &mut Context<Self>,
|
|
) -> ToolbarItemLocation {
|
|
if let Some(pane_item) = active_pane_item.as_ref() {
|
|
if let Some(editor) = pane_item.downcast::<ProjectDiagnosticsEditor>() {
|
|
self.editor = Some(Box::new(editor.downgrade()));
|
|
ToolbarItemLocation::PrimaryRight
|
|
} else if let Some(editor) = pane_item.downcast::<BufferDiagnosticsEditor>() {
|
|
self.editor = Some(Box::new(editor.downgrade()));
|
|
ToolbarItemLocation::PrimaryRight
|
|
} else {
|
|
ToolbarItemLocation::Hidden
|
|
}
|
|
} else {
|
|
ToolbarItemLocation::Hidden
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for ToolbarControls {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl ToolbarControls {
|
|
pub fn new() -> Self {
|
|
ToolbarControls { editor: None }
|
|
}
|
|
|
|
fn editor(&self) -> Option<&dyn DiagnosticsToolbarEditor> {
|
|
self.editor.as_deref()
|
|
}
|
|
}
|