Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show error and warning indicators in project panel items #18182

Merged
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9116e13
show errors in project panel
nilskch Sep 21, 2024
993c727
color root directory as well
nilskch Sep 21, 2024
09bb386
fix clippy
nilskch Sep 21, 2024
5dd0f94
add configuration for diagnostic errors
nilskch Sep 23, 2024
bc64eb2
some optimizations
nilskch Sep 23, 2024
18085fa
nit
nilskch Sep 23, 2024
4bebe91
docs nit
nilskch Sep 23, 2024
0761d36
final cleanup
nilskch Sep 23, 2024
d1b3217
Update crates/project_panel/src/project_panel_settings.rs
nilskch Sep 24, 2024
eaa0937
add warnings to project panel
nilskch Sep 24, 2024
cddc98e
rename var
nilskch Sep 24, 2024
53f07c5
cleanup
nilskch Sep 24, 2024
108ccf2
remove log dependency again
nilskch Sep 24, 2024
492de1f
test design with additional icons
nilskch Sep 25, 2024
ae949b5
merge main
nilskch Sep 25, 2024
15c4495
cleanup
nilskch Sep 25, 2024
edbb6c6
implement feedback
nilskch Sep 26, 2024
dcec8e3
fix default value in docs
nilskch Sep 26, 2024
96e6536
cleanup
nilskch Sep 26, 2024
026510e
merge main
nilskch Oct 15, 2024
370362a
implement new design
nilskch Oct 15, 2024
c22e70f
make warning icon smaller
nilskch Oct 15, 2024
3b2c990
cleanuo
nilskch Oct 16, 2024
0ffa167
merge main
nilskch Oct 16, 2024
3671a10
merge main
nilskch Nov 3, 2024
009db72
project panel: Make diagnostic error status overwrite git status
mrnugget Nov 7, 2024
49cb5d1
Merge branch 'main' into pr/18182
danilo-leal Nov 8, 2024
fc15825
Checkpoint: Create a dedicated icon for panel diagnostic display
danilo-leal Nov 10, 2024
52bb56e
Merge branch 'main' into pr/18182
danilo-leal Nov 11, 2024
7b6f663
Merge branch 'zed-industries:main' into feat/highlight-errors-in-proj…
nilskch Nov 11, 2024
37b1f49
Merge branch 'main' into pr/18182
danilo-leal Nov 11, 2024
a2b51d3
Use the new `DecoratedIcon` component
danilo-leal Nov 11, 2024
73d1334
Organize colors used for each state and scenario
danilo-leal Nov 11, 2024
88573c5
Remove unused SVGs
danilo-leal Nov 12, 2024
06cb366
Iterate on the setting's writing
danilo-leal Nov 12, 2024
2f3b8f3
Iterate on the triangle knockout icon
danilo-leal Nov 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions assets/icons/dot.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion assets/settings/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,11 @@
///
/// Default: always
"show": "always"
}
},
/// Which files containing diagnostic errors/warnings to mark in the project panel.
///
danilo-leal marked this conversation as resolved.
Show resolved Hide resolved
/// Default: "errors"
"show_diagnostics": "errors"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And what default setting do you prefer? With the Icons I actually prefer "all"

},
"outline_panel": {
// Whether to show the outline panel button in the status bar
Expand Down
38 changes: 38 additions & 0 deletions crates/editor/src/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use gpui::{
use language::{
proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, CharKind, Point, SelectionGoal,
};
use lsp::DiagnosticSeverity;
use multi_buffer::AnchorRangeExt;
use project::{
lsp_store::FormatTrigger, project_settings::ProjectSettings, search::SearchQuery, Item as _,
Expand Down Expand Up @@ -1481,6 +1482,43 @@ pub fn entry_label_color(selected: bool) -> Color {
}
}

pub fn entry_diagnostic_and_git_aware_extra_icon_name(
diagnostic_severity: Option<&DiagnosticSeverity>,
git_status: Option<GitFileStatus>,
ignored: bool,
) -> Option<IconName> {
if let Some(diagnostic_severity) = diagnostic_severity {
if *diagnostic_severity == DiagnosticSeverity::ERROR {
return Some(IconName::XCircle);
}
if *diagnostic_severity == DiagnosticSeverity::WARNING {
return Some(IconName::Warning);
}
}
if !ignored && git_status.is_some() {
Some(IconName::Dot)
} else {
None
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick, but I'd use a match statement:

Suggested change
if let Some(diagnostic_severity) = diagnostic_severity {
if *diagnostic_severity == DiagnosticSeverity::ERROR {
return Some(IconName::XCircle);
}
if *diagnostic_severity == DiagnosticSeverity::WARNING {
return Some(IconName::Warning);
}
}
if !ignored && git_status.is_some() {
Some(IconName::Dot)
} else {
None
}
match diagnostic_severity {
Some(&DiagnosticSeverity::ERROR) => Some(IconName::XCircle),
Some(&DiagnosticSeverity::WARNING) => Some(IconName::Warning),
_ => {
if !ignored && git_status.is_some() {
Some(IconName::Dot)
} else {
None
}
}
}

}

pub fn entry_diagnostic_and_git_aware_label_color(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd actually change these two functions to be a single function that returns (Color, Option<IconName>)

diagnostic_severity: Option<&DiagnosticSeverity>,
git_status: Option<GitFileStatus>,
ignored: bool,
selected: bool,
) -> Color {
if let Some(diagnostic_severity) = diagnostic_severity {
if *diagnostic_severity == DiagnosticSeverity::ERROR {
return Color::Error;
}
if *diagnostic_severity == DiagnosticSeverity::WARNING {
return Color::Warning;
}
}
entry_git_aware_label_color(git_status, ignored, selected)
}

pub fn entry_git_aware_label_color(
git_status: Option<GitFileStatus>,
ignored: bool,
Expand Down
1 change: 1 addition & 0 deletions crates/project_panel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ util.workspace = true
client.workspace = true
worktree.workspace = true
workspace.workspace = true
language.workspace = true

[dev-dependencies]
client = { workspace = true, features = ["test-support"] }
Expand Down
124 changes: 117 additions & 7 deletions crates/project_panel/src/project_panel.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
mod project_panel_settings;
mod scrollbar;
use client::{ErrorCode, ErrorExt};
use language::DiagnosticSeverity;
use scrollbar::ProjectPanelScrollbar;
use settings::{Settings, SettingsStore};

use db::kvp::KEY_VALUE_STORE;
use editor::{
items::entry_git_aware_label_color,
items::{
entry_diagnostic_and_git_aware_extra_icon_name, entry_diagnostic_and_git_aware_label_color,
},
scroll::{Autoscroll, ScrollbarAutoHide},
Editor,
};
Expand All @@ -29,7 +32,9 @@ use project::{
relativize_path, Entry, EntryKind, Fs, Project, ProjectEntryId, ProjectPath, Worktree,
WorktreeId,
};
use project_panel_settings::{ProjectPanelDockPosition, ProjectPanelSettings, ShowScrollbar};
use project_panel_settings::{
ProjectPanelDockPosition, ProjectPanelSettings, ShowDiagnostics, ShowScrollbar,
};
use serde::{Deserialize, Serialize};
use std::{
cell::{Cell, OnceCell},
Expand Down Expand Up @@ -82,6 +87,7 @@ pub struct ProjectPanel {
show_scrollbar: bool,
scrollbar_drag_thumb_offset: Rc<Cell<Option<f32>>>,
hide_scrollbar_task: Option<Task<()>>,
diagnostics: HashMap<(WorktreeId, PathBuf), DiagnosticSeverity>,
mrnugget marked this conversation as resolved.
Show resolved Hide resolved
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -113,6 +119,8 @@ struct EntryDetails {
is_editing: bool,
is_processing: bool,
is_cut: bool,
filename_text_color: Color,
extra_icon_name: Option<IconName>,
git_status: Option<GitFileStatus>,
is_private: bool,
worktree_id: WorktreeId,
Expand Down Expand Up @@ -237,6 +245,14 @@ impl ProjectPanel {
project::Event::ActivateProjectPanel => {
cx.emit(PanelEvent::Activate);
}
project::Event::DiskBasedDiagnosticsFinished { .. }
| project::Event::DiagnosticsUpdated { .. } => {
if ProjectPanelSettings::get_global(cx).show_diagnostics != ShowDiagnostics::Off
{
this.update_diagnostics(cx);
cx.notify();
}
}
project::Event::WorktreeRemoved(id) => {
this.expanded_dir_ids.remove(id);
this.update_visible_entries(None, cx);
Expand Down Expand Up @@ -279,9 +295,16 @@ impl ProjectPanel {
.detach();

let mut project_panel_settings = *ProjectPanelSettings::get_global(cx);
cx.observe_global::<SettingsStore>(move |_, cx| {
cx.observe_global::<SettingsStore>(move |this, cx| {
let new_settings = *ProjectPanelSettings::get_global(cx);
if project_panel_settings != new_settings {
if new_settings.show_diagnostics != project_panel_settings.show_diagnostics {
if new_settings.show_diagnostics != ShowDiagnostics::Off {
this.update_diagnostics(cx);
} else {
this.diagnostics = Default::default();
}
mrnugget marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to restructure the code so you could always call this.update_diagnostics(cx) in case the value changed. Since update_diagnostics already resets the diagnostics, the rest of it would only need to be restructured so it only adds diagnostics based on the settings (which it already kinda does by using show_warnings)

}
project_panel_settings = new_settings;
cx.notify();
}
Expand Down Expand Up @@ -312,6 +335,7 @@ impl ProjectPanel {
show_scrollbar: !Self::should_autohide_scrollbar(cx),
hide_scrollbar_task: None,
scrollbar_drag_thumb_offset: Default::default(),
diagnostics: Default::default(),
};
this.update_visible_entries(None, cx);

Expand Down Expand Up @@ -433,6 +457,56 @@ impl ProjectPanel {
})
}

fn update_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
let show_warnings =
ProjectPanelSettings::get_global(cx).show_diagnostics == ShowDiagnostics::All;
self.diagnostics = Default::default();
self.project
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it better here to store everything in a local HashMap first and assign the local variable at the end to self.diagnostics?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think that's how I'd do it, would make the code nicer, IMHO

.read(cx)
.diagnostic_summaries(false, cx)
.filter_map(|(path, _, diagnostic_summary)| {
if diagnostic_summary.error_count > 0 {
Some((path, DiagnosticSeverity::ERROR))
} else if show_warnings && diagnostic_summary.warning_count > 0 {
Some((path, DiagnosticSeverity::WARNING))
} else {
None
}
})
.for_each(|(project_path, diagnostic_severity)| {
let mut path_buffer = PathBuf::new();
self.update_highest_diagnostic_severity(
&project_path,
path_buffer.clone(),
diagnostic_severity,
);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This here is necessary to also include the root directory of the worktree. We need the empty buffer in the HashMap as well.


for component in project_path.path.components() {
path_buffer.push(component);
self.update_highest_diagnostic_severity(
&project_path,
path_buffer.clone(),
diagnostic_severity,
);
}
});
}

fn update_highest_diagnostic_severity(
&mut self,
project_path: &ProjectPath,
path_buffer: PathBuf,
diagnostic_severity: DiagnosticSeverity,
) {
self.diagnostics
.entry((project_path.worktree_id, path_buffer.clone()))
.and_modify(|strongest_diagnostic_severity| {
*strongest_diagnostic_severity =
std::cmp::min(*strongest_diagnostic_severity, diagnostic_severity);
})
.or_insert(diagnostic_severity);
}

fn serialize(&mut self, cx: &mut ViewContext<Self>) {
let width = self.width;
self.pending_serialization = cx.background_executor().spawn(
Expand Down Expand Up @@ -2027,12 +2101,13 @@ impl ProjectPanel {
}

let end_ix = range.end.min(ix + visible_worktree_entries.len());
let (git_status_setting, show_file_icons, show_folder_icons) = {
let (git_status_setting, show_file_icons, show_folder_icons, show_diagnostics) = {
let settings = ProjectPanelSettings::get_global(cx);
(
settings.git_status,
settings.file_icons,
settings.folder_icons,
settings.show_diagnostics != ShowDiagnostics::Off,
)
};
if let Some(worktree) = self.project.read(cx).worktree_for_id(*worktree_id, cx) {
Expand Down Expand Up @@ -2093,6 +2168,29 @@ impl ProjectPanel {
worktree_id: snapshot.id(),
entry_id: entry.id,
};

let is_marked = self.marked_entries.contains(&selection);

let diagnostic_severity = if show_diagnostics {
self.diagnostics
.get(&(*worktree_id, entry.path.to_path_buf()))
} else {
None
};

let filename_text_color = entry_diagnostic_and_git_aware_label_color(
diagnostic_severity,
status,
entry.is_ignored,
is_marked,
);

let extra_icon_name = entry_diagnostic_and_git_aware_extra_icon_name(
diagnostic_severity,
status,
entry.is_ignored,
);

let mut details = EntryDetails {
filename,
icon,
Expand All @@ -2102,13 +2200,15 @@ impl ProjectPanel {
is_ignored: entry.is_ignored,
is_expanded,
is_selected: self.selection == Some(selection),
is_marked: self.marked_entries.contains(&selection),
is_marked,
is_editing: false,
is_processing: false,
is_cut: self
.clipboard
.as_ref()
.map_or(false, |e| e.is_cut() && e.items().contains(&selection)),
filename_text_color,
extra_icon_name,
git_status: status,
is_private: entry.is_private,
worktree_id: *worktree_id,
Expand Down Expand Up @@ -2198,8 +2298,7 @@ impl ProjectPanel {
.selection
.map_or(false, |selection| selection.entry_id == entry_id);
let width = self.size(cx);
let filename_text_color =
entry_git_aware_label_color(details.git_status, details.is_ignored, is_marked);
let filename_text_color = details.filename_text_color;
let file_name = details.filename.clone();
let mut icon = details.icon.clone();
if settings.file_icons && show_editor && details.kind.is_file() {
Expand All @@ -2223,6 +2322,8 @@ impl ProjectPanel {
active_selection: selection,
marked_selections: selections,
};
let extra_icon_name = details.extra_icon_name;

div()
.id(entry_id.to_proto() as usize)
.on_drag_move::<ExternalPaths>(cx.listener(
Expand Down Expand Up @@ -2399,6 +2500,15 @@ impl ProjectPanel {
}
.ml_1(),
)
.child(if let Some(icon_name) = extra_icon_name {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can using end_slot in here

h_flex().size(IconSize::default().rems()).child(
Icon::new(icon_name)
.size(IconSize::XSmall)
.color(filename_text_color),
)
} else {
div()
})
.on_click(cx.listener(move |this, event: &gpui::ClickEvent, cx| {
if event.down.button == MouseButton::Right || event.down.first_mouse {
return;
Expand Down
20 changes: 20 additions & 0 deletions crates/project_panel/src/project_panel_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub struct ProjectPanelSettings {
pub auto_reveal_entries: bool,
pub auto_fold_dirs: bool,
pub scrollbar: ScrollbarSettings,
pub show_diagnostics: ShowDiagnostics,
}

/// When to show the scrollbar in the project panel.
Expand Down Expand Up @@ -53,6 +54,21 @@ pub struct ScrollbarSettingsContent {
pub show: Option<ShowScrollbar>,
}

/// Which files containing diagnostic errors/warnings to mark in the project panel.
///
/// Default: errors
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ShowDiagnostics {
/// Never mark the diagnostic errors/warnings in the project panel.
Off,
/// Mark files containing diagnostic errors or warnings in the project panel.
All,
#[default]
/// Mark files containing diagnostic errors in the project panel.
Errors,
}

#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
pub struct ProjectPanelSettingsContent {
/// Whether to show the project panel button in the status bar.
Expand Down Expand Up @@ -96,6 +112,10 @@ pub struct ProjectPanelSettingsContent {
pub auto_fold_dirs: Option<bool>,
/// Scrollbar-related settings
pub scrollbar: Option<ScrollbarSettingsContent>,
/// Which files containing diagnostic errors/warnings to mark in the project panel.
///
/// Default: errors
pub show_diagnostics: Option<ShowDiagnostics>,
}

impl Settings for ProjectPanelSettings {
Expand Down
1 change: 1 addition & 0 deletions crates/ui/src/components/icon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ pub enum IconName {
DatabaseZap,
Delete,
Disconnected,
Dot,
Download,
Ellipsis,
EllipsisVertical,
Expand Down
2 changes: 1 addition & 1 deletion crates/ui/src/styles/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use gpui::{Hsla, WindowContext};
use theme::ActiveTheme;

/// Sets a color that has a consistent meaning across all themes.
#[derive(Debug, Default, PartialEq, Copy, Clone)]
#[derive(Debug, Default, Eq, PartialEq, Copy, Clone)]
pub enum Color {
#[default]
Default,
Expand Down
Loading