mod anchor;#[cfg(test)]mod multi_buffer_tests;mod position; pub use anchor::{Anchor, AnchorRangeExt, Offset};pub use position::{TypedOffset, TypedPoint, TypedRow}; use anyhow::{Result, anyhow};use buffer_diff::{ BufferDiff, BufferDiffEvent, BufferDiffSnapshot, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind,};use clock::ReplicaId;use collections::{BTreeMap, Bound, HashMap, HashSet};use gpui::{App, AppContext as _, Context, Entity, EntityId, EventEmitter, Task};use itertools::Itertools;use language::{ AutoindentMode, Buffer, BufferChunks, BufferRow, BufferSnapshot, Capability, CharClassifier, CharKind, Chunk, CursorShape, DiagnosticEntry, DiskState, File, IndentSize, Language, LanguageScope, OffsetRangeExt, OffsetUtf16, Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, TextObject, ToOffset as _, ToPoint as _, TransactionId, TreeSitterOptions, Unclipped, language_settings::{IndentGuideSettings, LanguageSettings, language_settings},}; use rope::DimensionPair;use smallvec::SmallVec;use smol::future::yield_now;use std::{ any::type_name, borrow::Cow, cell::{Cell, Ref, RefCell}, cmp, fmt, future::Future, io, iter::{self, FromIterator}, mem, ops::{Range, RangeBounds, Sub}, path::{Path, PathBuf}, rc::Rc, str, sync::Arc, time::{Duration, Instant},};use sum_tree::{Bias, Cursor, SumTree, TreeMap};use text::{ BufferId, Edit, LineIndent, TextSummary, locator::Locator, subscription::{Subscription, Topic},};use theme::SyntaxTheme;use util::post_inc; const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize]; #[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]pub struct ExcerptId(usize); /// One or more [`Buffers`](Buffer) being edited in a single view.////// See <https://zed.dev/features#multi-buffers>pub struct MultiBuffer { /// A snapshot of the [`Excerpt`]s in the MultiBuffer. /// Use [`MultiBuffer::snapshot`] to get a up-to-date snapshot. snapshot: RefCell<MultiBufferSnapshot>, /// Contains the state of the buffers being edited buffers: RefCell<HashMap<BufferId, BufferState>>, // only used by consumers using `set_excerpts_for_buffer` excerpts_by_path: BTreeMap<PathKey, Vec<ExcerptId>>, paths_by_excerpt: HashMap<ExcerptId, PathKey>, diffs: HashMap<BufferId, DiffState>, // all_diff_hunks_expanded: bool, subscriptions: Topic, /// If true, the multi-buffer only contains a single [`Buffer`] and a single [`Excerpt`] singleton: bool, history: History, title: Option<String>, capability: Capability, buffer_changed_since_sync: Rc<Cell<bool>>,} #[derive(Clone, Debug, PartialEq, Eq)]pub enum MultiOrSingleBufferOffsetRange { Single(Range<usize>), Multi(Range<usize>),} #[derive(Clone, Debug, PartialEq, Eq)]pub enum Event { ExcerptsAdded { buffer: Entity<Buffer>, predecessor: ExcerptId, excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>, }, ExcerptsRemoved { ids: Vec<ExcerptId>, removed_buffer_ids: Vec<BufferId>, }, ExcerptsExpanded { ids: Vec<ExcerptId>, }, ExcerptsEdited { excerpt_ids: Vec<ExcerptId>, buffer_ids: Vec<BufferId>, }, DiffHunksToggled, Edited { singleton_buffer_edited: bool, edited_buffer: Option<Entity<Buffer>>, }, TransactionUndone { transaction_id: TransactionId, }, Reloaded, ReloadNeeded, LanguageChanged(BufferId), CapabilityChanged, Reparsed(BufferId), Saved, FileHandleChanged, Closed, Discarded, DirtyChanged, DiagnosticsUpdated, BufferDiffChanged,} /// A diff hunk, representing a range of consequent lines in a multibuffer.#[derive(Debug, Clone, PartialEq, Eq)]pub struct MultiBufferDiffHunk { /// The row range in the multibuffer where this diff hunk appears. pub row_range: Range<MultiBufferRow>, /// The buffer ID that this hunk belongs to. pub buffer_id: BufferId, /// The range of the underlying buffer that this hunk corresponds to. pub buffer_range: Range<text::Anchor>, /// 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<usize>, /// Whether or not this hunk also appears in the 'secondary diff'. pub secondary_status: DiffHunkSecondaryStatus,} impl MultiBufferDiffHunk { pub fn status(&self) -> DiffHunkStatus { let kind = if self.buffer_range.start == self.buffer_range.end { DiffHunkStatusKind::Deleted } else if self.diff_base_byte_range.is_empty() { DiffHunkStatusKind::Added } else { DiffHunkStatusKind::Modified }; DiffHunkStatus { kind, secondary: self.secondary_status, } } pub fn is_created_file(&self) -> bool { self.diff_base_byte_range == (0..0) && self.buffer_range == (text::Anchor::MIN..text::Anchor::MAX) } pub fn multi_buffer_range(&self) -> Range<Anchor> { let start = Anchor::in_buffer(self.excerpt_id, self.buffer_id, self.buffer_range.start); let end = Anchor::in_buffer(self.excerpt_id, self.buffer_id, self.buffer_range.end); start..end }} #[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)]pub struct PathKey { namespace: u32, path: Arc<Path>,} impl PathKey { pub fn namespaced(namespace: u32, path: Arc<Path>) -> Self { Self { namespace, path } } pub fn for_buffer(buffer: &Entity<Buffer>, cx: &App) -> Self { if let Some(file) = buffer.read(cx).file() { Self::namespaced(1, Arc::from(file.full_path(cx))) } else { Self::namespaced(0, Arc::from(PathBuf::from(buffer.entity_id().to_string()))) } } pub fn path(&self) -> &Arc<Path> { &self.path }} pub type MultiBufferPoint = Point;type ExcerptOffset = TypedOffset<Excerpt>;type ExcerptPoint = TypedPoint<Excerpt>; #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq, Hash, serde::Deserialize)]#[serde(transparent)]pub struct MultiBufferRow(pub u32); impl MultiBufferRow { pub const MIN: Self = Self(0); pub const MAX: Self = Self(u32::MAX);} impl std::ops::Add<usize> for MultiBufferRow { type Output = Self; fn add(self, rhs: usize) -> Self::Output { MultiBufferRow(self.0 + rhs as u32) }} #[derive(Clone)]struct History { next_transaction_id: TransactionId, undo_stack: Vec<Transaction>, redo_stack: Vec<Transaction>, transaction_depth: usize, group_interval: Duration,} #[derive(Clone)]struct Transaction { id: TransactionId, buffer_transactions: HashMap<BufferId, text::TransactionId>, first_edit_at: Instant, last_edit_at: Instant, suppress_grouping: bool,} pub trait ToOffset: 'static + fmt::Debug { fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize;} pub trait ToOffsetUtf16: 'static + fmt::Debug { fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16;} pub trait ToPoint: 'static + fmt::Debug { fn to_point(&self, snapshot: &MultiBufferSnapshot) -> Point;} pub trait ToPointUtf16: 'static + fmt::Debug { fn to_point_utf16(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16;} struct BufferState { buffer: Entity<Buffer>, last_version: clock::Global, last_non_text_state_update_count: usize, excerpts: Vec<Locator>, _subscriptions: [gpui::Subscription; 2],} struct DiffState { diff: Entity<BufferDiff>, _subscription: gpui::Subscription,} impl DiffState { fn new(diff: Entity<BufferDiff>, cx: &mut Context<MultiBuffer>) -> Self { DiffState { _subscription: cx.subscribe(&diff, |this, diff, event, cx| match event { BufferDiffEvent::DiffChanged { changed_range } => { if let Some(changed_range) = changed_range.clone() { this.buffer_diff_changed(diff, changed_range, cx) } cx.emit(Event::BufferDiffChanged); } BufferDiffEvent::LanguageChanged => this.buffer_diff_language_changed(diff, cx), _ => {} }), diff, } }} /// The contents of a [`MultiBuffer`] at a single point in time.#[derive(Clone, Default)]pub struct MultiBufferSnapshot { singleton: bool, excerpts: SumTree<Excerpt>, excerpt_ids: SumTree<ExcerptIdMapping>, diffs: TreeMap<BufferId, BufferDiffSnapshot>, diff_transforms: SumTree<DiffTransform>, replaced_excerpts: TreeMap<ExcerptId, ExcerptId>, trailing_excerpt_update_count: usize, all_diff_hunks_expanded: bool, non_text_state_update_count: usize, edit_count: usize, is_dirty: bool, has_deleted_file: bool, has_conflict: bool, show_headers: bool,} #[derive(Debug, Clone)]enum DiffTransform { BufferContent { summary: TextSummary, inserted_hunk_info: Option<DiffTransformHunkInfo>, }, DeletedHunk { summary: TextSummary, buffer_id: BufferId, hunk_info: DiffTransformHunkInfo, base_text_byte_range: Range<usize>, has_trailing_newline: bool, },} #[derive(Clone, Copy, Debug)]struct DiffTransformHunkInfo { excerpt_id: ExcerptId, hunk_start_anchor: text::Anchor, hunk_secondary_status: DiffHunkSecondaryStatus,} 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 }} impl std::hash::Hash for DiffTransformHunkInfo { fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.excerpt_id.hash(state); self.hunk_start_anchor.hash(state); }} #[derive(Clone)]pub struct ExcerptInfo { pub id: ExcerptId, pub buffer: BufferSnapshot, pub buffer_id: BufferId, pub range: ExcerptRange<text::Anchor>, pub end_row: MultiBufferRow,} impl std::fmt::Debug for ExcerptInfo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct(type_name::<Self>()) .field("id", &self.id) .field("buffer_id", &self.buffer_id) .field("path", &self.buffer.file().map(|f| f.path())) .field("range", &self.range) .finish() }} /// A boundary between `Excerpt`s in a [`MultiBuffer`]#[derive(Debug)]pub struct ExcerptBoundary { pub prev: Option<ExcerptInfo>, pub next: ExcerptInfo, /// The row in the `MultiBuffer` where the boundary is located pub row: MultiBufferRow,} impl ExcerptBoundary { pub fn starts_new_buffer(&self) -> bool { match (self.prev.as_ref(), &self.next) { (None, _) => true, (Some(prev), next) => prev.buffer_id != next.buffer_id, } }} #[derive(Copy, Clone, Debug, PartialEq, Eq)]pub struct ExpandInfo { pub direction: ExpandExcerptDirection, pub excerpt_id: ExcerptId,} #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]pub struct RowInfo { pub buffer_id: Option<BufferId>, pub buffer_row: Option<u32>, pub multibuffer_row: Option<MultiBufferRow>, pub diff_status: Option<buffer_diff::DiffHunkStatus>, pub expand_info: Option<ExpandInfo>,} /// A slice into a [`Buffer`] that is being edited in a [`MultiBuffer`].#[derive(Clone)]struct Excerpt { /// The unique identifier for this excerpt id: ExcerptId, /// The location of the excerpt in the [`MultiBuffer`] locator: Locator, /// The buffer being excerpted buffer_id: BufferId, /// A snapshot of the buffer being excerpted buffer: BufferSnapshot, /// The range of the buffer to be shown in the excerpt range: ExcerptRange<text::Anchor>, /// The last row in the excerpted slice of the buffer max_buffer_row: BufferRow, /// A summary of the text in the excerpt text_summary: TextSummary, has_trailing_newline: bool,} /// A public view into an `Excerpt` in a [`MultiBuffer`].////// Contains methods for getting the [`Buffer`] of the excerpt,/// as well as mapping offsets to/from buffer and multibuffer coordinates.#[derive(Clone)]pub struct MultiBufferExcerpt<'a> { excerpt: &'a Excerpt, diff_transforms: sum_tree::Cursor<'a, DiffTransform, (OutputDimension<usize>, ExcerptDimension<usize>)>, offset: usize, excerpt_offset: ExcerptDimension<usize>, buffer_offset: usize,} #[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)]pub struct ExcerptRange<T> { /// The full range of text to be shown in the excerpt. pub context: Range<T>, /// The primary range of text to be highlighted in the excerpt. /// In a multi-buffer search, this would be the text that matched the search pub primary: Range<T>,} impl<T: Clone> ExcerptRange<T> { pub fn new(context: Range<T>) -> Self { Self { context: context.clone(), primary: context, } }} #[derive(Clone, Debug, Default)]pub struct ExcerptSummary { excerpt_id: ExcerptId, /// The location of the last [`Excerpt`] being summarized excerpt_locator: Locator, widest_line_number: u32, text: TextSummary,} #[derive(Debug, Clone)]pub struct DiffTransformSummary { input: TextSummary, output: TextSummary,} #[derive(Clone)]pub struct MultiBufferRows<'a> { point: Point, is_empty: bool, is_singleton: bool, cursor: MultiBufferCursor<'a, Point>,} pub struct MultiBufferChunks<'a> { excerpts: Cursor<'a, Excerpt, ExcerptOffset>, diff_transforms: Cursor<'a, DiffTransform, (usize, ExcerptOffset)>, diffs: &'a TreeMap<BufferId, BufferDiffSnapshot>, diff_base_chunks: Option<(BufferId, BufferChunks<'a>)>, buffer_chunk: Option<Chunk<'a>>, range: Range<usize>, excerpt_offset_range: Range<ExcerptOffset>, excerpt_chunks: Option<ExcerptChunks<'a>>, language_aware: bool,} pub struct ReversedMultiBufferChunks<'a> { cursor: MultiBufferCursor<'a, usize>, current_chunks: Option<rope::Chunks<'a>>, start: usize, offset: usize,} pub struct MultiBufferBytes<'a> { range: Range<usize>, cursor: MultiBufferCursor<'a, usize>, excerpt_bytes: Option<text::Bytes<'a>>, has_trailing_newline: bool, chunk: &'a [u8],Lines 1–500 of 7832. Load more
mod anchor; #[cfg(test)] mod multi_buffer_tests; mod position; pub use anchor::{Anchor, AnchorRangeExt, Offset}; pub use position::{TypedOffset, TypedPoint, TypedRow}; use anyhow::{Result, anyhow}; use buffer_diff::{ BufferDiff, BufferDiffEvent, BufferDiffSnapshot, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind, }; use clock::ReplicaId; use collections::{BTreeMap, Bound, HashMap, HashSet}; use gpui::{App, AppContext as _, Context, Entity, EntityId, EventEmitter, Task}; use itertools::Itertools; use language::{ AutoindentMode, Buffer, BufferChunks, BufferRow, BufferSnapshot, Capability, CharClassifier, CharKind, Chunk, CursorShape, DiagnosticEntry, DiskState, File, IndentSize, Language, LanguageScope, OffsetRangeExt, OffsetUtf16, Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, TextObject, ToOffset as _, ToPoint as _, TransactionId, TreeSitterOptions, Unclipped, language_settings::{IndentGuideSettings, LanguageSettings, language_settings}, }; use rope::DimensionPair; use smallvec::SmallVec; use smol::future::yield_now; use std::{ any::type_name, borrow::Cow, cell::{Cell, Ref, RefCell}, cmp, fmt, future::Future, io, iter::{self, FromIterator}, mem, ops::{Range, RangeBounds, Sub}, path::{Path, PathBuf}, rc::Rc, str, sync::Arc, time::{Duration, Instant}, }; use sum_tree::{Bias, Cursor, SumTree, TreeMap}; use text::{ BufferId, Edit, LineIndent, TextSummary, locator::Locator, subscription::{Subscription, Topic}, }; use theme::SyntaxTheme; use util::post_inc; const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize]; #[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct ExcerptId(usize); /// One or more [`Buffers`](Buffer) being edited in a single view. /// /// See <https://zed.dev/features#multi-buffers> pub struct MultiBuffer { /// A snapshot of the [`Excerpt`]s in the MultiBuffer. /// Use [`MultiBuffer::snapshot`] to get a up-to-date snapshot. snapshot: RefCell<MultiBufferSnapshot>, /// Contains the state of the buffers being edited buffers: RefCell<HashMap<BufferId, BufferState>>, // only used by consumers using `set_excerpts_for_buffer` excerpts_by_path: BTreeMap<PathKey, Vec<ExcerptId>>, paths_by_excerpt: HashMap<ExcerptId, PathKey>, diffs: HashMap<BufferId, DiffState>, // all_diff_hunks_expanded: bool, subscriptions: Topic, /// If true, the multi-buffer only contains a single [`Buffer`] and a single [`Excerpt`] singleton: bool, history: History, title: Option<String>, capability: Capability, buffer_changed_since_sync: Rc<Cell<bool>>, } #[derive(Clone, Debug, PartialEq, Eq)] pub enum MultiOrSingleBufferOffsetRange { Single(Range<usize>), Multi(Range<usize>), } #[derive(Clone, Debug, PartialEq, Eq)] pub enum Event { ExcerptsAdded { buffer: Entity<Buffer>, predecessor: ExcerptId, excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>, }, ExcerptsRemoved { ids: Vec<ExcerptId>, removed_buffer_ids: Vec<BufferId>, }, ExcerptsExpanded { ids: Vec<ExcerptId>, }, ExcerptsEdited { excerpt_ids: Vec<ExcerptId>, buffer_ids: Vec<BufferId>, }, DiffHunksToggled, Edited { singleton_buffer_edited: bool, edited_buffer: Option<Entity<Buffer>>, }, TransactionUndone { transaction_id: TransactionId, }, Reloaded, ReloadNeeded, LanguageChanged(BufferId), CapabilityChanged, Reparsed(BufferId), Saved, FileHandleChanged, Closed, Discarded, DirtyChanged, DiagnosticsUpdated, BufferDiffChanged, } /// A diff hunk, representing a range of consequent lines in a multibuffer. #[derive(Debug, Clone, PartialEq, Eq)] pub struct MultiBufferDiffHunk { /// The row range in the multibuffer where this diff hunk appears. pub row_range: Range<MultiBufferRow>, /// The buffer ID that this hunk belongs to. pub buffer_id: BufferId, /// The range of the underlying buffer that this hunk corresponds to. pub buffer_range: Range<text::Anchor>, /// 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<usize>, /// Whether or not this hunk also appears in the 'secondary diff'. pub secondary_status: DiffHunkSecondaryStatus, } impl MultiBufferDiffHunk { pub fn status(&self) -> DiffHunkStatus { let kind = if self.buffer_range.start == self.buffer_range.end { DiffHunkStatusKind::Deleted } else if self.diff_base_byte_range.is_empty() { DiffHunkStatusKind::Added } else { DiffHunkStatusKind::Modified }; DiffHunkStatus { kind, secondary: self.secondary_status, } } pub fn is_created_file(&self) -> bool { self.diff_base_byte_range == (0..0) && self.buffer_range == (text::Anchor::MIN..text::Anchor::MAX) } pub fn multi_buffer_range(&self) -> Range<Anchor> { let start = Anchor::in_buffer(self.excerpt_id, self.buffer_id, self.buffer_range.start); let end = Anchor::in_buffer(self.excerpt_id, self.buffer_id, self.buffer_range.end); start..end } } #[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)] pub struct PathKey { namespace: u32, path: Arc<Path>, } impl PathKey { pub fn namespaced(namespace: u32, path: Arc<Path>) -> Self { Self { namespace, path } } pub fn for_buffer(buffer: &Entity<Buffer>, cx: &App) -> Self { if let Some(file) = buffer.read(cx).file() { Self::namespaced(1, Arc::from(file.full_path(cx))) } else { Self::namespaced(0, Arc::from(PathBuf::from(buffer.entity_id().to_string()))) } } pub fn path(&self) -> &Arc<Path> { &self.path } } pub type MultiBufferPoint = Point; type ExcerptOffset = TypedOffset<Excerpt>; type ExcerptPoint = TypedPoint<Excerpt>; #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq, Hash, serde::Deserialize)] #[serde(transparent)] pub struct MultiBufferRow(pub u32); impl MultiBufferRow { pub const MIN: Self = Self(0); pub const MAX: Self = Self(u32::MAX); } impl std::ops::Add<usize> for MultiBufferRow { type Output = Self; fn add(self, rhs: usize) -> Self::Output { MultiBufferRow(self.0 + rhs as u32) } } #[derive(Clone)] struct History { next_transaction_id: TransactionId, undo_stack: Vec<Transaction>, redo_stack: Vec<Transaction>, transaction_depth: usize, group_interval: Duration, } #[derive(Clone)] struct Transaction { id: TransactionId, buffer_transactions: HashMap<BufferId, text::TransactionId>, first_edit_at: Instant, last_edit_at: Instant, suppress_grouping: bool, } pub trait ToOffset: 'static + fmt::Debug { fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize; } pub trait ToOffsetUtf16: 'static + fmt::Debug { fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16; } pub trait ToPoint: 'static + fmt::Debug { fn to_point(&self, snapshot: &MultiBufferSnapshot) -> Point; } pub trait ToPointUtf16: 'static + fmt::Debug { fn to_point_utf16(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16; } struct BufferState { buffer: Entity<Buffer>, last_version: clock::Global, last_non_text_state_update_count: usize, excerpts: Vec<Locator>, _subscriptions: [gpui::Subscription; 2], } struct DiffState { diff: Entity<BufferDiff>, _subscription: gpui::Subscription, } impl DiffState { fn new(diff: Entity<BufferDiff>, cx: &mut Context<MultiBuffer>) -> Self { DiffState { _subscription: cx.subscribe(&diff, |this, diff, event, cx| match event { BufferDiffEvent::DiffChanged { changed_range } => { if let Some(changed_range) = changed_range.clone() { this.buffer_diff_changed(diff, changed_range, cx) } cx.emit(Event::BufferDiffChanged); } BufferDiffEvent::LanguageChanged => this.buffer_diff_language_changed(diff, cx), _ => {} }), diff, } } } /// The contents of a [`MultiBuffer`] at a single point in time. #[derive(Clone, Default)] pub struct MultiBufferSnapshot { singleton: bool, excerpts: SumTree<Excerpt>, excerpt_ids: SumTree<ExcerptIdMapping>, diffs: TreeMap<BufferId, BufferDiffSnapshot>, diff_transforms: SumTree<DiffTransform>, replaced_excerpts: TreeMap<ExcerptId, ExcerptId>, trailing_excerpt_update_count: usize, all_diff_hunks_expanded: bool, non_text_state_update_count: usize, edit_count: usize, is_dirty: bool, has_deleted_file: bool, has_conflict: bool, show_headers: bool, } #[derive(Debug, Clone)] enum DiffTransform { BufferContent { summary: TextSummary, inserted_hunk_info: Option<DiffTransformHunkInfo>, }, DeletedHunk { summary: TextSummary, buffer_id: BufferId, hunk_info: DiffTransformHunkInfo, base_text_byte_range: Range<usize>, has_trailing_newline: bool, }, } #[derive(Clone, Copy, Debug)] struct DiffTransformHunkInfo { excerpt_id: ExcerptId, hunk_start_anchor: text::Anchor, hunk_secondary_status: DiffHunkSecondaryStatus, } 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 } } impl std::hash::Hash for DiffTransformHunkInfo { fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.excerpt_id.hash(state); self.hunk_start_anchor.hash(state); } } #[derive(Clone)] pub struct ExcerptInfo { pub id: ExcerptId, pub buffer: BufferSnapshot, pub buffer_id: BufferId, pub range: ExcerptRange<text::Anchor>, pub end_row: MultiBufferRow, } impl std::fmt::Debug for ExcerptInfo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct(type_name::<Self>()) .field("id", &self.id) .field("buffer_id", &self.buffer_id) .field("path", &self.buffer.file().map(|f| f.path())) .field("range", &self.range) .finish() } } /// A boundary between `Excerpt`s in a [`MultiBuffer`] #[derive(Debug)] pub struct ExcerptBoundary { pub prev: Option<ExcerptInfo>, pub next: ExcerptInfo, /// The row in the `MultiBuffer` where the boundary is located pub row: MultiBufferRow, } impl ExcerptBoundary { pub fn starts_new_buffer(&self) -> bool { match (self.prev.as_ref(), &self.next) { (None, _) => true, (Some(prev), next) => prev.buffer_id != next.buffer_id, } } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct ExpandInfo { pub direction: ExpandExcerptDirection, pub excerpt_id: ExcerptId, } #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub struct RowInfo { pub buffer_id: Option<BufferId>, pub buffer_row: Option<u32>, pub multibuffer_row: Option<MultiBufferRow>, pub diff_status: Option<buffer_diff::DiffHunkStatus>, pub expand_info: Option<ExpandInfo>, } /// A slice into a [`Buffer`] that is being edited in a [`MultiBuffer`]. #[derive(Clone)] struct Excerpt { /// The unique identifier for this excerpt id: ExcerptId, /// The location of the excerpt in the [`MultiBuffer`] locator: Locator, /// The buffer being excerpted buffer_id: BufferId, /// A snapshot of the buffer being excerpted buffer: BufferSnapshot, /// The range of the buffer to be shown in the excerpt range: ExcerptRange<text::Anchor>, /// The last row in the excerpted slice of the buffer max_buffer_row: BufferRow, /// A summary of the text in the excerpt text_summary: TextSummary, has_trailing_newline: bool, } /// A public view into an `Excerpt` in a [`MultiBuffer`]. /// /// Contains methods for getting the [`Buffer`] of the excerpt, /// as well as mapping offsets to/from buffer and multibuffer coordinates. #[derive(Clone)] pub struct MultiBufferExcerpt<'a> { excerpt: &'a Excerpt, diff_transforms: sum_tree::Cursor<'a, DiffTransform, (OutputDimension<usize>, ExcerptDimension<usize>)>, offset: usize, excerpt_offset: ExcerptDimension<usize>, buffer_offset: usize, } #[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)] pub struct ExcerptRange<T> { /// The full range of text to be shown in the excerpt. pub context: Range<T>, /// The primary range of text to be highlighted in the excerpt. /// In a multi-buffer search, this would be the text that matched the search pub primary: Range<T>, } impl<T: Clone> ExcerptRange<T> { pub fn new(context: Range<T>) -> Self { Self { context: context.clone(), primary: context, } } } #[derive(Clone, Debug, Default)] pub struct ExcerptSummary { excerpt_id: ExcerptId, /// The location of the last [`Excerpt`] being summarized excerpt_locator: Locator, widest_line_number: u32, text: TextSummary, } #[derive(Debug, Clone)] pub struct DiffTransformSummary { input: TextSummary, output: TextSummary, } #[derive(Clone)] pub struct MultiBufferRows<'a> { point: Point, is_empty: bool, is_singleton: bool, cursor: MultiBufferCursor<'a, Point>, } pub struct MultiBufferChunks<'a> { excerpts: Cursor<'a, Excerpt, ExcerptOffset>, diff_transforms: Cursor<'a, DiffTransform, (usize, ExcerptOffset)>, diffs: &'a TreeMap<BufferId, BufferDiffSnapshot>, diff_base_chunks: Option<(BufferId, BufferChunks<'a>)>, buffer_chunk: Option<Chunk<'a>>, range: Range<usize>, excerpt_offset_range: Range<ExcerptOffset>, excerpt_chunks: Option<ExcerptChunks<'a>>, language_aware: bool, } pub struct ReversedMultiBufferChunks<'a> { cursor: MultiBufferCursor<'a, usize>, current_chunks: Option<rope::Chunks<'a>>, start: usize, offset: usize, } pub struct MultiBufferBytes<'a> { range: Range<usize>, cursor: MultiBufferCursor<'a, usize>, excerpt_bytes: Option<text::Bytes<'a>>, has_trailing_newline: bool, chunk: &'a [u8],