@@ -68,7 +68,8 @@ pub struct MultiBuffer {
/// Contains the state of the buffers being edited
buffers: RefCell<HashMap<BufferId, BufferState>>,
// only used by consumers using `set_excerpts_for_buffer`
- buffers_by_path: BTreeMap<PathKey, Vec<ExcerptId>>,
+ excerpts_by_path: BTreeMap<PathKey, Vec<ExcerptId>>,
+ paths_by_excerpt: HashMap<ExcerptId, PathKey>,
diffs: HashMap<BufferId, DiffState>,
// all_diff_hunks_expanded: bool,
subscriptions: Topic,
@@ -577,7 +578,8 @@ impl MultiBuffer {
singleton: false,
capability,
title: None,
- buffers_by_path: Default::default(),
+ excerpts_by_path: Default::default(),
+ paths_by_excerpt: Default::default(),
buffer_changed_since_sync: Default::default(),
history: History {
next_transaction_id: clock::Lamport::default(),
@@ -593,7 +595,8 @@ impl MultiBuffer {
Self {
snapshot: Default::default(),
buffers: Default::default(),
- buffers_by_path: Default::default(),
+ excerpts_by_path: Default::default(),
+ paths_by_excerpt: Default::default(),
diffs: HashMap::default(),
subscriptions: Default::default(),
singleton: false,
@@ -638,7 +641,8 @@ impl MultiBuffer {
Self {
snapshot: RefCell::new(self.snapshot.borrow().clone()),
buffers: RefCell::new(buffers),
- buffers_by_path: Default::default(),
+ excerpts_by_path: Default::default(),
+ paths_by_excerpt: Default::default(),
diffs: diff_bases,
subscriptions: Default::default(),
singleton: self.singleton,
@@ -1478,7 +1482,7 @@ impl MultiBuffer {
}
pub fn location_for_path(&self, path: &PathKey, cx: &App) -> Option<Anchor> {
- let excerpt_id = self.buffers_by_path.get(path)?.first()?;
+ let excerpt_id = self.excerpts_by_path.get(path)?.first()?;
let snapshot = self.snapshot(cx);
let excerpt = snapshot.excerpt(*excerpt_id)?;
Some(Anchor::in_buffer(
@@ -1489,7 +1493,93 @@ impl MultiBuffer {
}
pub fn excerpt_paths(&self) -> impl Iterator<Item = &PathKey> {
- self.buffers_by_path.keys()
+ self.excerpts_by_path.keys()
+ }
+
+ fn expand_excerpts_with_paths(
+ &mut self,
+ ids: impl IntoIterator<Item = ExcerptId>,
+ line_count: u32,
+ direction: ExpandExcerptDirection,
+ cx: &mut Context<Self>,
+ ) {
+ let grouped = ids
+ .into_iter()
+ .chunk_by(|id| self.paths_by_excerpt.get(id).cloned())
+ .into_iter()
+ .flat_map(|(k, v)| Some((k?, v.into_iter().collect::<Vec<_>>())))
+ .collect::<Vec<_>>();
+ let snapshot = self.snapshot(cx);
+
+ for (path, ids) in grouped.into_iter() {
+ let Some(excerpt_ids) = self.excerpts_by_path.get(&path) else {
+ continue;
+ };
+
+ let ids_to_expand = HashSet::from_iter(ids);
+ let expanded_ranges = excerpt_ids.iter().filter_map(|excerpt_id| {
+ let excerpt = snapshot.excerpt(*excerpt_id)?;
+
+ let mut context = excerpt.range.context.to_point(&excerpt.buffer);
+ if ids_to_expand.contains(excerpt_id) {
+ match direction {
+ ExpandExcerptDirection::Up => {
+ context.start.row = context.start.row.saturating_sub(line_count);
+ context.start.column = 0;
+ }
+ ExpandExcerptDirection::Down => {
+ context.end.row =
+ (context.end.row + line_count).min(excerpt.buffer.max_point().row);
+ context.end.column = excerpt.buffer.line_len(context.end.row);
+ }
+ ExpandExcerptDirection::UpAndDown => {
+ context.start.row = context.start.row.saturating_sub(line_count);
+ context.start.column = 0;
+ context.end.row =
+ (context.end.row + line_count).min(excerpt.buffer.max_point().row);
+ context.end.column = excerpt.buffer.line_len(context.end.row);
+ }
+ }
+ }
+
+ Some(ExcerptRange {
+ context,
+ primary: excerpt
+ .range
+ .primary
+ .as_ref()
+ .map(|range| range.to_point(&excerpt.buffer)),
+ })
+ });
+ let mut merged_ranges: Vec<ExcerptRange<Point>> = Vec::new();
+ for range in expanded_ranges {
+ if let Some(last_range) = merged_ranges.last_mut() {
+ if last_range.context.end >= range.context.start {
+ last_range.context.end = range.context.end;
+ continue;
+ }
+ }
+ merged_ranges.push(range)
+ }
+ let Some(excerpt_id) = excerpt_ids.first() else {
+ continue;
+ };
+ let Some(buffer_id) = &snapshot.buffer_id_for_excerpt(*excerpt_id) else {
+ continue;
+ };
+
+ let Some(buffer) = self
+ .buffers
+ .borrow()
+ .get(buffer_id)
+ .map(|b| b.buffer.clone())
+ else {
+ continue;
+ };
+
+ let buffer_snapshot = buffer.read(cx).snapshot();
+ self.update_path_excerpts(path.clone(), buffer, &buffer_snapshot, merged_ranges, cx);
+ }
}
/// Sets excerpts, returns `true` if at least one new excerpt was added.
@@ -1503,15 +1593,30 @@ impl MultiBuffer {
) -> bool {
let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
+ let (new, _) = build_excerpt_ranges(&buffer_snapshot, &ranges, context_line_count);
+ self.update_path_excerpts(path, buffer, &buffer_snapshot, new, cx)
+ }
+
+ fn update_path_excerpts(
+ &mut self,
+ path: PathKey,
+ buffer: Entity<Buffer>,
+ buffer_snapshot: &BufferSnapshot,
+ new: Vec<ExcerptRange<Point>>,
+ cx: &mut Context<Self>,
+ ) -> bool {
let mut insert_after = self
- .buffers_by_path
+ .excerpts_by_path
.range(..path.clone())
.next_back()
.map(|(_, value)| *value.last().unwrap())
.unwrap_or(ExcerptId::min());
- let existing = self.buffers_by_path.get(&path).cloned().unwrap_or_default();
- let (new, _) = build_excerpt_ranges(&buffer_snapshot, &ranges, context_line_count);
+ let existing = self
+ .excerpts_by_path
+ .get(&path)
+ .cloned()
+ .unwrap_or_default();
let mut new_iter = new.into_iter().peekable();
let mut existing_iter = existing.into_iter().peekable();
@@ -1594,20 +1699,23 @@ impl MultiBuffer {
));
self.remove_excerpts(to_remove, cx);
if new_excerpt_ids.is_empty() {
- self.buffers_by_path.remove(&path);
+ self.excerpts_by_path.remove(&path);
} else {
- self.buffers_by_path.insert(path, new_excerpt_ids);
+ for excerpt_id in &new_excerpt_ids {
+ self.paths_by_excerpt.insert(*excerpt_id, path.clone());
+ }
+ self.excerpts_by_path.insert(path, new_excerpt_ids);
}
added_a_new_excerpt
}
pub fn paths(&self) -> impl Iterator<Item = PathKey> + '_ {
- self.buffers_by_path.keys().cloned()
+ self.excerpts_by_path.keys().cloned()
}
pub fn remove_excerpts_for_path(&mut self, path: PathKey, cx: &mut Context<Self>) {
- if let Some(to_remove) = self.buffers_by_path.remove(&path) {
+ if let Some(to_remove) = self.excerpts_by_path.remove(&path) {
self.remove_excerpts(to_remove, cx)
}
}
@@ -2079,6 +2187,7 @@ impl MultiBuffer {
let mut removed_buffer_ids = Vec::new();
while let Some(excerpt_id) = excerpt_ids.next() {
+ self.paths_by_excerpt.remove(&excerpt_id);
// Seek to the next excerpt to remove, preserving any preceding excerpts.
let locator = snapshot.excerpt_locator_for_id(excerpt_id);
new_excerpts.append(cursor.slice(&Some(locator), Bias::Left, &()), &());
@@ -2643,6 +2752,10 @@ impl MultiBuffer {
return;
}
self.sync(cx);
+ if !self.excerpts_by_path.is_empty() {
+ self.expand_excerpts_with_paths(ids, line_count, direction, cx);
+ return;
+ }
let mut snapshot = self.snapshot.borrow_mut();
let ids = ids.into_iter().collect::<Vec<_>>();