Properly align excerpt and outline items (#13070)

This commit is contained in:
Kirill Bulatov 2024-06-14 23:26:07 +03:00 committed by GitHub
parent 9bc3c6810b
commit ff8486e67f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -19,12 +19,12 @@ use editor::{
use file_icons::FileIcons;
use futures::{stream::FuturesUnordered, StreamExt};
use gpui::{
actions, anchored, deferred, div, px, uniform_list, Action, AppContext, AssetSource,
AsyncWindowContext, ClipboardItem, DismissEvent, Div, ElementId, EntityId, EventEmitter,
FocusHandle, FocusableView, InteractiveElement, IntoElement, KeyContext, Model, MouseButton,
MouseDownEvent, ParentElement, Pixels, Point, Render, SharedString, Stateful, Styled,
Subscription, Task, UniformListScrollHandle, View, ViewContext, VisualContext, WeakView,
WindowContext,
actions, anchored, deferred, div, px, uniform_list, Action, AnyElement, AppContext,
AssetSource, AsyncWindowContext, ClipboardItem, DismissEvent, Div, ElementId, EntityId,
EventEmitter, FocusHandle, FocusableView, InteractiveElement, IntoElement, KeyContext, Model,
MouseButton, MouseDownEvent, ParentElement, Pixels, Point, Render, SharedString, Stateful,
Styled, Subscription, Task, UniformListScrollHandle, View, ViewContext, VisualContext,
WeakView, WindowContext,
};
use itertools::Itertools;
use language::{BufferId, BufferSnapshot, OffsetRangeExt, OutlineItem};
@ -97,6 +97,7 @@ enum CollapsedEntry {
Excerpt(BufferId, ExcerptId),
}
#[derive(Debug)]
struct Excerpt {
range: ExcerptRange<language::Anchor>,
outlines: ExcerptOutlines,
@ -126,6 +127,7 @@ impl Excerpt {
}
}
#[derive(Debug)]
enum ExcerptOutlines {
Outlines(Vec<Outline>),
Invalidated(Vec<Outline>),
@ -1247,12 +1249,11 @@ impl OutlinePanel {
let color = entry_git_aware_label_color(None, false, is_active);
let icon = if has_outlines {
FileIcons::get_chevron_icon(is_expanded, cx)
.map(|icon_path| Icon::from_path(icon_path).color(color).into_any_element())
} else {
None
}
.map(Icon::from_path)
.map(|icon| icon.color(color));
let depth = if icon.is_some() { depth + 1 } else { depth };
.unwrap_or_else(empty_icon);
let buffer_snapshot = self
.project
@ -1274,7 +1275,7 @@ impl OutlinePanel {
EntryRef::Excerpt(buffer_id, excerpt_id, range),
item_id,
depth,
icon,
Some(icon),
is_active,
label_element,
cx,
@ -1304,12 +1305,16 @@ impl OutlinePanel {
}
_ => false,
};
let icon = if self.is_singleton_active(cx) {
None
} else {
Some(empty_icon())
};
self.entry_element(
EntryRef::Outline(buffer_id, excerpt_id, rendered_outline),
item_id,
depth,
None,
icon,
is_active,
label_element,
cx,
@ -1334,18 +1339,17 @@ impl OutlinePanel {
entry_git_aware_label_color(entry.git_status, entry.is_ignored, is_active);
let icon = if settings.file_icons {
FileIcons::get_icon(&entry.path, cx)
.map(|icon_path| Icon::from_path(icon_path).color(color).into_any_element())
} else {
None
}
.map(Icon::from_path)
.map(|icon| icon.color(color));
};
(
ElementId::from(entry.id.to_proto() as usize),
Label::new(name)
.single_line()
.color(color)
.into_any_element(),
icon,
icon.unwrap_or_else(empty_icon),
)
}
FsEntry::Directory(worktree_id, entry) => {
@ -1362,14 +1366,14 @@ impl OutlinePanel {
FileIcons::get_chevron_icon(is_expanded, cx)
}
.map(Icon::from_path)
.map(|icon| icon.color(color));
.map(|icon| icon.color(color).into_any_element());
(
ElementId::from(entry.id.to_proto() as usize),
Label::new(name)
.single_line()
.color(color)
.into_any_element(),
icon,
icon.unwrap_or_else(empty_icon),
)
}
FsEntry::ExternalFile(buffer_id, ..) => {
@ -1384,7 +1388,7 @@ impl OutlinePanel {
None
}
.map(Icon::from_path)
.map(|icon| icon.color(color));
.map(|icon| icon.color(color).into_any_element());
(icon, file_name(path.as_ref()))
}
None => (None, "Untitled".to_string()),
@ -1397,7 +1401,7 @@ impl OutlinePanel {
.single_line()
.color(color)
.into_any_element(),
icon,
icon.unwrap_or_else(empty_icon),
)
}
};
@ -1406,7 +1410,7 @@ impl OutlinePanel {
EntryRef::Entry(rendered_entry),
item_id,
depth,
icon,
Some(icon),
is_active,
label_element,
cx,
@ -1450,7 +1454,7 @@ impl OutlinePanel {
FileIcons::get_chevron_icon(is_expanded, cx)
}
.map(Icon::from_path)
.map(|icon| icon.color(color));
.map(|icon| icon.color(color).into_any_element());
(
ElementId::from(
dir_entries
@ -1462,7 +1466,7 @@ impl OutlinePanel {
.single_line()
.color(color)
.into_any_element(),
icon,
icon.unwrap_or_else(empty_icon),
)
};
@ -1470,7 +1474,7 @@ impl OutlinePanel {
EntryRef::FoldedDirs(worktree_id, dir_entries),
item_id,
depth,
icon,
Some(icon),
is_active,
label_element,
cx,
@ -1483,7 +1487,7 @@ impl OutlinePanel {
rendered_entry: EntryRef<'_>,
item_id: ElementId,
depth: usize,
icon: Option<Icon>,
icon_element: Option<AnyElement>,
is_active: bool,
label_element: gpui::AnyElement,
cx: &mut ViewContext<OutlinePanel>,
@ -1498,13 +1502,8 @@ impl OutlinePanel {
.indent_level(depth)
.indent_step_size(px(settings.indent_size))
.selected(is_active)
.child(if let Some(icon) = icon {
h_flex().child(icon)
} else {
h_flex()
.size(IconSize::default().rems())
.invisible()
.flex_none()
.when_some(icon_element, |list_item, icon_element| {
list_item.child(h_flex().child(icon_element))
})
.child(h_flex().h_6().child(label_element).ml_1())
.on_click({
@ -2172,32 +2171,15 @@ impl OutlinePanel {
}
fn entries_with_depths(&mut self, cx: &AppContext) -> &[(usize, EntryOwned)] {
let is_singleton = self.is_singleton_active(cx);
self.cached_entries_with_depth.get_or_insert_with(|| {
let auto_fold_dirs = OutlinePanelSettings::get_global(cx).auto_fold_dirs;
let mut folded_dirs_entry = None::<(usize, WorktreeId, Vec<Entry>)>;
let mut entries = Vec::new();
let is_singleton = self
.active_item
.as_ref()
.and_then(|active_item| {
Some(
active_item
.active_editor
.upgrade()?
.read(cx)
.buffer()
.read(cx)
.is_singleton(),
)
})
.unwrap_or(false);
for entry in &self.fs_entries {
let depth = match entry {
FsEntry::Directory(worktree_id, dir_entry) => {
if is_singleton {
continue;
}
let depth = self
.fs_entries_depth
.get(&(*worktree_id, dir_entry.id))
@ -2240,16 +2222,11 @@ impl OutlinePanel {
depth
}
FsEntry::ExternalFile(..) => 0,
FsEntry::File(worktree_id, file_entry, ..) => {
if is_singleton {
0
} else {
self.fs_entries_depth
.get(&(*worktree_id, file_entry.id))
.map(|&(_, depth)| depth)
.unwrap_or(0)
}
}
FsEntry::File(worktree_id, file_entry, ..) => self
.fs_entries_depth
.get(&(*worktree_id, file_entry.id))
.map(|&(_, depth)| depth)
.unwrap_or(0),
};
if let Some((folded_depth, worktree_id, folded_dirs)) = folded_dirs_entry.take() {
entries.push((
@ -2267,7 +2244,7 @@ impl OutlinePanel {
let Some(excerpt) = excerpts.get(&entry_excerpt) else {
continue;
};
let excerpt_depth = depth;
let excerpt_depth = depth + 1;
entries.push((
excerpt_depth,
EntryOwned::Excerpt(
@ -2277,30 +2254,25 @@ impl OutlinePanel {
),
));
if !self
let mut outline_base_depth = excerpt_depth + 1;
if is_singleton {
outline_base_depth = 0;
entries.clear();
} else if self
.collapsed_entries
.contains(&CollapsedEntry::Excerpt(*buffer_id, entry_excerpt))
{
let mut outline_data_depth = None::<usize>;
let mut outline_depth = excerpt_depth + 1;
for outline in excerpt.iter_outlines() {
if let Some(outline_data_depth) = outline_data_depth {
match outline_data_depth.cmp(&outline.depth) {
cmp::Ordering::Less => outline_depth += 1,
cmp::Ordering::Equal => {}
cmp::Ordering::Greater => outline_depth -= 1,
};
}
outline_data_depth = Some(outline.depth);
entries.push((
outline_depth,
EntryOwned::Outline(
*buffer_id,
entry_excerpt,
outline.clone(),
),
));
}
continue;
}
for outline in excerpt.iter_outlines() {
entries.push((
outline_base_depth + outline.depth,
EntryOwned::Outline(*buffer_id, entry_excerpt, outline.clone()),
));
}
if is_singleton && entries.is_empty() {
entries.push((0, EntryOwned::Entry(entry.clone())));
}
}
}
@ -2316,6 +2288,23 @@ impl OutlinePanel {
})
}
fn is_singleton_active(&self, cx: &AppContext) -> bool {
self.active_item
.as_ref()
.and_then(|active_item| {
Some(
active_item
.active_editor
.upgrade()?
.read(cx)
.buffer()
.read(cx)
.is_singleton(),
)
})
.unwrap_or(false)
}
fn invalidate_outlines(&mut self, ids: &[ExcerptId]) {
self.outline_fetch_tasks.clear();
let mut ids = ids.into_iter().collect::<HashSet<_>>();
@ -2668,3 +2657,11 @@ fn range_contains(
range.start.cmp(&anchor, buffer_snapshot).is_le()
&& range.end.cmp(&anchor, buffer_snapshot).is_ge()
}
fn empty_icon() -> AnyElement {
h_flex()
.size(IconSize::default().rems())
.invisible()
.flex_none()
.into_any_element()
}