Properly align excerpt and outline items (#13070)
This commit is contained in:
parent
9bc3c6810b
commit
ff8486e67f
1 changed files with 79 additions and 82 deletions
|
@ -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()
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue