diff --git a/crates/agent_ui/src/connection_view/thread_view.rs b/crates/agent_ui/src/connection_view/thread_view.rs index 499b11e5c08bd9b2c811e4cf5119bf7f71663c4b..76bcadebbdbe0bf907757c40c05229d5bc21e813 100644 --- a/crates/agent_ui/src/connection_view/thread_view.rs +++ b/crates/agent_ui/src/connection_view/thread_view.rs @@ -5839,7 +5839,7 @@ impl ThreadView { && let Some(agent_buffer) = agent_location.buffer.upgrade() && agent_buffer.read(cx).remote_id() == buffer_id { - let anchor = editor::Anchor::in_buffer(excerpt_id, agent_location.position); + let anchor = editor::Anchor::text(excerpt_id, agent_location.position); editor.change_selections(Default::default(), window, cx, |selections| { selections.select_anchor_ranges([anchor..anchor]); }) diff --git a/crates/debugger_ui/src/debugger_ui.rs b/crates/debugger_ui/src/debugger_ui.rs index 124967650b31cd88e72b2867838fb3a4ecbcf920..f5947a4393b2eeb8ca6ad3f844962500aa4ecf2d 100644 --- a/crates/debugger_ui/src/debugger_ui.rs +++ b/crates/debugger_ui/src/debugger_ui.rs @@ -299,7 +299,7 @@ pub fn init(cx: &mut App) { return; } maybe!({ - let (buffer, position, _) = editor + let (buffer, position) = editor .update(cx, |editor, cx| { let cursor_point: language::Point = editor .selections diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index db7eb53b39088c6026d3d36bef636f748c80d587..b16f6b19eb6bb21f3f1a32beb44212bfe8e204d5 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -11,7 +11,7 @@ use collections::{Bound, HashMap, HashSet}; use gpui::{AnyElement, App, EntityId, Pixels, Window}; use language::{Patch, Point}; use multi_buffer::{ - Anchor, ExcerptId, ExcerptInfo, MultiBuffer, MultiBufferOffset, MultiBufferPoint, + Anchor, ExcerptBoundaryInfo, ExcerptId, MultiBuffer, MultiBufferOffset, MultiBufferPoint, MultiBufferRow, MultiBufferSnapshot, RowInfo, ToOffset, ToPoint as _, }; use parking_lot::Mutex; @@ -339,15 +339,15 @@ struct Transform { pub enum Block { Custom(Arc), FoldedBuffer { - first_excerpt: ExcerptInfo, + first_excerpt: ExcerptBoundaryInfo, height: u32, }, ExcerptBoundary { - excerpt: ExcerptInfo, + excerpt: ExcerptBoundaryInfo, height: u32, }, BufferHeader { - excerpt: ExcerptInfo, + excerpt: ExcerptBoundaryInfo, height: u32, }, Spacer { @@ -2563,7 +2563,7 @@ impl BlockChunks<'_> { } pub struct StickyHeaderExcerpt<'a> { - pub excerpt: &'a ExcerptInfo, + pub excerpt: &'a ExcerptBoundaryInfo, } impl<'a> Iterator for BlockChunks<'a> { diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index bc62d2a87a4fcd37f88fd013f779dc047a43f6e3..2fe2c838988a9b3d1bce49fad8f92345f20b2bb9 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -150,7 +150,8 @@ use markdown::Markdown; use mouse_context_menu::MouseContextMenu; use movement::TextLayoutDetails; use multi_buffer::{ - ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow, + ExcerptBoundaryInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, + MultiBufferRow, }; use parking_lot::Mutex; use persistence::DB; @@ -892,7 +893,7 @@ pub trait Addon: 'static { fn render_buffer_header_controls( &self, - _: &ExcerptInfo, + _: &ExcerptBoundaryInfo, _: &Window, _: &App, ) -> Option { @@ -5359,7 +5360,7 @@ impl Editor { let row = cursor.row; let point = Point::new(row, 0); - let Some((buffer_handle, buffer_point, _)) = + let Some((buffer_handle, buffer_point)) = self.buffer.read(cx).point_to_buffer_point(point, cx) else { continue; @@ -8818,7 +8819,7 @@ impl Editor { cx, ); for (breakpoint, state) in breakpoints { - let multi_buffer_anchor = Anchor::in_buffer(excerpt_id, breakpoint.position); + let multi_buffer_anchor = Anchor::text(excerpt_id, breakpoint.position); let position = multi_buffer_anchor .to_point(&multi_buffer_snapshot) .to_display_point(&snapshot); @@ -24010,7 +24011,7 @@ impl Editor { .for_each(|hint| { let inlay = Inlay::debugger( post_inc(&mut editor.next_inlay_id), - Anchor::in_buffer(excerpt_id, hint.position), + Anchor::text(excerpt_id, hint.position), hint.text(), ); if !inlay.text().chars().contains(&'\n') { @@ -24113,7 +24114,7 @@ impl Editor { excerpts: excerpts.clone(), }); } - multi_buffer::Event::ExcerptsRemoved { + multi_buffer::Event::BuffersRemoved { ids, removed_buffer_ids, } => { @@ -24137,7 +24138,7 @@ impl Editor { removed_buffer_ids: removed_buffer_ids.clone(), }); } - multi_buffer::Event::ExcerptsEdited { + multi_buffer::Event::BuffersEdited { excerpt_ids, buffer_ids, } => { @@ -24496,7 +24497,7 @@ impl Editor { line_offset_from_top, }) => { let point = MultiBufferPoint::new(row.0, 0); - if let Some((buffer, buffer_point, _)) = + if let Some((buffer, buffer_point)) = self.buffer.read(cx).point_to_buffer_point(point, cx) { let buffer_offset = buffer.read(cx).point_to_offset(buffer_point); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 59086fbedf397e05fbec50481d04771f878eff7c..ade457ae058c5a73c5e599026a9fd6cedf0375e2 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -55,7 +55,7 @@ use itertools::Itertools; use language::{IndentGuideSettings, language_settings::ShowWhitespaceSetting}; use markdown::Markdown; use multi_buffer::{ - Anchor, ExcerptId, ExcerptInfo, ExpandExcerptDirection, ExpandInfo, MultiBufferPoint, + Anchor, ExcerptBoundaryInfo, ExcerptId, ExpandExcerptDirection, ExpandInfo, MultiBufferPoint, MultiBufferRow, RowInfo, }; @@ -4183,7 +4183,7 @@ impl EditorElement { fn render_buffer_header( &self, - for_excerpt: &ExcerptInfo, + for_excerpt: &ExcerptBoundaryInfo, is_folded: bool, is_selected: bool, is_sticky: bool, @@ -8128,7 +8128,7 @@ pub(crate) fn header_jump_data( editor_snapshot: &EditorSnapshot, block_row_start: DisplayRow, height: u32, - first_excerpt: &ExcerptInfo, + first_excerpt: &ExcerptBoundaryInfo, latest_selection_anchors: &HashMap, ) -> JumpData { let jump_target = if let Some(anchor) = latest_selection_anchors.get(&first_excerpt.buffer_id) @@ -8195,7 +8195,7 @@ fn header_jump_data_inner( pub(crate) fn render_buffer_header( editor: &Entity, - for_excerpt: &ExcerptInfo, + for_excerpt: &ExcerptBoundaryInfo, is_folded: bool, is_selected: bool, is_sticky: bool, diff --git a/crates/editor/src/git/blame.rs b/crates/editor/src/git/blame.rs index c705eb3996ace228f915303f049853bb2364aa2e..9e9c3fbaaf324b7fa249502b4c2fbb35d8349f54 100644 --- a/crates/editor/src/git/blame.rs +++ b/crates/editor/src/git/blame.rs @@ -205,7 +205,7 @@ impl GitBlame { } } multi_buffer::Event::ExcerptsAdded { .. } - | multi_buffer::Event::ExcerptsEdited { .. } => git_blame.regenerate_on_edit(cx), + | multi_buffer::Event::BuffersEdited { .. } => git_blame.regenerate_on_edit(cx), _ => {} }, ); diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 685387342caf8e705a3648cb07acaa1867db55d8..a24a3485394de39d45b34304d0312c73ef5bb172 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -575,7 +575,7 @@ fn deserialize_selection(selection: proto::Selection) -> Option Option { let excerpt_id = ExcerptId::from_proto(anchor.excerpt_id); - Some(Anchor::in_buffer( + Some(Anchor::text( excerpt_id, language::proto::deserialize_anchor(anchor.anchor?)?, )) @@ -1459,8 +1459,7 @@ impl ProjectItem for Editor { }); } let (top_row, offset) = restoration_data.scroll_position; - let anchor = - Anchor::in_buffer(excerpt_id, snapshot.anchor_before(Point::new(top_row, 0))); + let anchor = Anchor::text(excerpt_id, snapshot.anchor_before(Point::new(top_row, 0))); editor.set_scroll_anchor(ScrollAnchor { anchor, offset }, window, cx); } diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index b042d66ce9ac5c45af2e5701da2d83db3c3ab907..9feebb349cff308ffc50a37386cbeedcfecc29b0 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -46,7 +46,7 @@ use language_model::{ ConfiguredModel, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role, }; use menu; -use multi_buffer::ExcerptInfo; +use multi_buffer::ExcerptBoundaryInfo; use notifications::status_toast::{StatusToast, ToastIcon}; use panel::{PanelHeader, panel_button, panel_filled_button, panel_icon_button}; use project::{ @@ -5754,7 +5754,7 @@ impl editor::Addon for GitPanelAddon { fn render_buffer_header_controls( &self, - excerpt_info: &ExcerptInfo, + excerpt_info: &ExcerptBoundaryInfo, window: &Window, cx: &App, ) -> Option { diff --git a/crates/multi_buffer/src/anchor.rs b/crates/multi_buffer/src/anchor.rs index 8ae154148379f7cb7d806196a03b354e2f6130c5..f457fa2e1e864ca3d0dc1623db9f08b4e537d31c 100644 --- a/crates/multi_buffer/src/anchor.rs +++ b/crates/multi_buffer/src/anchor.rs @@ -1,195 +1,266 @@ -use crate::{MultiBufferDimension, MultiBufferOffset, MultiBufferOffsetUtf16}; +use crate::{ExcerptSummary, MultiBufferDimension, MultiBufferOffset, MultiBufferOffsetUtf16}; -use super::{ExcerptId, MultiBufferSnapshot, ToOffset, ToPoint}; +use super::{MultiBufferSnapshot, ToOffset, ToPoint}; use language::Point; use std::{ cmp::Ordering, ops::{Add, AddAssign, Range, Sub}, }; use sum_tree::Bias; +use text::BufferId; +use util::debug_panic; /// A stable reference to a position within a [`MultiBuffer`](super::MultiBuffer). /// /// Unlike simple offsets, anchors remain valid as the text is edited, automatically /// adjusting to reflect insertions and deletions around them. #[derive(Clone, Copy, Eq, PartialEq, Hash)] -pub struct Anchor { - /// Identifies which excerpt within the multi-buffer this anchor belongs to. - /// A multi-buffer can contain multiple excerpts from different buffers. - pub excerpt_id: ExcerptId, - /// The position within the excerpt's underlying buffer. This is a stable - /// reference that remains valid as the buffer text is edited. - pub text_anchor: text::Anchor, - /// When present, indicates this anchor points into deleted text within an - /// expanded diff hunk. The anchor references a position in the diff base - /// (original) text rather than the current buffer text. This is used when - /// displaying inline diffs where deleted lines are shown. - pub diff_base_anchor: Option, +pub enum Anchor { + Min, + Max, + Text { + /// The position within the excerpt's underlying buffer. This is a stable + /// reference that remains valid as the buffer text is edited. + timestamp: clock::Lamport, + + /// The byte offset into the text inserted in the operation + /// at `timestamp`. + offset: u32, + /// Whether this anchor stays attached to the character *before* or *after* + /// the offset. + bias: Bias, + buffer_id: BufferId, + /// When present, indicates this anchor points into deleted text within an + /// expanded diff hunk. The anchor references a position in the diff base + /// (original) text rather than the current buffer text. This is used when + /// displaying inline diffs where deleted lines are shown. + diff_base_anchor: Option, + }, } impl std::fmt::Debug for Anchor { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if self.is_min() { - return write!(f, "Anchor::min({:?})", self.text_anchor.buffer_id); + return write!(f, "Anchor::Min"); } if self.is_max() { - return write!(f, "Anchor::max({:?})", self.text_anchor.buffer_id); + return write!(f, "Anchor::Max"); } f.debug_struct("Anchor") - .field("excerpt_id", &self.excerpt_id) - .field("text_anchor", &self.text_anchor) - .field("diff_base_anchor", &self.diff_base_anchor) + .field("text_anchor", &self.text_anchor().unwrap()) + .field("diff_base_anchor", &self.diff_base_anchor()) .finish() } } impl Anchor { - pub fn with_diff_base_anchor(self, diff_base_anchor: text::Anchor) -> Self { - Self { - diff_base_anchor: Some(diff_base_anchor), - ..self + pub fn text_anchor(&self) -> Option { + match self { + Self::Min | Self::Max => None, + Self::Text { + timestamp, + offset, + bias, + buffer_id, + .. + } => Some(text::Anchor::new( + *timestamp, + *offset, + *bias, + Some(*buffer_id), + )), } } - pub fn in_buffer(excerpt_id: ExcerptId, text_anchor: text::Anchor) -> Self { - Self { - excerpt_id, - text_anchor, - diff_base_anchor: None, + pub fn diff_base_anchor(&self) -> Option { + match self { + Self::Min | Self::Max => None, + Self::Text { + diff_base_anchor, .. + } => *diff_base_anchor, } } - pub fn range_in_buffer(excerpt_id: ExcerptId, range: Range) -> Range { - Self::in_buffer(excerpt_id, range.start)..Self::in_buffer(excerpt_id, range.end) + pub fn with_diff_base_anchor(mut self, diff_base_anchor: text::Anchor) -> Self { + match &mut self { + Self::Min | Self::Max => { + debug_panic!("with_diff_base_anchor called on min or max anchor"); + } + Self::Text { + diff_base_anchor: self_diff_base_anchor, + .. + } => { + *self_diff_base_anchor = Some(diff_base_anchor); + } + }; + self } - pub fn min() -> Self { - Self { - excerpt_id: ExcerptId::min(), - text_anchor: text::Anchor::MIN, + pub fn text(text_anchor: text::Anchor) -> Self { + let Some(buffer_id) = text_anchor.buffer_id else { + panic!("text_anchor must have a buffer_id"); + }; + Self::Text { diff_base_anchor: None, + timestamp: text_anchor.timestamp(), + buffer_id, + offset: text_anchor.offset, + bias: text_anchor.bias, } } + pub fn range_in_buffer(range: Range) -> Range { + Self::text(range.start)..Self::text(range.end) + } + + pub fn min() -> Self { + Self::Min + } + pub fn max() -> Self { - Self { - excerpt_id: ExcerptId::max(), - text_anchor: text::Anchor::MAX, - diff_base_anchor: None, - } + Self::Max } pub fn is_min(&self) -> bool { - self.excerpt_id == ExcerptId::min() - && self.text_anchor.is_min() - && self.diff_base_anchor.is_none() + matches!(self, Self::Min) } pub fn is_max(&self) -> bool { - self.excerpt_id == ExcerptId::max() - && self.text_anchor.is_max() - && self.diff_base_anchor.is_none() + matches!(self, Self::Max) } pub fn cmp(&self, other: &Anchor, snapshot: &MultiBufferSnapshot) -> Ordering { - if self == other { - return Ordering::Equal; - } + let (self_text_anchor, other_text_anchor) = match (self, other) { + (Anchor::Min, Anchor::Min) => return Ordering::Equal, + (Anchor::Max, Anchor::Max) => return Ordering::Equal, + (Anchor::Min, _) => return Ordering::Less, + (Anchor::Max, _) => return Ordering::Greater, + (_, Anchor::Max) => return Ordering::Less, + (_, Anchor::Min) => return Ordering::Greater, + (Anchor::Text { .. }, Anchor::Text { .. }) => { + (self.text_anchor().unwrap(), other.text_anchor().unwrap()) + } + }; + let self_buffer_id = self_text_anchor.buffer_id.unwrap(); + let other_buffer_id = other_text_anchor.buffer_id.unwrap(); - let self_excerpt_id = snapshot.latest_excerpt_id(self.excerpt_id); - let other_excerpt_id = snapshot.latest_excerpt_id(other.excerpt_id); + let Some(self_path_key) = snapshot.path_keys.get(&self_buffer_id) else { + panic!("path key was never set for buffer_id") + }; + let Some(other_path_key) = snapshot.path_keys.get(&other_buffer_id) else { + panic!("path key was never set for buffer_id") + }; - let excerpt_id_cmp = self_excerpt_id.cmp(&other_excerpt_id, snapshot); - if excerpt_id_cmp.is_ne() { - return excerpt_id_cmp; + if self_path_key.cmp(other_path_key) != Ordering::Equal { + return self_path_key.cmp(other_path_key); } - if self_excerpt_id == ExcerptId::max() - && self.text_anchor.is_max() - && self.text_anchor.is_max() - && self.diff_base_anchor.is_none() - && other.diff_base_anchor.is_none() - { + + // in the case that you removed the buffer contianing self, + // and added the buffer containing other with the same path key + if self_buffer_id != other_buffer_id { + return self_buffer_id.cmp(&other_buffer_id); + } + + let Some(buffer) = snapshot.buffer_for_path(&self_path_key) else { return Ordering::Equal; + }; + let text_cmp = self_text_anchor.cmp(&other_text_anchor, buffer); + if text_cmp != Ordering::Equal { + return text_cmp; } - if let Some(excerpt) = snapshot.excerpt(self_excerpt_id) { - let text_cmp = self.text_anchor.cmp(&other.text_anchor, &excerpt.buffer); - if text_cmp.is_ne() { - return text_cmp; - } - if (self.diff_base_anchor.is_some() || other.diff_base_anchor.is_some()) - && let Some(base_text) = snapshot - .diffs - .get(&excerpt.buffer_id) - .map(|diff| diff.base_text()) - { - let self_anchor = self.diff_base_anchor.filter(|a| a.is_valid(base_text)); - let other_anchor = other.diff_base_anchor.filter(|a| a.is_valid(base_text)); - return match (self_anchor, other_anchor) { - (Some(a), Some(b)) => a.cmp(&b, base_text), - (Some(_), None) => match other.text_anchor.bias { - Bias::Left => Ordering::Greater, - Bias::Right => Ordering::Less, - }, - (None, Some(_)) => match self.text_anchor.bias { - Bias::Left => Ordering::Less, - Bias::Right => Ordering::Greater, - }, - (None, None) => Ordering::Equal, - }; - } + + if (self.diff_base_anchor().is_some() || other.diff_base_anchor().is_some()) + && let Some(base_text) = snapshot + .diffs + .get(&self_buffer_id) + .map(|diff| diff.base_text()) + { + let self_anchor = self.diff_base_anchor().filter(|a| a.is_valid(base_text)); + let other_anchor = other.diff_base_anchor().filter(|a| a.is_valid(base_text)); + return match (self_anchor, other_anchor) { + (Some(a), Some(b)) => a.cmp(&b, base_text), + (Some(_), None) => match other_text_anchor.bias { + Bias::Left => Ordering::Greater, + Bias::Right => Ordering::Less, + }, + (None, Some(_)) => match self_text_anchor.bias { + Bias::Left => Ordering::Less, + Bias::Right => Ordering::Greater, + }, + (None, None) => Ordering::Equal, + }; } + Ordering::Equal } pub fn bias(&self) -> Bias { - self.text_anchor.bias + match self { + Anchor::Min => Bias::Left, + Anchor::Max => Bias::Right, + Anchor::Text { bias, .. } => *bias, + } } pub fn bias_left(&self, snapshot: &MultiBufferSnapshot) -> Anchor { - if self.text_anchor.bias != Bias::Left - && let Some(excerpt) = snapshot.excerpt(self.excerpt_id) - { - return Self { - excerpt_id: excerpt.id, - text_anchor: self.text_anchor.bias_left(&excerpt.buffer), - diff_base_anchor: self.diff_base_anchor.map(|a| { - if let Some(base_text) = snapshot - .diffs - .get(&excerpt.buffer_id) - .map(|diff| diff.base_text()) - && a.is_valid(&base_text) + match self { + Anchor::Min => *self, + Anchor::Max => snapshot.anchor_before(snapshot.max_point()), + Anchor::Text { + bias, buffer_id, .. + } => { + if *bias == Bias::Left { + return *self; + } + let Some(buffer) = snapshot.buffer_for_id(*buffer_id) else { + return *self; + }; + let text_anchor = self.text_anchor().unwrap().bias_left(&buffer); + let ret = Self::text(text_anchor); + if let Some(diff_base_anchor) = self.diff_base_anchor() { + if let Some(diff) = snapshot.diffs.get(&buffer_id) + && diff_base_anchor.is_valid(&diff.base_text()) { - return a.bias_left(base_text); + ret.with_diff_base_anchor(diff_base_anchor.bias_left(diff.base_text())) + } else { + ret.with_diff_base_anchor(diff_base_anchor) } - a - }), - }; + } else { + ret + } + } } - *self } pub fn bias_right(&self, snapshot: &MultiBufferSnapshot) -> Anchor { - if self.text_anchor.bias != Bias::Right - && let Some(excerpt) = snapshot.excerpt(self.excerpt_id) - { - return Self { - excerpt_id: excerpt.id, - text_anchor: self.text_anchor.bias_right(&excerpt.buffer), - diff_base_anchor: self.diff_base_anchor.map(|a| { - if let Some(base_text) = snapshot - .diffs - .get(&excerpt.buffer_id) - .map(|diff| diff.base_text()) - && a.is_valid(&base_text) + match self { + Anchor::Min => *self, + Anchor::Max => snapshot.anchor_after(Point::zero()), + Anchor::Text { + bias, buffer_id, .. + } => { + if *bias == Bias::Right { + return *self; + } + let Some(buffer) = snapshot.buffer_for_id(*buffer_id) else { + return *self; + }; + let text_anchor = self.text_anchor().unwrap().bias_right(&buffer); + let ret = Self::text(text_anchor); + if let Some(diff_base_anchor) = self.diff_base_anchor() { + if let Some(diff) = snapshot.diffs.get(&buffer_id) + && diff_base_anchor.is_valid(&diff.base_text()) { - return a.bias_right(base_text); + ret.with_diff_base_anchor(diff_base_anchor.bias_right(diff.base_text())) + } else { + ret.with_diff_base_anchor(diff_base_anchor) } - a - }), - }; + } else { + ret + } + } } - *self } pub fn summary(&self, snapshot: &MultiBufferSnapshot) -> D @@ -206,16 +277,35 @@ impl Anchor { } pub fn is_valid(&self, snapshot: &MultiBufferSnapshot) -> bool { - if self.is_min() || self.is_max() { - true - } else if let Some(excerpt) = snapshot.excerpt(self.excerpt_id) { - (self.text_anchor == excerpt.range.context.start - || self.text_anchor == excerpt.range.context.end - || self.text_anchor.is_valid(&excerpt.buffer)) - && excerpt.contains(self) - } else { - false - } + let Some(text_anchor) = self.text_anchor() else { + return true; + }; + let Some(buffer_id) = text_anchor.buffer_id else { + debug_panic!("missing buffer_id for anchor"); + return false; + }; + + let Some(target) = snapshot.anchor_seek_target(*self) else { + return false; + }; + let mut cursor = snapshot.excerpts.cursor::(()); + cursor.seek(&target, Bias::Left); + let Some(excerpt) = cursor.item() else { + return false; + }; + excerpt.buffer.remote_id() == buffer_id + && excerpt + .range + .context + .start + .cmp(&text_anchor, &excerpt.buffer) + .is_le() + && excerpt + .range + .context + .end + .cmp(&text_anchor, &excerpt.buffer) + .is_ge() } } diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index a593280d245fd01d623051953e48128c9935df45..461c64a019932a4febc3cc6b2a3e8bd5903f950b 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -15,7 +15,7 @@ use buffer_diff::{ }; use clock::ReplicaId; use collections::{BTreeMap, Bound, HashMap, HashSet}; -use gpui::{App, Context, Entity, EntityId, EventEmitter}; +use gpui::{App, Context, Entity, EventEmitter}; use itertools::Itertools; use language::{ AutoindentMode, BracketMatch, Buffer, BufferChunks, BufferRow, BufferSnapshot, Capability, @@ -36,7 +36,8 @@ use std::{ any::type_name, borrow::Cow, cell::{Cell, OnceCell, Ref, RefCell}, - cmp, fmt, + cmp::{self, Ordering}, + fmt, future::Future, io, iter::{self, FromIterator}, @@ -54,7 +55,6 @@ use text::{ subscription::{Subscription, Topic}, }; use theme::SyntaxTheme; -use util::post_inc; use ztracing::instrument; pub use self::path_key::{PathExcerptInsertResult, PathKey}; @@ -65,9 +65,6 @@ pub fn excerpt_context_lines(cx: &App) -> u32 { EXCERPT_CONTEXT_LINES.get().map(|f| f(cx)).unwrap_or(2) } -#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct ExcerptId(u32); - /// One or more [`Buffers`](Buffer) being edited in a single view. /// /// See @@ -77,10 +74,6 @@ pub struct MultiBuffer { snapshot: RefCell, /// Contains the state of the buffers being edited buffers: BTreeMap, - /// Mapping from path keys to their excerpts. - excerpts_by_path: BTreeMap>, - /// Mapping from excerpt IDs to their path key. - paths_by_excerpt: HashMap, /// Mapping from buffer IDs to their diff states diffs: HashMap, subscriptions: Topic, @@ -96,22 +89,23 @@ pub struct MultiBuffer { buffer_changed_since_sync: Rc>, } +pub struct ExcerptInfo { + path_key: PathKey, + buffer_id: BufferId, + range: ExcerptRange, +} + #[derive(Clone, Debug, PartialEq, Eq)] pub enum Event { - ExcerptsAdded { + BufferUpdated { buffer: Entity, - predecessor: ExcerptId, - excerpts: Vec<(ExcerptId, ExcerptRange)>, + path_key: PathKey, + ranges: Vec>, }, - ExcerptsRemoved { - ids: Vec, + BuffersRemoved { removed_buffer_ids: Vec, }, - ExcerptsExpanded { - ids: Vec, - }, - ExcerptsEdited { - excerpt_ids: Vec, + BuffersEdited { buffer_ids: Vec, }, DiffHunksToggled, @@ -140,8 +134,6 @@ pub struct MultiBufferDiffHunk { pub buffer_id: BufferId, /// The range of the underlying buffer that this hunk corresponds to. pub buffer_range: Range, - /// The excerpt that contains the diff hunk. - pub excerpt_id: ExcerptId, /// The range within the buffer's diff base that this hunk corresponds to. pub diff_base_byte_range: Range, /// The status of this hunk (added/modified/deleted and secondary status). @@ -162,8 +154,8 @@ impl MultiBufferDiffHunk { } pub fn multi_buffer_range(&self) -> Range { - let start = Anchor::in_buffer(self.excerpt_id, self.buffer_range.start); - let end = Anchor::in_buffer(self.excerpt_id, self.buffer_range.end); + let start = Anchor::text(self.buffer_range.start); + let end = Anchor::text(self.buffer_range.end); start..end } } @@ -513,10 +505,9 @@ pub trait ToPoint: 'static + fmt::Debug { struct BufferState { buffer: Entity, + path_key: PathKey, last_version: RefCell, last_non_text_state_update_count: Cell, - // Note, any changes to this field value require updating snapshot.buffer_locators as well - excerpts: Vec, _subscriptions: [gpui::Subscription; 2], } @@ -620,11 +611,9 @@ impl DiffState { #[derive(Clone, Default)] pub struct MultiBufferSnapshot { excerpts: SumTree, - buffer_locators: TreeMap>, + path_keys: TreeMap, diffs: TreeMap, diff_transforms: SumTree, - excerpt_ids: SumTree, - replaced_excerpts: Arc>, non_text_state_update_count: usize, edit_count: usize, is_dirty: bool, @@ -639,24 +628,12 @@ pub struct MultiBufferSnapshot { show_headers: bool, } -// follower: None -// - BufferContent(Some) -// - BufferContent(None) -// - DeletedHunk -// -// follower: Some -// - BufferContent(Some) -// - BufferContent(None) - #[derive(Debug, Clone)] enum DiffTransform { - // RealText BufferContent { summary: MBTextSummary, - // modified_hunk_info inserted_hunk_info: Option, }, - // ExpandedHunkText DeletedHunk { summary: TextSummary, buffer_id: BufferId, @@ -668,7 +645,7 @@ enum DiffTransform { #[derive(Clone, Copy, Debug)] struct DiffTransformHunkInfo { - excerpt_id: ExcerptId, + buffer_id: BufferId, hunk_start_anchor: text::Anchor, hunk_secondary_status: DiffHunkSecondaryStatus, is_logically_deleted: bool, @@ -678,30 +655,28 @@ impl Eq for DiffTransformHunkInfo {} impl PartialEq for DiffTransformHunkInfo { fn eq(&self, other: &DiffTransformHunkInfo) -> bool { - self.excerpt_id == other.excerpt_id && self.hunk_start_anchor == other.hunk_start_anchor + self.buffer_id == other.buffer_id && self.hunk_start_anchor == other.hunk_start_anchor } } impl std::hash::Hash for DiffTransformHunkInfo { fn hash(&self, state: &mut H) { - self.excerpt_id.hash(state); + self.buffer_id.hash(state); self.hunk_start_anchor.hash(state); } } #[derive(Clone)] -pub struct ExcerptInfo { - pub id: ExcerptId, +pub struct ExcerptBoundaryInfo { pub buffer: Arc, pub buffer_id: BufferId, pub range: ExcerptRange, pub end_row: MultiBufferRow, } -impl std::fmt::Debug for ExcerptInfo { +impl std::fmt::Debug for ExcerptBoundaryInfo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct(type_name::()) - .field("id", &self.id) .field("buffer_id", &self.buffer_id) .field("path", &self.buffer.file().map(|f| f.path())) .field("range", &self.range) @@ -712,8 +687,8 @@ impl std::fmt::Debug for ExcerptInfo { /// A boundary between `Excerpt`s in a [`MultiBuffer`] #[derive(Debug)] pub struct ExcerptBoundary { - pub prev: Option, - pub next: ExcerptInfo, + pub prev: Option, + pub next: ExcerptBoundaryInfo, /// The row in the `MultiBuffer` where the boundary is located pub row: MultiBufferRow, } @@ -727,13 +702,13 @@ impl ExcerptBoundary { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct ExpandInfo { pub direction: ExpandExcerptDirection, - pub excerpt_id: ExcerptId, + pub excerpt_range: Range, } -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct RowInfo { pub buffer_id: Option, pub buffer_row: Option, @@ -745,22 +720,19 @@ pub struct RowInfo { /// A slice into a [`Buffer`] that is being edited in a [`MultiBuffer`]. #[derive(Clone)] -struct Excerpt { - /// The unique identifier for this excerpt - id: ExcerptId, +pub(crate) struct Excerpt { /// The location of the excerpt in the [`MultiBuffer`] - locator: Locator, - /// The buffer being excerpted - buffer_id: BufferId, + pub(crate) path_key: PathKey, /// A snapshot of the buffer being excerpted - buffer: Arc, + pub(crate) buffer: Arc, /// The range of the buffer to be shown in the excerpt - range: ExcerptRange, + pub(crate) range: ExcerptRange, + /// The last row in the excerpted slice of the buffer - max_buffer_row: BufferRow, + pub(crate) max_buffer_row: BufferRow, /// A summary of the text in the excerpt - text_summary: TextSummary, - has_trailing_newline: bool, + pub(crate) text_summary: TextSummary, + pub(crate) has_trailing_newline: bool, } /// A public view into an `Excerpt` in a [`MultiBuffer`]. @@ -779,12 +751,6 @@ pub struct MultiBufferExcerpt<'a> { buffer_offset: BufferOffset, } -#[derive(Clone, Debug)] -struct ExcerptIdMapping { - id: ExcerptId, - locator: Locator, -} - /// A range of text from a single [`Buffer`], to be shown as an `Excerpt`. /// These ranges are relative to the buffer itself #[derive(Clone, Debug, Eq, PartialEq, Hash)] @@ -805,16 +771,34 @@ impl ExcerptRange { } } -#[derive(Clone, Debug, Default)] +impl ExcerptRange { + pub fn contains(&self, t: &text::Anchor, snapshot: &BufferSnapshot) -> bool { + self.context.start.cmp(t, snapshot).is_le() && self.context.end.cmp(t, snapshot).is_ge() + } +} + +#[derive(Clone, Debug)] pub struct ExcerptSummary { - excerpt_id: ExcerptId, - /// The location of the last [`Excerpt`] being summarized - excerpt_locator: Locator, + path_key: PathKey, + /// End of the excerpt + max_anchor: text::Anchor, widest_line_number: u32, text: MBTextSummary, count: usize, } +impl ExcerptSummary { + pub fn min() -> Self { + ExcerptSummary { + path_key: PathKey::min(), + max_anchor: text::Anchor::MIN, + widest_line_number: 0, + text: MBTextSummary::default(), + count: 0, + } + } +} + #[derive(Debug, Clone)] pub struct DiffTransformSummary { input: MBTextSummary, @@ -1066,7 +1050,6 @@ struct MultiBufferRegion<'a, MBD, BD> { } struct ExcerptChunks<'a> { - excerpt_id: ExcerptId, content_chunks: BufferChunks<'a>, has_footer: bool, } @@ -1077,7 +1060,6 @@ struct BufferEdit { new_text: Arc, is_insertion: bool, original_indent_column: Option, - excerpt_id: ExcerptId, } #[derive(Clone, Copy, Debug, PartialEq)] @@ -1180,8 +1162,6 @@ impl MultiBuffer { singleton: false, capability, title: None, - excerpts_by_path: Default::default(), - paths_by_excerpt: Default::default(), buffer_changed_since_sync: Default::default(), history: History::default(), } @@ -1198,11 +1178,11 @@ impl MultiBuffer { *buffer_id, BufferState { buffer: buffer_state.buffer.clone(), + path_key: buffer_state.path_key.clone(), last_version: buffer_state.last_version.clone(), last_non_text_state_update_count: buffer_state .last_non_text_state_update_count .clone(), - excerpts: buffer_state.excerpts.clone(), _subscriptions: [ new_cx.observe(&buffer_state.buffer, |_, _, cx| cx.notify()), new_cx.subscribe(&buffer_state.buffer, Self::on_buffer_event), @@ -1217,8 +1197,6 @@ impl MultiBuffer { Self { snapshot: RefCell::new(self.snapshot.borrow().clone()), buffers, - excerpts_by_path: Default::default(), - paths_by_excerpt: Default::default(), diffs: diff_bases, subscriptions: Default::default(), singleton: self.singleton, @@ -1366,7 +1344,7 @@ impl MultiBuffer { _ => Default::default(), }; - let (buffer_edits, edited_excerpt_ids) = MultiBuffer::convert_edits_to_buffer_edits( + let buffer_edits = MultiBuffer::convert_edits_to_buffer_edits( edits, this.snapshot.get_mut(), &original_indent_columns, @@ -1387,14 +1365,12 @@ impl MultiBuffer { mut new_text, mut is_insertion, original_indent_column, - excerpt_id, }) = edits.next() { while let Some(BufferEdit { range: next_range, is_insertion: next_is_insertion, new_text: next_new_text, - excerpt_id: next_excerpt_id, .. }) = edits.peek() { @@ -1407,9 +1383,7 @@ impl MultiBuffer { if should_coalesce { range.end = cmp::max(next_range.end, range.end); is_insertion |= *next_is_insertion; - if excerpt_id == *next_excerpt_id { - new_text = format!("{new_text}{next_new_text}").into(); - } + new_text = format!("{new_text}{next_new_text}").into(); edits.next(); } else { break; @@ -1457,10 +1431,7 @@ impl MultiBuffer { }) } - cx.emit(Event::ExcerptsEdited { - excerpt_ids: edited_excerpt_ids, - buffer_ids, - }); + cx.emit(Event::BuffersEdited { buffer_ids }); } } @@ -1468,9 +1439,8 @@ impl MultiBuffer { edits: Vec<(Range, Arc)>, snapshot: &MultiBufferSnapshot, original_indent_columns: &[Option], - ) -> (HashMap>, Vec) { + ) -> HashMap> { let mut buffer_edits: HashMap> = Default::default(); - let mut edited_excerpt_ids = Vec::new(); let mut cursor = snapshot.cursor::(); for (ix, (range, new_text)) in edits.into_iter().enumerate() { let original_indent_column = original_indent_columns.get(ix).copied().flatten(); @@ -1515,11 +1485,10 @@ impl MultiBuffer { let buffer_end = (end_region.buffer_range.start + end_overshoot).min(end_region.buffer_range.end); - if start_region.excerpt.id == end_region.excerpt.id { + if start_region.excerpt == end_region.excerpt { if start_region.buffer.capability == Capability::ReadWrite && start_region.is_main_buffer { - edited_excerpt_ids.push(start_region.excerpt.id); buffer_edits .entry(start_region.buffer.remote_id()) .or_default() @@ -1528,7 +1497,6 @@ impl MultiBuffer { new_text, is_insertion: true, original_indent_column, - excerpt_id: start_region.excerpt.id, }); } } else { @@ -1537,7 +1505,6 @@ impl MultiBuffer { if start_region.buffer.capability == Capability::ReadWrite && start_region.is_main_buffer { - edited_excerpt_ids.push(start_region.excerpt.id); buffer_edits .entry(start_region.buffer.remote_id()) .or_default() @@ -1546,14 +1513,11 @@ impl MultiBuffer { new_text: new_text.clone(), is_insertion: true, original_indent_column, - excerpt_id: start_region.excerpt.id, }); } - let excerpt_id = end_region.excerpt.id; if end_region.buffer.capability == Capability::ReadWrite && end_region.is_main_buffer { - edited_excerpt_ids.push(excerpt_id); buffer_edits .entry(end_region.buffer.remote_id()) .or_default() @@ -1562,18 +1526,17 @@ impl MultiBuffer { new_text: new_text.clone(), is_insertion: false, original_indent_column, - excerpt_id, }); } + let end_region_excerpt = end_region.excerpt.clone(); cursor.seek(&range.start); cursor.next_excerpt(); while let Some(region) = cursor.region() { - if region.excerpt.id == excerpt_id { + if region.excerpt == &end_region_excerpt { break; } if region.buffer.capability == Capability::ReadWrite && region.is_main_buffer { - edited_excerpt_ids.push(region.excerpt.id); buffer_edits .entry(region.buffer.remote_id()) .or_default() @@ -1582,14 +1545,13 @@ impl MultiBuffer { new_text: new_text.clone(), is_insertion: false, original_indent_column, - excerpt_id: region.excerpt.id, }); } cursor.next_excerpt(); } } } - (buffer_edits, edited_excerpt_ids) + buffer_edits } pub fn autoindent_ranges(&mut self, ranges: I, cx: &mut Context) @@ -1621,7 +1583,7 @@ impl MultiBuffer { edits: Vec<(Range, Arc)>, cx: &mut Context, ) { - let (buffer_edits, edited_excerpt_ids) = + let buffer_edits = MultiBuffer::convert_edits_to_buffer_edits(edits, this.snapshot.get_mut(), &[]); let mut buffer_ids = Vec::new(); @@ -1645,10 +1607,7 @@ impl MultiBuffer { }) } - cx.emit(Event::ExcerptsEdited { - excerpt_ids: edited_excerpt_ids, - buffer_ids, - }); + cx.emit(Event::BuffersEdited { buffer_ids }); } } @@ -1662,25 +1621,33 @@ impl MultiBuffer { let mut selections_by_buffer: HashMap>> = Default::default(); let snapshot = self.read(cx); - let mut cursor = snapshot.excerpts.cursor::>(()); + let mut cursor = snapshot.excerpts.cursor::(()); for selection in selections { - let start_locator = snapshot.excerpt_locator_for_id(selection.start.excerpt_id); - let end_locator = snapshot.excerpt_locator_for_id(selection.end.excerpt_id); + let Some(start) = snapshot.anchor_seek_target(selection.start) else { + continue; + }; + let Some(end) = snapshot.anchor_seek_target(selection.end) else { + continue; + }; - cursor.seek(&Some(start_locator), Bias::Left); + cursor.seek(&start, Bias::Left); while let Some(excerpt) = cursor.item() - && excerpt.locator <= *end_locator + && sum_tree::SeekTarget::cmp(&end, &cursor.position, ()).is_gt() { - let mut start = excerpt.range.context.start; - let mut end = excerpt.range.context.end; - if excerpt.id == selection.start.excerpt_id { - start = selection.start.text_anchor; - } - if excerpt.id == selection.end.excerpt_id { - end = selection.end.text_anchor; - } + let start = text::Anchor::max( + &excerpt.range.context.start, + &selection.start.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), + &excerpt.buffer, + ) + .clone(); selections_by_buffer - .entry(excerpt.buffer_id) + .entry(excerpt.buffer.remote_id()) .or_default() .push(Selection { id: selection.id, @@ -1702,25 +1669,9 @@ impl MultiBuffer { } } - for (buffer_id, mut selections) in selections_by_buffer { + for (buffer_id, selections) in selections_by_buffer { self.buffers[&buffer_id].buffer.update(cx, |buffer, cx| { - selections.sort_unstable_by(|a, b| a.start.cmp(&b.start, buffer)); - let mut selections = selections.into_iter().peekable(); - let merged_selections = Arc::from_iter(iter::from_fn(|| { - let mut selection = selections.next()?; - while let Some(next_selection) = selections.peek() { - if selection.end.cmp(&next_selection.start, buffer).is_ge() { - let next_selection = selections.next().unwrap(); - if next_selection.end.cmp(&selection.end, buffer).is_ge() { - selection.end = next_selection.end; - } - } else { - break; - } - } - Some(selection) - })); - buffer.set_active_selections(merged_selections, line_mode, cursor_shape, cx); + buffer.set_active_selections(selections.into(), line_mode, cursor_shape, cx); }); } } @@ -1759,175 +1710,11 @@ impl MultiBuffer { (merged_ranges, counts) } - pub fn insert_excerpts_after( - &mut self, - prev_excerpt_id: ExcerptId, - buffer: Entity, - ranges: impl IntoIterator>, - cx: &mut Context, - ) -> Vec - where - O: text::ToOffset, - { - let mut ids = Vec::new(); - let mut next_excerpt_id = - if let Some(last_entry) = self.snapshot.borrow().excerpt_ids.last() { - last_entry.id.0 + 1 - } else { - 1 - }; - self.insert_excerpts_with_ids_after( - prev_excerpt_id, - buffer, - ranges.into_iter().map(|range| { - let id = ExcerptId(post_inc(&mut next_excerpt_id)); - ids.push(id); - (id, range) - }), - cx, - ); - ids - } - - pub fn insert_excerpts_with_ids_after( - &mut self, - prev_excerpt_id: ExcerptId, - buffer: Entity, - ranges: impl IntoIterator)>, - cx: &mut Context, - ) where - O: text::ToOffset, - { - assert_eq!(self.history.transaction_depth(), 0); - let mut ranges = ranges.into_iter().peekable(); - if ranges.peek().is_none() { - return Default::default(); - } - - 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.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(), - ), - excerpts: Default::default(), - _subscriptions: [ - cx.observe(&buffer, |_, _, cx| cx.notify()), - cx.subscribe(&buffer, Self::on_buffer_event), - ], - buffer: buffer.clone(), - } - }); - - let mut snapshot = self.snapshot.get_mut(); - - let mut prev_locator = snapshot.excerpt_locator_for_id(prev_excerpt_id).clone(); - let mut new_excerpt_ids = mem::take(&mut snapshot.excerpt_ids); - let mut cursor = snapshot.excerpts.cursor::>(()); - let mut new_excerpts = cursor.slice(&prev_locator, Bias::Right); - prev_locator = cursor.start().unwrap_or(Locator::min_ref()).clone(); - - let edit_start = ExcerptDimension(new_excerpts.summary().text.len); - new_excerpts.update_last( - |excerpt| { - excerpt.has_trailing_newline = true; - }, - (), - ); - - let next_locator = if let Some(excerpt) = cursor.item() { - excerpt.locator.clone() - } else { - Locator::max() - }; - - let mut excerpts = Vec::new(); - let buffer_snapshot = Arc::new(buffer_snapshot); - while let Some((id, range)) = ranges.next() { - let locator = Locator::between(&prev_locator, &next_locator); - if let Err(ix) = buffer_state.excerpts.binary_search(&locator) { - buffer_state.excerpts.insert(ix, locator.clone()); - } - let range = ExcerptRange { - context: buffer_snapshot.anchor_before(&range.context.start) - ..buffer_snapshot.anchor_after(&range.context.end), - primary: buffer_snapshot.anchor_before(&range.primary.start) - ..buffer_snapshot.anchor_after(&range.primary.end), - }; - excerpts.push((id, range.clone())); - let excerpt = Excerpt::new( - id, - locator.clone(), - buffer_id, - buffer_snapshot.clone(), - range, - ranges.peek().is_some() || cursor.item().is_some(), - ); - new_excerpts.push(excerpt, ()); - prev_locator = locator.clone(); - - if let Some(last_mapping_entry) = new_excerpt_ids.last() { - assert!(id > last_mapping_entry.id, "excerpt ids must be increasing"); - } - new_excerpt_ids.push(ExcerptIdMapping { id, locator }, ()); - } - snapshot - .buffer_locators - .insert(buffer_id, buffer_state.excerpts.iter().cloned().collect()); - - let edit_end = ExcerptDimension(new_excerpts.summary().text.len); - - let suffix = cursor.suffix(); - let changed_trailing_excerpt = suffix.is_empty(); - new_excerpts.append(suffix, ()); - drop(cursor); - snapshot.excerpts = new_excerpts; - snapshot.excerpt_ids = new_excerpt_ids; - if changed_trailing_excerpt { - snapshot.trailing_excerpt_update_count += 1; - } - - let edits = Self::sync_diff_transforms( - &mut snapshot, - vec![Edit { - old: edit_start..edit_start, - new: edit_start..edit_end, - }], - DiffChangeKind::BufferEdited, - ); - if !edits.is_empty() { - self.subscriptions.publish(edits); - } - - cx.emit(Event::Edited { - edited_buffer: None, - }); - cx.emit(Event::ExcerptsAdded { - buffer, - predecessor: prev_excerpt_id, - excerpts, - }); - cx.notify(); - } - pub fn clear(&mut self, cx: &mut Context) { self.sync_mut(cx); - let ids = self.excerpt_ids(); let removed_buffer_ids = std::mem::take(&mut self.buffers).into_keys().collect(); - self.excerpts_by_path.clear(); - self.paths_by_excerpt.clear(); let MultiBufferSnapshot { excerpts, - buffer_locators, diffs: _, diff_transforms: _, non_text_state_update_count: _, @@ -1937,15 +1724,13 @@ impl MultiBuffer { has_conflict, has_inverted_diff, singleton: _, - excerpt_ids: _, - replaced_excerpts, trailing_excerpt_update_count, all_diff_hunks_expanded: _, show_deleted_hunks: _, use_extended_diff_range: _, show_headers: _, + path_keys: _, } = self.snapshot.get_mut(); - buffer_locators.clear(); let start = ExcerptDimension(MultiBufferOffset::ZERO); let prev_len = ExcerptDimension(excerpts.summary().text.len); *excerpts = Default::default(); @@ -1954,10 +1739,6 @@ impl MultiBuffer { *has_deleted_file = false; *has_conflict = false; *has_inverted_diff = false; - match Arc::get_mut(replaced_excerpts) { - Some(replaced_excerpts) => replaced_excerpts.clear(), - None => *replaced_excerpts = Default::default(), - } let edits = Self::sync_diff_transforms( self.snapshot.get_mut(), @@ -1973,10 +1754,7 @@ impl MultiBuffer { cx.emit(Event::Edited { edited_buffer: None, }); - cx.emit(Event::ExcerptsRemoved { - ids, - removed_buffer_ids, - }); + cx.emit(Event::BuffersRemoved { removed_buffer_ids }); cx.notify(); } @@ -1985,21 +1763,22 @@ impl MultiBuffer { &self, buffer_id: BufferId, cx: &App, - ) -> Vec<(ExcerptId, ExcerptRange)> { + ) -> Vec> { let mut excerpts = Vec::new(); let snapshot = self.read(cx); - let mut cursor = snapshot.excerpts.cursor::>(()); - if let Some(locators) = snapshot.buffer_locators.get(&buffer_id) { - for locator in &**locators { - cursor.seek_forward(&Some(locator), Bias::Left); - if let Some(excerpt) = cursor.item() - && excerpt.locator == *locator - { - excerpts.push((excerpt.id, excerpt.range.clone())); - } - } - } + let Some(path_key) = snapshot.path_keys.get(&buffer_id).cloned() else { + return excerpts; + }; + + let mut cursor = snapshot.excerpts.cursor::(()); + cursor.seek_forward(&path_key, Bias::Left); + while let Some(item) = cursor.item() + && item.path_key == path_key + { + excerpts.push(item.range.clone()); + cursor.next() + } excerpts } @@ -2007,84 +1786,67 @@ impl MultiBuffer { let snapshot = self.read(cx); let mut excerpts = snapshot .excerpts - .cursor::, ExcerptPoint>>(()); + .cursor::>(()); let mut diff_transforms = snapshot .diff_transforms .cursor::>>(()); diff_transforms.next(); - let locators = snapshot - .buffer_locators - .get(&buffer_id) - .into_iter() - .flat_map(|v| &**v); let mut result = Vec::new(); - for locator in locators { - excerpts.seek_forward(&Some(locator), Bias::Left); - if let Some(excerpt) = excerpts.item() - && excerpt.locator == *locator - { - let excerpt_start = excerpts.start().1; - let excerpt_end = excerpt_start + excerpt.text_summary.lines; + let Some(path_key) = snapshot.path_keys.get(&buffer_id) else { + return result; + }; - diff_transforms.seek_forward(&excerpt_start, Bias::Left); - let overshoot = excerpt_start - diff_transforms.start().0; - let start = diff_transforms.start().1 + overshoot; + while let Some(excerpt) = excerpts.item() + && &excerpt.path_key == path_key + { + let excerpt_start = excerpts.start().1; + let excerpt_end = excerpt_start + excerpt.text_summary.lines; - diff_transforms.seek_forward(&excerpt_end, Bias::Right); - let overshoot = excerpt_end - diff_transforms.start().0; - let end = diff_transforms.start().1 + overshoot; + diff_transforms.seek_forward(&excerpt_start, Bias::Left); + let overshoot = excerpt_start - diff_transforms.start().0; + let start = diff_transforms.start().1 + overshoot; - result.push(start.0..end.0) - } + diff_transforms.seek_forward(&excerpt_end, Bias::Right); + let overshoot = excerpt_end - diff_transforms.start().0; + let end = diff_transforms.start().1 + overshoot; + + result.push(start.0..end.0); + excerpts.next() } result } + // todo!() this seems bogus pub fn excerpt_buffer_ids(&self) -> Vec { self.snapshot .borrow() .excerpts .iter() - .map(|entry| entry.buffer_id) + .map(|entry| entry.buffer.remote_id()) .collect() } - pub fn excerpt_ids(&self) -> Vec { - let snapshot = self.snapshot.borrow(); - let mut ids = Vec::with_capacity(snapshot.excerpts.summary().count); - ids.extend(snapshot.excerpts.iter().map(|entry| entry.id)); - ids - } - - pub fn excerpt_containing( - &self, - position: impl ToOffset, - cx: &App, - ) -> Option<(ExcerptId, Entity, Range)> { + pub fn excerpt_containing(&self, position: impl ToOffset, cx: &App) -> Option { let snapshot = self.read(cx); let offset = position.to_offset(&snapshot); let mut cursor = snapshot.cursor::(); cursor.seek(&offset); - cursor - .excerpt() - .or_else(|| snapshot.excerpts.last()) - .map(|excerpt| { - ( - excerpt.id, - self.buffers.get(&excerpt.buffer_id).unwrap().buffer.clone(), - excerpt.range.context.clone(), - ) - }) + 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(), + }) } pub fn buffer_for_anchor(&self, anchor: Anchor, cx: &App) -> Option> { - if let Some(buffer_id) = anchor.text_anchor.buffer_id { - self.buffer(buffer_id) - } else { - let (_, buffer, _) = self.excerpt_containing(anchor, cx)?; - Some(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, + }; + self.buffer(buffer_id) } // If point is at the end of the buffer, the last excerpt is returned @@ -2106,15 +1868,10 @@ impl MultiBuffer { &self, point: T, cx: &App, - ) -> Option<(Entity, Point, ExcerptId)> { + ) -> Option<(Entity, Point)> { let snapshot = self.read(cx); - let (buffer, point, is_main_buffer) = - snapshot.point_to_buffer_point(point.to_point(&snapshot))?; - Some(( - self.buffers.get(&buffer.remote_id())?.buffer.clone(), - point, - is_main_buffer, - )) + let (buffer, point) = snapshot.point_to_buffer_point(point.to_point(&snapshot))?; + Some((self.buffers.get(&buffer.remote_id())?.buffer.clone(), point)) } pub fn buffer_point_to_anchor( @@ -2126,25 +1883,22 @@ impl MultiBuffer { ) -> Option { let mut found = None; let snapshot = buffer.read(cx).snapshot(); - for (excerpt_id, range) in self.excerpts_for_buffer(snapshot.remote_id(), cx) { + 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), excerpt_id)); + found = Some(snapshot.clip_point(point, Bias::Left)); break; } if point < start { - found = Some((start, excerpt_id)); + found = Some(start); } if point > end { - found = Some((end, excerpt_id)); + found = Some(end); } } - found.map(|(point, excerpt_id)| { - let text_anchor = snapshot.anchor_after(point); - Anchor::in_buffer(excerpt_id, text_anchor) - }) + Some(Anchor::text(snapshot.anchor_after(found?))) } pub fn buffer_anchor_to_anchor( @@ -2155,187 +1909,15 @@ impl MultiBuffer { cx: &App, ) -> Option { let snapshot = buffer.read(cx).snapshot(); - for (excerpt_id, range) in self.excerpts_for_buffer(snapshot.remote_id(), cx) { - if range.context.start.cmp(&anchor, &snapshot).is_le() - && range.context.end.cmp(&anchor, &snapshot).is_ge() - { - return Some(Anchor::in_buffer(excerpt_id, anchor)); + for range in self.excerpts_for_buffer(snapshot.remote_id(), cx) { + if range.contains(&anchor, &snapshot) { + return Some(Anchor::text(anchor)); } } None } - pub fn merge_excerpts( - &mut self, - excerpt_ids: &[ExcerptId], - cx: &mut Context, - ) -> ExcerptId { - debug_assert!(!excerpt_ids.is_empty()); - if excerpt_ids.len() == 1 { - return excerpt_ids[0]; - } - - let snapshot = self.snapshot(cx); - - let first_range = snapshot - .context_range_for_excerpt(excerpt_ids[0]) - .expect("first excerpt must exist"); - let last_range = snapshot - .context_range_for_excerpt(*excerpt_ids.last().unwrap()) - .expect("last excerpt must exist"); - - let union_range = first_range.start..last_range.end; - - drop(snapshot); - - self.resize_excerpt(excerpt_ids[0], union_range, cx); - let removed = &excerpt_ids[1..]; - for &excerpt_id in removed { - if let Some(path) = self.paths_by_excerpt.get(&excerpt_id) { - if let Some(excerpt_list) = self.excerpts_by_path.get_mut(path) { - excerpt_list.retain(|id| *id != excerpt_id); - if excerpt_list.is_empty() { - let path = path.clone(); - self.excerpts_by_path.remove(&path); - } - } - } - } - self.remove_excerpts(removed.iter().copied(), cx); - - excerpt_ids[0] - } - - pub fn remove_excerpts( - &mut self, - excerpt_ids: impl IntoIterator, - cx: &mut Context, - ) { - self.sync_mut(cx); - let ids = excerpt_ids.into_iter().collect::>(); - if ids.is_empty() { - return; - } - self.buffer_changed_since_sync.replace(true); - - let mut snapshot = self.snapshot.get_mut(); - let mut new_excerpts = SumTree::default(); - let mut cursor = snapshot - .excerpts - .cursor::, ExcerptOffset>>(()); - let mut edits = Vec::new(); - let mut excerpt_ids = ids.iter().copied().peekable(); - let mut removed_buffer_ids = Vec::new(); - let mut removed_excerpts_for_buffers = HashSet::default(); - - 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), ()); - - if let Some(mut excerpt) = cursor.item() { - if excerpt.id != excerpt_id { - continue; - } - let mut old_start = cursor.start().1; - - // Skip over the removed excerpt. - 'remove_excerpts: loop { - if let Some(buffer_state) = self.buffers.get_mut(&excerpt.buffer_id) { - removed_excerpts_for_buffers.insert(excerpt.buffer_id); - buffer_state.excerpts.retain(|l| l != &excerpt.locator); - if buffer_state.excerpts.is_empty() { - log::debug!( - "removing buffer and diff for buffer {}", - excerpt.buffer_id - ); - self.buffers.remove(&excerpt.buffer_id); - removed_buffer_ids.push(excerpt.buffer_id); - } - } - cursor.next(); - - // Skip over any subsequent excerpts that are also removed. - if let Some(&next_excerpt_id) = excerpt_ids.peek() { - let next_locator = snapshot.excerpt_locator_for_id(next_excerpt_id); - if let Some(next_excerpt) = cursor.item() - && next_excerpt.locator == *next_locator - { - excerpt_ids.next(); - excerpt = next_excerpt; - continue 'remove_excerpts; - } - } - - break; - } - - // When removing the last excerpt, remove the trailing newline from - // the previous excerpt. - if cursor.item().is_none() && old_start > MultiBufferOffset::ZERO { - old_start -= 1; - new_excerpts.update_last(|e| e.has_trailing_newline = false, ()); - } - - // Push an edit for the removal of this run of excerpts. - let old_end = cursor.start().1; - let new_start = ExcerptDimension(new_excerpts.summary().text.len); - edits.push(Edit { - old: old_start..old_end, - new: new_start..new_start, - }); - } - } - let suffix = cursor.suffix(); - let changed_trailing_excerpt = suffix.is_empty(); - new_excerpts.append(suffix, ()); - drop(cursor); - 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); - } - } - } - snapshot.excerpts = new_excerpts; - for buffer_id in &removed_buffer_ids { - self.diffs.remove(buffer_id); - snapshot.diffs.remove(buffer_id); - } - - // Recalculate has_inverted_diff after removing diffs - if !removed_buffer_ids.is_empty() { - snapshot.has_inverted_diff = snapshot - .diffs - .iter() - .any(|(_, diff)| diff.main_buffer.is_some()); - } - - if changed_trailing_excerpt { - snapshot.trailing_excerpt_update_count += 1; - } - - let edits = Self::sync_diff_transforms(&mut snapshot, edits, DiffChangeKind::BufferEdited); - if !edits.is_empty() { - self.subscriptions.publish(edits); - } - cx.emit(Event::Edited { - edited_buffer: None, - }); - cx.emit(Event::ExcerptsRemoved { - ids, - removed_buffer_ids, - }); - cx.notify(); - } - pub fn wait_for_anchors<'a, Anchors: 'a + Iterator>( &self, anchors: Anchors, @@ -2344,14 +1926,15 @@ impl MultiBuffer { let mut error = None; let mut futures = Vec::new(); for anchor in anchors { - if let Some(buffer_id) = anchor.text_anchor.buffer_id { - if let Some(buffer) = self.buffers.get(&buffer_id) { + if let Some(text_anchor) = anchor.text_anchor() { + if let Some(buffer) = self.buffers.get(&text_anchor.buffer_id.unwrap()) { buffer.buffer.update(cx, |buffer, _| { - futures.push(buffer.wait_for_anchors([anchor.text_anchor])) + futures.push(buffer.wait_for_anchors([text_anchor])) }); } else { error = Some(anyhow!( - "buffer {buffer_id} is not part of this multi-buffer" + "buffer {:?} is not part of this multi-buffer", + text_anchor.buffer_id )); break; } @@ -2372,15 +1955,11 @@ impl MultiBuffer { &self, position: T, cx: &App, - ) -> Option<(Entity, language::Anchor)> { + ) -> Option<(Entity, text::Anchor)> { let snapshot = self.read(cx); - let anchor = snapshot.anchor_before(position); - let buffer = self - .buffers - .get(&anchor.text_anchor.buffer_id?)? - .buffer - .clone(); - Some((buffer, anchor.text_anchor)) + let anchor = snapshot.anchor_before(position).text_anchor()?; + let buffer = self.buffers.get(&anchor.buffer_id.unwrap())?.buffer.clone(); + Some((buffer, anchor)) } fn on_buffer_event( @@ -2841,8 +2420,8 @@ impl MultiBuffer { if last_hunk_row.is_some_and(|row| row >= diff_hunk.row_range.start) { continue; } - let start = Anchor::in_buffer(diff_hunk.excerpt_id, diff_hunk.buffer_range.start); - let end = Anchor::in_buffer(diff_hunk.excerpt_id, diff_hunk.buffer_range.end); + let start = Anchor::text(diff_hunk.excerpt_id, diff_hunk.buffer_range.start); + let end = Anchor::text(diff_hunk.excerpt_id, diff_hunk.buffer_range.end); let start = snapshot.excerpt_offset_for_anchor(&start); let end = snapshot.excerpt_offset_for_anchor(&end); last_hunk_row = Some(diff_hunk.row_range.start); @@ -3100,7 +2679,7 @@ impl MultiBuffer { ) -> Vec> { let MultiBufferSnapshot { excerpts, - buffer_locators: _, + buffer_paths: _, diffs: buffer_diff, diff_transforms: _, non_text_state_update_count, @@ -3110,8 +2689,6 @@ impl MultiBuffer { has_conflict, has_inverted_diff: _, singleton: _, - excerpt_ids: _, - replaced_excerpts: _, trailing_excerpt_update_count: _, all_diff_hunks_expanded: _, show_deleted_hunks: _, @@ -4060,14 +3637,13 @@ impl MultiBufferSnapshot { if self.show_deleted_hunks || is_inverted { let hunk_start_offset = if is_inverted { - Anchor::in_buffer( + Anchor::text( excerpt.id, excerpt.buffer.anchor_after(hunk.diff_base_byte_range.start), ) .to_offset(self) } else { - Anchor::in_buffer(excerpt.id, hunk.buffer_range.start) - .to_offset(self) + Anchor::text(excerpt.id, hunk.buffer_range.start).to_offset(self) }; word_diffs.extend(hunk.base_word_diffs.iter().map(|diff| { @@ -4168,7 +3744,7 @@ impl MultiBufferSnapshot { let mut same_buffer_anchors = anchors.peeking_take_while(|a| a.buffer_id.is_some_and(|b| buffer_id == b)); - if let Some(locators) = self.buffer_locators.get(&buffer_id) { + if let Some(locators) = self.buffer_paths.get(&buffer_id) { let Some(mut next) = same_buffer_anchors.next() else { continue 'anchors; }; @@ -4200,7 +3776,7 @@ impl MultiBufferSnapshot { .is_ge() { // record it and all following anchors that are within - result.push(Some(Anchor::in_buffer(excerpt.id, next))); + result.push(Some(Anchor::text(excerpt.id, next))); result.extend( same_buffer_anchors .peeking_take_while(|a| { @@ -4211,7 +3787,7 @@ impl MultiBufferSnapshot { .cmp(a, &excerpt.buffer) .is_ge() }) - .map(|a| Some(Anchor::in_buffer(excerpt.id, a))), + .map(|a| Some(Anchor::text(excerpt.id, a))), ); match same_buffer_anchors.next() { Some(anchor) => next = anchor, @@ -4606,7 +4182,7 @@ impl MultiBufferSnapshot { continue; } let hunk_start = excerpt.buffer.anchor_after(hunk.diff_base_byte_range.start); - let start = Anchor::in_buffer(excerpt.id, hunk_start).to_point(self); + let start = Anchor::text(excerpt.id, hunk_start).to_point(self); return Some(MultiBufferRow(start.row)); } } else { @@ -4621,8 +4197,7 @@ impl MultiBufferSnapshot { if hunk_end >= current_position { continue; } - let start = - Anchor::in_buffer(excerpt.id, hunk.buffer_range.start).to_point(self); + let start = Anchor::text(excerpt.id, hunk.buffer_range.start).to_point(self); return Some(MultiBufferRow(start.row)); } } @@ -4646,7 +4221,7 @@ impl MultiBufferSnapshot { continue; }; let hunk_start = excerpt.buffer.anchor_after(hunk.diff_base_byte_range.start); - let start = Anchor::in_buffer(excerpt.id, hunk_start).to_point(self); + let start = Anchor::text(excerpt.id, hunk_start).to_point(self); return Some(MultiBufferRow(start.row)); } else { let Some(hunk) = diff @@ -4655,7 +4230,7 @@ impl MultiBufferSnapshot { else { continue; }; - let start = Anchor::in_buffer(excerpt.id, hunk.buffer_range.start).to_point(self); + let start = Anchor::text(excerpt.id, hunk.buffer_range.start).to_point(self); return Some(MultiBufferRow(start.row)); } } @@ -4995,10 +4570,7 @@ impl MultiBufferSnapshot { Some((region.buffer, buffer_offset)) } - pub fn point_to_buffer_point( - &self, - point: Point, - ) -> Option<(&BufferSnapshot, Point, ExcerptId)> { + pub fn point_to_buffer_point(&self, point: Point) -> Option<(&BufferSnapshot, Point)> { let mut cursor = self.cursor::(); cursor.seek(&point); let region = cursor.region()?; @@ -5009,11 +4581,11 @@ impl MultiBufferSnapshot { && region.has_trailing_newline && !region.is_main_buffer { - return Some((&excerpt.buffer, cursor.main_buffer_position()?, excerpt.id)); + return Some((&excerpt.buffer, cursor.main_buffer_position()?)); } else if buffer_point > region.buffer.max_point() { return None; } - Some((region.buffer, buffer_point, excerpt.id)) + Some((region.buffer, buffer_point)) } pub fn suggested_indents( @@ -5828,7 +5400,7 @@ impl MultiBufferSnapshot { { text_anchor = excerpt.range.context.end; } - Anchor::in_buffer(excerpt.id, text_anchor) + Anchor::text(excerpt.id, text_anchor) } else if let Some(excerpt) = prev_excerpt { let mut text_anchor = excerpt .range @@ -5841,7 +5413,7 @@ impl MultiBufferSnapshot { { text_anchor = excerpt.range.context.start; } - Anchor::in_buffer(excerpt.id, text_anchor) + Anchor::text(excerpt.id, text_anchor) } else if anchor.text_anchor.bias == Bias::Left { Anchor::min() } else { @@ -5923,7 +5495,7 @@ impl MultiBufferSnapshot { let buffer_start = excerpt.range.context.start.to_offset(&excerpt.buffer); let text_anchor = excerpt.clip_anchor(excerpt.buffer.anchor_at(buffer_start + overshoot, bias)); - let anchor = Anchor::in_buffer(excerpt.id, text_anchor); + let anchor = Anchor::text(excerpt.id, text_anchor); match diff_base_anchor { Some(diff_base_anchor) => anchor.with_diff_base_anchor(diff_base_anchor), None => anchor, @@ -5940,7 +5512,7 @@ impl MultiBufferSnapshot { pub fn as_singleton_anchor(&self, text_anchor: text::Anchor) -> Option { let (excerpt, buffer, _) = self.as_singleton()?; if text_anchor.buffer_id.is_none_or(|id| id == buffer) { - Some(Anchor::in_buffer(excerpt, text_anchor)) + Some(Anchor::text(excerpt, text_anchor)) } else { None } @@ -5991,7 +5563,7 @@ impl MultiBufferSnapshot { Some(buffer_id) if buffer_id == excerpt.buffer_id => (), Some(_) => return None, None if text_anchor.is_max() || text_anchor.is_min() => { - return Some(Anchor::in_buffer(excerpt.id, text_anchor)); + return Some(Anchor::text(excerpt.id, text_anchor)); } None => return None, } @@ -6003,7 +5575,7 @@ impl MultiBufferSnapshot { return None; } - Some(Anchor::in_buffer(excerpt.id, text_anchor)) + Some(Anchor::text(excerpt.id, text_anchor)) } pub fn context_range_for_excerpt(&self, excerpt_id: ExcerptId) -> Option> { @@ -6145,7 +5717,7 @@ impl MultiBufferSnapshot { self.max_point() }; - let prev = prev_region.as_ref().map(|region| ExcerptInfo { + let prev = prev_region.as_ref().map(|region| ExcerptBoundaryInfo { id: region.id, buffer: region.buffer.clone(), buffer_id: region.buffer_id, @@ -6153,7 +5725,7 @@ impl MultiBufferSnapshot { end_row: MultiBufferRow(next_region_start.row), }); - let next = ExcerptInfo { + let next = ExcerptBoundaryInfo { id: next_excerpt.id, buffer: next_excerpt.buffer.clone(), buffer_id: next_excerpt.buffer_id, @@ -6938,6 +6510,27 @@ impl MultiBufferSnapshot { )) } + fn anchor_seek_target(&self, anchor: Anchor) -> Option { + match anchor { + Anchor::Min => Some(AnchorSeekTarget::Min), + Anchor::Max => Some(AnchorSeekTarget::Max), + Anchor::Text { buffer_id, .. } => { + let Some(path_key) = self.path_for_buffer(buffer_id) else { + return None; + }; + let Some(snapshot) = self.buffer_for_path(path_key) else { + return None; + }; + + Some(AnchorSeekTarget::Text { + path_key: path_key.clone(), + anchor: anchor.text_anchor().unwrap(), + snapshot: snapshot.clone(), + }) + } + } + } + fn excerpt_locator_for_id(&self, id: ExcerptId) -> &Locator { self.try_excerpt_locator_for_id(id) .unwrap_or_else(|| panic!("invalid excerpt id {id:?}")) @@ -7002,6 +6595,18 @@ impl MultiBufferSnapshot { Some(&self.excerpt(excerpt_id)?.buffer) } + pub fn buffer_for_path(&self, path: &PathKey) -> Option<&BufferSnapshot> { + todo!() + } + + pub fn path_for_buffer(&self, buffer_id: BufferId) -> Option<&PathKey> { + self.path_keys.get(&buffer_id) + } + + pub fn buffer_for_id(&self, id: BufferId) -> Option<&BufferSnapshot> { + self.buffer_for_path(self.path_keys.get(&id)?) + } + pub fn range_for_excerpt(&self, excerpt_id: ExcerptId) -> Option> { let mut cursor = self .excerpts @@ -7118,8 +6723,8 @@ impl MultiBufferSnapshot { .selections_in_range(query_range, include_local) .flat_map(move |(replica_id, line_mode, cursor_shape, selections)| { selections.map(move |selection| { - let mut start = Anchor::in_buffer(excerpt.id, selection.start); - let mut end = Anchor::in_buffer(excerpt.id, selection.end); + let mut start = Anchor::text(excerpt.id, selection.start); + let mut end = Anchor::text(excerpt.id, selection.end); if range.start.cmp(&start, self).is_gt() { start = range.start; } @@ -7626,20 +7231,16 @@ where impl Excerpt { fn new( - id: ExcerptId, - locator: Locator, - buffer_id: BufferId, + path_key: PathKey, buffer: Arc, range: ExcerptRange, has_trailing_newline: bool, ) -> Self { Excerpt { - id, - locator, + path_key, max_buffer_row: range.context.end.to_point(&buffer).row, text_summary: buffer .text_summary_for_range::(range.context.to_offset(&buffer)), - buffer_id, buffer, range, has_trailing_newline, @@ -7658,7 +7259,6 @@ impl Excerpt { let content_chunks = self.buffer.chunks(chunks_start..chunks_end, language_aware); ExcerptChunks { - excerpt_id: self.id, content_chunks, has_footer, } @@ -7718,6 +7318,13 @@ impl Excerpt { } } +impl PartialEq for Excerpt { + fn eq(&self, other: &Self) -> bool { + self.buffer.remote_id() == other.buffer.remote_id() + && self.range.context == other.range.context + } +} + impl<'a> MultiBufferExcerpt<'a> { pub fn id(&self) -> ExcerptId { self.excerpt.id @@ -7728,11 +7335,11 @@ impl<'a> MultiBufferExcerpt<'a> { } pub fn start_anchor(&self) -> Anchor { - Anchor::in_buffer(self.excerpt.id, self.excerpt.range.context.start) + Anchor::text(self.excerpt.id, self.excerpt.range.context.start) } pub fn end_anchor(&self) -> Anchor { - Anchor::in_buffer(self.excerpt.id, self.excerpt.range.context.end) + Anchor::text(self.excerpt.id, self.excerpt.range.context.end) } pub fn buffer(&self) -> &'a BufferSnapshot { @@ -7846,42 +7453,10 @@ impl<'a> MultiBufferExcerpt<'a> { } } -impl ExcerptId { - pub fn min() -> Self { - Self(0) - } - - pub fn max() -> Self { - Self(u32::MAX) - } - - pub fn to_proto(self) -> u64 { - self.0 as _ - } - - pub fn from_proto(proto: u64) -> Self { - Self(proto as _) - } - - pub fn cmp(&self, other: &Self, snapshot: &MultiBufferSnapshot) -> cmp::Ordering { - let a = snapshot.excerpt_locator_for_id(*self); - let b = snapshot.excerpt_locator_for_id(*other); - a.cmp(b).then_with(|| self.0.cmp(&other.0)) - } -} - -impl From for usize { - fn from(val: ExcerptId) -> Self { - val.0 as usize - } -} - impl fmt::Debug for Excerpt { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Excerpt") - .field("id", &self.id) - .field("locator", &self.locator) - .field("buffer_id", &self.buffer_id) + .field("path_key", &self.path_key) .field("range", &self.range) .field("text_summary", &self.text_summary) .field("has_trailing_newline", &self.has_trailing_newline) @@ -7898,8 +7473,8 @@ impl sum_tree::Item for Excerpt { text += TextSummary::from("\n"); } ExcerptSummary { - excerpt_id: self.id, - excerpt_locator: self.locator.clone(), + path_key: self.path_key, + max_anchor: self.range.context.end, widest_line_number: self.max_buffer_row, text: text.into(), count: 1, @@ -7907,22 +7482,6 @@ impl sum_tree::Item for Excerpt { } } -impl sum_tree::Item for ExcerptIdMapping { - type Summary = ExcerptId; - - fn summary(&self, _cx: ()) -> Self::Summary { - self.id - } -} - -impl sum_tree::KeyedItem for ExcerptIdMapping { - type Key = ExcerptId; - - fn key(&self) -> Self::Key { - self.id - } -} - impl DiffTransform { fn hunk_info(&self) -> Option { match self { @@ -7971,45 +7530,68 @@ impl sum_tree::ContextLessSummary for DiffTransformSummary { } } -impl sum_tree::ContextLessSummary for ExcerptId { - fn zero() -> Self { - Self(0) +impl sum_tree::Dimension<'_, ExcerptSummary> for PathKey { + fn zero(_: ::Context<'_>) -> Self { + PathKey::min() } - fn add_summary(&mut self, summary: &Self) { - *self = cmp::max(*self, *summary); + fn add_summary( + &mut self, + summary: &'_ ExcerptSummary, + _cx: ::Context<'_>, + ) { + *self = summary.path_key.clone(); } } impl sum_tree::ContextLessSummary for ExcerptSummary { fn zero() -> Self { - Self::default() + Self::min() } fn add_summary(&mut self, summary: &Self) { debug_assert!( - summary.excerpt_locator > self.excerpt_locator - || self.excerpt_locator == Locator::min(), - "Excerpt locators must be in ascending order: {:?} > {:?}", - summary.excerpt_locator, - self.excerpt_locator + summary.path_key >= self.path_key, + "Path keys must be in ascending order: {:?} > {:?}", + summary.path_key, + self.path_key ); - self.excerpt_locator = summary.excerpt_locator.clone(); + + self.path_key = summary.path_key.clone(); + self.max_anchor = summary.max_anchor; self.text += summary.text; self.widest_line_number = cmp::max(self.widest_line_number, summary.widest_line_number); self.count += summary.count; } } -impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, Option<&'a Locator>> for Locator { - fn cmp(&self, cursor_location: &Option<&'a Locator>, _: ()) -> cmp::Ordering { - Ord::cmp(&Some(self), cursor_location) - } +// todo!() should this take a lifetime? +enum AnchorSeekTarget { + Min, + Max, + Text { + path_key: PathKey, + anchor: text::Anchor, + snapshot: BufferSnapshot, + }, } -impl sum_tree::SeekTarget<'_, ExcerptSummary, ExcerptSummary> for Locator { - fn cmp(&self, cursor_location: &ExcerptSummary, _: ()) -> cmp::Ordering { - Ord::cmp(self, &cursor_location.excerpt_locator) +impl sum_tree::SeekTarget<'_, ExcerptSummary, ExcerptSummary> for AnchorSeekTarget { + fn cmp( + &self, + cursor_location: &ExcerptSummary, + _cx: ::Context<'_>, + ) -> cmp::Ordering { + match self { + AnchorSeekTarget::Min => Ordering::Less, + AnchorSeekTarget::Max => Ordering::Greater, + AnchorSeekTarget::Text { + path_key, + anchor, + snapshot, + } => Ord::cmp(path_key, &cursor_location.path_key) + .then_with(|| anchor.cmp(&cursor_location.max_anchor, snapshot)), + } } } @@ -8026,26 +7608,6 @@ where } } -impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<&'a Locator> { - fn zero(_cx: ()) -> Self { - Default::default() - } - - fn add_summary(&mut self, summary: &'a ExcerptSummary, _: ()) { - *self = Some(&summary.excerpt_locator); - } -} - -impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option { - fn zero(_cx: ()) -> Self { - Default::default() - } - - fn add_summary(&mut self, summary: &'a ExcerptSummary, _: ()) { - *self = Some(summary.excerpt_id); - } -} - #[derive(Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Debug)] struct OutputDimension(T); @@ -8733,12 +8295,6 @@ impl ToPoint for PointUtf16 { } } -impl From for EntityId { - fn from(id: ExcerptId) -> Self { - EntityId::from(id.0 as u64) - } -} - #[cfg(debug_assertions)] pub mod debug { use super::*; diff --git a/crates/multi_buffer/src/multi_buffer_tests.rs b/crates/multi_buffer/src/multi_buffer_tests.rs index 7e27786a76a14783f54e42c73850a888e87a3ac7..ee7fdec006d52ad3712cb27405ffad000433c822 100644 --- a/crates/multi_buffer/src/multi_buffer_tests.rs +++ b/crates/multi_buffer/src/multi_buffer_tests.rs @@ -744,7 +744,7 @@ fn test_excerpt_events(cx: &mut App) { predecessor, excerpts, } => follower.insert_excerpts_with_ids_after(predecessor, buffer, excerpts, cx), - Event::ExcerptsRemoved { ids, .. } => follower.remove_excerpts(ids, cx), + Event::BuffersRemoved { ids, .. } => follower.remove_excerpts(ids, cx), Event::Edited { .. } => { *follower_edit_event_count.write() += 1; } @@ -3794,11 +3794,11 @@ async fn test_summaries_for_anchors(cx: &mut TestAppContext) { ), ); - let anchor_1 = Anchor::in_buffer(ids[0], text::Anchor::MIN); + let anchor_1 = Anchor::text(ids[0], text::Anchor::MIN); let point_1 = snapshot.summaries_for_anchors::([&anchor_1])[0]; assert_eq!(point_1, Point::new(0, 0)); - let anchor_2 = Anchor::in_buffer(ids[1], text::Anchor::MIN); + let anchor_2 = Anchor::text(ids[1], text::Anchor::MIN); let point_2 = snapshot.summaries_for_anchors::([&anchor_2])[0]; assert_eq!(point_2, Point::new(3, 0)); } @@ -5257,8 +5257,8 @@ fn test_cannot_seek_backward_after_excerpt_replacement(cx: &mut TestAppContext) let e_b2 = snapshot.excerpt(e_b2_id).expect("E_B2 should exist"); let e_b3 = snapshot.excerpt(e_b3_id).expect("E_B3 should exist"); - let anchor_b2 = Anchor::in_buffer(e_b2_id, e_b2.range.context.start); - let anchor_b3 = Anchor::in_buffer(e_b3_id, e_b3.range.context.start); + let anchor_b2 = Anchor::text(e_b2_id, e_b2.range.context.start); + let anchor_b3 = Anchor::text(e_b3_id, e_b3.range.context.start); (anchor_b2, anchor_b3) }); diff --git a/crates/multi_buffer/src/path_key.rs b/crates/multi_buffer/src/path_key.rs index 09d17d7b7fe2e9e666ba6c5777216c9c8ba4dea0..c8a4a7a8d1d0eda5de0b4c241f7e832dd55d5475 100644 --- a/crates/multi_buffer/src/path_key.rs +++ b/crates/multi_buffer/src/path_key.rs @@ -27,6 +27,20 @@ pub struct PathKey { } impl PathKey { + pub fn min() -> Self { + Self { + sort_prefix: None, + path: RelPath::empty().into_arc(), + } + } + + pub fn max() -> Self { + Self { + sort_prefix: Some(u64::MAX), + path: RelPath::empty().into_arc(), + } + } + pub fn sorted(sort_prefix: u64) -> Self { Self { sort_prefix: Some(sort_prefix), @@ -89,7 +103,7 @@ impl MultiBuffer { let excerpt_id = self.excerpts_by_path.get(path)?.first()?; let snapshot = self.read(cx); let excerpt = snapshot.excerpt(*excerpt_id)?; - Some(Anchor::in_buffer(excerpt.id, excerpt.range.context.start)) + Some(Anchor::text(excerpt.id, excerpt.range.context.start)) } pub fn set_excerpts_for_buffer( diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index 5161d77e06778091acd259994f1904b84e05acda..fca7b27a39ded43d6e087f0d8b96baa3faacd923 100644 --- a/crates/vim/src/motion.rs +++ b/crates/vim/src/motion.rs @@ -2359,10 +2359,10 @@ fn go_to_line(map: &DisplaySnapshot, display_point: DisplayPoint, line: usize) - ..language::ToOffset::to_offset(&range.context.end, buffer); if offset >= excerpt_range.start && offset <= excerpt_range.end { let text_anchor = buffer.anchor_after(offset); - let anchor = Anchor::in_buffer(excerpt, text_anchor); + let anchor = Anchor::text(excerpt, text_anchor); return anchor.to_display_point(map); } else if offset <= excerpt_range.start { - let anchor = Anchor::in_buffer(excerpt, range.context.start); + let anchor = Anchor::text(excerpt, range.context.start); return anchor.to_display_point(map); } } diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index 1501d29c7b9b712f3f8edc25025545d0fa0baa08..67de855e95da5332484310ce79042ae3e29c5734 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -924,7 +924,7 @@ impl Vim { Vim::take_forced_motion(cx); self.update_editor(cx, |vim, editor, cx| { let selection = editor.selections.newest_anchor(); - let Some((buffer, point, _)) = editor + let Some((buffer, point)) = editor .buffer() .read(cx) .point_to_buffer_point(selection.head(), cx) diff --git a/crates/vim/src/state.rs b/crates/vim/src/state.rs index 0244a14c83b422a1fed803c761c7e873b42bd267..c8d95ccdd66305812ac5902aef3c8091ac042a38 100644 --- a/crates/vim/src/state.rs +++ b/crates/vim/src/state.rs @@ -617,7 +617,7 @@ impl MarksState { let text_anchors = anchors.get(name)?; let anchors = text_anchors .iter() - .map(|anchor| Anchor::in_buffer(excerpt_id, *anchor)) + .map(|anchor| Anchor::text(excerpt_id, *anchor)) .collect(); return Some(Mark::Local(anchors)); }