mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-23 18:32:17 +00:00
Merge branch 'project-diagnostics-pinned-tab' into style-project-diagnostics
This commit is contained in:
commit
c9b4bb78f2
13 changed files with 118 additions and 70 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1412,6 +1412,7 @@ dependencies = [
|
|||
"postage",
|
||||
"project",
|
||||
"serde_json",
|
||||
"theme",
|
||||
"unindent",
|
||||
"util",
|
||||
"workspace",
|
||||
|
|
|
@ -13,6 +13,7 @@ editor = { path = "../editor" }
|
|||
language = { path = "../language" }
|
||||
gpui = { path = "../gpui" }
|
||||
project = { path = "../project" }
|
||||
theme = { path = "../theme" }
|
||||
util = { path = "../util" }
|
||||
workspace = { path = "../workspace" }
|
||||
postage = { version = "0.4", features = ["futures-traits"] }
|
||||
|
|
|
@ -17,7 +17,7 @@ use language::{
|
|||
Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Point, Selection, SelectionGoal,
|
||||
};
|
||||
use postage::watch;
|
||||
use project::{Project, ProjectPath};
|
||||
use project::{DiagnosticSummary, Project, ProjectPath};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cmp::Ordering,
|
||||
|
@ -57,6 +57,7 @@ struct ProjectDiagnosticsEditor {
|
|||
model: ModelHandle<ProjectDiagnostics>,
|
||||
workspace: WeakViewHandle<Workspace>,
|
||||
editor: ViewHandle<Editor>,
|
||||
summary: DiagnosticSummary,
|
||||
excerpts: ModelHandle<MultiBuffer>,
|
||||
path_states: Vec<PathState>,
|
||||
paths_to_update: BTreeSet<ProjectPath>,
|
||||
|
@ -132,6 +133,7 @@ impl ProjectDiagnosticsEditor {
|
|||
project::Event::DiskBasedDiagnosticsFinished => {
|
||||
let paths = mem::take(&mut this.paths_to_update);
|
||||
this.update_excerpts(paths, cx);
|
||||
cx.emit(Event::TitleChanged)
|
||||
}
|
||||
project::Event::DiagnosticsUpdated(path) => {
|
||||
this.paths_to_update.insert(path.clone());
|
||||
|
@ -147,13 +149,11 @@ impl ProjectDiagnosticsEditor {
|
|||
cx.subscribe(&editor, |_, _, event, cx| cx.emit(*event))
|
||||
.detach();
|
||||
|
||||
let paths_to_update = project
|
||||
.read(cx)
|
||||
.diagnostic_summaries(cx)
|
||||
.map(|e| e.0)
|
||||
.collect();
|
||||
let project = project.read(cx);
|
||||
let paths_to_update = project.diagnostic_summaries(cx).map(|e| e.0).collect();
|
||||
let this = Self {
|
||||
model,
|
||||
summary: project.diagnostic_summary(cx),
|
||||
workspace,
|
||||
excerpts,
|
||||
editor,
|
||||
|
@ -556,8 +556,38 @@ impl workspace::ItemView for ProjectDiagnosticsEditor {
|
|||
self.model.clone()
|
||||
}
|
||||
|
||||
fn title(&self, _: &AppContext) -> String {
|
||||
"Project Diagnostics".to_string()
|
||||
fn tab_content(&self, style: &theme::Tab, _: &AppContext) -> ElementBox {
|
||||
let theme = &self.settings.borrow().theme.project_diagnostics;
|
||||
let icon_width = theme.tab_icon_width;
|
||||
let icon_spacing = theme.tab_icon_spacing;
|
||||
let summary_spacing = theme.tab_summary_spacing;
|
||||
Flex::row()
|
||||
.with_children([
|
||||
Svg::new("icons/no.svg")
|
||||
.with_color(style.label.text.color)
|
||||
.constrained()
|
||||
.with_width(icon_width)
|
||||
.aligned()
|
||||
.contained()
|
||||
.with_margin_right(icon_spacing)
|
||||
.named("no-icon"),
|
||||
Label::new(self.summary.error_count.to_string(), style.label.clone())
|
||||
.aligned()
|
||||
.boxed(),
|
||||
Svg::new("icons/warning.svg")
|
||||
.with_color(style.label.text.color)
|
||||
.constrained()
|
||||
.with_width(icon_width)
|
||||
.aligned()
|
||||
.contained()
|
||||
.with_margin_left(summary_spacing)
|
||||
.with_margin_right(icon_spacing)
|
||||
.named("warn-icon"),
|
||||
Label::new(self.summary.warning_count.to_string(), style.label.clone())
|
||||
.aligned()
|
||||
.boxed(),
|
||||
])
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn project_path(&self, _: &AppContext) -> Option<project::ProjectPath> {
|
||||
|
@ -603,10 +633,7 @@ impl workspace::ItemView for ProjectDiagnosticsEditor {
|
|||
}
|
||||
|
||||
fn should_update_tab_on_event(event: &Event) -> bool {
|
||||
matches!(
|
||||
event,
|
||||
Event::Saved | Event::Dirtied | Event::FileHandleChanged
|
||||
)
|
||||
matches!(event, Event::Saved | Event::Dirtied | Event::TitleChanged)
|
||||
}
|
||||
|
||||
fn clone_on_split(&self, cx: &mut ViewContext<Self>) -> Option<Self>
|
||||
|
|
|
@ -551,6 +551,19 @@ impl Editor {
|
|||
&self.buffer
|
||||
}
|
||||
|
||||
pub fn title(&self, cx: &AppContext) -> String {
|
||||
let filename = self
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.file(cx)
|
||||
.map(|file| file.file_name(cx));
|
||||
if let Some(name) = filename {
|
||||
name.to_string_lossy().into()
|
||||
} else {
|
||||
"untitled".into()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn snapshot(&mut self, cx: &mut MutableAppContext) -> EditorSnapshot {
|
||||
EditorSnapshot {
|
||||
mode: self.mode,
|
||||
|
@ -3762,8 +3775,8 @@ impl Editor {
|
|||
language::Event::Edited => cx.emit(Event::Edited),
|
||||
language::Event::Dirtied => cx.emit(Event::Dirtied),
|
||||
language::Event::Saved => cx.emit(Event::Saved),
|
||||
language::Event::FileHandleChanged => cx.emit(Event::FileHandleChanged),
|
||||
language::Event::Reloaded => cx.emit(Event::FileHandleChanged),
|
||||
language::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
|
||||
language::Event::Reloaded => cx.emit(Event::TitleChanged),
|
||||
language::Event::Closed => cx.emit(Event::Closed),
|
||||
_ => {}
|
||||
}
|
||||
|
@ -3903,7 +3916,7 @@ pub enum Event {
|
|||
Blurred,
|
||||
Dirtied,
|
||||
Saved,
|
||||
FileHandleChanged,
|
||||
TitleChanged,
|
||||
Closed,
|
||||
}
|
||||
|
||||
|
|
|
@ -121,13 +121,9 @@ impl ItemView for Editor {
|
|||
}
|
||||
}
|
||||
|
||||
fn title(&self, cx: &AppContext) -> String {
|
||||
let file = self.buffer().read(cx).file(cx);
|
||||
if let Some(file) = file {
|
||||
file.file_name(cx).to_string_lossy().into()
|
||||
} else {
|
||||
"untitled".into()
|
||||
}
|
||||
fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox {
|
||||
let title = self.title(cx);
|
||||
Label::new(title, style.label.clone()).boxed()
|
||||
}
|
||||
|
||||
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
|
||||
|
@ -207,10 +203,7 @@ impl ItemView for Editor {
|
|||
}
|
||||
|
||||
fn should_update_tab_on_event(event: &Event) -> bool {
|
||||
matches!(
|
||||
event,
|
||||
Event::Saved | Event::Dirtied | Event::FileHandleChanged
|
||||
)
|
||||
matches!(event, Event::Saved | Event::Dirtied | Event::TitleChanged)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -492,7 +492,15 @@ mod tests {
|
|||
.await;
|
||||
cx.read(|cx| {
|
||||
let active_item = active_pane.read(cx).active_item().unwrap();
|
||||
assert_eq!(active_item.title(cx), "bandana");
|
||||
assert_eq!(
|
||||
active_item
|
||||
.to_any()
|
||||
.downcast::<Editor>()
|
||||
.unwrap()
|
||||
.read(cx)
|
||||
.title(cx),
|
||||
"bandana"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -235,6 +235,9 @@ pub struct ProjectDiagnostics {
|
|||
pub container: ContainerStyle,
|
||||
pub empty_message: TextStyle,
|
||||
pub status_bar_item: ContainedText,
|
||||
pub tab_icon_width: f32,
|
||||
pub tab_icon_spacing: f32,
|
||||
pub tab_summary_spacing: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Default)]
|
||||
|
|
|
@ -70,8 +70,6 @@ pub enum Event {
|
|||
Split(SplitDirection),
|
||||
}
|
||||
|
||||
const MAX_TAB_TITLE_LEN: usize = 24;
|
||||
|
||||
pub struct Pane {
|
||||
item_views: Vec<(usize, Box<dyn ItemViewHandle>)>,
|
||||
active_item_index: usize,
|
||||
|
@ -79,6 +77,11 @@ pub struct Pane {
|
|||
nav_history: Rc<RefCell<NavHistory>>,
|
||||
}
|
||||
|
||||
// #[derive(Debug, Eq, PartialEq)]
|
||||
// pub struct State {
|
||||
// pub tabs: Vec<TabState>,
|
||||
// }
|
||||
|
||||
pub struct ItemNavHistory {
|
||||
history: Rc<RefCell<NavHistory>>,
|
||||
item_view: Rc<dyn WeakItemViewHandle>,
|
||||
|
@ -373,15 +376,12 @@ impl Pane {
|
|||
let is_active = ix == self.active_item_index;
|
||||
|
||||
row.add_child({
|
||||
let mut title = item_view.title(cx);
|
||||
if title.len() > MAX_TAB_TITLE_LEN {
|
||||
let mut truncated_len = MAX_TAB_TITLE_LEN;
|
||||
while !title.is_char_boundary(truncated_len) {
|
||||
truncated_len -= 1;
|
||||
}
|
||||
title.truncate(truncated_len);
|
||||
title.push('…');
|
||||
}
|
||||
let tab_style = if is_active {
|
||||
theme.workspace.active_tab.clone()
|
||||
} else {
|
||||
theme.workspace.tab.clone()
|
||||
};
|
||||
let title = item_view.tab_content(&tab_style, cx);
|
||||
|
||||
let mut style = if is_active {
|
||||
theme.workspace.active_tab.clone()
|
||||
|
@ -430,29 +430,16 @@ impl Pane {
|
|||
.boxed(),
|
||||
)
|
||||
.with_child(
|
||||
Container::new(
|
||||
Align::new(
|
||||
Label::new(
|
||||
title,
|
||||
if is_active {
|
||||
theme.workspace.active_tab.label.clone()
|
||||
} else {
|
||||
theme.workspace.tab.label.clone()
|
||||
},
|
||||
)
|
||||
.boxed(),
|
||||
)
|
||||
.boxed(),
|
||||
)
|
||||
.with_style(ContainerStyle {
|
||||
margin: Margin {
|
||||
left: style.spacing,
|
||||
right: style.spacing,
|
||||
Container::new(Align::new(title).boxed())
|
||||
.with_style(ContainerStyle {
|
||||
margin: Margin {
|
||||
left: style.spacing,
|
||||
right: style.spacing,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.boxed(),
|
||||
})
|
||||
.boxed(),
|
||||
)
|
||||
.with_child(
|
||||
Align::new(
|
||||
|
|
|
@ -155,7 +155,7 @@ pub trait ItemView: View {
|
|||
fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
|
||||
fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) {}
|
||||
fn item_handle(&self, cx: &AppContext) -> Self::ItemHandle;
|
||||
fn title(&self, cx: &AppContext) -> String;
|
||||
fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox;
|
||||
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
|
||||
fn clone_on_split(&self, _: &mut ViewContext<Self>) -> Option<Self>
|
||||
where
|
||||
|
@ -223,7 +223,7 @@ pub trait WeakItemHandle {
|
|||
|
||||
pub trait ItemViewHandle: 'static {
|
||||
fn item_handle(&self, cx: &AppContext) -> Box<dyn ItemHandle>;
|
||||
fn title(&self, cx: &AppContext) -> String;
|
||||
fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox;
|
||||
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
|
||||
fn boxed_clone(&self) -> Box<dyn ItemViewHandle>;
|
||||
fn clone_on_split(&self, cx: &mut MutableAppContext) -> Option<Box<dyn ItemViewHandle>>;
|
||||
|
@ -358,8 +358,8 @@ impl<T: ItemView> ItemViewHandle for ViewHandle<T> {
|
|||
Box::new(self.read(cx).item_handle(cx))
|
||||
}
|
||||
|
||||
fn title(&self, cx: &AppContext) -> String {
|
||||
self.read(cx).title(cx)
|
||||
fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox {
|
||||
self.read(cx).tab_content(style, cx)
|
||||
}
|
||||
|
||||
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
|
||||
|
|
4
crates/zed/assets/icons/no.svg
Normal file
4
crates/zed/assets/icons/no.svg
Normal file
|
@ -0,0 +1,4 @@
|
|||
<svg width="9" height="9" viewBox="0 0 9 9" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.5 9C4.11176 9 3.72353 8.95294 3.33529 8.85882C2.94706 8.75294 2.58235 8.6 2.24118 8.4C1.9 8.2 1.58824 7.96471 1.30588 7.69412C1.03529 7.41177 0.8 7.1 0.6 6.75882C0.4 6.41765 0.247059 6.05294 0.141177 5.66471C0.0470589 5.27647 0 4.88824 0 4.5C0 4.11176 0.0470589 3.72353 0.141177 3.33529C0.247059 2.94706 0.4 2.58235 0.6 2.24118C0.8 1.9 1.03529 1.59412 1.30588 1.32353C1.58824 1.04118 1.9 0.799999 2.24118 0.599999C2.58235 0.4 2.94706 0.252941 3.33529 0.158823C3.72353 0.0529408 4.11176 0 4.5 0C4.88824 0 5.27647 0.0529408 5.66471 0.158823C6.05294 0.252941 6.41765 0.4 6.75882 0.599999C7.1 0.799999 7.40588 1.04118 7.67647 1.32353C7.95882 1.59412 8.2 1.9 8.4 2.24118C8.6 2.58235 8.74706 2.94706 8.84118 3.33529C8.94706 3.72353 9 4.11176 9 4.5C9 4.88824 8.94706 5.27647 8.84118 5.66471C8.74706 6.05294 8.6 6.41765 8.4 6.75882C8.2 7.1 7.95882 7.41177 7.67647 7.69412C7.40588 7.96471 7.1 8.2 6.75882 8.4C6.41765 8.6 6.05294 8.75294 5.66471 8.85882C5.27647 8.95294 4.88824 9 4.5 9ZM1.97647 5.92941L6.03529 1.88823C5.81176 1.72353 5.56471 1.6 5.29412 1.51765C5.03529 1.43529 4.77059 1.39412 4.5 1.39412C4.11176 1.39412 3.73529 1.48235 3.37059 1.65882C3.01765 1.82353 2.71177 2.05294 2.45294 2.34706C2.19412 2.64118 1.99412 2.97647 1.85294 3.35294C1.72353 3.72941 1.65882 4.11176 1.65882 4.5C1.65882 4.74706 1.68235 4.99412 1.72941 5.24118C1.78824 5.47647 1.87059 5.70588 1.97647 5.92941ZM4.5 7.60588C4.88824 7.60588 5.25882 7.52353 5.61176 7.35882C5.97647 7.18235 6.28824 6.94706 6.54706 6.65294C6.80588 6.35882 7 6.02353 7.12941 5.64706C7.27059 5.27059 7.34118 4.88824 7.34118 4.5C7.34118 4.25294 7.31177 4.01177 7.25294 3.77647C7.20588 3.52941 7.12941 3.29412 7.02353 3.07059L2.96471 7.11177C3.18824 7.27647 3.42941 7.4 3.68824 7.48235C3.95882 7.56471 4.22941 7.60588 4.5 7.60588Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -1,3 +1,3 @@
|
|||
<svg width="12" height="10" viewBox="0 0 12 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.1329 8.29967L6.76186 0.840494C6.59383 0.553602 6.29874 0.410156 6.00365 0.410156C5.70856 0.410156 5.41347 0.553602 5.22699 0.840494L0.858047 8.29967C0.540622 8.87141 0.959504 9.59068 1.63347 9.59068H10.3755C11.0468 9.59068 11.4669 8.87346 11.1329 8.29967ZM1.83512 8.60706L5.98521 1.49215L10.1718 8.60706H1.83512ZM6.00365 6.66234C5.64791 6.66234 5.35937 6.95087 5.35937 7.30662C5.35937 7.66236 5.64852 7.95089 6.00447 7.95089C6.36042 7.95089 6.64793 7.66236 6.64793 7.30662C6.64711 6.95128 6.36022 6.66234 6.00365 6.66234ZM5.51184 3.52498V5.49223C5.51184 5.76478 5.73315 5.98405 6.00365 5.98405C6.27415 5.98405 6.49546 5.76376 6.49546 5.49223V3.52498C6.49546 3.25448 6.2762 3.03316 6.00365 3.03316C5.7311 3.03316 5.51184 3.25448 5.51184 3.52498Z" fill="white"/>
|
||||
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.577381 9.14286C0.414683 9.14286 0.277778 9.0873 0.166667 8.97619C0.0555556 8.86508 0 8.72817 0 8.56548C0 8.50992 0.00793651 8.45635 0.0238095 8.40476C0.0396825 8.35317 0.0595238 8.30556 0.0833333 8.2619L4.44643 0.369048C4.58135 0.123016 4.76587 0 5 0C5.23413 0 5.41865 0.123016 5.55357 0.369048L9.91667 8.2619C9.94048 8.30556 9.96032 8.35317 9.97619 8.40476C9.99206 8.45635 10 8.50992 10 8.56548C10 8.72817 9.94444 8.86508 9.83333 8.97619C9.72222 9.0873 9.58532 9.14286 9.42262 9.14286H0.577381ZM5.9881 2.40476H4.01786V3.77976L4.31548 5.61905H5.69048L5.9881 3.77976V2.40476ZM6 7.375C6 7.09722 5.90079 6.8631 5.70238 6.67262C5.50794 6.47817 5.27381 6.38095 5 6.38095C4.72619 6.38095 4.49206 6.47817 4.29762 6.67262C4.10714 6.8631 4.0119 7.09722 4.0119 7.375C4.0119 7.64881 4.10714 7.88095 4.29762 8.07143C4.49206 8.2619 4.72619 8.35714 5 8.35714C5.27381 8.35714 5.50794 8.2619 5.70238 8.07143C5.90079 7.88095 6 7.64881 6 7.375Z" fill="white"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 877 B After Width: | Height: | Size: 1 KiB |
|
@ -316,3 +316,6 @@ message.highlight_text.color = "$text.3.color"
|
|||
background = "$surface.1"
|
||||
empty_message = "$text.0"
|
||||
status_bar_item = { extends = "$text.2", margin.right = 10 }
|
||||
tab_icon_width = 9
|
||||
tab_icon_spacing = 3
|
||||
tab_summary_spacing = 10
|
||||
|
|
|
@ -378,6 +378,10 @@ mod tests {
|
|||
.read(cx)
|
||||
.active_item()
|
||||
.unwrap()
|
||||
.to_any()
|
||||
.downcast::<Editor>()
|
||||
.unwrap()
|
||||
.read(cx)
|
||||
.title(cx),
|
||||
"a.txt"
|
||||
);
|
||||
|
@ -408,6 +412,10 @@ mod tests {
|
|||
.read(cx)
|
||||
.active_item()
|
||||
.unwrap()
|
||||
.to_any()
|
||||
.downcast::<Editor>()
|
||||
.unwrap()
|
||||
.read(cx)
|
||||
.title(cx),
|
||||
"b.txt"
|
||||
);
|
||||
|
@ -491,14 +499,14 @@ mod tests {
|
|||
});
|
||||
|
||||
editor.update(&mut cx, |editor, cx| {
|
||||
assert!(!editor.is_dirty(cx.as_ref()));
|
||||
assert_eq!(editor.title(cx.as_ref()), "untitled");
|
||||
assert!(!editor.is_dirty(cx));
|
||||
assert_eq!(editor.title(cx), "untitled");
|
||||
assert!(Arc::ptr_eq(
|
||||
editor.language(cx).unwrap(),
|
||||
&language::PLAIN_TEXT
|
||||
));
|
||||
editor.handle_input(&editor::Input("hi".into()), cx);
|
||||
assert!(editor.is_dirty(cx.as_ref()));
|
||||
assert!(editor.is_dirty(cx));
|
||||
});
|
||||
|
||||
// Save the buffer. This prompts for a filename.
|
||||
|
@ -509,7 +517,7 @@ mod tests {
|
|||
});
|
||||
cx.read(|cx| {
|
||||
assert!(editor.is_dirty(cx));
|
||||
assert_eq!(editor.title(cx), "untitled");
|
||||
assert_eq!(editor.read(cx).title(cx), "untitled");
|
||||
});
|
||||
|
||||
// When the save completes, the buffer's title is updated and the language is assigned based
|
||||
|
|
Loading…
Reference in a new issue