Detailed changes
@@ -1999,7 +1999,7 @@ impl CodeActionProvider for AssistantCodeActionProvider {
}
let multibuffer_snapshot = multibuffer.read(cx);
- multibuffer_snapshot.anchor_range_in_excerpt(excerpt_id, action.range)
+ multibuffer_snapshot.anchor_range_in_buffer(excerpt_id, action.range)
})
})
.context("invalid range")?;
@@ -1666,7 +1666,7 @@ mod tests {
editor.update_in(cx, |editor, window, cx| {
let snapshot = editor.buffer().read(cx).snapshot(cx);
let range = snapshot
- .anchor_range_in_excerpt(excerpt_id, completion.replace_range)
+ .anchor_range_in_buffer(excerpt_id, completion.replace_range)
.unwrap();
editor.edit([(range, completion.new_text)], cx);
(completion.confirm.unwrap())(CompletionIntent::Complete, window, cx);
@@ -740,7 +740,7 @@ impl TextThreadEditor {
};
let range = buffer
- .anchor_range_in_excerpt(excerpt_id, command.source_range.clone())
+ .anchor_range_in_buffer(excerpt_id, command.source_range.clone())
.unwrap();
Crease::inline(range, placeholder, render_toggle, render_trailer)
}),
@@ -813,7 +813,7 @@ impl TextThreadEditor {
let (excerpt_id, _buffer_id, _buffer_snapshot) = buffer.as_singleton().unwrap();
let range = buffer
- .anchor_range_in_excerpt(excerpt_id, invoked_slash_command.range.clone())
+ .anchor_range_in_buffer(excerpt_id, invoked_slash_command.range.clone())
.unwrap();
editor.remove_folds_with_type(
&[range],
@@ -833,7 +833,7 @@ impl TextThreadEditor {
let (excerpt_id, _buffer_id, _buffer_snapshot) = buffer.as_singleton().unwrap();
let context = self.text_thread.downgrade();
let range = buffer
- .anchor_range_in_excerpt(excerpt_id, invoked_slash_command.range.clone())
+ .anchor_range_in_buffer(excerpt_id, invoked_slash_command.range.clone())
.unwrap();
let crease = Crease::inline(
range,
@@ -875,7 +875,7 @@ impl TextThreadEditor {
let mut creases = Vec::new();
for (section, status) in sections {
let range = buffer
- .anchor_range_in_excerpt(excerpt_id, section.range)
+ .anchor_range_in_buffer(excerpt_id, section.range)
.unwrap();
let buffer_row = MultiBufferRow(range.start.to_point(&buffer).row);
buffer_rows_to_fold.insert(buffer_row);
@@ -924,7 +924,7 @@ impl TextThreadEditor {
let mut creases = Vec::new();
for section in sections {
let range = buffer
- .anchor_range_in_excerpt(excerpt_id, section.range)
+ .anchor_range_in_buffer(excerpt_id, section.range)
.unwrap();
let buffer_row = MultiBufferRow(range.start.to_point(&buffer).row);
buffer_rows_to_fold.insert(buffer_row);
@@ -1054,9 +1054,9 @@ impl DisplayMap {
let base_placeholder = self.fold_placeholder.clone();
let creases = ranges.into_iter().filter_map(|folding_range| {
- let mb_range = excerpt_ids.iter().find_map(|&id| {
- snapshot.anchor_range_in_excerpt(id, folding_range.range.clone())
- })?;
+ let mb_range = excerpt_ids
+ .iter()
+ .find_map(|&id| snapshot.anchor_range_in_buffer(id, folding_range.range.clone()))?;
let placeholder = if let Some(collapsed_text) = folding_range.collapsed_text {
FoldPlaceholder {
render: Arc::new({
@@ -1989,7 +1989,7 @@ impl DisplaySnapshot {
let start_anchor = buffer.anchor_before(buffer_range.start);
let end_anchor = buffer.anchor_after(buffer_range.end);
let mb_range =
- multibuffer.anchor_range_in_excerpt(excerpt_id, start_anchor..end_anchor)?;
+ multibuffer.anchor_range_in_buffer(excerpt_id, start_anchor..end_anchor)?;
Some(mb_range.start.to_offset(multibuffer)..mb_range.end.to_offset(multibuffer))
});
@@ -254,7 +254,7 @@ impl Editor {
buffer_snapshot.clip_point_utf16(color_end, Bias::Right),
);
let Some(range) = multi_buffer_snapshot
- .anchor_range_in_excerpt(*excerpt_id, start..end)
+ .anchor_range_in_buffer(*excerpt_id, start..end)
else {
continue;
};
@@ -8620,7 +8620,7 @@ impl Editor {
.into_iter()
.flat_map(|(range, new_text)| {
Some((
- multibuffer.anchor_range_in_excerpt(excerpt_id, range)?,
+ multibuffer.anchor_range_in_buffer(excerpt_id, range)?,
new_text,
))
})
@@ -24114,14 +24114,14 @@ impl Editor {
excerpts: excerpts.clone(),
});
}
- multi_buffer::Event::BuffersRemoved {
- ids,
- removed_buffer_ids,
- } => {
+ multi_buffer::Event::BuffersRemoved { removed_buffer_ids } => {
if let Some(inlay_hints) = &mut self.inlay_hints {
inlay_hints.remove_inlay_chunk_data(removed_buffer_ids);
}
- self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
+ self.refresh_inlay_hints(
+ InlayHintRefreshReason::ExcerptsRemoved(removed_buffer_ids.clone()),
+ cx,
+ );
for buffer_id in removed_buffer_ids {
self.registered_buffers.remove(buffer_id);
self.tasks
@@ -24687,7 +24687,7 @@ impl Editor {
let range = buffer_snapshot.anchor_before(range.start)
..buffer_snapshot.anchor_after(range.end);
multibuffer_snapshot
- .anchor_range_in_excerpt(excerpt_id, range)
+ .anchor_range_in_buffer(excerpt_id, range)
.unwrap()
}));
},
@@ -7888,7 +7888,7 @@ impl EditorElement {
let clipped_start = range.start.max(&buffer_range.start, buffer);
let clipped_end = range.end.min(&buffer_range.end, buffer);
let range = buffer_snapshot
- .anchor_range_in_excerpt(excerpt_id, *clipped_start..*clipped_end)?;
+ .anchor_range_in_buffer(excerpt_id, *clipped_start..*clipped_end)?;
let start = range.start.to_display_point(display_snapshot);
let end = range.end.to_display_point(display_snapshot);
let selection_layout = SelectionLayout {
@@ -365,7 +365,7 @@ pub fn show_link_definition(
this.read_with(cx, |_, _| {
let range = maybe!({
let range =
- snapshot.anchor_range_in_excerpt(excerpt_id, url_range)?;
+ snapshot.anchor_range_in_buffer(excerpt_id, url_range)?;
Some(RangeInEditor::Text(range))
});
(range, vec![HoverLink::Url(url)])
@@ -376,7 +376,7 @@ pub fn show_link_definition(
{
let range = maybe!({
let range =
- snapshot.anchor_range_in_excerpt(excerpt_id, filename_range)?;
+ snapshot.anchor_range_in_buffer(excerpt_id, filename_range)?;
Some(RangeInEditor::Text(range))
});
@@ -390,7 +390,7 @@ pub fn show_link_definition(
(
definition_result.iter().find_map(|link| {
link.origin.as_ref().and_then(|origin| {
- let range = snapshot.anchor_range_in_excerpt(
+ let range = snapshot.anchor_range_in_buffer(
excerpt_id,
origin.range.clone(),
)?;
@@ -477,7 +477,7 @@ fn show_hover(
.and_then(|range| {
let range = snapshot
.buffer_snapshot()
- .anchor_range_in_excerpt(excerpt_id, range)?;
+ .anchor_range_in_buffer(excerpt_id, range)?;
Some(range)
})
.or_else(|| {
@@ -237,7 +237,7 @@ fn conflicts_updated(
continue;
};
let excerpt_id = *excerpt_id;
- let Some(range) = snapshot.anchor_range_in_excerpt(excerpt_id, conflict_range) else {
+ let Some(range) = snapshot.anchor_range_in_buffer(excerpt_id, conflict_range) else {
continue;
};
removed_highlighted_ranges.push(range.clone());
@@ -324,9 +324,9 @@ fn update_conflict_highlighting(
) -> Option<()> {
log::debug!("update conflict highlighting for {conflict:?}");
- let outer = buffer.anchor_range_in_excerpt(excerpt_id, conflict.range.clone())?;
- let ours = buffer.anchor_range_in_excerpt(excerpt_id, conflict.ours.clone())?;
- let theirs = buffer.anchor_range_in_excerpt(excerpt_id, conflict.theirs.clone())?;
+ let outer = buffer.anchor_range_in_buffer(excerpt_id, conflict.range.clone())?;
+ let ours = buffer.anchor_range_in_buffer(excerpt_id, conflict.ours.clone())?;
+ let theirs = buffer.anchor_range_in_buffer(excerpt_id, conflict.theirs.clone())?;
let ours_background = cx.theme().colors().version_control_conflict_marker_ours;
let theirs_background = cx.theme().colors().version_control_conflict_marker_theirs;
@@ -470,8 +470,7 @@ pub(crate) fn resolve_conflict(
})
.ok()?;
let &(_, block_id) = &state.block_ids[ix];
- let range =
- snapshot.anchor_range_in_excerpt(excerpt_id, resolved_conflict.range)?;
+ let range = snapshot.anchor_range_in_buffer(excerpt_id, resolved_conflict.range)?;
editor.remove_gutter_highlights::<ConflictsOuter>(vec![range.clone()], cx);
@@ -361,7 +361,7 @@ impl SyntaxTreeView {
let multibuffer = editor_state.editor.read(cx).buffer();
let multibuffer = multibuffer.read(cx).snapshot(cx);
let excerpt_id = buffer_state.excerpt_id;
- let range = multibuffer.anchor_range_in_excerpt(excerpt_id, range)?;
+ let range = multibuffer.anchor_range_in_buffer(excerpt_id, range)?;
let key = cx.entity_id().as_u64() as usize;
// Update the editor with the anchor range.
@@ -95,7 +95,7 @@ impl ExcerptAnchor {
self
}
- fn cmp(&self, other: &Self, snapshot: &MultiBufferSnapshot) -> Ordering {
+ pub(crate) fn cmp(&self, other: &Self, snapshot: &MultiBufferSnapshot) -> Ordering {
let Some(self_path_key) = snapshot.path_keys_by_index.get(&self.path) else {
panic!("anchor's path was never added to multibuffer")
};
@@ -206,7 +206,7 @@ impl ExcerptAnchor {
}
fn is_valid(&self, snapshot: &MultiBufferSnapshot) -> bool {
- let Some(target) = snapshot.try_anchor_seek_target(*self) else {
+ let Some(target) = self.try_seek_target(snapshot) else {
return false;
};
let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>(());
@@ -230,13 +230,21 @@ impl ExcerptAnchor {
}
pub(crate) fn seek_target(&self, snapshot: &MultiBufferSnapshot) -> AnchorSeekTarget {
- let path_key = snapshot.path_for_anchor(*self);
+ self.try_seek_target(snapshot)
+ .expect("anchor is from different multi-buffer")
+ }
+
+ pub(crate) fn try_seek_target(
+ &self,
+ snapshot: &MultiBufferSnapshot,
+ ) -> Option<AnchorSeekTarget> {
+ let path_key = snapshot.try_path_for_anchor(*self)?;
let buffer = snapshot.buffer_for_path(&path_key).cloned();
- AnchorSeekTarget::Excerpt {
+ Some(AnchorSeekTarget::Excerpt {
path_key,
anchor: *self,
snapshot: buffer,
- }
+ })
}
}
@@ -338,12 +346,14 @@ impl Anchor {
}
}
- pub(crate) fn try_seek_target(&self, arg: &MultiBufferSnapshot) -> Option<AnchorSeekTarget> {
- todo!()
+ pub(crate) fn try_seek_target(
+ &self,
+ snapshot: &MultiBufferSnapshot,
+ ) -> Option<AnchorSeekTarget> {
match self {
- Anchor::Min => AnchorSeekTarget::Min,
- Anchor::Excerpt(excerpt_anchor) => excerpt_anchor.seek_target(snapshot),
- Anchor::Max => AnchorSeekTarget::Max,
+ Anchor::Min => Some(AnchorSeekTarget::Min),
+ Anchor::Excerpt(excerpt_anchor) => excerpt_anchor.try_seek_target(snapshot),
+ Anchor::Max => Some(AnchorSeekTarget::Max),
}
}
}
@@ -60,7 +60,7 @@ use text::{
use theme::SyntaxTheme;
use ztracing::instrument;
-pub use self::path_key::{PathExcerptInsertResult, PathKey};
+pub use self::path_key::PathKey;
pub static EXCERPT_CONTEXT_LINES: OnceLock<fn(&App) -> u32> = OnceLock::new();
@@ -96,7 +96,7 @@ pub struct MultiBuffer {
pub struct PathKeyIndex(u64);
pub struct ExcerptInfo {
- path_key: PathKey,
+ path_key_index: PathKeyIndex,
buffer_id: BufferId,
range: ExcerptRange<text::Anchor>,
}
@@ -168,7 +168,9 @@ impl MultiBufferDiffHunk {
}
pub type MultiBufferPoint = Point;
+/// ExcerptOffset is offset into the non-deleted text of the multibuffer
type ExcerptOffset = ExcerptDimension<MultiBufferOffset>;
+/// ExcerptOffset is based on the non-deleted text of the multibuffer
type ExcerptPoint = ExcerptDimension<Point>;
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq, Hash, serde::Deserialize)]
@@ -789,7 +791,7 @@ impl ExcerptRange<text::Anchor> {
#[derive(Clone, Debug)]
pub struct ExcerptSummary {
path_key: PathKey,
- max_anchor: Anchor,
+ max_anchor: text::Anchor,
widest_line_number: u32,
text: MBTextSummary,
count: usize,
@@ -799,7 +801,7 @@ impl ExcerptSummary {
pub fn min() -> Self {
ExcerptSummary {
path_key: PathKey::min(),
- max_anchor: Anchor::Min,
+ max_anchor: text::Anchor::MIN,
widest_line_number: 0,
text: MBTextSummary::default(),
count: 0,
@@ -1647,13 +1649,21 @@ impl MultiBuffer {
}
let start = text::Anchor::max(
&excerpt.range.context.start,
- &selection.start.text_anchor().unwrap_or(text::Anchor::MIN),
+ &selection
+ .start
+ .excerpt_anchor()
+ .map(|excerpt_anchor| excerpt_anchor.text_anchor())
+ .unwrap_or(text::Anchor::MIN),
&excerpt.buffer,
)
.clone();
let end = text::Anchor::min(
&excerpt.range.context.end,
- &selection.end.text_anchor().unwrap_or(text::Anchor::MAX),
+ &selection
+ .end
+ .excerpt_anchor()
+ .map(|excerpt_anchor| excerpt_anchor.text_anchor())
+ .unwrap_or(text::Anchor::MAX),
&excerpt.buffer,
)
.clone();
@@ -1771,11 +1781,7 @@ impl MultiBuffer {
}
#[ztracing::instrument(skip_all)]
- pub fn excerpts_for_buffer(
- &self,
- buffer_id: BufferId,
- cx: &App,
- ) -> Vec<ExcerptRange<text::Anchor>> {
+ pub fn excerpts_for_buffer(&self, buffer_id: BufferId, cx: &App) -> Vec<ExcerptInfo> {
let mut excerpts = Vec::new();
let snapshot = self.read(cx);
let Some(path_key) = snapshot.path_keys_by_buffer.get(&buffer_id).cloned() else {
@@ -1785,10 +1791,10 @@ impl MultiBuffer {
let mut cursor = snapshot.excerpts.cursor::<PathKey>(());
cursor.seek_forward(&path_key, Bias::Left);
- while let Some(item) = cursor.item()
- && item.path_key == path_key
+ while let Some(excerpt) = cursor.item()
+ && excerpt.path_key == path_key
{
- excerpts.push(item.range.clone());
+ excerpts.push(excerpt.info());
cursor.next()
}
excerpts
@@ -1845,18 +1851,14 @@ impl MultiBuffer {
let mut cursor = snapshot.cursor::<MultiBufferOffset, BufferOffset>();
cursor.seek(&offset);
let excerpt = cursor.excerpt().or(snapshot.excerpts.last())?;
- Some(ExcerptInfo {
- path_key: excerpt.path_key.clone(),
- buffer_id: excerpt.buffer.remote_id(),
- range: excerpt.range.clone(),
- })
+ Some(excerpt.info())
}
pub fn buffer_for_anchor(&self, anchor: Anchor, cx: &App) -> Option<Entity<Buffer>> {
let buffer_id = match anchor {
Anchor::Min => self.snapshot(cx).excerpts.first()?.buffer.remote_id(),
Anchor::Max => self.snapshot(cx).excerpts.last()?.buffer.remote_id(),
- Anchor::Text { buffer_id, .. } => buffer_id,
+ Anchor::Excerpt(excerpt_anchor) => excerpt_anchor.buffer_id,
};
self.buffer(buffer_id)
}
@@ -1895,22 +1897,32 @@ impl MultiBuffer {
) -> Option<Anchor> {
let mut found = None;
let snapshot = buffer.read(cx).snapshot();
- for range in self.excerpts_for_buffer(snapshot.remote_id(), cx) {
- let start = range.context.start.to_point(&snapshot);
- let end = range.context.end.to_point(&snapshot);
- if start <= point && point < end {
- found = Some(snapshot.clip_point(point, Bias::Left));
+ let text_anchor = snapshot.anchor_after(&point);
+ for excerpt in self.excerpts_for_buffer(snapshot.remote_id(), cx) {
+ if excerpt.range.contains(&text_anchor, &snapshot) {
+ found = Some(Anchor::in_buffer(excerpt.path_key_index, text_anchor));
break;
}
- if point < start {
- found = Some(start);
- }
- if point > end {
- found = Some(end);
+ if excerpt
+ .range
+ .context
+ .start
+ .cmp(&text_anchor, &snapshot)
+ .is_gt()
+ {
+ found = Some(Anchor::in_buffer(
+ excerpt.path_key_index,
+ excerpt.range.context.start,
+ ));
+ break;
}
+ found = Some(Anchor::in_buffer(
+ excerpt.path_key_index,
+ excerpt.range.context.end,
+ ));
}
- Some(Anchor::text(snapshot.anchor_after(found?)))
+ found
}
pub fn buffer_anchor_to_anchor(
@@ -1921,9 +1933,9 @@ impl MultiBuffer {
cx: &App,
) -> Option<Anchor> {
let snapshot = buffer.read(cx).snapshot();
- for range in self.excerpts_for_buffer(snapshot.remote_id(), cx) {
- if range.contains(&anchor, &snapshot) {
- return Some(Anchor::text(anchor));
+ for excerpt in self.excerpts_for_buffer(snapshot.remote_id(), cx) {
+ if excerpt.range.contains(&anchor, &snapshot) {
+ return Some(Anchor::in_buffer(excerpt.path_key_index, anchor));
}
}
@@ -5377,12 +5389,15 @@ impl MultiBufferSnapshot {
/// Returns an anchor for the given excerpt and text anchor,
/// Returns [`None`] if the excerpt_id is no longer valid or the text anchor range is out of excerpt's bounds.
- pub fn anchor_range_in_excerpt(
+ pub fn anchor_range_in_buffer(
&self,
- excerpt_id: ExcerptId,
+ excerpt_id: BufferId,
text_anchor: Range<text::Anchor>,
) -> Option<Range<Anchor>> {
- let excerpt = self.excerpt(self.latest_excerpt_id(excerpt_id))?;
+ let path_key_index = self.first_excerpt_for_buffer(buffer_id)?.path_key_index;
+
+ let target = self.anchor_see
+ self.excerpts.cursor.find(&snapshot, (path_key_index, anchor))
Some(
Self::anchor_in_excerpt_(excerpt, text_anchor.start)?
@@ -6293,17 +6308,17 @@ impl MultiBufferSnapshot {
.flat_map(|item| {
Some(OutlineItem {
depth: item.depth,
- range: self.anchor_range_in_excerpt(excerpt_id, item.range)?,
+ range: self.anchor_range_in_buffer(excerpt_id, item.range)?,
source_range_for_text: self
- .anchor_range_in_excerpt(excerpt_id, item.source_range_for_text)?,
+ .anchor_range_in_buffer(excerpt_id, item.source_range_for_text)?,
text: item.text,
highlight_ranges: item.highlight_ranges,
name_ranges: item.name_ranges,
body_range: item.body_range.and_then(|body_range| {
- self.anchor_range_in_excerpt(excerpt_id, body_range)
+ self.anchor_range_in_buffer(excerpt_id, body_range)
}),
annotation_range: item.annotation_range.and_then(|annotation_range| {
- self.anchor_range_in_excerpt(excerpt_id, annotation_range)
+ self.anchor_range_in_buffer(excerpt_id, annotation_range)
}),
})
})
@@ -6371,6 +6386,14 @@ impl MultiBufferSnapshot {
self.path_keys_by_buffer.get(&buffer_id)
}
+ pub fn first_excerpt_for_buffer(&self, buffer_id: BufferId) -> Option<&Excerpt> {
+ let path_key = self.path_keys_by_buffer.get(&buffer_id)?;
+ let (_, _, first_excerpt) =
+ self.excerpts
+ .find::<ExcerptSummary, _>((), path_key, Bias::Left);
+ first_excerpt
+ }
+
pub fn buffer_for_id(&self, id: BufferId) -> Option<&BufferSnapshot> {
self.buffer_for_path(self.path_keys_by_buffer.get(&id)?)
}
@@ -6609,11 +6632,15 @@ impl MultiBufferSnapshot {
excerpt_edits
}
- fn excerpt_ranges_for_path(
- &self,
- path_key: &PathKey,
- ) -> impl Iterator<Item = ExcerptRange<text::Anchor>> {
- todo!()
+ fn excerpts_for_path<'a>(
+ &'a self,
+ path_key: &'a PathKey,
+ ) -> impl Iterator<Item = ExcerptInfo> + 'a {
+ let mut cursor = self.excerpts.cursor::<ExcerptSummary>(());
+ cursor.seek(path_key, Bias::Left);
+ cursor
+ .take_while(move |item| &item.path_key == path_key)
+ .map(|excerpt| excerpt.info())
}
}
@@ -6632,27 +6659,46 @@ impl MultiBufferSnapshot {
#[cfg(any(test, feature = "test-support"))]
fn check_invariants(&self) {
let excerpts = self.excerpts.items(());
- let excerpt_ids = self.excerpt_ids.items(());
- for (ix, excerpt) in excerpts.iter().enumerate() {
- if ix == 0 {
- if excerpt.locator <= Locator::min() {
- panic!("invalid first excerpt locator {:?}", excerpt.locator);
- }
- } else if excerpt.locator <= excerpts[ix - 1].locator {
- panic!("excerpts are out-of-order: {:?}", excerpts);
- }
- }
+ let all_buffer_path_keys = HashSet::from_iter(self.path_keys_by_buffer.values().cloned());
+ let all_excerpt_path_keys = HashSet::from_iter(excerpts.iter().map(|e| e.path_key.clone()));
- for (ix, entry) in excerpt_ids.iter().enumerate() {
- if ix == 0 {
- if entry.id.cmp(&ExcerptId::min(), self).is_le() {
- panic!("invalid first excerpt id {:?}", entry.id);
+ for (ix, excerpt) in excerpts.iter().enumerate() {
+ if ix > 0 {
+ let prev = &excerpts[ix - 1];
+
+ if excerpt.path_key < prev.path_key {
+ panic!("excerpt path_keys are out-of-order: {:?}", excerpts);
+ } else if excerpt.path_key == prev.path_key {
+ if excerpt
+ .start_anchor()
+ .cmp(&prev.end_anchor(), &self)
+ .is_le()
+ {
+ panic!("excerpt anchors are out-of-order: {:?}", excerpts);
+ }
+ if excerpt
+ .start_anchor()
+ .cmp(&excerpt.end_anchor(), &self)
+ .is_ge()
+ {
+ panic!("excerpt with backward range: {:?}", excerpts);
+ }
}
- } else if entry.id <= excerpt_ids[ix - 1].id {
- panic!("excerpt ids are out-of-order: {:?}", excerpt_ids);
}
+ assert!(
+ all_buffer_path_keys.contains(&excerpt.path_key),
+ "excerpt path key not found in active path keys: {:?}",
+ excerpt.path_key
+ );
+ assert_eq!(
+ self.path_keys_by_index.get(&excerpt.path_key_index),
+ Some(&excerpt.path_key),
+ "excerpt path key index does not match path key: {:?}",
+ excerpt.path_key,
+ );
}
+ assert_eq!(all_buffer_path_keys, all_excerpt_path_keys);
if self.diff_transforms.summary().input != self.excerpts.summary().text {
panic!(
@@ -7009,12 +7055,14 @@ where
impl Excerpt {
fn new(
path_key: PathKey,
+ path_key_index: PathKeyIndex,
buffer: Arc<BufferSnapshot>,
range: ExcerptRange<text::Anchor>,
has_trailing_newline: bool,
) -> Self {
Excerpt {
path_key,
+ path_key_index,
max_buffer_row: range.context.end.to_point(&buffer).row,
text_summary: buffer
.text_summary_for_range::<TextSummary, _>(range.context.to_offset(&buffer)),
@@ -7024,6 +7072,14 @@ impl Excerpt {
}
}
+ fn info(&self) -> ExcerptInfo {
+ ExcerptInfo {
+ path_key_index: self.path_key_index,
+ buffer_id: self.buffer.remote_id(),
+ range: self.range.clone(),
+ }
+ }
+
fn chunks_in_range(&self, range: Range<usize>, language_aware: bool) -> ExcerptChunks<'_> {
let content_start = self.range.context.start.to_offset(&self.buffer);
let chunks_start = content_start + range.start;
@@ -7247,7 +7303,7 @@ impl sum_tree::Item for Excerpt {
text += TextSummary::from("\n");
}
ExcerptSummary {
- path_key: self.path_key,
+ path_key: self.path_key.clone(),
max_anchor: self.range.context.end,
widest_line_number: self.max_buffer_row,
text: text.into(),
@@ -3533,8 +3533,8 @@ fn test_history(cx: &mut App) {
assert_eq!(
multibuffer.edited_ranges_for_transaction(transaction_1, cx),
&[
- Point::new(0, 0)..Point::new(0, 2),
- Point::new(1, 0)..Point::new(1, 2)
+ MultiBufferOffset(0)..MultiBufferOffset(2),
+ MultiBufferOffset(7)..MultiBufferOffset(9),
]
);
@@ -5,20 +5,17 @@ use std::{
sync::Arc,
};
-use collections::HashSet;
use gpui::{App, AppContext, Context, Entity};
-use itertools::Itertools;
use language::{Buffer, BufferSnapshot};
use rope::Point;
use sum_tree::{Dimensions, SumTree};
-use text::{Bias, BufferId, Edit, OffsetRangeExt, Patch};
+use text::{Bias, Edit, OffsetRangeExt, Patch};
use util::rel_path::RelPath;
use ztracing::instrument;
use crate::{
Anchor, BufferState, DiffChangeKind, Event, Excerpt, ExcerptOffset, ExcerptRange,
- ExcerptSummary, ExpandExcerptDirection, MultiBuffer, MultiBufferOffset, PathKeyIndex,
- build_excerpt_ranges,
+ ExcerptSummary, ExpandExcerptDirection, MultiBuffer, PathKeyIndex, build_excerpt_ranges,
};
#[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)]
@@ -64,31 +61,19 @@ impl PathKey {
}
impl MultiBuffer {
- pub fn paths(&self) -> impl Iterator<Item = &PathKey> + '_ {
- self.excerpts_by_path.keys()
- }
-
- pub fn excerpts_for_path(&self, path: &PathKey) -> impl '_ + Iterator<Item = ExcerptId> {
- self.excerpts_by_path
- .get(path)
- .map(|excerpts| excerpts.as_slice())
- .unwrap_or_default()
- .iter()
- .copied()
- }
-
pub fn buffer_for_path(&self, path: &PathKey, cx: &App) -> Option<Entity<Buffer>> {
- let excerpt_id = self.excerpts_by_path.get(path)?.first()?;
- let snapshot = self.read(cx);
- let excerpt = snapshot.excerpt(*excerpt_id)?;
+ let snapshot = self.snapshot(cx);
+ let excerpt = snapshot.excerpts_for_path(path).next()?;
self.buffer(excerpt.buffer_id)
}
pub fn location_for_path(&self, path: &PathKey, cx: &App) -> Option<Anchor> {
- let excerpt_id = self.excerpts_by_path.get(path)?.first()?;
- let snapshot = self.read(cx);
- let excerpt = snapshot.excerpt(*excerpt_id)?;
- Some(Anchor::text(excerpt.id, excerpt.range.context.start))
+ let snapshot = self.snapshot(cx);
+ let excerpt = snapshot.excerpts_for_path(path).next()?;
+ Some(Anchor::in_buffer(
+ excerpt.path_key_index,
+ excerpt.range.context.start,
+ ))
}
pub fn set_excerpts_for_buffer(
@@ -118,12 +103,14 @@ impl MultiBuffer {
build_excerpt_ranges(ranges.clone(), context_line_count, &buffer_snapshot);
let (new, _) = Self::merge_excerpt_ranges(&excerpt_ranges);
- let inserted =
+ let (inserted, path_key_index) =
self.set_merged_excerpt_ranges_for_path(path, buffer, &buffer_snapshot, new, cx);
// todo!() move this into the callers that care
let anchors = ranges
.into_iter()
- .map(|range| Anchor::range_in_buffer(buffer_snapshot.anchor_range_around(range)))
+ .map(|range| {
+ Anchor::range_in_buffer(path_key_index, buffer_snapshot.anchor_range_around(range))
+ })
.collect::<Vec<_>>();
(anchors, inserted)
}
@@ -137,13 +124,16 @@ impl MultiBuffer {
cx: &mut Context<Self>,
) -> (Vec<Range<Anchor>>, bool) {
let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
- let inserted =
+ let (inserted, path_key_index) =
self.set_merged_excerpt_ranges_for_path(path, buffer, buffer_snapshot, new, cx);
// todo!() move this into the callers that care
let anchors = excerpt_ranges
.into_iter()
.map(|range| {
- Anchor::range_in_buffer(buffer_snapshot.anchor_range_around(range.primary))
+ Anchor::range_in_buffer(
+ path_key_index,
+ buffer_snapshot.anchor_range_around(range.primary),
+ )
})
.collect::<Vec<_>>();
(anchors, inserted)
@@ -174,7 +164,7 @@ impl MultiBuffer {
multi_buffer
.update(&mut app, move |multi_buffer, cx| {
- multi_buffer.set_merged_excerpt_ranges_for_path(
+ let (_, path_key_index) = multi_buffer.set_merged_excerpt_ranges_for_path(
path_key,
buffer,
&buffer_snapshot,
@@ -183,7 +173,7 @@ impl MultiBuffer {
);
ranges
.into_iter()
- .map(|range| Anchor::range_in_buffer(range))
+ .map(|range| Anchor::range_in_buffer(path_key_index, range))
.collect()
})
.ok()
@@ -199,7 +189,10 @@ impl MultiBuffer {
cx: &mut Context<Self>,
) {
let snapshot = self.snapshot(cx);
- let mut sorted_anchors = anchors.into_iter().collect::<Vec<_>>();
+ let mut sorted_anchors = anchors
+ .into_iter()
+ .filter_map(|anchor| anchor.excerpt_anchor())
+ .collect::<Vec<_>>();
sorted_anchors.sort_by(|a, b| a.cmp(b, &snapshot));
let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>(());
let mut sorted_anchors = sorted_anchors.into_iter().peekable();
@@ -217,9 +210,7 @@ impl MultiBuffer {
&& snapshot.path_for_anchor(anchor) == path
{
sorted_anchors.next();
- let Some(target) = snapshot.anchor_seek_target(anchor) else {
- continue;
- };
+ 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
@@ -295,7 +286,7 @@ impl MultiBuffer {
buffer_snapshot: &BufferSnapshot,
new: Vec<ExcerptRange<Point>>,
cx: &mut Context<Self>,
- ) -> bool {
+ ) -> (bool, PathKeyIndex) {
let anchor_ranges = new
.into_iter()
.map(|r| ExcerptRange {
@@ -306,6 +297,29 @@ impl MultiBuffer {
self.update_path_excerpts(path, buffer, buffer_snapshot, &anchor_ranges, cx)
}
+ fn get_or_create_path_key_index(&mut self, path_key: &PathKey) -> PathKeyIndex {
+ let mut snapshot = self.snapshot.borrow_mut();
+ let existing = snapshot
+ .path_keys_by_index
+ .iter()
+ // todo!() perf? (but ExcerptIdMapping was doing this)
+ .find(|(_, existing_path)| existing_path == &path_key)
+ .map(|(index, _)| *index);
+
+ if let Some(existing) = existing {
+ return existing;
+ }
+
+ let index = snapshot
+ .path_keys_by_index
+ .last()
+ .map(|(index, _)| PathKeyIndex(index.0 + 1))
+ .unwrap_or(PathKeyIndex(0));
+ snapshot.path_keys_by_index.insert(index, path_key.clone());
+ index
+ }
+
+ // todo!() re-instate nonshrinking version for project diff / diagnostics
pub fn update_path_excerpts<'a>(
&mut self,
path_key: PathKey,
@@ -313,25 +327,27 @@ impl MultiBuffer {
buffer_snapshot: &BufferSnapshot,
to_insert: &Vec<ExcerptRange<text::Anchor>>,
cx: &mut Context<Self>,
- ) -> bool {
+ ) -> (bool, PathKeyIndex) {
+ let path_key_index = self.get_or_create_path_key_index(&path_key);
+ if let Some(old_path_key) = self
+ .snapshot(cx)
+ .path_for_buffer(buffer_snapshot.remote_id())
+ && old_path_key != &path_key
+ {
+ self.remove_excerpts_for_path(old_path_key.clone(), cx);
+ }
+
if to_insert.len() == 0 {
self.remove_excerpts_for_path(path_key.clone(), cx);
- if let Some(old_path_key) = self
- .snapshot(cx)
- .path_for_buffer(buffer_snapshot.remote_id())
- && old_path_key != &path_key
- {
- self.remove_excerpts_for_path(old_path_key.clone(), cx);
- }
- return false;
+ return (false, path_key_index);
}
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();
- let buffer_state = self.buffers.entry(buffer_id).or_insert_with(|| {
+ 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));
@@ -359,39 +375,6 @@ impl MultiBuffer {
let mut patch = Patch::empty();
let mut added_new_excerpt = false;
- let path_key_index = snapshot
- .path_keys_by_index
- .iter()
- // todo!() perf? (but ExcerptIdMapping was doing this)
- .find(|(_, existing_path)| existing_path == &&path_key)
- .map(|(index, _)| *index);
- let path_key_index = path_key_index.unwrap_or_else(|| {
- let index = snapshot
- .path_keys_by_index
- .last()
- .map(|(index, _)| PathKeyIndex(index.0 + 1))
- .unwrap_or(PathKeyIndex(0));
- snapshot.path_keys_by_index.insert(index, path_key.clone());
- index
- });
- let old_path_key = snapshot
- .path_keys_by_buffer
- .insert_or_replace(buffer_id, path_key.clone());
- // handle the case where the buffer's path key has changed by
- // removing any old excerpts for the buffer
- if let Some(old_path_key) = &old_path_key
- && old_path_key < &path_key
- {
- new_excerpts.append(cursor.slice(old_path_key, Bias::Left), ());
- let before = cursor.position.1;
- cursor.seek_forward(old_path_key, Bias::Right);
- let after = cursor.position.1;
- patch.push(Edit {
- old: before..after,
- new: new_excerpts.summary().len()..new_excerpts.summary().len(),
- });
- }
-
new_excerpts.append(cursor.slice(&path_key, Bias::Left), ());
// handle the case where the path key used to be associated
@@ -417,7 +400,7 @@ impl MultiBuffer {
let Some(next_excerpt) = to_insert.peek() else {
break;
};
- if &&excerpt.range == next_excerpt {
+ if &excerpt.range == *next_excerpt {
new_excerpts.push(excerpt.clone(), ());
to_insert.next();
cursor.next();
@@ -447,6 +430,7 @@ impl MultiBuffer {
new_excerpts.push(
Excerpt::new(
path_key.clone(),
+ path_key_index,
buffer_snapshot.clone(),
next_excerpt.clone(),
to_insert.peek().is_some(),
@@ -470,28 +454,9 @@ impl MultiBuffer {
new: new_excerpts.summary().len()..new_excerpts.summary().len(),
});
- // handle the case where the buffer's path key has changed by
- // removing any old excerpts for the buffer
- if let Some(old_path_key) = &old_path_key
- && old_path_key > &path_key
- {
- new_excerpts.append(cursor.slice(old_path_key, Bias::Left), ());
- let before = cursor.position.1;
- cursor.seek_forward(old_path_key, Bias::Right);
- let after = cursor.position.1;
- patch.push(Edit {
- old: before..after,
- new: new_excerpts.summary().len()..new_excerpts.summary().len(),
- });
- }
-
let suffix = cursor.suffix();
let changed_trailing_excerpt = suffix.is_empty();
new_excerpts.append(suffix, ());
- let new_ranges = snapshot
- .excerpt_ranges_for_path(&path_key)
- .map(|range| range.context)
- .collect();
drop(cursor);
snapshot.excerpts = new_excerpts;
if changed_trailing_excerpt {
@@ -513,61 +478,56 @@ impl MultiBuffer {
cx.emit(Event::BufferUpdated {
buffer,
path_key: path_key.clone(),
- ranges: new_ranges,
+ ranges: to_insert.map(|range| range.context.clone()).collect(),
});
cx.notify();
- added_new_excerpt
+ (added_new_excerpt, path_key_index)
}
pub fn remove_excerpts_for_path(&mut self, path: PathKey, cx: &mut Context<Self>) {
- let mut patch = Patch::empty();
+ assert_eq!(self.history.transaction_depth(), 0);
+ self.sync_mut(cx);
let mut snapshot = self.snapshot.get_mut();
let mut cursor = snapshot
.excerpts
.cursor::<Dimensions<PathKey, ExcerptOffset>>(());
let mut new_excerpts = SumTree::new(());
-
- if let Some(old_path_key) = old_path_key
- && old_path_key < path_key
+ new_excerpts.append(cursor.slice(&path, Bias::Left), ());
+ let edit_start = cursor.position.1;
+ let mut buffer_id = None;
+ if let Some(excerpt) = cursor.item()
+ && excerpt.path_key == path
{
- new_excerpts.append(cursor.slice(&old_path_key, Bias::Left), ());
- let before = cursor.position.1;
- cursor.seek_forward(&old_path_key, Bias::Right);
- let after = cursor.position.1;
- patch.push(Edit {
- old: before..after,
- new: new_excerpts.summary().len()..new_excerpts.summary().len(),
- });
+ buffer_id = Some(excerpt.buffer.remote_id());
}
-
+ cursor.seek(&path, Bias::Right);
+ let edit_end = cursor.position.1;
let suffix = cursor.suffix();
let changed_trailing_excerpt = suffix.is_empty();
new_excerpts.append(suffix, ());
- for buffer_id in removed_excerpts_for_buffers {
- match self.buffers.get(&buffer_id) {
- Some(buffer_state) => {
- snapshot
- .buffer_locators
- .insert(buffer_id, buffer_state.excerpts.iter().cloned().collect());
- }
- None => {
- snapshot.buffer_locators.remove(&buffer_id);
- }
- }
+ let edit = Edit {
+ old: edit_start..edit_end,
+ new: edit_start..edit_start,
+ };
+
+ if let Some(buffer_id) = buffer_id {
+ snapshot.path_keys_by_buffer.remove(&buffer_id);
+ self.buffers.remove(&buffer_id);
+ cx.emit(Event::BuffersRemoved {
+ removed_buffer_ids: vec![buffer_id],
+ })
}
+ drop(cursor);
snapshot.excerpts = new_excerpts;
if changed_trailing_excerpt {
snapshot.trailing_excerpt_update_count += 1;
}
- let edits = Self::sync_diff_transforms(
- &mut snapshot,
- patch.into_inner(),
- DiffChangeKind::BufferEdited,
- );
+ let edits =
+ Self::sync_diff_transforms(&mut snapshot, vec![edit], DiffChangeKind::BufferEdited);
if !edits.is_empty() {
self.subscriptions.publish(edits);
}
@@ -575,12 +535,6 @@ impl MultiBuffer {
cx.emit(Event::Edited {
edited_buffer: None,
});
- // todo!() is this right? different event?
- cx.emit(Event::BufferUpdated {
- buffer,
- path_key: path.clone(),
- ranges: Vec::new(),
- });
cx.notify();
}
}
@@ -2,15 +2,15 @@ use gpui::{App, Context, Entity};
use language::{self, Buffer, TransactionId};
use std::{
collections::HashMap,
- ops::{AddAssign, Range, Sub},
+ ops::Range,
time::{Duration, Instant},
};
use sum_tree::Bias;
use text::BufferId;
-use crate::{BufferState, MultiBufferDimension};
+use crate::{Anchor, BufferState, MultiBufferOffset};
-use super::{Event, ExcerptSummary, MultiBuffer};
+use super::{Event, MultiBuffer};
#[derive(Clone)]
pub(super) struct History {
@@ -314,71 +314,49 @@ impl MultiBuffer {
}
}
- pub fn edited_ranges_for_transaction<D>(
+ pub fn edited_ranges_for_transaction(
&self,
transaction_id: TransactionId,
cx: &App,
- ) -> Vec<Range<D>>
- where
- D: MultiBufferDimension
- + Ord
- + Sub<D, Output = D::TextDimension>
- + AddAssign<D::TextDimension>,
- D::TextDimension: PartialOrd + Sub<D::TextDimension, Output = D::TextDimension>,
- {
+ ) -> Vec<Range<MultiBufferOffset>> {
let Some(transaction) = self.history.transaction(transaction_id) else {
return Vec::new();
};
- let mut ranges = Vec::new();
let snapshot = self.read(cx);
- let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>(());
+ let mut buffer_anchors = Vec::new();
for (buffer_id, buffer_transaction) in &transaction.buffer_transactions {
- let Some(buffer_state) = self.buffers.get(buffer_id) else {
+ let Some(buffer) = self.buffer(*buffer_id) else {
+ continue;
+ };
+ let Some(excerpt) = snapshot.first_excerpt_for_buffer(*buffer_id) else {
continue;
};
- let buffer = buffer_state.buffer.read(cx);
- for range in
- buffer.edited_ranges_for_transaction_id::<D::TextDimension>(*buffer_transaction)
+ for range in buffer
+ .read(cx)
+ .edited_ranges_for_transaction_id::<usize>(*buffer_transaction)
{
- for excerpt_id in &buffer_state.excerpts {
- cursor.seek(excerpt_id, Bias::Left);
- if let Some(excerpt) = cursor.item()
- && excerpt.locator == *excerpt_id
- {
- let excerpt_buffer_start = excerpt
- .range
- .context
- .start
- .summary::<D::TextDimension>(buffer);
- let excerpt_buffer_end = excerpt
- .range
- .context
- .end
- .summary::<D::TextDimension>(buffer);
- let excerpt_range = excerpt_buffer_start..excerpt_buffer_end;
- if excerpt_range.contains(&range.start)
- && excerpt_range.contains(&range.end)
- {
- let excerpt_start = D::from_summary(&cursor.start().text);
-
- let mut start = excerpt_start;
- start += range.start - excerpt_buffer_start;
- let mut end = excerpt_start;
- end += range.end - excerpt_buffer_start;
-
- ranges.push(start..end);
- break;
- }
- }
- }
+ buffer_anchors.push(Anchor::in_buffer(
+ excerpt.path_key_index,
+ excerpt.buffer.anchor_at(range.start, Bias::Left),
+ ));
+ buffer_anchors.push(Anchor::in_buffer(
+ excerpt.path_key_index,
+ excerpt.buffer.anchor_at(range.end, Bias::Right),
+ ));
}
}
+ buffer_anchors.sort_unstable_by(|a, b| a.cmp(b, &snapshot));
- ranges.sort_by_key(|range| range.start);
- ranges
+ snapshot
+ .summaries_for_anchors(buffer_anchors.iter())
+ .as_chunks::<2>()
+ .0
+ .iter()
+ .map(|&[s, e]| s..e)
+ .collect::<Vec<_>>()
}
pub fn merge_transactions(
@@ -3332,7 +3332,7 @@ impl OutlinePanel {
.flat_map(|excerpt| excerpt.iter_outlines())
.flat_map(|outline| {
let range = multi_buffer_snapshot
- .anchor_range_in_excerpt(excerpt_id, outline.range.clone())?;
+ .anchor_range_in_buffer(excerpt_id, outline.range.clone())?;
Some((
range.start.to_display_point(&editor_snapshot)
..range.end.to_display_point(&editor_snapshot),