diff --git a/Cargo.lock b/Cargo.lock index 2dba6b34b2..c2142511ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7162,7 +7162,6 @@ dependencies = [ "db", "editor", "file_icons", - "futures 0.3.28", "gpui", "itertools 0.11.0", "language", diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 4b7de01b9c..1f7cd118dd 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -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); diff --git a/crates/outline_panel/Cargo.toml b/crates/outline_panel/Cargo.toml index 9dfdba92c5..6b5f7bb9f3 100644 --- a/crates/outline_panel/Cargo.toml +++ b/crates/outline_panel/Cargo.toml @@ -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 diff --git a/crates/outline_panel/src/outline_panel.rs b/crates/outline_panel/src/outline_panel.rs index 4122162ae9..24ef453405 100644 --- a/crates/outline_panel/src/outline_panel.rs +++ b/crates/outline_panel/src/outline_panel.rs @@ -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, Point, Subscription)>, focus_handle: FocusHandle, pending_serialization: Task>, - fs_entries_depth: HashMap<(WorktreeId, ProjectEntryId), (bool, usize)>, + fs_entries_depth: HashMap<(WorktreeId, ProjectEntryId), usize>, fs_entries: Vec, collapsed_entries: HashSet, unfolded_dirs: HashMap>, @@ -86,7 +86,7 @@ pub struct OutlinePanel { active_item: Option, _subscriptions: Vec, update_task: Task<()>, - outline_fetch_tasks: Vec>, + outline_fetch_tasks: HashMap<(BufferId, ExcerptId), Task<()>>, excerpts: HashMap>, cached_entries_with_depth: Option>, } @@ -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::>(); + 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>, - ) -> 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, cx: &mut ViewContext, ) { + 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, cx: &mut ViewContext, - ) -> Option<(BufferId, ExcerptId, Option)> { + ) -> Option { let selection = editor .read(cx) .selections .newest::(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::>(); + + let mut matching_outline_indices = Vec::new(); + let mut children = HashMap::default(); + let mut parents_stack = Vec::<(&Range, &&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, cx: &mut ViewContext) { - 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>, - ), - >::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::>(); - (buffer_id, new_outlines) - }) - }) - .collect::>(); - - 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, + cx: &AppContext, + ) -> HashMap< + BufferId, + ( + BufferSnapshot, + HashMap>, + ), + > { + 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, - 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())