use gpui::{App, Context, Entity};
use language::{self, Buffer, TransactionId};
use std::{
collections::HashMap,
ops::{AddAssign, Range, Sub},
time::{Duration, Instant},
};
use sum_tree::Bias;
use text::BufferId;
use crate::{BufferState, MultiBufferDimension};
use super::{Event, ExcerptSummary, MultiBuffer};
#[derive(Clone)]
pub(super) struct History {
next_transaction_id: TransactionId,
undo_stack: Vec<Transaction>,
redo_stack: Vec<Transaction>,
transaction_depth: usize,
group_interval: Duration,
}
impl Default for History {
fn default() -> Self {
History {
next_transaction_id: clock::Lamport::MIN,
undo_stack: Vec::new(),
redo_stack: Vec::new(),
transaction_depth: 0,
group_interval: Duration::from_millis(300),
}
}
}
#[derive(Clone)]
struct Transaction {
id: TransactionId,
buffer_transactions: HashMap<BufferId, text::TransactionId>,
first_edit_at: Instant,
last_edit_at: Instant,
suppress_grouping: bool,
}
impl History {
fn start_transaction(&mut self, now: Instant) -> Option<TransactionId> {
self.transaction_depth += 1;
if self.transaction_depth == 1 {
let id = self.next_transaction_id.tick();
self.undo_stack.push(Transaction {
id,
buffer_transactions: Default::default(),
first_edit_at: now,
last_edit_at: now,
suppress_grouping: false,
});
Some(id)
} else {
None
}
}
fn end_transaction(
&mut self,
now: Instant,
buffer_transactions: HashMap<BufferId, text::TransactionId>,
) -> bool {
assert_ne!(self.transaction_depth, 0);
self.transaction_depth -= 1;
if self.transaction_depth == 0 {
if buffer_transactions.is_empty() {
self.undo_stack.pop();
false
} else {
self.redo_stack.clear();
let transaction = self.undo_stack.last_mut().unwrap();
transaction.last_edit_at = now;
for (buffer_id, transaction_id) in buffer_transactions {
transaction
.buffer_transactions
.entry(buffer_id)
.or_insert(transaction_id);
}
true
}
} else {
false
}
}
fn push_transaction<'a, T>(
&mut self,
buffer_transactions: T,
now: Instant,
cx: &Context<MultiBuffer>,
) where
T: IntoIterator<Item = (&'a Entity<Buffer>, &'a language::Transaction)>,
{
assert_eq!(self.transaction_depth, 0);
let transaction = Transaction {
id: self.next_transaction_id.tick(),
buffer_transactions: buffer_transactions
.into_iter()
.map(|(buffer, transaction)| (buffer.read(cx).remote_id(), transaction.id))
.collect(),
first_edit_at: now,
last_edit_at: now,
suppress_grouping: false,
};
if !transaction.buffer_transactions.is_empty() {
self.undo_stack.push(transaction);
self.redo_stack.clear();
}
}
fn finalize_last_transaction(&mut self) {
if let Some(transaction) = self.undo_stack.last_mut() {
transaction.suppress_grouping = true;
}
}
fn forget(&mut self, transaction_id: TransactionId) -> Option<Transaction> {
if let Some(ix) = self
.undo_stack
.iter()
.rposition(|transaction| transaction.id == transaction_id)
{
Some(self.undo_stack.remove(ix))
} else if let Some(ix) = self
.redo_stack
.iter()
.rposition(|transaction| transaction.id == transaction_id)
{
Some(self.redo_stack.remove(ix))
} else {
None
}
}
fn transaction(&self, transaction_id: TransactionId) -> Option<&Transaction> {
self.undo_stack
.iter()
.find(|transaction| transaction.id == transaction_id)
.or_else(|| {
self.redo_stack
.iter()
.find(|transaction| transaction.id == transaction_id)
})
}
fn transaction_mut(&mut self, transaction_id: TransactionId) -> Option<&mut Transaction> {
self.undo_stack
.iter_mut()
.find(|transaction| transaction.id == transaction_id)
.or_else(|| {
self.redo_stack
.iter_mut()
.find(|transaction| transaction.id == transaction_id)
})
}
fn pop_undo(&mut self) -> Option<&mut Transaction> {
assert_eq!(self.transaction_depth, 0);
if let Some(transaction) = self.undo_stack.pop() {
self.redo_stack.push(transaction);
self.redo_stack.last_mut()
} else {
None
}
}
fn pop_redo(&mut self) -> Option<&mut Transaction> {
assert_eq!(self.transaction_depth, 0);
if let Some(transaction) = self.redo_stack.pop() {
self.undo_stack.push(transaction);
self.undo_stack.last_mut()
} else {
None
}
}
fn remove_from_undo(&mut self, transaction_id: TransactionId) -> Option<&Transaction> {
let ix = self
.undo_stack
.iter()
.rposition(|transaction| transaction.id == transaction_id)?;
let transaction = self.undo_stack.remove(ix);
self.redo_stack.push(transaction);
self.redo_stack.last()
}
fn group(&mut self) -> Option<TransactionId> {
let mut count = 0;
let mut transactions = self.undo_stack.iter();
if let Some(mut transaction) = transactions.next_back() {
while let Some(prev_transaction) = transactions.next_back() {
if !prev_transaction.suppress_grouping
&& transaction.first_edit_at - prev_transaction.last_edit_at
<= self.group_interval
{
transaction = prev_transaction;
count += 1;
} else {
break;
}
}
}
self.group_trailing(count)
}
fn group_until(&mut self, transaction_id: TransactionId) {
let mut count = 0;
for transaction in self.undo_stack.iter().rev() {
if transaction.id == transaction_id {
self.group_trailing(count);
break;
} else if transaction.suppress_grouping {
break;
} else {
count += 1;
}
}
}
fn group_trailing(&mut self, n: usize) -> Option<TransactionId> {
let new_len = self.undo_stack.len() - n;
let (transactions_to_keep, transactions_to_merge) = self.undo_stack.split_at_mut(new_len);
if let Some(last_transaction) = transactions_to_keep.last_mut() {
if let Some(transaction) = transactions_to_merge.last() {
last_transaction.last_edit_at = transaction.last_edit_at;
}
for to_merge in transactions_to_merge {
for (buffer_id, transaction_id) in &to_merge.buffer_transactions {
last_transaction
.buffer_transactions
.entry(*buffer_id)
.or_insert(*transaction_id);
}
}
}
self.undo_stack.truncate(new_len);
self.undo_stack.last().map(|t| t.id)
}
pub(super) fn transaction_depth(&self) -> usize {
self.transaction_depth
}
pub fn set_group_interval(&mut self, group_interval: Duration) {
self.group_interval = group_interval;
}
}
impl MultiBuffer {
pub fn start_transaction(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> {
self.start_transaction_at(Instant::now(), cx)
}
pub fn start_transaction_at(
&mut self,
now: Instant,
cx: &mut Context<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.values() {
buffer.update(cx, |buffer, _| buffer.start_transaction_at(now));
}
self.history.start_transaction(now)
}
pub fn last_transaction_id(&self, cx: &App) -> Option<TransactionId> {
if let Some(buffer) = self.as_singleton() {
buffer
.read(cx)
.peek_undo_stack()
.map(|history_entry| history_entry.transaction_id())
} else {
let last_transaction = self.history.undo_stack.last()?;
Some(last_transaction.id)
}
}
pub fn end_transaction(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> {
self.end_transaction_at(Instant::now(), cx)
}
pub fn end_transaction_at(
&mut self,
now: Instant,
cx: &mut Context<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 = HashMap::default();
for BufferState { buffer, .. } in self.buffers.values() {
if let Some(transaction_id) =
buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
{
buffer_transactions.insert(buffer.read(cx).remote_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 edited_ranges_for_transaction<D>(
&self,
transaction_id: TransactionId,
cx: &App,
) -> Vec<Range<D>>
where
D: MultiBufferDimension
+ Ord
+ Sub<D, Output = D::TextDimension>
+ AddAssign<D::TextDimension>,
D::TextDimension: PartialOrd + Sub<D::TextDimension, Output = D::TextDimension>,
{
let Some(transaction) = self.history.transaction(transaction_id) else {
return Vec::new();
};
let mut ranges = Vec::new();
let snapshot = self.read(cx);
let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>(());
for (buffer_id, buffer_transaction) in &transaction.buffer_transactions {
let Some(buffer_state) = self.buffers.get(buffer_id) else {
continue;
};
let buffer = buffer_state.buffer.read(cx);
for range in
buffer.edited_ranges_for_transaction_id::<D::TextDimension>(*buffer_transaction)
{
for excerpt_id in &buffer_state.excerpts {
cursor.seek(excerpt_id, Bias::Left);
if let Some(excerpt) = cursor.item()
&& excerpt.locator == *excerpt_id
{
let excerpt_buffer_start = excerpt
.range
.context
.start
.summary::<D::TextDimension>(buffer);
let excerpt_buffer_end = excerpt
.range
.context
.end
.summary::<D::TextDimension>(buffer);
let excerpt_range = excerpt_buffer_start..excerpt_buffer_end;
if excerpt_range.contains(&range.start)
&& excerpt_range.contains(&range.end)
{
let excerpt_start = D::from_summary(&cursor.start().text);
let mut start = excerpt_start;
start += range.start - excerpt_buffer_start;
let mut end = excerpt_start;
end += range.end - excerpt_buffer_start;
ranges.push(start..end);
break;
}
}
}
}
}
ranges.sort_by_key(|range| range.start);
ranges
}
pub fn merge_transactions(
&mut self,
transaction: TransactionId,
destination: TransactionId,
cx: &mut Context<Self>,
) {
if let Some(buffer) = self.as_singleton() {
buffer.update(cx, |buffer, _| {
buffer.merge_transactions(transaction, destination)
});
} else if let Some(transaction) = self.history.forget(transaction)
&& let Some(destination) = self.history.transaction_mut(destination)
{
for (buffer_id, buffer_transaction_id) in transaction.buffer_transactions {
if let Some(destination_buffer_transaction_id) =
destination.buffer_transactions.get(&buffer_id)
{
if let Some(state) = self.buffers.get(&buffer_id) {
state.buffer.update(cx, |buffer, _| {
buffer.merge_transactions(
buffer_transaction_id,
*destination_buffer_transaction_id,
)
});
}
} else {
destination
.buffer_transactions
.insert(buffer_id, buffer_transaction_id);
}
}
}
}
pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
self.history.finalize_last_transaction();
for BufferState { buffer, .. } in self.buffers.values() {
buffer.update(cx, |buffer, _| {
buffer.finalize_last_transaction();
});
}
}
pub fn push_transaction<'a, T>(&mut self, buffer_transactions: T, cx: &Context<Self>)
where
T: IntoIterator<Item = (&'a Entity<Buffer>, &'a language::Transaction)>,
{
self.history
.push_transaction(buffer_transactions, Instant::now(), cx);
self.history.finalize_last_transaction();
}
pub fn group_until_transaction(
&mut self,
transaction_id: TransactionId,
cx: &mut Context<Self>,
) {
if let Some(buffer) = self.as_singleton() {
buffer.update(cx, |buffer, _| {
buffer.group_until_transaction(transaction_id)
});
} else {
self.history.group_until(transaction_id);
}
}
pub fn undo(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> {
let mut transaction_id = None;
if let Some(buffer) = self.as_singleton() {
transaction_id = buffer.update(cx, |buffer, cx| buffer.undo(cx));
} else {
while let Some(transaction) = self.history.pop_undo() {
let mut undone = false;
for (buffer_id, buffer_transaction_id) in &mut transaction.buffer_transactions {
if let Some(BufferState { buffer, .. }) = self.buffers.get(buffer_id) {
undone |= buffer.update(cx, |buffer, cx| {
let undo_to = *buffer_transaction_id;
if let Some(entry) = buffer.peek_undo_stack() {
*buffer_transaction_id = entry.transaction_id();
}
buffer.undo_to_transaction(undo_to, cx)
});
}
}
if undone {
transaction_id = Some(transaction.id);
break;
}
}
}
if let Some(transaction_id) = transaction_id {
cx.emit(Event::TransactionUndone { transaction_id });
}
transaction_id
}
pub fn redo(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> {
if let Some(buffer) = self.as_singleton() {
return buffer.update(cx, |buffer, cx| buffer.redo(cx));
}
while let Some(transaction) = self.history.pop_redo() {
let mut redone = false;
for (buffer_id, buffer_transaction_id) in transaction.buffer_transactions.iter_mut() {
if let Some(BufferState { buffer, .. }) = self.buffers.get(buffer_id) {
redone |= buffer.update(cx, |buffer, cx| {
let redo_to = *buffer_transaction_id;
if let Some(entry) = buffer.peek_redo_stack() {
*buffer_transaction_id = entry.transaction_id();
}
buffer.redo_to_transaction(redo_to, cx)
});
}
}
Lines 1–500 of 538. Load more
use gpui::{App, Context, Entity}; use language::{self, Buffer, TransactionId}; use std::{ collections::HashMap, ops::{AddAssign, Range, Sub}, time::{Duration, Instant}, }; use sum_tree::Bias; use text::BufferId; use crate::{BufferState, MultiBufferDimension}; use super::{Event, ExcerptSummary, MultiBuffer}; #[derive(Clone)] pub(super) struct History { next_transaction_id: TransactionId, undo_stack: Vec<Transaction>, redo_stack: Vec<Transaction>, transaction_depth: usize, group_interval: Duration, } impl Default for History { fn default() -> Self { History { next_transaction_id: clock::Lamport::MIN, undo_stack: Vec::new(), redo_stack: Vec::new(), transaction_depth: 0, group_interval: Duration::from_millis(300), } } } #[derive(Clone)] struct Transaction { id: TransactionId, buffer_transactions: HashMap<BufferId, text::TransactionId>, first_edit_at: Instant, last_edit_at: Instant, suppress_grouping: bool, } impl History { fn start_transaction(&mut self, now: Instant) -> Option<TransactionId> { self.transaction_depth += 1; if self.transaction_depth == 1 { let id = self.next_transaction_id.tick(); self.undo_stack.push(Transaction { id, buffer_transactions: Default::default(), first_edit_at: now, last_edit_at: now, suppress_grouping: false, }); Some(id) } else { None } } fn end_transaction( &mut self, now: Instant, buffer_transactions: HashMap<BufferId, text::TransactionId>, ) -> bool { assert_ne!(self.transaction_depth, 0); self.transaction_depth -= 1; if self.transaction_depth == 0 { if buffer_transactions.is_empty() { self.undo_stack.pop(); false } else { self.redo_stack.clear(); let transaction = self.undo_stack.last_mut().unwrap(); transaction.last_edit_at = now; for (buffer_id, transaction_id) in buffer_transactions { transaction .buffer_transactions .entry(buffer_id) .or_insert(transaction_id); } true } } else { false } } fn push_transaction<'a, T>( &mut self, buffer_transactions: T, now: Instant, cx: &Context<MultiBuffer>, ) where T: IntoIterator<Item = (&'a Entity<Buffer>, &'a language::Transaction)>, { assert_eq!(self.transaction_depth, 0); let transaction = Transaction { id: self.next_transaction_id.tick(), buffer_transactions: buffer_transactions .into_iter() .map(|(buffer, transaction)| (buffer.read(cx).remote_id(), transaction.id)) .collect(), first_edit_at: now, last_edit_at: now, suppress_grouping: false, }; if !transaction.buffer_transactions.is_empty() { self.undo_stack.push(transaction); self.redo_stack.clear(); } } fn finalize_last_transaction(&mut self) { if let Some(transaction) = self.undo_stack.last_mut() { transaction.suppress_grouping = true; } } fn forget(&mut self, transaction_id: TransactionId) -> Option<Transaction> { if let Some(ix) = self .undo_stack .iter() .rposition(|transaction| transaction.id == transaction_id) { Some(self.undo_stack.remove(ix)) } else if let Some(ix) = self .redo_stack .iter() .rposition(|transaction| transaction.id == transaction_id) { Some(self.redo_stack.remove(ix)) } else { None } } fn transaction(&self, transaction_id: TransactionId) -> Option<&Transaction> { self.undo_stack .iter() .find(|transaction| transaction.id == transaction_id) .or_else(|| { self.redo_stack .iter() .find(|transaction| transaction.id == transaction_id) }) } fn transaction_mut(&mut self, transaction_id: TransactionId) -> Option<&mut Transaction> { self.undo_stack .iter_mut() .find(|transaction| transaction.id == transaction_id) .or_else(|| { self.redo_stack .iter_mut() .find(|transaction| transaction.id == transaction_id) }) } fn pop_undo(&mut self) -> Option<&mut Transaction> { assert_eq!(self.transaction_depth, 0); if let Some(transaction) = self.undo_stack.pop() { self.redo_stack.push(transaction); self.redo_stack.last_mut() } else { None } } fn pop_redo(&mut self) -> Option<&mut Transaction> { assert_eq!(self.transaction_depth, 0); if let Some(transaction) = self.redo_stack.pop() { self.undo_stack.push(transaction); self.undo_stack.last_mut() } else { None } } fn remove_from_undo(&mut self, transaction_id: TransactionId) -> Option<&Transaction> { let ix = self .undo_stack .iter() .rposition(|transaction| transaction.id == transaction_id)?; let transaction = self.undo_stack.remove(ix); self.redo_stack.push(transaction); self.redo_stack.last() } fn group(&mut self) -> Option<TransactionId> { let mut count = 0; let mut transactions = self.undo_stack.iter(); if let Some(mut transaction) = transactions.next_back() { while let Some(prev_transaction) = transactions.next_back() { if !prev_transaction.suppress_grouping && transaction.first_edit_at - prev_transaction.last_edit_at <= self.group_interval { transaction = prev_transaction; count += 1; } else { break; } } } self.group_trailing(count) } fn group_until(&mut self, transaction_id: TransactionId) { let mut count = 0; for transaction in self.undo_stack.iter().rev() { if transaction.id == transaction_id { self.group_trailing(count); break; } else if transaction.suppress_grouping { break; } else { count += 1; } } } fn group_trailing(&mut self, n: usize) -> Option<TransactionId> { let new_len = self.undo_stack.len() - n; let (transactions_to_keep, transactions_to_merge) = self.undo_stack.split_at_mut(new_len); if let Some(last_transaction) = transactions_to_keep.last_mut() { if let Some(transaction) = transactions_to_merge.last() { last_transaction.last_edit_at = transaction.last_edit_at; } for to_merge in transactions_to_merge { for (buffer_id, transaction_id) in &to_merge.buffer_transactions { last_transaction .buffer_transactions .entry(*buffer_id) .or_insert(*transaction_id); } } } self.undo_stack.truncate(new_len); self.undo_stack.last().map(|t| t.id) } pub(super) fn transaction_depth(&self) -> usize { self.transaction_depth } pub fn set_group_interval(&mut self, group_interval: Duration) { self.group_interval = group_interval; } } impl MultiBuffer { pub fn start_transaction(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> { self.start_transaction_at(Instant::now(), cx) } pub fn start_transaction_at( &mut self, now: Instant, cx: &mut Context<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.values() { buffer.update(cx, |buffer, _| buffer.start_transaction_at(now)); } self.history.start_transaction(now) } pub fn last_transaction_id(&self, cx: &App) -> Option<TransactionId> { if let Some(buffer) = self.as_singleton() { buffer .read(cx) .peek_undo_stack() .map(|history_entry| history_entry.transaction_id()) } else { let last_transaction = self.history.undo_stack.last()?; Some(last_transaction.id) } } pub fn end_transaction(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> { self.end_transaction_at(Instant::now(), cx) } pub fn end_transaction_at( &mut self, now: Instant, cx: &mut Context<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 = HashMap::default(); for BufferState { buffer, .. } in self.buffers.values() { if let Some(transaction_id) = buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx)) { buffer_transactions.insert(buffer.read(cx).remote_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 edited_ranges_for_transaction<D>( &self, transaction_id: TransactionId, cx: &App, ) -> Vec<Range<D>> where D: MultiBufferDimension + Ord + Sub<D, Output = D::TextDimension> + AddAssign<D::TextDimension>, D::TextDimension: PartialOrd + Sub<D::TextDimension, Output = D::TextDimension>, { let Some(transaction) = self.history.transaction(transaction_id) else { return Vec::new(); }; let mut ranges = Vec::new(); let snapshot = self.read(cx); let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>(()); for (buffer_id, buffer_transaction) in &transaction.buffer_transactions { let Some(buffer_state) = self.buffers.get(buffer_id) else { continue; }; let buffer = buffer_state.buffer.read(cx); for range in buffer.edited_ranges_for_transaction_id::<D::TextDimension>(*buffer_transaction) { for excerpt_id in &buffer_state.excerpts { cursor.seek(excerpt_id, Bias::Left); if let Some(excerpt) = cursor.item() && excerpt.locator == *excerpt_id { let excerpt_buffer_start = excerpt .range .context .start .summary::<D::TextDimension>(buffer); let excerpt_buffer_end = excerpt .range .context .end .summary::<D::TextDimension>(buffer); let excerpt_range = excerpt_buffer_start..excerpt_buffer_end; if excerpt_range.contains(&range.start) && excerpt_range.contains(&range.end) { let excerpt_start = D::from_summary(&cursor.start().text); let mut start = excerpt_start; start += range.start - excerpt_buffer_start; let mut end = excerpt_start; end += range.end - excerpt_buffer_start; ranges.push(start..end); break; } } } } } ranges.sort_by_key(|range| range.start); ranges } pub fn merge_transactions( &mut self, transaction: TransactionId, destination: TransactionId, cx: &mut Context<Self>, ) { if let Some(buffer) = self.as_singleton() { buffer.update(cx, |buffer, _| { buffer.merge_transactions(transaction, destination) }); } else if let Some(transaction) = self.history.forget(transaction) && let Some(destination) = self.history.transaction_mut(destination) { for (buffer_id, buffer_transaction_id) in transaction.buffer_transactions { if let Some(destination_buffer_transaction_id) = destination.buffer_transactions.get(&buffer_id) { if let Some(state) = self.buffers.get(&buffer_id) { state.buffer.update(cx, |buffer, _| { buffer.merge_transactions( buffer_transaction_id, *destination_buffer_transaction_id, ) }); } } else { destination .buffer_transactions .insert(buffer_id, buffer_transaction_id); } } } } pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) { self.history.finalize_last_transaction(); for BufferState { buffer, .. } in self.buffers.values() { buffer.update(cx, |buffer, _| { buffer.finalize_last_transaction(); }); } } pub fn push_transaction<'a, T>(&mut self, buffer_transactions: T, cx: &Context<Self>) where T: IntoIterator<Item = (&'a Entity<Buffer>, &'a language::Transaction)>, { self.history .push_transaction(buffer_transactions, Instant::now(), cx); self.history.finalize_last_transaction(); } pub fn group_until_transaction( &mut self, transaction_id: TransactionId, cx: &mut Context<Self>, ) { if let Some(buffer) = self.as_singleton() { buffer.update(cx, |buffer, _| { buffer.group_until_transaction(transaction_id) }); } else { self.history.group_until(transaction_id); } } pub fn undo(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> { let mut transaction_id = None; if let Some(buffer) = self.as_singleton() { transaction_id = buffer.update(cx, |buffer, cx| buffer.undo(cx)); } else { while let Some(transaction) = self.history.pop_undo() { let mut undone = false; for (buffer_id, buffer_transaction_id) in &mut transaction.buffer_transactions { if let Some(BufferState { buffer, .. }) = self.buffers.get(buffer_id) { undone |= buffer.update(cx, |buffer, cx| { let undo_to = *buffer_transaction_id; if let Some(entry) = buffer.peek_undo_stack() { *buffer_transaction_id = entry.transaction_id(); } buffer.undo_to_transaction(undo_to, cx) }); } } if undone { transaction_id = Some(transaction.id); break; } } } if let Some(transaction_id) = transaction_id { cx.emit(Event::TransactionUndone { transaction_id }); } transaction_id } pub fn redo(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> { if let Some(buffer) = self.as_singleton() { return buffer.update(cx, |buffer, cx| buffer.redo(cx)); } while let Some(transaction) = self.history.pop_redo() { let mut redone = false; for (buffer_id, buffer_transaction_id) in transaction.buffer_transactions.iter_mut() { if let Some(BufferState { buffer, .. }) = self.buffers.get(buffer_id) { redone |= buffer.update(cx, |buffer, cx| { let redo_to = *buffer_transaction_id; if let Some(entry) = buffer.peek_redo_stack() { *buffer_transaction_id = entry.transaction_id(); } buffer.redo_to_transaction(redo_to, cx) }); } }