Cargo.lock 🔗
@@ -7162,7 +7162,6 @@ dependencies = [
"db",
"editor",
"file_icons",
- "futures 0.3.28",
"gpui",
"itertools 0.11.0",
"language",
Kirill Bulatov created
Release Notes:
- N/A
Cargo.lock | 1
crates/editor/src/display_map.rs | 16
crates/outline_panel/Cargo.toml | 1
crates/outline_panel/src/outline_panel.rs | 570 ++++++++++++++----------
4 files changed, 346 insertions(+), 242 deletions(-)
@@ -7162,7 +7162,6 @@ dependencies = [
"db",
"editor",
"file_icons",
- "futures 0.3.28",
"gpui",
"itertools 0.11.0",
"language",
@@ -1021,6 +1021,22 @@ impl Debug for DisplayPoint {
}
}
+impl Add for DisplayPoint {
+ type Output = Self;
+
+ fn add(self, other: Self) -> Self::Output {
+ DisplayPoint(BlockPoint(self.0 .0 + other.0 .0))
+ }
+}
+
+impl Sub for DisplayPoint {
+ type Output = Self;
+
+ fn sub(self, other: Self) -> Self::Output {
+ DisplayPoint(BlockPoint(self.0 .0 - other.0 .0))
+ }
+}
+
#[derive(Debug, Copy, Clone, Default, Eq, Ord, PartialOrd, PartialEq, Deserialize, Hash)]
#[serde(transparent)]
pub struct DisplayRow(pub u32);
@@ -18,7 +18,6 @@ collections.workspace = true
db.workspace = true
editor.workspace = true
file_icons.workspace = true
-futures.workspace = true
itertools.workspace = true
gpui.workspace = true
language.workspace = true
@@ -12,12 +12,12 @@ use anyhow::Context;
use collections::{hash_map, BTreeSet, HashMap, HashSet};
use db::kvp::KEY_VALUE_STORE;
use editor::{
+ display_map::ToDisplayPoint,
items::{entry_git_aware_label_color, entry_label_color},
scroll::ScrollAnchor,
- Editor, EditorEvent, ExcerptId, ExcerptRange,
+ DisplayPoint, Editor, EditorEvent, ExcerptId, ExcerptRange,
};
use file_icons::FileIcons;
-use futures::{stream::FuturesUnordered, StreamExt};
use gpui::{
actions, anchored, deferred, div, px, uniform_list, Action, AnyElement, AppContext,
AssetSource, AsyncWindowContext, ClipboardItem, DismissEvent, Div, ElementId, EntityId,
@@ -31,10 +31,10 @@ use language::{BufferId, BufferSnapshot, OffsetRangeExt, OutlineItem};
use menu::{SelectFirst, SelectLast, SelectNext, SelectPrev};
use outline_panel_settings::{OutlinePanelDockPosition, OutlinePanelSettings};
-use project::{File, Fs, Project};
+use project::{File, Fs, Item, Project};
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore};
-use util::{ResultExt, TryFutureExt};
+use util::{RangeExt, ResultExt, TryFutureExt};
use workspace::{
dock::{DockPosition, Panel, PanelEvent},
item::ItemHandle,
@@ -77,7 +77,7 @@ pub struct OutlinePanel {
context_menu: Option<(View<ContextMenu>, Point<Pixels>, Subscription)>,
focus_handle: FocusHandle,
pending_serialization: Task<Option<()>>,
- fs_entries_depth: HashMap<(WorktreeId, ProjectEntryId), (bool, usize)>,
+ fs_entries_depth: HashMap<(WorktreeId, ProjectEntryId), usize>,
fs_entries: Vec<FsEntry>,
collapsed_entries: HashSet<CollapsedEntry>,
unfolded_dirs: HashMap<WorktreeId, BTreeSet<ProjectEntryId>>,
@@ -86,7 +86,7 @@ pub struct OutlinePanel {
active_item: Option<ActiveItem>,
_subscriptions: Vec<Subscription>,
update_task: Task<()>,
- outline_fetch_tasks: Vec<Task<()>>,
+ outline_fetch_tasks: HashMap<(BufferId, ExcerptId), Task<()>>,
excerpts: HashMap<BufferId, HashMap<ExcerptId, Excerpt>>,
cached_entries_with_depth: Option<Vec<(usize, EntryOwned)>>,
}
@@ -382,7 +382,7 @@ impl OutlinePanel {
active_item: None,
pending_serialization: Task::ready(None),
update_task: Task::ready(()),
- outline_fetch_tasks: Vec::new(),
+ outline_fetch_tasks: HashMap::default(),
excerpts: HashMap::default(),
last_visible_range: 0..0,
cached_entries_with_depth: None,
@@ -1009,12 +1009,23 @@ impl OutlinePanel {
return;
};
- self.collapsed_entries.extend(
- self.fs_entries_depth
- .iter()
- .filter(|(_, &(is_dir, depth))| is_dir && depth == 0)
- .map(|(&(worktree_id, entry_id), _)| CollapsedEntry::Dir(worktree_id, entry_id)),
- );
+ let new_entries = self
+ .entries_with_depths(cx)
+ .iter()
+ .flat_map(|(_, entry)| match entry {
+ EntryOwned::Entry(FsEntry::Directory(worktree_id, entry)) => {
+ Some(CollapsedEntry::Dir(*worktree_id, entry.id))
+ }
+ EntryOwned::FoldedDirs(worktree_id, entries) => {
+ Some(CollapsedEntry::Dir(*worktree_id, entries.last()?.id))
+ }
+ EntryOwned::Excerpt(buffer_id, excerpt_id, _) => {
+ Some(CollapsedEntry::Excerpt(*buffer_id, *excerpt_id))
+ }
+ _ => None,
+ })
+ .collect::<Vec<_>>();
+ self.collapsed_entries.extend(new_entries);
self.update_fs_entries(&editor, HashSet::default(), None, None, false, cx);
}
@@ -1138,55 +1149,80 @@ impl OutlinePanel {
if !OutlinePanelSettings::get_global(cx).auto_reveal_entries {
return;
}
- let Some((buffer_id, excerpt_id, outline)) = self.location_for_editor_selection(editor, cx)
- else {
+ let Some(entry_with_selection) = self.location_for_editor_selection(editor, cx) else {
+ self.selected_entry = None;
+ cx.notify();
return;
};
- let Some((file_entry_with_selection, entry_with_selection)) =
- self.entry_for_selection(buffer_id, excerpt_id, outline)
- else {
- return;
+ let related_buffer_entry = match entry_with_selection {
+ EntryOwned::Entry(FsEntry::File(worktree_id, _, buffer_id, _)) => {
+ let project = self.project.read(cx);
+ let entry_id = project
+ .buffer_for_id(buffer_id)
+ .and_then(|buffer| buffer.read(cx).entry_id(cx));
+ project
+ .worktree_for_id(worktree_id, cx)
+ .zip(entry_id)
+ .and_then(|(worktree, entry_id)| {
+ let entry = worktree.read(cx).entry_for_id(entry_id)?.clone();
+ Some((worktree, entry))
+ })
+ }
+ EntryOwned::Outline(buffer_id, excerpt_id, _)
+ | EntryOwned::Excerpt(buffer_id, excerpt_id, _) => {
+ self.collapsed_entries
+ .remove(&CollapsedEntry::Excerpt(buffer_id, excerpt_id));
+ let project = self.project.read(cx);
+ let entry_id = project
+ .buffer_for_id(buffer_id)
+ .and_then(|buffer| buffer.read(cx).entry_id(cx));
+ entry_id.and_then(|entry_id| {
+ let worktree = project.worktree_for_entry(entry_id, cx)?;
+ let entry = worktree.read(cx).entry_for_id(entry_id)?.clone();
+ Some((worktree, entry))
+ })
+ }
+ EntryOwned::Entry(FsEntry::ExternalFile(..)) => None,
+ _ => return,
};
- if self.selected_entry.as_ref() == Some(&entry_with_selection) {
- return;
- }
+ if let Some((worktree, buffer_entry)) = related_buffer_entry {
+ let worktree_id = worktree.read(cx).id();
+ let mut dirs_to_expand = Vec::new();
+ {
+ let mut traversal = worktree.read(cx).traverse_from_path(
+ true,
+ true,
+ true,
+ buffer_entry.path.as_ref(),
+ );
+ let mut current_entry = buffer_entry;
+ loop {
+ if current_entry.is_dir() {
+ if self
+ .collapsed_entries
+ .remove(&CollapsedEntry::Dir(worktree_id, current_entry.id))
+ {
+ dirs_to_expand.push(current_entry.id);
+ }
+ }
- if let FsEntry::File(file_worktree_id, file_entry, ..) = file_entry_with_selection {
- if let Some(worktree) = self.project.read(cx).worktree_for_id(file_worktree_id, cx) {
- let parent_entry = {
- let mut traversal = worktree.read(cx).traverse_from_path(
- true,
- true,
- true,
- file_entry.path.as_ref(),
- );
if traversal.back_to_parent() {
- traversal.entry()
- } else {
- None
- }
- .cloned()
- };
- if let Some(directory_entry) = parent_entry {
- let worktree_id = worktree.read(cx).id();
- let entry_id = directory_entry.id;
- if self
- .collapsed_entries
- .remove(&CollapsedEntry::Dir(worktree_id, entry_id))
- {
- self.project
- .update(cx, |project, cx| {
- project.expand_entry(worktree_id, entry_id, cx)
- })
- .unwrap_or_else(|| Task::ready(Ok(())))
- .detach_and_log_err(cx)
+ if let Some(parent_entry) = traversal.entry() {
+ current_entry = parent_entry.clone();
+ continue;
+ }
}
+ break;
}
}
- }
- if let EntryOwned::Outline(buffer_id, excerpt_id, _) = &entry_with_selection {
- self.collapsed_entries
- .remove(&CollapsedEntry::Excerpt(*buffer_id, *excerpt_id));
+ for dir_to_expand in dirs_to_expand {
+ self.project
+ .update(cx, |project, cx| {
+ project.expand_entry(worktree_id, dir_to_expand, cx)
+ })
+ .unwrap_or_else(|| Task::ready(Ok(())))
+ .detach_and_log_err(cx)
+ }
}
self.update_fs_entries(
@@ -1199,26 +1235,6 @@ impl OutlinePanel {
);
}
- fn entry_for_selection(
- &mut self,
- buffer_id: BufferId,
- excerpt_id: ExcerptId,
- outline: Option<OutlineItem<language::Anchor>>,
- ) -> Option<(FsEntry, EntryOwned)> {
- let fs_entry_with_selection = self.fs_entries.iter().find(|entry| match entry {
- FsEntry::File(_, _, file_buffer_id, excerpts)
- | FsEntry::ExternalFile(file_buffer_id, excerpts) => {
- file_buffer_id == &buffer_id && excerpts.contains(&excerpt_id)
- }
- _ => false,
- });
- let entry_with_selection = outline
- .map(|outline| EntryOwned::Outline(buffer_id, excerpt_id, outline))
- .or_else(|| Some(EntryOwned::Entry(fs_entry_with_selection.cloned()?)));
-
- fs_entry_with_selection.cloned().zip(entry_with_selection)
- }
-
fn render_excerpt(
&self,
buffer_id: BufferId,
@@ -1828,7 +1844,7 @@ impl OutlinePanel {
),
new_depth_map
.get(&(worktree_id, id))
- .map(|&(_, depth)| depth)
+ .copied()
.unwrap_or(0),
),
@@ -1875,15 +1891,12 @@ impl OutlinePanel {
} else {
parent_id
.and_then(|(worktree_id, id)| {
- new_depth_map
- .get(&(worktree_id, id))
- .map(|&(_, depth)| depth)
+ new_depth_map.get(&(worktree_id, id)).copied()
})
.unwrap_or(0)
+ 1
};
- new_depth_map
- .insert((*worktree_id, dir_entry.id), (true, depth));
+ new_depth_map.insert((*worktree_id, dir_entry.id), depth);
}
FsEntry::File(worktree_id, file_entry, ..) => {
let parent_id = back_to_common_visited_parent(
@@ -1896,15 +1909,12 @@ impl OutlinePanel {
} else {
parent_id
.and_then(|(worktree_id, id)| {
- new_depth_map
- .get(&(worktree_id, id))
- .map(|&(_, depth)| depth)
+ new_depth_map.get(&(worktree_id, id)).copied()
})
.unwrap_or(0)
+ 1
};
- new_depth_map
- .insert((*worktree_id, file_entry.id), (false, depth));
+ new_depth_map.insert((*worktree_id, file_entry.id), depth);
}
FsEntry::ExternalFile(..) => {
visited_dirs.clear();
@@ -1960,18 +1970,13 @@ impl OutlinePanel {
new_active_editor: View<Editor>,
cx: &mut ViewContext<Self>,
) {
+ let new_selected_entry = self.location_for_editor_selection(&new_active_editor, cx);
self.clear_previous();
self.active_item = Some(ActiveItem {
item_id: new_active_editor.item_id(),
_editor_subscrpiption: subscribe_for_editor_events(&new_active_editor, cx),
active_editor: new_active_editor.downgrade(),
});
- let new_selected_entry = self
- .location_for_editor_selection(&new_active_editor, cx)
- .and_then(|(buffer_id, excerpt_id, outline)| {
- let (_, entry) = self.entry_for_selection(buffer_id, excerpt_id, outline)?;
- Some(entry)
- });
let new_entries =
HashSet::from_iter(new_active_editor.read(cx).buffer().read(cx).excerpt_ids());
self.update_fs_entries(
@@ -2002,172 +2007,192 @@ impl OutlinePanel {
&self,
editor: &View<Editor>,
cx: &mut ViewContext<Self>,
- ) -> Option<(BufferId, ExcerptId, Option<Outline>)> {
+ ) -> Option<EntryOwned> {
let selection = editor
.read(cx)
.selections
.newest::<language::Point>(cx)
.head();
+ let editor_snapshot = editor.update(cx, |editor, cx| editor.snapshot(cx));
let multi_buffer = editor.read(cx).buffer();
let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
- let selection = multi_buffer_snapshot.anchor_before(selection);
- let excerpt_id = selection.excerpt_id;
- let buffer_snapshot = multi_buffer_snapshot.buffer_for_excerpt(selection.excerpt_id)?;
- let buffer_id = buffer_snapshot.remote_id();
+ let (excerpt_id, buffer, _) = editor
+ .read(cx)
+ .buffer()
+ .read(cx)
+ .excerpt_containing(selection, cx)?;
+ let buffer_id = buffer.read(cx).remote_id();
+ let selection_display_point = selection.to_display_point(&editor_snapshot);
- let outline_item = self
+ let excerpt_outlines = self
.excerpts
.get(&buffer_id)
.and_then(|excerpts| excerpts.get(&excerpt_id))
.into_iter()
.flat_map(|excerpt| excerpt.iter_outlines())
- .filter(|outline_item| {
- range_contains(&outline_item.range, selection.text_anchor, buffer_snapshot)
+ .flat_map(|outline| {
+ let start = multi_buffer_snapshot
+ .anchor_in_excerpt(excerpt_id, outline.range.start)?
+ .to_display_point(&editor_snapshot);
+ let end = multi_buffer_snapshot
+ .anchor_in_excerpt(excerpt_id, outline.range.end)?
+ .to_display_point(&editor_snapshot);
+ Some((start..end, outline))
})
- .min_by_key(|outline| {
- let range = outline.range.start.offset..outline.range.end.offset;
- let cursor_offset = selection.text_anchor.offset as isize;
- let distance_to_closest_endpoint = cmp::min(
- (range.start as isize - cursor_offset).abs(),
- (range.end as isize - cursor_offset).abs(),
- );
- distance_to_closest_endpoint
+ .collect::<Vec<_>>();
+
+ let mut matching_outline_indices = Vec::new();
+ let mut children = HashMap::default();
+ let mut parents_stack = Vec::<(&Range<DisplayPoint>, &&Outline, usize)>::new();
+
+ for (i, (outline_range, outline)) in excerpt_outlines.iter().enumerate() {
+ if outline_range
+ .to_inclusive()
+ .contains(&selection_display_point)
+ {
+ matching_outline_indices.push(i);
+ } else if (outline_range.start.row()..outline_range.end.row())
+ .to_inclusive()
+ .contains(&selection_display_point.row())
+ {
+ matching_outline_indices.push(i);
+ }
+
+ while let Some((parent_range, parent_outline, _)) = parents_stack.last() {
+ if parent_outline.depth >= outline.depth
+ || !parent_range.contains(&outline_range.start)
+ {
+ parents_stack.pop();
+ } else {
+ break;
+ }
+ }
+ if let Some((_, _, parent_index)) = parents_stack.last_mut() {
+ children
+ .entry(*parent_index)
+ .or_insert_with(Vec::new)
+ .push(i);
+ }
+ parents_stack.push((outline_range, outline, i));
+ }
+
+ let outline_item = matching_outline_indices
+ .into_iter()
+ .flat_map(|i| Some((i, excerpt_outlines.get(i)?)))
+ .filter(|(i, _)| {
+ children
+ .get(i)
+ .map(|children| {
+ children.iter().all(|child_index| {
+ excerpt_outlines
+ .get(*child_index)
+ .map(|(child_range, _)| child_range.start > selection_display_point)
+ .unwrap_or(false)
+ })
+ })
+ .unwrap_or(true)
})
+ .min_by_key(|(_, (outline_range, outline))| {
+ let distance_from_start = if outline_range.start > selection_display_point {
+ outline_range.start - selection_display_point
+ } else {
+ selection_display_point - outline_range.start
+ };
+ let distance_from_end = if outline_range.end > selection_display_point {
+ outline_range.end - selection_display_point
+ } else {
+ selection_display_point - outline_range.end
+ };
+
+ (
+ cmp::Reverse(outline.depth),
+ distance_from_start + distance_from_end,
+ )
+ })
+ .map(|(_, (_, outline))| *outline)
.cloned();
- Some((buffer_id, excerpt_id, outline_item))
+ let closest_container = match outline_item {
+ Some(outline) => EntryOwned::Outline(buffer_id, excerpt_id, outline),
+ None => self
+ .cached_entries_with_depth
+ .iter()
+ .flatten()
+ .rev()
+ .find_map(|(_, entry)| match entry {
+ EntryOwned::Excerpt(entry_buffer_id, entry_excerpt_id, _) => {
+ if entry_buffer_id == &buffer_id && entry_excerpt_id == &excerpt_id {
+ Some(entry.clone())
+ } else {
+ None
+ }
+ }
+ EntryOwned::Entry(
+ FsEntry::ExternalFile(file_buffer_id, file_excerpts)
+ | FsEntry::File(_, _, file_buffer_id, file_excerpts),
+ ) => {
+ if file_buffer_id == &buffer_id && file_excerpts.contains(&excerpt_id) {
+ Some(entry.clone())
+ } else {
+ None
+ }
+ }
+ _ => None,
+ })?,
+ };
+ Some(closest_container)
}
fn fetch_outlines(&mut self, range: &Range<usize>, cx: &mut ViewContext<Self>) {
- let project = self.project.clone();
let range_len = range.len();
let half_range = range_len / 2;
let entries = self.entries_with_depths(cx);
let expanded_range =
range.start.saturating_sub(half_range)..(range.end + half_range).min(entries.len());
- let entries = entries
- .get(expanded_range)
- .map(|slice| slice.to_vec())
- .unwrap_or_default();
- let excerpt_fetch_ranges = entries.into_iter().fold(
- HashMap::<
- BufferId,
- (
- BufferSnapshot,
- HashMap<ExcerptId, ExcerptRange<language::Anchor>>,
- ),
- >::default(),
- |mut excerpts_to_fetch, (_, entry)| {
- match entry {
- EntryOwned::Entry(FsEntry::File(_, _, buffer_id, file_excerpts))
- | EntryOwned::Entry(FsEntry::ExternalFile(buffer_id, file_excerpts)) => {
- let excerpts = self.excerpts.get(&buffer_id);
- for file_excerpt in file_excerpts {
- if let Some(excerpt) = excerpts
- .and_then(|excerpts| excerpts.get(&file_excerpt))
- .filter(|excerpt| excerpt.should_fetch_outlines())
- {
- match excerpts_to_fetch.entry(buffer_id) {
- hash_map::Entry::Occupied(mut o) => {
- o.get_mut().1.insert(file_excerpt, excerpt.range.clone());
- }
- hash_map::Entry::Vacant(v) => {
- if let Some(buffer_snapshot) = project
- .read(cx)
- .buffer_for_id(buffer_id)
- .map(|buffer| buffer.read(cx).snapshot())
- {
- v.insert((buffer_snapshot, HashMap::default()))
- .1
- .insert(file_excerpt, excerpt.range.clone());
- }
- }
- }
- }
- }
- }
- EntryOwned::Excerpt(buffer_id, excerpt_id, _) => {
- if let Some(excerpt) = self
- .excerpts
- .get(&buffer_id)
- .and_then(|excerpts| excerpts.get(&excerpt_id))
- .filter(|excerpt| excerpt.should_fetch_outlines())
- {
- match excerpts_to_fetch.entry(buffer_id) {
- hash_map::Entry::Occupied(mut o) => {
- o.get_mut().1.insert(excerpt_id, excerpt.range.clone());
- }
- hash_map::Entry::Vacant(v) => {
- if let Some(buffer_snapshot) = project
- .read(cx)
- .buffer_for_id(buffer_id)
- .map(|buffer| buffer.read(cx).snapshot())
- {
- v.insert((buffer_snapshot, HashMap::default()))
- .1
- .insert(excerpt_id, excerpt.range.clone());
- }
- }
- }
- }
- }
- _ => {}
- }
- excerpts_to_fetch
- },
- );
-
+ let excerpt_fetch_ranges = self.excerpt_fetch_ranges(expanded_range, cx);
if excerpt_fetch_ranges.is_empty() {
return;
}
let syntax_theme = cx.theme().syntax().clone();
- self.outline_fetch_tasks
- .push(cx.spawn(|outline_panel, mut cx| async move {
- let mut fetch_tasks = excerpt_fetch_ranges
- .into_iter()
- .map(|(buffer_id, (buffer_snapshot, excerpt_ranges))| {
- let syntax_theme = syntax_theme.clone();
- cx.background_executor().spawn(async move {
- let new_outlines = excerpt_ranges
- .into_iter()
- .map(|(excerpt_id, excerpt_range)| {
- let outlines = buffer_snapshot
- .outline_items_containing(
- excerpt_range.context,
- false,
- Some(&syntax_theme),
- )
- .unwrap_or_default();
- (excerpt_id, outlines)
- })
- .collect::<HashMap<_, _>>();
- (buffer_id, new_outlines)
- })
- })
- .collect::<FuturesUnordered<_>>();
-
- while let Some((buffer_id, fetched_outlines)) = fetch_tasks.next().await {
- outline_panel
- .update(&mut cx, |outline_panel, cx| {
- for (excerpt_id, fetched_outlines) in fetched_outlines {
+ for (buffer_id, (buffer_snapshot, excerpt_ranges)) in excerpt_fetch_ranges {
+ for (excerpt_id, excerpt_range) in excerpt_ranges {
+ let syntax_theme = syntax_theme.clone();
+ let buffer_snapshot = buffer_snapshot.clone();
+ self.outline_fetch_tasks.insert(
+ (buffer_id, excerpt_id),
+ cx.spawn(|outline_panel, mut cx| async move {
+ let fetched_outlines = cx
+ .background_executor()
+ .spawn(async move {
+ buffer_snapshot
+ .outline_items_containing(
+ excerpt_range.context,
+ false,
+ Some(&syntax_theme),
+ )
+ .unwrap_or_default()
+ })
+ .await;
+ outline_panel
+ .update(&mut cx, |outline_panel, cx| {
if let Some(excerpt) = outline_panel
.excerpts
.entry(buffer_id)
.or_default()
.get_mut(&excerpt_id)
- .filter(|excerpt| excerpt.should_fetch_outlines())
{
excerpt.outlines = ExcerptOutlines::Outlines(fetched_outlines);
}
- }
- outline_panel.cached_entries_with_depth = None;
- cx.notify();
- })
- .ok();
- }
- }));
+ outline_panel.cached_entries_with_depth = None;
+ cx.notify();
+ })
+ .ok();
+ }),
+ );
+ }
+ }
}
fn entries_with_depths(&mut self, cx: &AppContext) -> &[(usize, EntryOwned)] {
@@ -2183,7 +2208,7 @@ impl OutlinePanel {
let depth = self
.fs_entries_depth
.get(&(*worktree_id, dir_entry.id))
- .map(|&(_, depth)| depth)
+ .copied()
.unwrap_or(0);
if auto_fold_dirs {
let folded = self
@@ -2225,7 +2250,7 @@ impl OutlinePanel {
FsEntry::File(worktree_id, file_entry, ..) => self
.fs_entries_depth
.get(&(*worktree_id, file_entry.id))
- .map(|&(_, depth)| depth)
+ .copied()
.unwrap_or(0),
};
if let Some((folded_depth, worktree_id, folded_dirs)) = folded_dirs_entry.take() {
@@ -2322,6 +2347,87 @@ impl OutlinePanel {
}
}
}
+
+ fn excerpt_fetch_ranges(
+ &self,
+ entry_range: Range<usize>,
+ cx: &AppContext,
+ ) -> HashMap<
+ BufferId,
+ (
+ BufferSnapshot,
+ HashMap<ExcerptId, ExcerptRange<language::Anchor>>,
+ ),
+ > {
+ match self.cached_entries_with_depth.as_ref() {
+ Some(entries) => entries.get(entry_range).into_iter().flatten().fold(
+ HashMap::default(),
+ |mut excerpts_to_fetch, (_, entry)| {
+ match entry {
+ EntryOwned::Entry(FsEntry::File(_, _, buffer_id, file_excerpts))
+ | EntryOwned::Entry(FsEntry::ExternalFile(buffer_id, file_excerpts)) => {
+ let excerpts = self.excerpts.get(&buffer_id);
+ for &file_excerpt in file_excerpts {
+ if let Some(excerpt) = excerpts
+ .and_then(|excerpts| excerpts.get(&file_excerpt))
+ .filter(|excerpt| excerpt.should_fetch_outlines())
+ {
+ match excerpts_to_fetch.entry(*buffer_id) {
+ hash_map::Entry::Occupied(mut o) => {
+ o.get_mut()
+ .1
+ .insert(file_excerpt, excerpt.range.clone());
+ }
+ hash_map::Entry::Vacant(v) => {
+ if let Some(buffer_snapshot) = self
+ .project
+ .read(cx)
+ .buffer_for_id(*buffer_id)
+ .map(|buffer| buffer.read(cx).snapshot())
+ {
+ v.insert((buffer_snapshot, HashMap::default()))
+ .1
+ .insert(file_excerpt, excerpt.range.clone());
+ }
+ }
+ }
+ }
+ }
+ }
+ EntryOwned::Excerpt(buffer_id, excerpt_id, _) => {
+ if let Some(excerpt) = self
+ .excerpts
+ .get(&buffer_id)
+ .and_then(|excerpts| excerpts.get(&excerpt_id))
+ .filter(|excerpt| excerpt.should_fetch_outlines())
+ {
+ match excerpts_to_fetch.entry(*buffer_id) {
+ hash_map::Entry::Occupied(mut o) => {
+ o.get_mut().1.insert(*excerpt_id, excerpt.range.clone());
+ }
+ hash_map::Entry::Vacant(v) => {
+ if let Some(buffer_snapshot) = self
+ .project
+ .read(cx)
+ .buffer_for_id(*buffer_id)
+ .map(|buffer| buffer.read(cx).snapshot())
+ {
+ v.insert((buffer_snapshot, HashMap::default()))
+ .1
+ .insert(*excerpt_id, excerpt.range.clone());
+ }
+ }
+ }
+ }
+ }
+ _ => {}
+ }
+ excerpts_to_fetch
+ },
+ ),
+ None => HashMap::default(),
+ }
+ }
}
fn back_to_common_visited_parent(
@@ -2429,13 +2535,7 @@ impl Panel for OutlinePanel {
if self.active_item.as_ref().map(|item| item.item_id)
== Some(active_editor.item_id())
{
- let new_selected_entry = self
- .location_for_editor_selection(&active_editor, cx)
- .and_then(|(buffer_id, excerpt_id, outline)| {
- let (_, entry) =
- self.entry_for_selection(buffer_id, excerpt_id, outline)?;
- Some(entry)
- });
+ let new_selected_entry = self.location_for_editor_selection(&active_editor, cx);
self.update_fs_entries(
&active_editor,
HashSet::default(),
@@ -2520,12 +2620,11 @@ impl Render for OutlinePanel {
move |outline_panel, range, cx| {
outline_panel.last_visible_range = range.clone();
outline_panel.fetch_outlines(&range, cx);
- outline_panel
- .entries_with_depths(cx)
- .get(range)
+ let entries = outline_panel.entries_with_depths(cx).get(range);
+ entries
.map(|entries| entries.to_vec())
+ .unwrap_or_default()
.into_iter()
- .flatten()
.filter_map(|(depth, entry)| match entry {
EntryOwned::Entry(entry) => {
Some(outline_panel.render_entry(&entry, depth, cx))
@@ -2649,15 +2748,6 @@ fn subscribe_for_editor_events(
)
}
-fn range_contains(
- range: &Range<language::Anchor>,
- anchor: language::Anchor,
- buffer_snapshot: &language::BufferSnapshot,
-) -> bool {
- 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())