mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-24 02:46:43 +00:00
Show error+warning counts in project diagnostics tab
Allow workspace items' tab contents to be arbitrary elements Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
6ad9ff10c1
commit
6865a42df9
13 changed files with 121 additions and 77 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1412,6 +1412,7 @@ dependencies = [
|
||||||
"postage",
|
"postage",
|
||||||
"project",
|
"project",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"theme",
|
||||||
"unindent",
|
"unindent",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
|
|
@ -13,6 +13,7 @@ editor = { path = "../editor" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
|
theme = { path = "../theme" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
postage = { version = "0.4", features = ["futures-traits"] }
|
postage = { version = "0.4", features = ["futures-traits"] }
|
||||||
|
|
|
@ -14,7 +14,7 @@ use gpui::{
|
||||||
};
|
};
|
||||||
use language::{Bias, Buffer, Diagnostic, DiagnosticEntry, Point, Selection, SelectionGoal};
|
use language::{Bias, Buffer, Diagnostic, DiagnosticEntry, Point, Selection, SelectionGoal};
|
||||||
use postage::watch;
|
use postage::watch;
|
||||||
use project::{Project, ProjectPath, WorktreeId};
|
use project::{DiagnosticSummary, Project, ProjectPath, WorktreeId};
|
||||||
use std::{cmp::Ordering, mem, ops::Range, sync::Arc};
|
use std::{cmp::Ordering, mem, ops::Range, sync::Arc};
|
||||||
use util::TryFutureExt;
|
use util::TryFutureExt;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
@ -47,6 +47,7 @@ struct ProjectDiagnosticsEditor {
|
||||||
model: ModelHandle<ProjectDiagnostics>,
|
model: ModelHandle<ProjectDiagnostics>,
|
||||||
workspace: WeakViewHandle<Workspace>,
|
workspace: WeakViewHandle<Workspace>,
|
||||||
editor: ViewHandle<Editor>,
|
editor: ViewHandle<Editor>,
|
||||||
|
summary: DiagnosticSummary,
|
||||||
excerpts: ModelHandle<MultiBuffer>,
|
excerpts: ModelHandle<MultiBuffer>,
|
||||||
path_states: Vec<PathState>,
|
path_states: Vec<PathState>,
|
||||||
paths_to_update: HashMap<WorktreeId, BTreeSet<ProjectPath>>,
|
paths_to_update: HashMap<WorktreeId, BTreeSet<ProjectPath>>,
|
||||||
|
@ -120,9 +121,11 @@ impl ProjectDiagnosticsEditor {
|
||||||
let project = model.read(cx).project.clone();
|
let project = model.read(cx).project.clone();
|
||||||
cx.subscribe(&project, |this, _, event, cx| match event {
|
cx.subscribe(&project, |this, _, event, cx| match event {
|
||||||
project::Event::DiskBasedDiagnosticsUpdated { worktree_id } => {
|
project::Event::DiskBasedDiagnosticsUpdated { worktree_id } => {
|
||||||
|
this.summary = this.model.read(cx).project.read(cx).diagnostic_summary(cx);
|
||||||
if let Some(paths) = this.paths_to_update.remove(&worktree_id) {
|
if let Some(paths) = this.paths_to_update.remove(&worktree_id) {
|
||||||
this.update_excerpts(paths, cx);
|
this.update_excerpts(paths, cx);
|
||||||
}
|
}
|
||||||
|
cx.emit(Event::TitleChanged)
|
||||||
}
|
}
|
||||||
project::Event::DiagnosticsUpdated(path) => {
|
project::Event::DiagnosticsUpdated(path) => {
|
||||||
this.paths_to_update
|
this.paths_to_update
|
||||||
|
@ -141,13 +144,11 @@ impl ProjectDiagnosticsEditor {
|
||||||
cx.subscribe(&editor, |_, _, event, cx| cx.emit(*event))
|
cx.subscribe(&editor, |_, _, event, cx| cx.emit(*event))
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
let paths_to_update = project
|
let project = project.read(cx);
|
||||||
.read(cx)
|
let paths_to_update = project.diagnostic_summaries(cx).map(|e| e.0).collect();
|
||||||
.diagnostic_summaries(cx)
|
|
||||||
.map(|e| e.0)
|
|
||||||
.collect();
|
|
||||||
let this = Self {
|
let this = Self {
|
||||||
model,
|
model,
|
||||||
|
summary: project.diagnostic_summary(cx),
|
||||||
workspace,
|
workspace,
|
||||||
excerpts,
|
excerpts,
|
||||||
editor,
|
editor,
|
||||||
|
@ -544,8 +545,38 @@ impl workspace::ItemView for ProjectDiagnosticsEditor {
|
||||||
self.model.clone()
|
self.model.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self, _: &AppContext) -> String {
|
fn tab_content(&self, style: &theme::Tab, _: &AppContext) -> ElementBox {
|
||||||
"Project Diagnostics".to_string()
|
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> {
|
fn project_path(&self, _: &AppContext) -> Option<project::ProjectPath> {
|
||||||
|
@ -586,10 +617,7 @@ impl workspace::ItemView for ProjectDiagnosticsEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_update_tab_on_event(event: &Event) -> bool {
|
fn should_update_tab_on_event(event: &Event) -> bool {
|
||||||
matches!(
|
matches!(event, Event::Saved | Event::Dirtied | Event::TitleChanged)
|
||||||
event,
|
|
||||||
Event::Saved | Event::Dirtied | Event::FileHandleChanged
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -535,6 +535,19 @@ impl Editor {
|
||||||
&self.buffer
|
&self.buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn title(&self, cx: &AppContext) -> String {
|
||||||
|
let filename = self
|
||||||
|
.buffer()
|
||||||
|
.read(cx)
|
||||||
|
.file(cx)
|
||||||
|
.and_then(|file| file.file_name());
|
||||||
|
if let Some(name) = filename {
|
||||||
|
name.to_string_lossy().into()
|
||||||
|
} else {
|
||||||
|
"untitled".into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn snapshot(&mut self, cx: &mut MutableAppContext) -> EditorSnapshot {
|
pub fn snapshot(&mut self, cx: &mut MutableAppContext) -> EditorSnapshot {
|
||||||
EditorSnapshot {
|
EditorSnapshot {
|
||||||
mode: self.mode,
|
mode: self.mode,
|
||||||
|
@ -3619,8 +3632,8 @@ impl Editor {
|
||||||
language::Event::Edited => cx.emit(Event::Edited),
|
language::Event::Edited => cx.emit(Event::Edited),
|
||||||
language::Event::Dirtied => cx.emit(Event::Dirtied),
|
language::Event::Dirtied => cx.emit(Event::Dirtied),
|
||||||
language::Event::Saved => cx.emit(Event::Saved),
|
language::Event::Saved => cx.emit(Event::Saved),
|
||||||
language::Event::FileHandleChanged => cx.emit(Event::FileHandleChanged),
|
language::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
|
||||||
language::Event::Reloaded => cx.emit(Event::FileHandleChanged),
|
language::Event::Reloaded => cx.emit(Event::TitleChanged),
|
||||||
language::Event::Closed => cx.emit(Event::Closed),
|
language::Event::Closed => cx.emit(Event::Closed),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -3727,7 +3740,7 @@ pub enum Event {
|
||||||
Blurred,
|
Blurred,
|
||||||
Dirtied,
|
Dirtied,
|
||||||
Saved,
|
Saved,
|
||||||
FileHandleChanged,
|
TitleChanged,
|
||||||
Closed,
|
Closed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -102,17 +102,9 @@ impl ItemView for Editor {
|
||||||
BufferItemHandle(self.buffer.read(cx).as_singleton().unwrap())
|
BufferItemHandle(self.buffer.read(cx).as_singleton().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self, cx: &AppContext) -> String {
|
fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox {
|
||||||
let filename = self
|
let title = self.title(cx);
|
||||||
.buffer()
|
Label::new(title, style.label.clone()).boxed()
|
||||||
.read(cx)
|
|
||||||
.file(cx)
|
|
||||||
.and_then(|file| file.file_name());
|
|
||||||
if let Some(name) = filename {
|
|
||||||
name.to_string_lossy().into()
|
|
||||||
} else {
|
|
||||||
"untitled".into()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
|
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
|
||||||
|
@ -218,10 +210,7 @@ impl ItemView for Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_update_tab_on_event(event: &Event) -> bool {
|
fn should_update_tab_on_event(event: &Event) -> bool {
|
||||||
matches!(
|
matches!(event, Event::Saved | Event::Dirtied | Event::TitleChanged)
|
||||||
event,
|
|
||||||
Event::Saved | Event::Dirtied | Event::FileHandleChanged
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -491,7 +491,15 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
cx.read(|cx| {
|
cx.read(|cx| {
|
||||||
let active_item = active_pane.read(cx).active_item().unwrap();
|
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 container: ContainerStyle,
|
||||||
pub empty_message: TextStyle,
|
pub empty_message: TextStyle,
|
||||||
pub status_bar_item: ContainedText,
|
pub status_bar_item: ContainedText,
|
||||||
|
pub tab_icon_width: f32,
|
||||||
|
pub tab_icon_spacing: f32,
|
||||||
|
pub tab_summary_spacing: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default)]
|
||||||
|
|
|
@ -55,8 +55,6 @@ pub enum Event {
|
||||||
Split(SplitDirection),
|
Split(SplitDirection),
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_TAB_TITLE_LEN: usize = 24;
|
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub tabs: Vec<TabState>,
|
pub tabs: Vec<TabState>,
|
||||||
|
@ -213,15 +211,12 @@ impl Pane {
|
||||||
let is_active = ix == self.active_item;
|
let is_active = ix == self.active_item;
|
||||||
|
|
||||||
row.add_child({
|
row.add_child({
|
||||||
let mut title = item_view.title(cx);
|
let tab_style = if is_active {
|
||||||
if title.len() > MAX_TAB_TITLE_LEN {
|
theme.workspace.active_tab.clone()
|
||||||
let mut truncated_len = MAX_TAB_TITLE_LEN;
|
} else {
|
||||||
while !title.is_char_boundary(truncated_len) {
|
theme.workspace.tab.clone()
|
||||||
truncated_len -= 1;
|
};
|
||||||
}
|
let title = item_view.tab_content(&tab_style, cx);
|
||||||
title.truncate(truncated_len);
|
|
||||||
title.push('…');
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut style = if is_active {
|
let mut style = if is_active {
|
||||||
theme.workspace.active_tab.clone()
|
theme.workspace.active_tab.clone()
|
||||||
|
@ -270,29 +265,16 @@ impl Pane {
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
.with_child(
|
.with_child(
|
||||||
Container::new(
|
Container::new(Align::new(title).boxed())
|
||||||
Align::new(
|
.with_style(ContainerStyle {
|
||||||
Label::new(
|
margin: Margin {
|
||||||
title,
|
left: style.spacing,
|
||||||
if is_active {
|
right: style.spacing,
|
||||||
theme.workspace.active_tab.label.clone()
|
..Default::default()
|
||||||
} else {
|
},
|
||||||
theme.workspace.tab.label.clone()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.boxed(),
|
|
||||||
)
|
|
||||||
.boxed(),
|
|
||||||
)
|
|
||||||
.with_style(ContainerStyle {
|
|
||||||
margin: Margin {
|
|
||||||
left: style.spacing,
|
|
||||||
right: style.spacing,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
})
|
||||||
..Default::default()
|
.boxed(),
|
||||||
})
|
|
||||||
.boxed(),
|
|
||||||
)
|
)
|
||||||
.with_child(
|
.with_child(
|
||||||
Align::new(
|
Align::new(
|
||||||
|
|
|
@ -142,7 +142,7 @@ pub trait ItemView: View {
|
||||||
type ItemHandle: ItemHandle;
|
type ItemHandle: ItemHandle;
|
||||||
|
|
||||||
fn item_handle(&self, cx: &AppContext) -> Self::ItemHandle;
|
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 project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
|
||||||
fn clone_on_split(&self, _: &mut ViewContext<Self>) -> Option<Self>
|
fn clone_on_split(&self, _: &mut ViewContext<Self>) -> Option<Self>
|
||||||
where
|
where
|
||||||
|
@ -197,7 +197,7 @@ pub trait WeakItemHandle {
|
||||||
|
|
||||||
pub trait ItemViewHandle {
|
pub trait ItemViewHandle {
|
||||||
fn item_handle(&self, cx: &AppContext) -> Box<dyn ItemHandle>;
|
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 project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
|
||||||
fn boxed_clone(&self) -> Box<dyn ItemViewHandle>;
|
fn boxed_clone(&self) -> Box<dyn ItemViewHandle>;
|
||||||
fn clone_on_split(&self, cx: &mut MutableAppContext) -> Option<Box<dyn ItemViewHandle>>;
|
fn clone_on_split(&self, cx: &mut MutableAppContext) -> Option<Box<dyn ItemViewHandle>>;
|
||||||
|
@ -308,8 +308,8 @@ impl<T: ItemView> ItemViewHandle for ViewHandle<T> {
|
||||||
Box::new(self.read(cx).item_handle(cx))
|
Box::new(self.read(cx).item_handle(cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self, cx: &AppContext) -> String {
|
fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox {
|
||||||
self.read(cx).title(cx)
|
self.read(cx).tab_content(style, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
|
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
|
||||||
|
@ -980,7 +980,11 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn activate_next_pane(&mut self, cx: &mut ViewContext<Self>) {
|
pub fn activate_next_pane(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
let ix = self.panes.iter().position(|pane| pane == &self.active_pane).unwrap();
|
let ix = self
|
||||||
|
.panes
|
||||||
|
.iter()
|
||||||
|
.position(|pane| pane == &self.active_pane)
|
||||||
|
.unwrap();
|
||||||
let next_ix = (ix + 1) % self.panes.len();
|
let next_ix = (ix + 1) % self.panes.len();
|
||||||
self.activate_pane(self.panes[next_ix].clone(), cx);
|
self.activate_pane(self.panes[next_ix].clone(), cx);
|
||||||
}
|
}
|
||||||
|
|
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">
|
<svg width="10" height="10" viewBox="0 0 10 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"/>
|
<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>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 877 B After Width: | Height: | Size: 1 KiB |
|
@ -281,3 +281,6 @@ border = { width = 1, top = true, color = "$border.0" }
|
||||||
background = "$surface.1"
|
background = "$surface.1"
|
||||||
empty_message = "$text.0"
|
empty_message = "$text.0"
|
||||||
status_bar_item = { extends = "$text.2", margin.right = 10 }
|
status_bar_item = { extends = "$text.2", margin.right = 10 }
|
||||||
|
tab_icon_width = 9
|
||||||
|
tab_icon_spacing = 3
|
||||||
|
tab_summary_spacing = 10
|
||||||
|
|
|
@ -382,6 +382,10 @@ mod tests {
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.active_item()
|
.active_item()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.to_any()
|
||||||
|
.downcast::<Editor>()
|
||||||
|
.unwrap()
|
||||||
|
.read(cx)
|
||||||
.title(cx),
|
.title(cx),
|
||||||
"a.txt"
|
"a.txt"
|
||||||
);
|
);
|
||||||
|
@ -413,6 +417,10 @@ mod tests {
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.active_item()
|
.active_item()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.to_any()
|
||||||
|
.downcast::<Editor>()
|
||||||
|
.unwrap()
|
||||||
|
.read(cx)
|
||||||
.title(cx),
|
.title(cx),
|
||||||
"b.txt"
|
"b.txt"
|
||||||
);
|
);
|
||||||
|
@ -502,14 +510,14 @@ mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
editor.update(&mut cx, |editor, cx| {
|
editor.update(&mut cx, |editor, cx| {
|
||||||
assert!(!editor.is_dirty(cx.as_ref()));
|
assert!(!editor.is_dirty(cx));
|
||||||
assert_eq!(editor.title(cx.as_ref()), "untitled");
|
assert_eq!(editor.title(cx), "untitled");
|
||||||
assert!(Arc::ptr_eq(
|
assert!(Arc::ptr_eq(
|
||||||
editor.language(cx).unwrap(),
|
editor.language(cx).unwrap(),
|
||||||
&language::PLAIN_TEXT
|
&language::PLAIN_TEXT
|
||||||
));
|
));
|
||||||
editor.handle_input(&editor::Input("hi".into()), cx);
|
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.
|
// Save the buffer. This prompts for a filename.
|
||||||
|
@ -522,7 +530,7 @@ mod tests {
|
||||||
});
|
});
|
||||||
cx.read(|cx| {
|
cx.read(|cx| {
|
||||||
assert!(editor.is_dirty(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.
|
// When the save completes, the buffer's title is updated.
|
||||||
|
@ -531,7 +539,7 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
cx.read(|cx| {
|
cx.read(|cx| {
|
||||||
assert!(!editor.is_dirty(cx));
|
assert!(!editor.is_dirty(cx));
|
||||||
assert_eq!(editor.title(cx), "the-new-name.rs");
|
assert_eq!(editor.read(cx).title(cx), "the-new-name.rs");
|
||||||
});
|
});
|
||||||
// The language is assigned based on the path
|
// The language is assigned based on the path
|
||||||
editor.read_with(&cx, |editor, cx| {
|
editor.read_with(&cx, |editor, cx| {
|
||||||
|
@ -550,7 +558,7 @@ mod tests {
|
||||||
editor
|
editor
|
||||||
.condition(&cx, |editor, cx| !editor.is_dirty(cx))
|
.condition(&cx, |editor, cx| !editor.is_dirty(cx))
|
||||||
.await;
|
.await;
|
||||||
cx.read(|cx| assert_eq!(editor.title(cx), "the-new-name.rs"));
|
cx.read(|cx| assert_eq!(editor.read(cx).title(cx), "the-new-name.rs"));
|
||||||
|
|
||||||
// Open the same newly-created file in another pane item. The new editor should reuse
|
// Open the same newly-created file in another pane item. The new editor should reuse
|
||||||
// the same buffer.
|
// the same buffer.
|
||||||
|
|
Loading…
Reference in a new issue