mod anchor; pub use anchor::{Anchor, AnchorRangeExt};use anyhow::Result;use clock::ReplicaId;use collections::{HashMap, HashSet};use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task};use language::{ Buffer, BufferChunks, BufferSnapshot, Chunk, DiagnosticEntry, Event, File, Language, Outline, OutlineItem, Selection, ToOffset as _, ToPoint as _, TransactionId,};use std::{ cell::{Ref, RefCell}, cmp, fmt, io, iter::{self, FromIterator}, ops::{Range, Sub}, str, sync::Arc, time::{Duration, Instant},};use sum_tree::{Bias, Cursor, SumTree};use text::{ locator::Locator, rope::TextDimension, subscription::{Subscription, Topic}, AnchorRangeExt as _, Edit, Point, PointUtf16, TextSummary,};use theme::SyntaxTheme;use util::post_inc; const NEWLINES: &'static [u8] = &[b'\n'; u8::MAX as usize]; pub type ExcerptId = Locator; pub struct MultiBuffer { snapshot: RefCell<MultiBufferSnapshot>, buffers: RefCell<HashMap<usize, BufferState>>, subscriptions: Topic, singleton: bool, replica_id: ReplicaId, history: History,} struct History { next_transaction_id: usize, undo_stack: Vec<Transaction>, redo_stack: Vec<Transaction>, transaction_depth: usize, group_interval: Duration,} struct Transaction { id: usize, buffer_transactions: HashSet<(usize, text::TransactionId)>, first_edit_at: Instant, last_edit_at: Instant,} pub trait ToOffset: 'static + fmt::Debug { fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize;} pub trait ToPoint: 'static + fmt::Debug { fn to_point(&self, snapshot: &MultiBufferSnapshot) -> Point;} struct BufferState { buffer: ModelHandle<Buffer>, last_version: clock::Global, last_parse_count: usize, last_selections_update_count: usize, last_diagnostics_update_count: usize, excerpts: Vec<ExcerptId>, _subscriptions: [gpui::Subscription; 2],} #[derive(Clone, Default)]pub struct MultiBufferSnapshot { excerpts: SumTree<Excerpt>, parse_count: usize, diagnostics_update_count: usize, is_dirty: bool, has_conflict: bool,} pub struct ExcerptProperties<'a, T> { pub buffer: &'a ModelHandle<Buffer>, pub range: Range<T>,} #[derive(Clone)]struct Excerpt { id: ExcerptId, buffer_id: usize, buffer: BufferSnapshot, range: Range<text::Anchor>, max_buffer_row: u32, text_summary: TextSummary, has_trailing_newline: bool,} #[derive(Clone, Debug, Default)]struct ExcerptSummary { excerpt_id: ExcerptId, max_buffer_row: u32, text: TextSummary,} pub struct MultiBufferRows<'a> { buffer_row_range: Range<u32>, excerpts: Cursor<'a, Excerpt, Point>,} pub struct MultiBufferChunks<'a> { range: Range<usize>, excerpts: Cursor<'a, Excerpt, usize>, excerpt_chunks: Option<ExcerptChunks<'a>>, theme: Option<&'a SyntaxTheme>,} pub struct MultiBufferBytes<'a> { range: Range<usize>, excerpts: Cursor<'a, Excerpt, usize>, excerpt_bytes: Option<ExcerptBytes<'a>>, chunk: &'a [u8],} struct ExcerptChunks<'a> { content_chunks: BufferChunks<'a>, footer_height: usize,} struct ExcerptBytes<'a> { content_bytes: language::rope::Bytes<'a>, footer_height: usize,} impl MultiBuffer { pub fn new(replica_id: ReplicaId) -> Self { Self { snapshot: Default::default(), buffers: Default::default(), subscriptions: Default::default(), singleton: false, replica_id, history: History { next_transaction_id: Default::default(), undo_stack: Default::default(), redo_stack: Default::default(), transaction_depth: 0, group_interval: Duration::from_millis(300), }, } } pub fn singleton(buffer: ModelHandle<Buffer>, cx: &mut ModelContext<Self>) -> Self { let mut this = Self::new(buffer.read(cx).replica_id()); this.singleton = true; this.push_excerpt( ExcerptProperties { buffer: &buffer, range: text::Anchor::min()..text::Anchor::max(), }, cx, ); this } #[cfg(any(test, feature = "test-support"))] pub fn build_simple(text: &str, cx: &mut gpui::MutableAppContext) -> ModelHandle<Self> { let buffer = cx.add_model(|cx| Buffer::new(0, text, cx)); cx.add_model(|cx| Self::singleton(buffer, cx)) } #[cfg(any(test, feature = "test-support"))] pub fn build_random( mut rng: &mut impl rand::Rng, cx: &mut gpui::MutableAppContext, ) -> ModelHandle<Self> { use rand::prelude::*; use std::env; use text::RandomCharIter; let max_excerpts = env::var("MAX_EXCERPTS") .map(|i| i.parse().expect("invalid `MAX_EXCERPTS` variable")) .unwrap_or(5); let excerpts = rng.gen_range(1..=max_excerpts); cx.add_model(|cx| { let mut multibuffer = MultiBuffer::new(0); let mut buffers = Vec::new(); for _ in 0..excerpts { let buffer_handle = if rng.gen() || buffers.is_empty() { let text = RandomCharIter::new(&mut rng).take(10).collect::<String>(); buffers.push(cx.add_model(|cx| Buffer::new(0, text, cx))); let buffer = buffers.last().unwrap(); log::info!( "Creating new buffer {} with text: {:?}", buffer.id(), buffer.read(cx).text() ); buffers.last().unwrap() } else { buffers.choose(rng).unwrap() }; let buffer = buffer_handle.read(cx); let end_ix = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right); let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left); let header_height = rng.gen_range(0..=5); log::info!( "Inserting excerpt from buffer {} with header height {} and range {:?}: {:?}", buffer_handle.id(), header_height, start_ix..end_ix, &buffer.text()[start_ix..end_ix] ); multibuffer.push_excerpt( ExcerptProperties { buffer: buffer_handle, range: start_ix..end_ix, }, cx, ); } multibuffer }) } pub fn replica_id(&self) -> ReplicaId { self.replica_id } pub fn snapshot(&self, cx: &AppContext) -> MultiBufferSnapshot { self.sync(cx); self.snapshot.borrow().clone() } pub fn read(&self, cx: &AppContext) -> Ref<MultiBufferSnapshot> { self.sync(cx); self.snapshot.borrow() } pub fn as_singleton(&self) -> Option<ModelHandle<Buffer>> { if self.singleton { return Some( self.buffers .borrow() .values() .next() .unwrap() .buffer .clone(), ); } else { None } } pub fn subscribe(&mut self) -> Subscription { self.subscriptions.subscribe() } pub fn edit<I, S, T>(&mut self, ranges: I, new_text: T, cx: &mut ModelContext<Self>) where I: IntoIterator<Item = Range<S>>, S: ToOffset, T: Into<String>, { self.edit_internal(ranges, new_text, false, cx) } pub fn edit_with_autoindent<I, S, T>( &mut self, ranges: I, new_text: T, cx: &mut ModelContext<Self>, ) where I: IntoIterator<Item = Range<S>>, S: ToOffset, T: Into<String>, { self.edit_internal(ranges, new_text, true, cx) } pub fn edit_internal<I, S, T>( &mut self, ranges_iter: I, new_text: T, autoindent: bool, cx: &mut ModelContext<Self>, ) where I: IntoIterator<Item = Range<S>>, S: ToOffset, T: Into<String>, { if let Some(buffer) = self.as_singleton() { let snapshot = self.read(cx); let ranges = ranges_iter .into_iter() .map(|range| range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot)); return buffer.update(cx, |buffer, cx| { if autoindent { buffer.edit_with_autoindent(ranges, new_text, cx) } else { buffer.edit(ranges, new_text, cx) } }); } let snapshot = self.read(cx); let mut buffer_edits: HashMap<usize, Vec<(Range<usize>, bool)>> = Default::default(); let mut cursor = snapshot.excerpts.cursor::<usize>(); for range in ranges_iter { let start = range.start.to_offset(&snapshot); let end = range.end.to_offset(&snapshot); cursor.seek(&start, Bias::Right, &()); if cursor.item().is_none() && start == *cursor.start() { cursor.prev(&()); } let start_excerpt = cursor.item().expect("start offset out of bounds"); let start_overshoot = start - cursor.start(); let buffer_start = start_excerpt.range.start.to_offset(&start_excerpt.buffer) + start_overshoot; cursor.seek(&end, Bias::Right, &()); if cursor.item().is_none() && end == *cursor.start() { cursor.prev(&()); } let end_excerpt = cursor.item().expect("end offset out of bounds"); let end_overshoot = end - cursor.start(); let buffer_end = end_excerpt.range.start.to_offset(&end_excerpt.buffer) + end_overshoot; if start_excerpt.id == end_excerpt.id { buffer_edits .entry(start_excerpt.buffer_id) .or_insert(Vec::new()) .push((buffer_start..buffer_end, true)); } else { let start_excerpt_range = buffer_start..start_excerpt.range.end.to_offset(&start_excerpt.buffer); let end_excerpt_range = end_excerpt.range.start.to_offset(&end_excerpt.buffer)..buffer_end; buffer_edits .entry(start_excerpt.buffer_id) .or_insert(Vec::new()) .push((start_excerpt_range, true)); buffer_edits .entry(end_excerpt.buffer_id) .or_insert(Vec::new()) .push((end_excerpt_range, false)); cursor.seek(&start, Bias::Right, &()); cursor.next(&()); while let Some(excerpt) = cursor.item() { if excerpt.id == end_excerpt.id { break; } buffer_edits .entry(excerpt.buffer_id) .or_insert(Vec::new()) .push((excerpt.range.to_offset(&excerpt.buffer), false)); cursor.next(&()); } } } let new_text = new_text.into(); for (buffer_id, mut edits) in buffer_edits { edits.sort_unstable_by_key(|(range, _)| range.start); self.buffers.borrow()[&buffer_id] .buffer .update(cx, |buffer, cx| { let mut edits = edits.into_iter().peekable(); let mut insertions = Vec::new(); let mut deletions = Vec::new(); while let Some((mut range, mut is_insertion)) = edits.next() { while let Some((next_range, next_is_insertion)) = edits.peek() { if range.end >= next_range.start { range.end = cmp::max(next_range.end, range.end); is_insertion |= *next_is_insertion; edits.next(); } else { break; } } if is_insertion { insertions.push( buffer.anchor_before(range.start)..buffer.anchor_before(range.end), ); } else if !range.is_empty() { deletions.push( buffer.anchor_before(range.start)..buffer.anchor_before(range.end), ); } } if autoindent { buffer.edit_with_autoindent(deletions, "", cx); buffer.edit_with_autoindent(insertions, new_text.clone(), cx); } else { buffer.edit(deletions, "", cx); buffer.edit(insertions, new_text.clone(), cx); } }) } } pub fn start_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> { self.start_transaction_at(Instant::now(), cx) } pub(crate) fn start_transaction_at( &mut self, now: Instant, cx: &mut ModelContext<Self>, ) -> Option<TransactionId> { if let Some(buffer) = self.as_singleton() { return buffer.update(cx, |buffer, _| buffer.start_transaction_at(now)); } for BufferState { buffer, .. } in self.buffers.borrow().values() { buffer.update(cx, |buffer, _| buffer.start_transaction_at(now)); } self.history.start_transaction(now) } pub fn end_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> { self.end_transaction_at(Instant::now(), cx) } pub(crate) fn end_transaction_at( &mut self, now: Instant, cx: &mut ModelContext<Self>, ) -> Option<TransactionId> { if let Some(buffer) = self.as_singleton() { return buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx)); } let mut buffer_transactions = HashSet::default(); for BufferState { buffer, .. } in self.buffers.borrow().values() { if let Some(transaction_id) = buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx)) { buffer_transactions.insert((buffer.id(), transaction_id)); } } if self.history.end_transaction(now, buffer_transactions) { let transaction_id = self.history.group().unwrap(); Some(transaction_id) } else { None } } pub fn set_active_selections( &mut self, selections: &[Selection<Anchor>], cx: &mut ModelContext<Self>, ) { let mut selections_by_buffer: HashMap<usize, Vec<Selection<text::Anchor>>> = Default::default(); let snapshot = self.read(cx); let mut cursor = snapshot.excerpts.cursor::<Option<&ExcerptId>>(); for selection in selections { cursor.seek(&Some(&selection.start.excerpt_id), Bias::Left, &()); while let Some(excerpt) = cursor.item() { if excerpt.id > selection.end.excerpt_id { break; } let mut start = excerpt.range.start.clone(); let mut end = excerpt.range.end.clone(); if excerpt.id == selection.start.excerpt_id { start = selection.start.text_anchor.clone(); } if excerpt.id == selection.end.excerpt_id { end = selection.end.text_anchor.clone(); } selections_by_buffer .entry(excerpt.buffer_id) .or_default() .push(Selection { id: selection.id, start, end, reversed: selection.reversed, goal: selection.goal, }); cursor.next(&()); } } for (buffer_id, buffer_state) in self.buffers.borrow().iter() { if !selections_by_buffer.contains_key(buffer_id) {Lines 1–500 of 3180. Load more
mod anchor; pub use anchor::{Anchor, AnchorRangeExt}; use anyhow::Result; use clock::ReplicaId; use collections::{HashMap, HashSet}; use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task}; use language::{ Buffer, BufferChunks, BufferSnapshot, Chunk, DiagnosticEntry, Event, File, Language, Outline, OutlineItem, Selection, ToOffset as _, ToPoint as _, TransactionId, }; use std::{ cell::{Ref, RefCell}, cmp, fmt, io, iter::{self, FromIterator}, ops::{Range, Sub}, str, sync::Arc, time::{Duration, Instant}, }; use sum_tree::{Bias, Cursor, SumTree}; use text::{ locator::Locator, rope::TextDimension, subscription::{Subscription, Topic}, AnchorRangeExt as _, Edit, Point, PointUtf16, TextSummary, }; use theme::SyntaxTheme; use util::post_inc; const NEWLINES: &'static [u8] = &[b'\n'; u8::MAX as usize]; pub type ExcerptId = Locator; pub struct MultiBuffer { snapshot: RefCell<MultiBufferSnapshot>, buffers: RefCell<HashMap<usize, BufferState>>, subscriptions: Topic, singleton: bool, replica_id: ReplicaId, history: History, } struct History { next_transaction_id: usize, undo_stack: Vec<Transaction>, redo_stack: Vec<Transaction>, transaction_depth: usize, group_interval: Duration, } struct Transaction { id: usize, buffer_transactions: HashSet<(usize, text::TransactionId)>, first_edit_at: Instant, last_edit_at: Instant, } pub trait ToOffset: 'static + fmt::Debug { fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize; } pub trait ToPoint: 'static + fmt::Debug { fn to_point(&self, snapshot: &MultiBufferSnapshot) -> Point; } struct BufferState { buffer: ModelHandle<Buffer>, last_version: clock::Global, last_parse_count: usize, last_selections_update_count: usize, last_diagnostics_update_count: usize, excerpts: Vec<ExcerptId>, _subscriptions: [gpui::Subscription; 2], } #[derive(Clone, Default)] pub struct MultiBufferSnapshot { excerpts: SumTree<Excerpt>, parse_count: usize, diagnostics_update_count: usize, is_dirty: bool, has_conflict: bool, } pub struct ExcerptProperties<'a, T> { pub buffer: &'a ModelHandle<Buffer>, pub range: Range<T>, } #[derive(Clone)] struct Excerpt { id: ExcerptId, buffer_id: usize, buffer: BufferSnapshot, range: Range<text::Anchor>, max_buffer_row: u32, text_summary: TextSummary, has_trailing_newline: bool, } #[derive(Clone, Debug, Default)] struct ExcerptSummary { excerpt_id: ExcerptId, max_buffer_row: u32, text: TextSummary, } pub struct MultiBufferRows<'a> { buffer_row_range: Range<u32>, excerpts: Cursor<'a, Excerpt, Point>, } pub struct MultiBufferChunks<'a> { range: Range<usize>, excerpts: Cursor<'a, Excerpt, usize>, excerpt_chunks: Option<ExcerptChunks<'a>>, theme: Option<&'a SyntaxTheme>, } pub struct MultiBufferBytes<'a> { range: Range<usize>, excerpts: Cursor<'a, Excerpt, usize>, excerpt_bytes: Option<ExcerptBytes<'a>>, chunk: &'a [u8], } struct ExcerptChunks<'a> { content_chunks: BufferChunks<'a>, footer_height: usize, } struct ExcerptBytes<'a> { content_bytes: language::rope::Bytes<'a>, footer_height: usize, } impl MultiBuffer { pub fn new(replica_id: ReplicaId) -> Self { Self { snapshot: Default::default(), buffers: Default::default(), subscriptions: Default::default(), singleton: false, replica_id, history: History { next_transaction_id: Default::default(), undo_stack: Default::default(), redo_stack: Default::default(), transaction_depth: 0, group_interval: Duration::from_millis(300), }, } } pub fn singleton(buffer: ModelHandle<Buffer>, cx: &mut ModelContext<Self>) -> Self { let mut this = Self::new(buffer.read(cx).replica_id()); this.singleton = true; this.push_excerpt( ExcerptProperties { buffer: &buffer, range: text::Anchor::min()..text::Anchor::max(), }, cx, ); this } #[cfg(any(test, feature = "test-support"))] pub fn build_simple(text: &str, cx: &mut gpui::MutableAppContext) -> ModelHandle<Self> { let buffer = cx.add_model(|cx| Buffer::new(0, text, cx)); cx.add_model(|cx| Self::singleton(buffer, cx)) } #[cfg(any(test, feature = "test-support"))] pub fn build_random( mut rng: &mut impl rand::Rng, cx: &mut gpui::MutableAppContext, ) -> ModelHandle<Self> { use rand::prelude::*; use std::env; use text::RandomCharIter; let max_excerpts = env::var("MAX_EXCERPTS") .map(|i| i.parse().expect("invalid `MAX_EXCERPTS` variable")) .unwrap_or(5); let excerpts = rng.gen_range(1..=max_excerpts); cx.add_model(|cx| { let mut multibuffer = MultiBuffer::new(0); let mut buffers = Vec::new(); for _ in 0..excerpts { let buffer_handle = if rng.gen() || buffers.is_empty() { let text = RandomCharIter::new(&mut rng).take(10).collect::<String>(); buffers.push(cx.add_model(|cx| Buffer::new(0, text, cx))); let buffer = buffers.last().unwrap(); log::info!( "Creating new buffer {} with text: {:?}", buffer.id(), buffer.read(cx).text() ); buffers.last().unwrap() } else { buffers.choose(rng).unwrap() }; let buffer = buffer_handle.read(cx); let end_ix = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right); let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left); let header_height = rng.gen_range(0..=5); log::info!( "Inserting excerpt from buffer {} with header height {} and range {:?}: {:?}", buffer_handle.id(), header_height, start_ix..end_ix, &buffer.text()[start_ix..end_ix] ); multibuffer.push_excerpt( ExcerptProperties { buffer: buffer_handle, range: start_ix..end_ix, }, cx, ); } multibuffer }) } pub fn replica_id(&self) -> ReplicaId { self.replica_id } pub fn snapshot(&self, cx: &AppContext) -> MultiBufferSnapshot { self.sync(cx); self.snapshot.borrow().clone() } pub fn read(&self, cx: &AppContext) -> Ref<MultiBufferSnapshot> { self.sync(cx); self.snapshot.borrow() } pub fn as_singleton(&self) -> Option<ModelHandle<Buffer>> { if self.singleton { return Some( self.buffers .borrow() .values() .next() .unwrap() .buffer .clone(), ); } else { None } } pub fn subscribe(&mut self) -> Subscription { self.subscriptions.subscribe() } pub fn edit<I, S, T>(&mut self, ranges: I, new_text: T, cx: &mut ModelContext<Self>) where I: IntoIterator<Item = Range<S>>, S: ToOffset, T: Into<String>, { self.edit_internal(ranges, new_text, false, cx) } pub fn edit_with_autoindent<I, S, T>( &mut self, ranges: I, new_text: T, cx: &mut ModelContext<Self>, ) where I: IntoIterator<Item = Range<S>>, S: ToOffset, T: Into<String>, { self.edit_internal(ranges, new_text, true, cx) } pub fn edit_internal<I, S, T>( &mut self, ranges_iter: I, new_text: T, autoindent: bool, cx: &mut ModelContext<Self>, ) where I: IntoIterator<Item = Range<S>>, S: ToOffset, T: Into<String>, { if let Some(buffer) = self.as_singleton() { let snapshot = self.read(cx); let ranges = ranges_iter .into_iter() .map(|range| range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot)); return buffer.update(cx, |buffer, cx| { if autoindent { buffer.edit_with_autoindent(ranges, new_text, cx) } else { buffer.edit(ranges, new_text, cx) } }); } let snapshot = self.read(cx); let mut buffer_edits: HashMap<usize, Vec<(Range<usize>, bool)>> = Default::default(); let mut cursor = snapshot.excerpts.cursor::<usize>(); for range in ranges_iter { let start = range.start.to_offset(&snapshot); let end = range.end.to_offset(&snapshot); cursor.seek(&start, Bias::Right, &()); if cursor.item().is_none() && start == *cursor.start() { cursor.prev(&()); } let start_excerpt = cursor.item().expect("start offset out of bounds"); let start_overshoot = start - cursor.start(); let buffer_start = start_excerpt.range.start.to_offset(&start_excerpt.buffer) + start_overshoot; cursor.seek(&end, Bias::Right, &()); if cursor.item().is_none() && end == *cursor.start() { cursor.prev(&()); } let end_excerpt = cursor.item().expect("end offset out of bounds"); let end_overshoot = end - cursor.start(); let buffer_end = end_excerpt.range.start.to_offset(&end_excerpt.buffer) + end_overshoot; if start_excerpt.id == end_excerpt.id { buffer_edits .entry(start_excerpt.buffer_id) .or_insert(Vec::new()) .push((buffer_start..buffer_end, true)); } else { let start_excerpt_range = buffer_start..start_excerpt.range.end.to_offset(&start_excerpt.buffer); let end_excerpt_range = end_excerpt.range.start.to_offset(&end_excerpt.buffer)..buffer_end; buffer_edits .entry(start_excerpt.buffer_id) .or_insert(Vec::new()) .push((start_excerpt_range, true)); buffer_edits .entry(end_excerpt.buffer_id) .or_insert(Vec::new()) .push((end_excerpt_range, false)); cursor.seek(&start, Bias::Right, &()); cursor.next(&()); while let Some(excerpt) = cursor.item() { if excerpt.id == end_excerpt.id { break; } buffer_edits .entry(excerpt.buffer_id) .or_insert(Vec::new()) .push((excerpt.range.to_offset(&excerpt.buffer), false)); cursor.next(&()); } } } let new_text = new_text.into(); for (buffer_id, mut edits) in buffer_edits { edits.sort_unstable_by_key(|(range, _)| range.start); self.buffers.borrow()[&buffer_id] .buffer .update(cx, |buffer, cx| { let mut edits = edits.into_iter().peekable(); let mut insertions = Vec::new(); let mut deletions = Vec::new(); while let Some((mut range, mut is_insertion)) = edits.next() { while let Some((next_range, next_is_insertion)) = edits.peek() { if range.end >= next_range.start { range.end = cmp::max(next_range.end, range.end); is_insertion |= *next_is_insertion; edits.next(); } else { break; } } if is_insertion { insertions.push( buffer.anchor_before(range.start)..buffer.anchor_before(range.end), ); } else if !range.is_empty() { deletions.push( buffer.anchor_before(range.start)..buffer.anchor_before(range.end), ); } } if autoindent { buffer.edit_with_autoindent(deletions, "", cx); buffer.edit_with_autoindent(insertions, new_text.clone(), cx); } else { buffer.edit(deletions, "", cx); buffer.edit(insertions, new_text.clone(), cx); } }) } } pub fn start_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> { self.start_transaction_at(Instant::now(), cx) } pub(crate) fn start_transaction_at( &mut self, now: Instant, cx: &mut ModelContext<Self>, ) -> Option<TransactionId> { if let Some(buffer) = self.as_singleton() { return buffer.update(cx, |buffer, _| buffer.start_transaction_at(now)); } for BufferState { buffer, .. } in self.buffers.borrow().values() { buffer.update(cx, |buffer, _| buffer.start_transaction_at(now)); } self.history.start_transaction(now) } pub fn end_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> { self.end_transaction_at(Instant::now(), cx) } pub(crate) fn end_transaction_at( &mut self, now: Instant, cx: &mut ModelContext<Self>, ) -> Option<TransactionId> { if let Some(buffer) = self.as_singleton() { return buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx)); } let mut buffer_transactions = HashSet::default(); for BufferState { buffer, .. } in self.buffers.borrow().values() { if let Some(transaction_id) = buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx)) { buffer_transactions.insert((buffer.id(), transaction_id)); } } if self.history.end_transaction(now, buffer_transactions) { let transaction_id = self.history.group().unwrap(); Some(transaction_id) } else { None } } pub fn set_active_selections( &mut self, selections: &[Selection<Anchor>], cx: &mut ModelContext<Self>, ) { let mut selections_by_buffer: HashMap<usize, Vec<Selection<text::Anchor>>> = Default::default(); let snapshot = self.read(cx); let mut cursor = snapshot.excerpts.cursor::<Option<&ExcerptId>>(); for selection in selections { cursor.seek(&Some(&selection.start.excerpt_id), Bias::Left, &()); while let Some(excerpt) = cursor.item() { if excerpt.id > selection.end.excerpt_id { break; } let mut start = excerpt.range.start.clone(); let mut end = excerpt.range.end.clone(); if excerpt.id == selection.start.excerpt_id { start = selection.start.text_anchor.clone(); } if excerpt.id == selection.end.excerpt_id { end = selection.end.text_anchor.clone(); } selections_by_buffer .entry(excerpt.buffer_id) .or_default() .push(Selection { id: selection.id, start, end, reversed: selection.reversed, goal: selection.goal, }); cursor.next(&()); } } for (buffer_id, buffer_state) in self.buffers.borrow().iter() { if !selections_by_buffer.contains_key(buffer_id) {