diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index e057d6e7e22127794aa343c6b27e0bc9efd2ec81..889c8e9d2e221f47888203249e13b305f448dc22 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -54,7 +54,6 @@ use std::{ use sum_tree::{Bias, Cursor, Dimension, Dimensions, SumTree, TreeMap}; use text::{ BufferId, Edit, LineIndent, TextSummary, - locator::Locator, subscription::{Subscription, Topic}, }; use theme::SyntaxTheme; @@ -95,6 +94,7 @@ pub struct MultiBuffer { #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PathKeyIndex(u64); +#[derive(Clone, Debug, PartialEq, Eq)] pub struct ExcerptInfo { path_key_index: PathKeyIndex, buffer_id: BufferId, @@ -1710,9 +1710,8 @@ impl MultiBuffer { #[instrument(skip_all)] fn merge_excerpt_ranges<'a>( expanded_ranges: impl IntoIterator> + 'a, - ) -> (Vec>, Vec) { + ) -> Vec> { let mut merged_ranges: Vec> = Vec::new(); - let mut counts: Vec = Vec::new(); for range in expanded_ranges { if let Some(last_range) = merged_ranges.last_mut() { assert!( @@ -1723,14 +1722,12 @@ impl MultiBuffer { || last_range.context.end.row + 1 == range.context.start.row { last_range.context.end = range.context.end.max(last_range.context.end); - *counts.last_mut().unwrap() += 1; continue; } } merged_ranges.push(range.clone()); - counts.push(1); } - (merged_ranges, counts) + merged_ranges } pub fn clear(&mut self, cx: &mut Context) { @@ -1839,9 +1836,9 @@ impl MultiBuffer { pub fn buffer_for_anchor(&self, anchor: Anchor, cx: &App) -> Option> { match anchor { - Anchor::Min => Some(self.snapshot(cx).excerpts.first()?.buffer(self, cx)), + Anchor::Min => Some(self.snapshot(cx).excerpts.first()?.buffer(self)), Anchor::Excerpt(excerpt_anchor) => self.buffer(excerpt_anchor.buffer_id), - Anchor::Max => Some(self.snapshot(cx).excerpts.first()?.buffer(self, cx)), + Anchor::Max => Some(self.snapshot(cx).excerpts.first()?.buffer(self)), } } @@ -2149,7 +2146,7 @@ impl MultiBuffer { let buffer = snapshot .excerpts .first() - .map(|excerpt| excerpt.buffer(self, cx)); + .map(|excerpt| excerpt.buffer(self)); buffer .map(|buffer| { let buffer = buffer.read(cx); @@ -3255,38 +3252,14 @@ impl MultiBuffer { use std::env; use util::RandomCharIter; - let max_excerpts = env::var("MAX_EXCERPTS") + let max_buffers = env::var("MAX_BUFFERS") .map(|i| i.parse().expect("invalid `MAX_EXCERPTS` variable")) .unwrap_or(5); let mut buffers = Vec::new(); for _ in 0..mutation_count { - if rng.random_bool(0.05) { - log::info!("Clearing multi-buffer"); - self.clear(cx); - continue; - } else if rng.random_bool(0.1) && !self.excerpt_ids().is_empty() { - let ids = self.excerpt_ids(); - let mut excerpts = HashSet::default(); - for _ in 0..rng.random_range(0..ids.len()) { - excerpts.extend(ids.choose(rng).copied()); - } - - let line_count = rng.random_range(0..5); - - log::info!("Expanding excerpts {excerpts:?} by {line_count} lines"); - - self.expand_excerpts( - excerpts.iter().cloned(), - line_count, - ExpandExcerptDirection::UpAndDown, - cx, - ); - continue; - } - - let excerpt_ids = self.excerpt_ids(); - if excerpt_ids.is_empty() || (rng.random() && excerpt_ids.len() < max_excerpts) { + let buffer_ids = self.all_buffer_ids(); + if buffer_ids.is_empty() || (rng.random() && buffer_ids.len() < max_buffers) { let buffer_handle = if rng.random() || self.buffers.is_empty() { let text = RandomCharIter::new(&mut *rng).take(10).collect::(); buffers.push(cx.new(|cx| Buffer::local(text, cx))); @@ -3303,12 +3276,21 @@ impl MultiBuffer { let buffer = buffer_handle.read(cx); let buffer_text = buffer.text(); + let buffer_snapshot = buffer.snapshot(); + let mut next_min_start_ix = 0; let ranges = (0..rng.random_range(0..5)) - .map(|_| { - let end_ix = - buffer.clip_offset(rng.random_range(0..=buffer.len()), Bias::Right); - let start_ix = buffer.clip_offset(rng.random_range(0..=end_ix), Bias::Left); - ExcerptRange::new(start_ix..end_ix) + .filter_map(|_| { + if next_min_start_ix == buffer.len() { + return None; + } + let end_ix = buffer.clip_offset( + rng.random_range(next_min_start_ix..=buffer.len()), + Bias::Right, + ); + let start_ix = buffer + .clip_offset(rng.random_range(next_min_start_ix..=end_ix), Bias::Left); + next_min_start_ix = end_ix; + Some(ExcerptRange::new(start_ix..end_ix)) }) .collect::>(); log::info!( @@ -3321,20 +3303,25 @@ impl MultiBuffer { .collect::>() ); - let excerpt_id = - self.insert_excerpts_after(ExcerptId::max(), buffer_handle, ranges, cx); - log::info!("Inserted with ids: {:?}", excerpt_id); + let path_key = PathKey::sorted(rng.random_range(0..max_buffers * 3) as u64); + self.set_merged_excerpt_ranges_for_path( + path_key.clone(), + buffer_handle, + &buffer_snapshot, + ranges, + cx, + ); + log::info!("Inserted with path_key: {:?}", path_key); } else { - let remove_count = rng.random_range(1..=excerpt_ids.len()); - let mut excerpts_to_remove = excerpt_ids - .choose_multiple(rng, remove_count) + let path_key = self + .snapshot + .borrow() + .path_keys_by_buffer + .get(&buffer_ids.choose(rng).unwrap()) .cloned() - .collect::>(); - let snapshot = self.snapshot.borrow(); - excerpts_to_remove.sort_unstable_by(|a, b| a.cmp(b, &snapshot)); - drop(snapshot); - log::info!("Removing excerpts {:?}", excerpts_to_remove); - self.remove_excerpts(excerpts_to_remove, cx); + .unwrap(); + log::info!("Removing excerpts {:?}", path_key); + self.remove_excerpts_for_path(path_key, cx); } } } @@ -3716,6 +3703,7 @@ impl MultiBufferSnapshot { let mut result: Vec<(&BufferSnapshot, Range, Range)> = Vec::new(); + let mut last_excerpt = None; while let Some(region) = cursor.region() { let dominated_by_end_bound = match end_bound { Bound::Included(end) => region.range.start > end, @@ -3741,12 +3729,18 @@ impl MultiBufferSnapshot { .end .min(region.buffer_range.start + end_overshoot); let context = region.excerpt.range.context.clone(); - if let Some(prev) = result.last_mut().filter(|(_, prev_range, excerpt_id, _)| { - *excerpt_id == region.excerpt.id && prev_range.end == start - }) { + if last_excerpt + .as_ref() + .is_some_and(|prev_excerpt| prev_excerpt == ®ion.excerpt.info()) + && let Some(prev) = result + .last_mut() + .filter(|(_, prev_range, _)| prev_range.end == start) + { prev.1.end = end; } else { - result.push((region.buffer, start..end, region.excerpt.id, context)); + result.push((region.buffer, start..end, context)); + // todo!() is there a better way to do this? + last_excerpt = Some(region.excerpt.info()) } } cursor.next(); @@ -3754,24 +3748,15 @@ impl MultiBufferSnapshot { if let Some(excerpt) = cursor.excerpt() { let dominated_by_prev_excerpt = - result.last().is_some_and(|(_, _, id, _)| *id == excerpt.id); + last_excerpt.is_some_and(|last_excerpt| last_excerpt == excerpt.info()); if !dominated_by_prev_excerpt && excerpt.text_summary.len == 0 { let excerpt_position = self.len(); + let buffer_snapshot = excerpt.buffer_snapshot(self); if bounds.contains(&excerpt_position) { - let buffer_offset = BufferOffset( - excerpt - .range - .context - .start - .to_offset(&excerpt.buffer_snapshot), - ); + let buffer_offset = + BufferOffset(excerpt.range.context.start.to_offset(buffer_snapshot)); let context = excerpt.range.context.clone(); - result.push(( - &excerpt.buffer_snapshot, - buffer_offset..buffer_offset, - excerpt.id, - context, - )); + result.push((buffer_snapshot, buffer_offset..buffer_offset, context)); } } } @@ -3805,18 +3790,12 @@ impl MultiBufferSnapshot { .end .min(region.buffer_range.start + end_overshoot); - let region_excerpt_id = region.excerpt.id; let deleted_hunk_anchor = if region.is_main_buffer { None } else { Some(self.anchor_before(region.range.start)) }; - let result = ( - region.buffer, - start..end, - region_excerpt_id, - deleted_hunk_anchor, - ); + let result = (region.buffer, start..end, deleted_hunk_anchor); cursor.next(); Some(result) }) @@ -5368,32 +5347,6 @@ impl MultiBufferSnapshot { ) } - fn anchor_in_excerpt_(excerpt: &Excerpt, text_anchor: text::Anchor) -> Option { - match text_anchor.buffer_id { - Some(buffer_id) if buffer_id == excerpt.buffer_snapshot.remote_id() => (), - Some(_) => return None, - None if text_anchor.is_max() || text_anchor.is_min() => { - return Some(Anchor::in_buffer(excerpt.path_key_index, text_anchor)); - } - None => return None, - } - - let context = &excerpt.range.context; - if context - .start - .cmp(&text_anchor, &excerpt.buffer_snapshot) - .is_gt() - || context - .end - .cmp(&text_anchor, &excerpt.buffer_snapshot) - .is_lt() - { - return None; - } - - Some(Anchor::in_buffer(excerpt.path_key_index, text_anchor)) - } - pub fn can_resolve(&self, anchor: &Anchor) -> bool { match anchor { // todo(lw): should be `!self.is_empty()` @@ -6342,38 +6295,40 @@ impl MultiBufferSnapshot { .expect("invalid anchor: path was never added to multibuffer") } - pub fn range_for_excerpt(&self, excerpt_id: ExcerptId) -> Option> { - let mut cursor = self - .excerpts - .cursor::, ExcerptPoint>>(()); - let locator = self.excerpt_locator_for_id(excerpt_id); - let mut sought_exact = cursor.seek(&Some(locator), Bias::Left); - if cursor.item().is_none() && excerpt_id == ExcerptId::max() { - sought_exact = true; - cursor.prev(); - } else if excerpt_id == ExcerptId::min() { - sought_exact = true; - } - if sought_exact { - let start = cursor.start().1; - let end = cursor.end().1; - let mut diff_transforms = self - .diff_transforms - .cursor::>>(()); - diff_transforms.seek(&start, Bias::Left); - let overshoot = start - diff_transforms.start().0; - let start = diff_transforms.start().1 + overshoot; - diff_transforms.seek(&end, Bias::Right); - let overshoot = end - diff_transforms.start().0; - let end = diff_transforms.start().1 + overshoot; - Some(start.0..end.0) - } else { - None - } + // todo!() this may not make sense any more + pub fn range_for_excerpt(&self, excerpt_id: BufferId) -> Option> { + todo!() + // let mut cursor = self + // .excerpts + // .cursor::, ExcerptPoint>>(()); + // let locator = self.excerpt_locator_for_id(excerpt_id); + // let mut sought_exact = cursor.seek(&Some(locator), Bias::Left); + // if cursor.item().is_none() && excerpt_id == ExcerptId::max() { + // sought_exact = true; + // cursor.prev(); + // } else if excerpt_id == ExcerptId::min() { + // sought_exact = true; + // } + // if sought_exact { + // let start = cursor.start().1; + // let end = cursor.end().1; + // let mut diff_transforms = self + // .diff_transforms + // .cursor::>>(()); + // diff_transforms.seek(&start, Bias::Left); + // let overshoot = start - diff_transforms.start().0; + // let start = diff_transforms.start().1 + overshoot; + // diff_transforms.seek(&end, Bias::Right); + // let overshoot = end - diff_transforms.start().0; + // let end = diff_transforms.start().1 + overshoot; + // Some(start.0..end.0) + // } else { + // None + // } } // todo!() sort out excerpt_containing etc. - fn excerpt_at(&self, position: impl ToOffset) -> Option<&Excerpt> { + fn excerpt_at(&self, _position: impl ToOffset) -> Option<&Excerpt> { todo!() } @@ -6389,7 +6344,7 @@ impl MultiBufferSnapshot { let start_excerpt = cursor.excerpt()?; if range.end != range.start { cursor.seek_forward(&range.end); - if cursor.excerpt()?.id != start_excerpt.id { + if cursor.excerpt()? != start_excerpt { return None; } } @@ -7011,11 +6966,9 @@ impl Excerpt { .buffer_snapshot } - fn buffer(&self, multibuffer: &MultiBuffer, cx: &App) -> Entity { - let snapshot = multibuffer.snapshot(cx); - let buffer_snapshot = self.buffer_snapshot(&snapshot); + fn buffer(&self, multibuffer: &MultiBuffer) -> Entity { multibuffer - .buffer(buffer_snapshot.remote_id()) + .buffer(self.buffer_id) .expect("buffer entity not found for excerpt") } diff --git a/crates/multi_buffer/src/path_key.rs b/crates/multi_buffer/src/path_key.rs index b5202856117320454ac7622f712ab891142be17c..b433efc00d0e8e8deda9df07882f0d2027f78787 100644 --- a/crates/multi_buffer/src/path_key.rs +++ b/crates/multi_buffer/src/path_key.rs @@ -1,11 +1,7 @@ -use std::{ - cell::{Cell, RefCell}, - ops::Range, - rc::Rc, - sync::Arc, -}; +use std::{ops::Range, rc::Rc, sync::Arc}; use gpui::{App, AppContext, Context, Entity}; +use itertools::Itertools; use language::{Buffer, BufferSnapshot}; use rope::Point; use sum_tree::{Dimensions, SumTree}; @@ -15,8 +11,8 @@ use ztracing::instrument; use crate::{ Anchor, BufferState, BufferStateSnapshot, DiffChangeKind, Event, Excerpt, ExcerptOffset, - ExcerptRange, ExcerptSummary, ExpandExcerptDirection, MultiBuffer, PathKeyIndex, - build_excerpt_ranges, + ExcerptRange, ExcerptSummary, ExpandExcerptDirection, MultiBuffer, MultiBufferDimension, + PathKeyIndex, ToOffset, build_excerpt_ranges, }; #[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)] @@ -103,9 +99,9 @@ impl MultiBuffer { let excerpt_ranges = build_excerpt_ranges(ranges.clone(), context_line_count, &buffer_snapshot); - let (new, _) = Self::merge_excerpt_ranges(&excerpt_ranges); + let merged = Self::merge_excerpt_ranges(&excerpt_ranges); let (inserted, path_key_index) = - self.set_merged_excerpt_ranges_for_path(path, buffer, &buffer_snapshot, new, cx); + self.set_merged_excerpt_ranges_for_path(path, buffer, &buffer_snapshot, merged, cx); // todo!() move this into the callers that care let anchors = ranges .into_iter() @@ -124,9 +120,9 @@ impl MultiBuffer { excerpt_ranges: Vec>, cx: &mut Context, ) -> (Vec>, bool) { - let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges); + let merged = Self::merge_excerpt_ranges(&excerpt_ranges); let (inserted, path_key_index) = - self.set_merged_excerpt_ranges_for_path(path, buffer, buffer_snapshot, new, cx); + self.set_merged_excerpt_ranges_for_path(path, buffer, buffer_snapshot, merged, cx); // todo!() move this into the callers that care let anchors = excerpt_ranges .into_iter() @@ -158,8 +154,8 @@ impl MultiBuffer { let point_ranges = ranges.iter().map(|range| range.to_point(&snapshot)); let excerpt_ranges = build_excerpt_ranges(point_ranges, context_line_count, &snapshot); - let (new, _) = Self::merge_excerpt_ranges(&excerpt_ranges); - (ranges, new) + let merged = Self::merge_excerpt_ranges(&excerpt_ranges); + (ranges, merged) }) .await; @@ -195,99 +191,90 @@ impl MultiBuffer { .filter_map(|anchor| anchor.excerpt_anchor()) .collect::>(); sorted_anchors.sort_by(|a, b| a.cmp(b, &snapshot)); + let buffers = sorted_anchors.into_iter().chunk_by(|anchor| anchor.path); let mut cursor = snapshot.excerpts.cursor::(()); - let mut sorted_anchors = sorted_anchors.into_iter().peekable(); - while let Some(anchor) = sorted_anchors.next() { - let path = snapshot.path_for_anchor(anchor); - let Some(buffer) = self.buffer_for_path(&path, cx) else { + + for (path_index, excerpt_anchors) in &buffers { + let path = snapshot + .path_keys_by_index + .get(&path_index) + .expect("anchor from wrong multibuffer"); + let Some((buffer, buffer_snapshot)) = cursor + .item() + .map(|excerpt| (excerpt.buffer(&self), excerpt.buffer_snapshot(&snapshot))) + else { continue; }; - let buffer_snapshot = buffer.read(cx).snapshot(); - let mut expanded_ranges = Vec::new(); - // Move to the first excerpt for this path - cursor.seek_forward(&path, Bias::Left); - while let Some(anchor) = sorted_anchors.peek().copied() - && snapshot.path_for_anchor(anchor) == path + let mut excerpt_anchors = excerpt_anchors.peekable(); + let mut ranges = Vec::new(); + + cursor.seek_forward(path, Bias::Left); + while let Some(excerpt) = cursor.item() + && &excerpt.path_key == path { - sorted_anchors.next(); - let target = anchor.seek_target(&snapshot); - // Move to the next excerpt to be expanded, and push unchanged ranges for intervening excerpts - expanded_ranges.extend( - cursor - .slice(&target, Bias::Left) - .iter() - .map(|excerpt| excerpt.range.clone()), - ); - let Some(excerpt) = cursor.item() else { - continue; + let mut range = ExcerptRange { + context: excerpt.range.context.to_point(buffer_snapshot), + primary: excerpt.range.primary.to_point(buffer_snapshot), }; - if excerpt.path_key != path { - continue; + + let mut needs_expand = false; + while excerpt_anchors.peek().is_some_and(|anchor| { + excerpt + .range + .contains(&anchor.text_anchor(), buffer_snapshot) + }) { + needs_expand = true; + excerpt_anchors.next(); } - // Expand the range for this excerpt - let mut context = excerpt.range.context.to_point(&buffer_snapshot); - 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_snapshot.max_point().row); - context.end.column = excerpt.buffer_snapshot.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_snapshot.max_point().row); - context.end.column = excerpt.buffer_snapshot.line_len(context.end.row); + + if needs_expand { + match direction { + ExpandExcerptDirection::Up => { + range.context.start.row = + range.context.start.row.saturating_sub(line_count); + range.context.start.column = 0; + } + ExpandExcerptDirection::Down => { + range.context.end.row = (range.context.end.row + line_count) + .min(excerpt.buffer_snapshot(&snapshot).max_point().row); + range.context.end.column = excerpt + .buffer_snapshot(&snapshot) + .line_len(range.context.end.row); + } + ExpandExcerptDirection::UpAndDown => { + range.context.start.row = + range.context.start.row.saturating_sub(line_count); + range.context.start.column = 0; + range.context.end.row = (range.context.end.row + line_count) + .min(excerpt.buffer_snapshot(&snapshot).max_point().row); + range.context.end.column = excerpt + .buffer_snapshot(&snapshot) + .line_len(range.context.end.row); + } } } - let context = excerpt.buffer_snapshot.anchor_range_around(context); - expanded_ranges.push(ExcerptRange { - context, - primary: excerpt.range.primary.clone(), - }); - cursor.next(); - } - // Add unchanged ranges for this path after the last expanded excerpt - while let Some(excerpt) = cursor.item() - && excerpt.path_key == path - { - expanded_ranges.push(excerpt.range.clone()); + ranges.push(range); cursor.next(); } - let mut merged_ranges: Vec> = Vec::new(); - for range in expanded_ranges { - if let Some(last_range) = merged_ranges.last_mut() - && last_range - .context - .end - .cmp(&range.context.start, &buffer_snapshot) - .is_ge() - { - last_range.context.end = range.context.end; - continue; - } - merged_ranges.push(range) - } - self.update_path_excerpts(path.clone(), buffer, &buffer_snapshot, &merged_ranges, cx); + self.set_excerpt_ranges_for_path(path.clone(), buffer, buffer_snapshot, ranges, cx); } } /// Sets excerpts, returns `true` if at least one new excerpt was added. - fn set_merged_excerpt_ranges_for_path( + pub(crate) fn set_merged_excerpt_ranges_for_path( &mut self, path: PathKey, buffer: Entity, buffer_snapshot: &BufferSnapshot, - new: Vec>, + new: Vec>, cx: &mut Context, - ) -> (bool, PathKeyIndex) { + ) -> (bool, PathKeyIndex) + where + T: language::ToOffset, + { let anchor_ranges = new .into_iter() .map(|r| ExcerptRange { @@ -346,18 +333,14 @@ impl MultiBuffer { assert_eq!(self.history.transaction_depth(), 0); self.sync_mut(cx); - let buffer_snapshot = buffer.read(cx).snapshot(); let buffer_id = buffer_snapshot.remote_id(); + self.buffers.entry(buffer_id).or_insert_with(|| { self.buffer_changed_since_sync.replace(true); buffer.update(cx, |buffer, _| { buffer.record_changes(Rc::downgrade(&self.buffer_changed_since_sync)); }); BufferState { - last_version: RefCell::new(buffer_snapshot.version().clone()), - last_non_text_state_update_count: Cell::new( - buffer_snapshot.non_text_state_update_count(), - ), _subscriptions: [ cx.observe(&buffer, |_, _, cx| cx.notify()), cx.subscribe(&buffer, Self::on_buffer_event), @@ -431,7 +414,7 @@ impl MultiBuffer { Excerpt::new( path_key.clone(), path_key_index, - buffer_snapshot.remote_id(), + &buffer_snapshot, next_excerpt.clone(), to_insert.peek().is_some(), ), @@ -462,8 +445,8 @@ impl MultiBuffer { snapshot.buffers.insert( buffer_id, BufferStateSnapshot { - path_key, - buffer_snapshot, + path_key: path_key.clone(), + buffer_snapshot: buffer_snapshot.clone(), }, ); if changed_trailing_excerpt {