use super::{ anchor::Anchor, position::{TypedOffset, TypedPoint, TypedPointUtf16, TypedRow}, Event, ExcerptId, ExcerptRange, MultiBuffer,};use anyhow::{anyhow, Result};use collections::{BTreeMap, Bound, HashMap, HashSet};use futures::{channel::mpsc, SinkExt};use gpui::{AppContext, EventEmitter, Model, ModelContext, Task};use itertools::Itertools;use language::{ AutoindentMode, Buffer, BufferChunks, BufferRow, BufferSnapshot, Capability, Chunk, CursorShape, DiagnosticEntry, DiskState, File, IndentGuide, IndentSize, OffsetRangeExt, OffsetUtf16, Point, Selection, TextDimension, ToOffset as _, ToPoint as _, TransactionId,};use smallvec::SmallVec;use std::{ any::type_name, borrow::Cow, cell::{Ref, RefCell}, cmp, fmt, future::Future, io, iter::{self, FromIterator}, mem, ops::{Range, RangeBounds, Sub}, str, sync::Arc, time::{Duration, Instant},};use sum_tree::{Bias, Cursor, SumTree};use text::{locator::Locator, BufferId, Edit, Patch, TextSummary};use util::post_inc; pub type ExcerptOffset = TypedOffset<ExcerptMap>;pub type ExcerptPoint = TypedPoint<ExcerptMap>;pub type ExcerptPointUtf16 = TypedPointUtf16<ExcerptMap>;pub type ExcerptRow = TypedRow<ExcerptMap>; #[cfg(any(test, feature = "test-support"))]use gpui::Context; const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize]; /// One or more [`Buffers`](Buffer) being edited in a single view.////// See <https://zed.dev/features#multi-buffers>pub struct ExcerptMap { /// A snapshot of the [`Excerpt`]s in the MultiBuffer. /// Use [`MultiBuffer::snapshot`] to get a up-to-date snapshot. snapshot: RefCell<ExcerptMapSnapshot>, /// Contains the state of the buffers being edited buffers: RefCell<HashMap<BufferId, BufferState>>, /// If true, the multi-buffer only contains a single [`Buffer`] and a single [`Excerpt`] pub(crate) singleton: bool, history: History, title: Option<String>, capability: Capability, edits: RefCell<Patch<ExcerptOffset>>,} /// 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 range within the buffer's diff base that this hunk corresponds to. pub diff_base_byte_range: Range<usize>,} #[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);} #[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,} struct BufferState { buffer: Model<Buffer>, last_version: clock::Global, last_non_text_state_update_count: usize, excerpts: Vec<Locator>, _subscriptions: [gpui::Subscription; 2],} /// The contents of a [`MultiBuffer`] at a single point in time.#[derive(Clone, Default)]pub struct ExcerptMapSnapshot { singleton: bool, excerpts: SumTree<Excerpt>, excerpt_ids: SumTree<ExcerptIdMapping>, trailing_excerpt_update_count: usize, non_text_state_update_count: usize, edit_count: usize, is_dirty: bool, has_deleted_file: bool, has_conflict: bool, show_headers: bool,} #[derive(Clone)]pub struct ExcerptInfo { pub id: ExcerptId, pub buffer: BufferSnapshot, pub buffer_id: BufferId, pub range: ExcerptRange<text::Anchor>, pub text_summary: TextSummary,} 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: Option<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.as_ref()) { (None, _) => true, (Some(_), None) => false, (Some(prev), Some(next)) => prev.buffer_id != next.buffer_id, } }} /// 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, excerpt_offset: ExcerptOffset, excerpt_position: ExcerptPoint,} #[derive(Clone, Debug)]struct ExcerptIdMapping { id: ExcerptId, locator: Locator,} #[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(Clone)]pub struct ExcerptMapRows<'a> { buffer_row_range: Range<u32>, excerpts: Cursor<'a, Excerpt, ExcerptPoint>,} pub struct ExcerptMapChunks<'a> { range: Range<ExcerptOffset>, excerpts: Cursor<'a, Excerpt, ExcerptOffset>, excerpt_chunks: Option<ExcerptChunks<'a>>, language_aware: bool,} pub struct ExcerptMapBytes<'a> { range: Range<ExcerptOffset>, excerpts: Cursor<'a, Excerpt, ExcerptOffset>, excerpt_bytes: Option<ExcerptBytes<'a>>, chunk: &'a [u8],} pub struct ReversedExcerptMapBytes<'a> { range: Range<ExcerptOffset>, excerpts: Cursor<'a, Excerpt, ExcerptOffset>, excerpt_bytes: Option<ExcerptBytes<'a>>, chunk: &'a [u8],} struct ExcerptChunks<'a> { excerpt_id: ExcerptId, content_chunks: BufferChunks<'a>, footer_height: usize,} struct ExcerptBytes<'a> { content_bytes: text::Bytes<'a>, padding_height: usize, reversed: bool,} struct BufferEdit { range: Range<usize>, new_text: Arc<str>, is_insertion: bool, original_indent_column: u32,} #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]pub enum ExpandExcerptDirection { Up, Down, UpAndDown,} impl ExpandExcerptDirection { pub fn should_expand_up(&self) -> bool { match self { ExpandExcerptDirection::Up => true, ExpandExcerptDirection::Down => false, ExpandExcerptDirection::UpAndDown => true, } } pub fn should_expand_down(&self) -> bool { match self { ExpandExcerptDirection::Up => false, ExpandExcerptDirection::Down => true, ExpandExcerptDirection::UpAndDown => true, } }} #[derive(Clone, Debug, PartialEq)]pub struct MultiBufferIndentGuide { pub multibuffer_row_range: Range<MultiBufferRow>, pub buffer: IndentGuide,} impl std::ops::Deref for MultiBufferIndentGuide { type Target = IndentGuide; fn deref(&self) -> &Self::Target { &self.buffer }} impl ExcerptMap { pub fn new(capability: Capability) -> Self { Self { snapshot: RefCell::new(ExcerptMapSnapshot { show_headers: true, ..ExcerptMapSnapshot::default() }), buffers: RefCell::default(), edits: Default::default(), singleton: false, capability, title: None, history: History { next_transaction_id: clock::Lamport::default(), undo_stack: Vec::new(), redo_stack: Vec::new(), transaction_depth: 0, group_interval: Duration::from_millis(300), }, } } pub fn without_headers(capability: Capability) -> Self { Self { snapshot: Default::default(), buffers: Default::default(), edits: Default::default(), singleton: false, capability, history: History { next_transaction_id: Default::default(), undo_stack: Default::default(), redo_stack: Default::default(), transaction_depth: 0, group_interval: Duration::from_millis(300), }, title: Default::default(), } } pub fn clone(&self, new_cx: &mut ModelContext<MultiBuffer>) -> Self { let mut buffers = HashMap::default(); for (buffer_id, buffer_state) in self.buffers.borrow().iter() { buffers.insert( *buffer_id, BufferState { buffer: buffer_state.buffer.clone(), last_version: buffer_state.last_version.clone(), last_non_text_state_update_count: buffer_state.last_non_text_state_update_count, 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), ], }, ); } Self { snapshot: RefCell::new(self.snapshot.borrow().clone()), buffers: RefCell::new(buffers), singleton: self.singleton, capability: self.capability, history: self.history.clone(), edits: self.edits.clone(), title: self.title.clone(), } } pub fn with_title(mut self, title: String) -> Self { self.title = Some(title); self } pub fn read_only(&self) -> bool { self.capability == Capability::ReadOnly } pub fn singleton(buffer: Model<Buffer>, cx: &mut ModelContext<MultiBuffer>) -> Self { let mut this = Self::new(buffer.read(cx).capability()); this.singleton = true; this.push_excerpts( buffer, [ExcerptRange { context: text::Anchor::MIN..text::Anchor::MAX, primary: None, }], cx, ); this.snapshot.borrow_mut().singleton = true; this } /// Returns an up-to-date snapshot of the MultiBuffer. pub fn snapshot(&self, cx: &AppContext) -> ExcerptMapSnapshot { self.sync(cx); self.snapshot.borrow().clone() } pub fn read(&self, cx: &AppContext) -> Ref<ExcerptMapSnapshot> { self.sync(cx); self.snapshot.borrow() } pub fn as_singleton(&self) -> Option<Model<Buffer>> { if self.singleton { return Some( self.buffers .borrow() .values() .next() .unwrap() .buffer .clone(), ); } else { None } } pub fn is_singleton(&self) -> bool { self.singleton } pub fn is_dirty(&self, cx: &AppContext) -> bool { self.read(cx).is_dirty() } pub fn has_deleted_file(&self, cx: &AppContext) -> bool { self.read(cx).has_deleted_file() } pub fn has_conflict(&self, cx: &AppContext) -> bool { self.read(cx).has_conflict() } // The `is_empty` signature doesn't match what clippy expects #[allow(clippy::len_without_is_empty)] pub fn len(&self, cx: &AppContext) -> ExcerptOffset { self.read(cx).len() } pub fn is_empty(&self, cx: &AppContext) -> bool { self.len(cx).value != 0 } pub fn edit<I, T>( &self, edits: I, autoindent_mode: Option<AutoindentMode>, cx: &mut ModelContext<MultiBuffer>, ) where I: IntoIterator<Item = (Range<ExcerptOffset>, T)>, T: Into<Arc<str>>, { let snapshot = self.read(cx); let edits = edits .into_iter() .map(|(mut range, new_text)| { if range.start > range.end { mem::swap(&mut range.start, &mut range.end); } (range, new_text.into()) }) .collect::<Vec<_>>(); return edit_internal(self, snapshot, edits, autoindent_mode, cx); // Non-generic part of edit, hoisted out to avoid blowing up LLVM IR. fn edit_internal( this: &ExcerptMap, snapshot: Ref<ExcerptMapSnapshot>, edits: Vec<(Range<ExcerptOffset>, Arc<str>)>, mut autoindent_mode: Option<AutoindentMode>, cx: &mut ModelContext<MultiBuffer>, ) { if this.read_only() || this.buffers.borrow().is_empty() { return; } if let Some(buffer) = this.as_singleton() { buffer.update(cx, |buffer, cx| { buffer.edit( edits .into_iter() .map(|(range, text)| (range.start.value..range.end.value, text)), autoindent_mode, cx, ); }); cx.emit(Event::ExcerptsEdited { ids: this.excerpt_ids(), }); return; } let original_indent_columns = match &mut autoindent_mode { Some(AutoindentMode::Block { original_indent_columns, }) => mem::take(original_indent_columns), _ => Default::default(), }; let (buffer_edits, edited_excerpt_ids) = this.convert_edits_to_buffer_edits(edits, &snapshot, &original_indent_columns); drop(snapshot); Lines 1–500 of 4763. Load more
use super::{ anchor::Anchor, position::{TypedOffset, TypedPoint, TypedPointUtf16, TypedRow}, Event, ExcerptId, ExcerptRange, MultiBuffer, }; use anyhow::{anyhow, Result}; use collections::{BTreeMap, Bound, HashMap, HashSet}; use futures::{channel::mpsc, SinkExt}; use gpui::{AppContext, EventEmitter, Model, ModelContext, Task}; use itertools::Itertools; use language::{ AutoindentMode, Buffer, BufferChunks, BufferRow, BufferSnapshot, Capability, Chunk, CursorShape, DiagnosticEntry, DiskState, File, IndentGuide, IndentSize, OffsetRangeExt, OffsetUtf16, Point, Selection, TextDimension, ToOffset as _, ToPoint as _, TransactionId, }; use smallvec::SmallVec; use std::{ any::type_name, borrow::Cow, cell::{Ref, RefCell}, cmp, fmt, future::Future, io, iter::{self, FromIterator}, mem, ops::{Range, RangeBounds, Sub}, str, sync::Arc, time::{Duration, Instant}, }; use sum_tree::{Bias, Cursor, SumTree}; use text::{locator::Locator, BufferId, Edit, Patch, TextSummary}; use util::post_inc; pub type ExcerptOffset = TypedOffset<ExcerptMap>; pub type ExcerptPoint = TypedPoint<ExcerptMap>; pub type ExcerptPointUtf16 = TypedPointUtf16<ExcerptMap>; pub type ExcerptRow = TypedRow<ExcerptMap>; #[cfg(any(test, feature = "test-support"))] use gpui::Context; const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize]; /// One or more [`Buffers`](Buffer) being edited in a single view. /// /// See <https://zed.dev/features#multi-buffers> pub struct ExcerptMap { /// A snapshot of the [`Excerpt`]s in the MultiBuffer. /// Use [`MultiBuffer::snapshot`] to get a up-to-date snapshot. snapshot: RefCell<ExcerptMapSnapshot>, /// Contains the state of the buffers being edited buffers: RefCell<HashMap<BufferId, BufferState>>, /// If true, the multi-buffer only contains a single [`Buffer`] and a single [`Excerpt`] pub(crate) singleton: bool, history: History, title: Option<String>, capability: Capability, edits: RefCell<Patch<ExcerptOffset>>, } /// 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 range within the buffer's diff base that this hunk corresponds to. pub diff_base_byte_range: Range<usize>, } #[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); } #[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, } struct BufferState { buffer: Model<Buffer>, last_version: clock::Global, last_non_text_state_update_count: usize, excerpts: Vec<Locator>, _subscriptions: [gpui::Subscription; 2], } /// The contents of a [`MultiBuffer`] at a single point in time. #[derive(Clone, Default)] pub struct ExcerptMapSnapshot { singleton: bool, excerpts: SumTree<Excerpt>, excerpt_ids: SumTree<ExcerptIdMapping>, trailing_excerpt_update_count: usize, non_text_state_update_count: usize, edit_count: usize, is_dirty: bool, has_deleted_file: bool, has_conflict: bool, show_headers: bool, } #[derive(Clone)] pub struct ExcerptInfo { pub id: ExcerptId, pub buffer: BufferSnapshot, pub buffer_id: BufferId, pub range: ExcerptRange<text::Anchor>, pub text_summary: TextSummary, } 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: Option<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.as_ref()) { (None, _) => true, (Some(_), None) => false, (Some(prev), Some(next)) => prev.buffer_id != next.buffer_id, } } } /// 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, excerpt_offset: ExcerptOffset, excerpt_position: ExcerptPoint, } #[derive(Clone, Debug)] struct ExcerptIdMapping { id: ExcerptId, locator: Locator, } #[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(Clone)] pub struct ExcerptMapRows<'a> { buffer_row_range: Range<u32>, excerpts: Cursor<'a, Excerpt, ExcerptPoint>, } pub struct ExcerptMapChunks<'a> { range: Range<ExcerptOffset>, excerpts: Cursor<'a, Excerpt, ExcerptOffset>, excerpt_chunks: Option<ExcerptChunks<'a>>, language_aware: bool, } pub struct ExcerptMapBytes<'a> { range: Range<ExcerptOffset>, excerpts: Cursor<'a, Excerpt, ExcerptOffset>, excerpt_bytes: Option<ExcerptBytes<'a>>, chunk: &'a [u8], } pub struct ReversedExcerptMapBytes<'a> { range: Range<ExcerptOffset>, excerpts: Cursor<'a, Excerpt, ExcerptOffset>, excerpt_bytes: Option<ExcerptBytes<'a>>, chunk: &'a [u8], } struct ExcerptChunks<'a> { excerpt_id: ExcerptId, content_chunks: BufferChunks<'a>, footer_height: usize, } struct ExcerptBytes<'a> { content_bytes: text::Bytes<'a>, padding_height: usize, reversed: bool, } struct BufferEdit { range: Range<usize>, new_text: Arc<str>, is_insertion: bool, original_indent_column: u32, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ExpandExcerptDirection { Up, Down, UpAndDown, } impl ExpandExcerptDirection { pub fn should_expand_up(&self) -> bool { match self { ExpandExcerptDirection::Up => true, ExpandExcerptDirection::Down => false, ExpandExcerptDirection::UpAndDown => true, } } pub fn should_expand_down(&self) -> bool { match self { ExpandExcerptDirection::Up => false, ExpandExcerptDirection::Down => true, ExpandExcerptDirection::UpAndDown => true, } } } #[derive(Clone, Debug, PartialEq)] pub struct MultiBufferIndentGuide { pub multibuffer_row_range: Range<MultiBufferRow>, pub buffer: IndentGuide, } impl std::ops::Deref for MultiBufferIndentGuide { type Target = IndentGuide; fn deref(&self) -> &Self::Target { &self.buffer } } impl ExcerptMap { pub fn new(capability: Capability) -> Self { Self { snapshot: RefCell::new(ExcerptMapSnapshot { show_headers: true, ..ExcerptMapSnapshot::default() }), buffers: RefCell::default(), edits: Default::default(), singleton: false, capability, title: None, history: History { next_transaction_id: clock::Lamport::default(), undo_stack: Vec::new(), redo_stack: Vec::new(), transaction_depth: 0, group_interval: Duration::from_millis(300), }, } } pub fn without_headers(capability: Capability) -> Self { Self { snapshot: Default::default(), buffers: Default::default(), edits: Default::default(), singleton: false, capability, history: History { next_transaction_id: Default::default(), undo_stack: Default::default(), redo_stack: Default::default(), transaction_depth: 0, group_interval: Duration::from_millis(300), }, title: Default::default(), } } pub fn clone(&self, new_cx: &mut ModelContext<MultiBuffer>) -> Self { let mut buffers = HashMap::default(); for (buffer_id, buffer_state) in self.buffers.borrow().iter() { buffers.insert( *buffer_id, BufferState { buffer: buffer_state.buffer.clone(), last_version: buffer_state.last_version.clone(), last_non_text_state_update_count: buffer_state.last_non_text_state_update_count, 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), ], }, ); } Self { snapshot: RefCell::new(self.snapshot.borrow().clone()), buffers: RefCell::new(buffers), singleton: self.singleton, capability: self.capability, history: self.history.clone(), edits: self.edits.clone(), title: self.title.clone(), } } pub fn with_title(mut self, title: String) -> Self { self.title = Some(title); self } pub fn read_only(&self) -> bool { self.capability == Capability::ReadOnly } pub fn singleton(buffer: Model<Buffer>, cx: &mut ModelContext<MultiBuffer>) -> Self { let mut this = Self::new(buffer.read(cx).capability()); this.singleton = true; this.push_excerpts( buffer, [ExcerptRange { context: text::Anchor::MIN..text::Anchor::MAX, primary: None, }], cx, ); this.snapshot.borrow_mut().singleton = true; this } /// Returns an up-to-date snapshot of the MultiBuffer. pub fn snapshot(&self, cx: &AppContext) -> ExcerptMapSnapshot { self.sync(cx); self.snapshot.borrow().clone() } pub fn read(&self, cx: &AppContext) -> Ref<ExcerptMapSnapshot> { self.sync(cx); self.snapshot.borrow() } pub fn as_singleton(&self) -> Option<Model<Buffer>> { if self.singleton { return Some( self.buffers .borrow() .values() .next() .unwrap() .buffer .clone(), ); } else { None } } pub fn is_singleton(&self) -> bool { self.singleton } pub fn is_dirty(&self, cx: &AppContext) -> bool { self.read(cx).is_dirty() } pub fn has_deleted_file(&self, cx: &AppContext) -> bool { self.read(cx).has_deleted_file() } pub fn has_conflict(&self, cx: &AppContext) -> bool { self.read(cx).has_conflict() } // The `is_empty` signature doesn't match what clippy expects #[allow(clippy::len_without_is_empty)] pub fn len(&self, cx: &AppContext) -> ExcerptOffset { self.read(cx).len() } pub fn is_empty(&self, cx: &AppContext) -> bool { self.len(cx).value != 0 } pub fn edit<I, T>( &self, edits: I, autoindent_mode: Option<AutoindentMode>, cx: &mut ModelContext<MultiBuffer>, ) where I: IntoIterator<Item = (Range<ExcerptOffset>, T)>, T: Into<Arc<str>>, { let snapshot = self.read(cx); let edits = edits .into_iter() .map(|(mut range, new_text)| { if range.start > range.end { mem::swap(&mut range.start, &mut range.end); } (range, new_text.into()) }) .collect::<Vec<_>>(); return edit_internal(self, snapshot, edits, autoindent_mode, cx); // Non-generic part of edit, hoisted out to avoid blowing up LLVM IR. fn edit_internal( this: &ExcerptMap, snapshot: Ref<ExcerptMapSnapshot>, edits: Vec<(Range<ExcerptOffset>, Arc<str>)>, mut autoindent_mode: Option<AutoindentMode>, cx: &mut ModelContext<MultiBuffer>, ) { if this.read_only() || this.buffers.borrow().is_empty() { return; } if let Some(buffer) = this.as_singleton() { buffer.update(cx, |buffer, cx| { buffer.edit( edits .into_iter() .map(|(range, text)| (range.start.value..range.end.value, text)), autoindent_mode, cx, ); }); cx.emit(Event::ExcerptsEdited { ids: this.excerpt_ids(), }); return; } let original_indent_columns = match &mut autoindent_mode { Some(AutoindentMode::Block { original_indent_columns, }) => mem::take(original_indent_columns), _ => Default::default(), }; let (buffer_edits, edited_excerpt_ids) = this.convert_edits_to_buffer_edits(edits, &snapshot, &original_indent_columns); drop(snapshot);