Undo temporary edits before performing rename or canceling it

Also, wire up remote renames.
This commit is contained in:
Antonio Scandurra 2022-02-18 11:41:47 +01:00
parent 514d69e83d
commit f9723ae16b
9 changed files with 331 additions and 60 deletions

View File

@ -439,6 +439,7 @@ pub struct Editor {
next_completion_id: CompletionId, next_completion_id: CompletionId,
available_code_actions: Option<(ModelHandle<Buffer>, Arc<[CodeAction]>)>, available_code_actions: Option<(ModelHandle<Buffer>, Arc<[CodeAction]>)>,
code_actions_task: Option<Task<()>>, code_actions_task: Option<Task<()>>,
pending_rename: Option<RenameState>,
} }
pub struct EditorSnapshot { pub struct EditorSnapshot {
@ -477,6 +478,11 @@ struct SnippetState {
active_index: usize, active_index: usize,
} }
struct RenameState {
range: Range<Anchor>,
first_transaction: Option<TransactionId>,
}
struct InvalidationStack<T>(Vec<T>); struct InvalidationStack<T>(Vec<T>);
enum ContextMenu { enum ContextMenu {
@ -892,6 +898,7 @@ impl Editor {
next_completion_id: 0, next_completion_id: 0,
available_code_actions: Default::default(), available_code_actions: Default::default(),
code_actions_task: Default::default(), code_actions_task: Default::default(),
pending_rename: Default::default(),
}; };
this.end_selection(cx); this.end_selection(cx);
this this
@ -1913,6 +1920,10 @@ impl Editor {
} }
fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) { fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
if self.pending_rename.is_some() {
return;
}
let project = if let Some(project) = self.project.clone() { let project = if let Some(project) = self.project.clone() {
project project
} else { } else {
@ -4101,11 +4112,17 @@ impl Editor {
let offset = position.to_offset(&buffer); let offset = position.to_offset(&buffer);
let start = offset - lookbehind; let start = offset - lookbehind;
let end = offset + lookahead; let end = offset + lookahead;
let highlight_range = buffer.anchor_before(start)..buffer.anchor_after(end); let rename_range = buffer.anchor_before(start)..buffer.anchor_after(end);
drop(buffer); drop(buffer);
this.buffer
.update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
this.pending_rename = Some(RenameState {
range: rename_range.clone(),
first_transaction: None,
});
this.select_ranges([start..end], None, cx); this.select_ranges([start..end], None, cx);
this.highlight_ranges::<Rename>(vec![highlight_range], Color::red(), cx); this.highlight_ranges::<Rename>(vec![rename_range], Color::red(), cx);
}); });
} }
@ -4121,17 +4138,16 @@ impl Editor {
let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?; let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
let (buffer, position, new_name) = editor.update(cx, |editor, cx| { let (buffer, position, new_name) = editor.update(cx, |editor, cx| {
let range = editor.take_rename_range(cx)?; let (range, new_name) = editor.take_rename(cx)?;
let multibuffer = editor.buffer.read(cx); let (buffer, position) = editor
let (buffer, position) = .buffer
multibuffer.text_anchor_for_position(range.start.clone(), cx)?; .read(cx)
let snapshot = multibuffer.read(cx); .text_anchor_for_position(range.start.clone(), cx)?;
let new_name = snapshot.text_for_range(range.clone()).collect::<String>();
Some((buffer, position, new_name)) Some((buffer, position, new_name))
})?; })?;
let rename = workspace.project().clone().update(cx, |project, cx| { let rename = workspace.project().clone().update(cx, |project, cx| {
project.perform_rename(buffer, position, new_name.clone(), cx) project.perform_rename(buffer, position, new_name.clone(), true, cx)
}); });
Some(cx.spawn(|workspace, cx| async move { Some(cx.spawn(|workspace, cx| async move {
@ -4147,14 +4163,23 @@ impl Editor {
})) }))
} }
fn rename_range(&self) -> Option<&Range<Anchor>> { fn take_rename(&mut self, cx: &mut ViewContext<Self>) -> Option<(Range<Anchor>, String)> {
self.highlighted_ranges_for_type::<Rename>() let rename = self.pending_rename.take()?;
.and_then(|(_, range)| range.last()) let new_name = self
} .buffer
.read(cx)
.read(cx)
.text_for_range(rename.range.clone())
.collect::<String>();
fn take_rename_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<Anchor>> { self.clear_highlighted_ranges::<Rename>(cx);
self.clear_highlighted_ranges::<Rename>(cx) if let Some(transaction_id) = rename.first_transaction {
.and_then(|(_, mut ranges)| ranges.pop()) self.buffer.update(cx, |buffer, cx| {
buffer.undo_to_transaction(transaction_id, false, cx)
});
}
Some((rename.range, new_name))
} }
fn invalidate_rename_range( fn invalidate_rename_range(
@ -4162,15 +4187,16 @@ impl Editor {
buffer: &MultiBufferSnapshot, buffer: &MultiBufferSnapshot,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) { ) {
if let Some(range) = &self.rename_range() { if let Some(rename) = self.pending_rename.as_ref() {
if self.selections.len() == 1 { if self.selections.len() == 1 {
let head = self.selections[0].head().to_offset(&buffer); let head = self.selections[0].head().to_offset(buffer);
if range.start.to_offset(&buffer) <= head && range.end.to_offset(&buffer) >= head { let range = rename.range.to_offset(buffer).to_inclusive();
if range.contains(&head) {
return; return;
} }
} }
eprintln!("clearing highlight range");
self.clear_highlighted_ranges::<Rename>(cx); self.take_rename(cx);
} }
} }
@ -4659,6 +4685,12 @@ impl Editor {
.buffer .buffer
.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx)) .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
{ {
if let Some(rename) = self.pending_rename.as_mut() {
if rename.first_transaction.is_none() {
rename.first_transaction = Some(tx_id);
}
}
if let Some((_, end_selections)) = self.selection_history.get_mut(&tx_id) { if let Some((_, end_selections)) = self.selection_history.get_mut(&tx_id) {
*end_selections = Some(self.selections.clone()); *end_selections = Some(self.selections.clone());
} else { } else {
@ -5197,7 +5229,7 @@ impl View for Editor {
EditorMode::Full => "full", EditorMode::Full => "full",
}; };
cx.map.insert("mode".into(), mode.into()); cx.map.insert("mode".into(), mode.into());
if self.rename_range().is_some() { if self.pending_rename.is_some() {
cx.set.insert("renaming".into()); cx.set.insert("renaming".into());
} }
match self.context_menu.as_ref() { match self.context_menu.as_ref() {

View File

@ -27,6 +27,7 @@ use text::{
AnchorRangeExt as _, Edit, Point, PointUtf16, TextSummary, AnchorRangeExt as _, Edit, Point, PointUtf16, TextSummary,
}; };
use theme::SyntaxTheme; use theme::SyntaxTheme;
use util::CowMut;
const NEWLINES: &'static [u8] = &[b'\n'; u8::MAX as usize]; const NEWLINES: &'static [u8] = &[b'\n'; u8::MAX as usize];
@ -58,6 +59,7 @@ pub enum CharKind {
Word, Word,
} }
#[derive(Clone)]
struct Transaction { struct Transaction {
id: TransactionId, id: TransactionId,
buffer_transactions: HashMap<usize, text::TransactionId>, buffer_transactions: HashMap<usize, text::TransactionId>,
@ -564,7 +566,7 @@ impl MultiBuffer {
if let Some(entry) = buffer.peek_undo_stack() { if let Some(entry) = buffer.peek_undo_stack() {
*buffer_transaction_id = entry.transaction_id(); *buffer_transaction_id = entry.transaction_id();
} }
buffer.undo_to_transaction(undo_to, cx) buffer.undo_to_transaction(undo_to, true, cx)
}); });
} }
} }
@ -577,6 +579,35 @@ impl MultiBuffer {
None None
} }
pub fn undo_to_transaction(
&mut self,
transaction_id: TransactionId,
push_redo: bool,
cx: &mut ModelContext<Self>,
) -> bool {
if let Some(buffer) = self.as_singleton() {
return buffer.update(cx, |buffer, cx| {
buffer.undo_to_transaction(transaction_id, push_redo, cx)
});
}
let mut undone = false;
for transaction in &mut *self.history.remove_from_undo(transaction_id, push_redo) {
for (buffer_id, buffer_transaction_id) in &mut transaction.buffer_transactions {
if let Some(BufferState { buffer, .. }) = self.buffers.borrow().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, true, cx)
});
}
}
}
undone
}
pub fn redo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> { pub fn redo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
if let Some(buffer) = self.as_singleton() { if let Some(buffer) = self.as_singleton() {
return buffer.update(cx, |buffer, cx| buffer.redo(cx)); return buffer.update(cx, |buffer, cx| buffer.redo(cx));
@ -591,7 +622,7 @@ impl MultiBuffer {
if let Some(entry) = buffer.peek_redo_stack() { if let Some(entry) = buffer.peek_redo_stack() {
*buffer_transaction_id = entry.transaction_id(); *buffer_transaction_id = entry.transaction_id();
} }
buffer.redo_to_transaction(redo_to, cx) buffer.redo_to_transaction(redo_to, true, cx)
}); });
} }
} }
@ -2314,6 +2345,31 @@ impl History {
} }
} }
fn remove_from_undo(
&mut self,
transaction_id: TransactionId,
push_redo: bool,
) -> CowMut<[Transaction]> {
assert_eq!(self.transaction_depth, 0);
if let Some(entry_ix) = self
.undo_stack
.iter()
.rposition(|transaction| transaction.id == transaction_id)
{
let transactions = self.undo_stack.drain(entry_ix..).rev();
if push_redo {
let redo_stack_start_len = self.redo_stack.len();
self.redo_stack.extend(transactions);
CowMut::Borrowed(&mut self.redo_stack[redo_stack_start_len..])
} else {
CowMut::Owned(transactions.collect())
}
} else {
CowMut::Owned(Default::default())
}
}
fn pop_redo(&mut self) -> Option<&mut Transaction> { fn pop_redo(&mut self) -> Option<&mut Transaction> {
assert_eq!(self.transaction_depth, 0); assert_eq!(self.transaction_depth, 0);
if let Some(transaction) = self.redo_stack.pop() { if let Some(transaction) = self.redo_stack.pop() {

View File

@ -1738,12 +1738,13 @@ impl Buffer {
pub fn undo_to_transaction( pub fn undo_to_transaction(
&mut self, &mut self,
transaction_id: TransactionId, transaction_id: TransactionId,
push_redo: bool,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> bool { ) -> bool {
let was_dirty = self.is_dirty(); let was_dirty = self.is_dirty();
let old_version = self.version.clone(); let old_version = self.version.clone();
let operations = self.text.undo_to_transaction(transaction_id); let operations = self.text.undo_to_transaction(transaction_id, push_redo);
let undone = !operations.is_empty(); let undone = !operations.is_empty();
for operation in operations { for operation in operations {
self.send_operation(Operation::Buffer(operation), cx); self.send_operation(Operation::Buffer(operation), cx);
@ -1770,12 +1771,13 @@ impl Buffer {
pub fn redo_to_transaction( pub fn redo_to_transaction(
&mut self, &mut self,
transaction_id: TransactionId, transaction_id: TransactionId,
push_undo: bool,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> bool { ) -> bool {
let was_dirty = self.is_dirty(); let was_dirty = self.is_dirty();
let old_version = self.version.clone(); let old_version = self.version.clone();
let operations = self.text.redo_to_transaction(transaction_id); let operations = self.text.redo_to_transaction(transaction_id, push_undo);
let redone = !operations.is_empty(); let redone = !operations.is_empty();
for operation in operations { for operation in operations {
self.send_operation(Operation::Buffer(operation), cx); self.send_operation(Operation::Buffer(operation), cx);

View File

@ -43,6 +43,7 @@ pub(crate) struct PerformRename {
pub buffer: ModelHandle<Buffer>, pub buffer: ModelHandle<Buffer>,
pub position: PointUtf16, pub position: PointUtf16,
pub new_name: String, pub new_name: String,
pub push_to_history: bool,
} }
impl LspCommand for PrepareRename { impl LspCommand for PrepareRename {
@ -60,11 +61,14 @@ impl LspCommand for PrepareRename {
} }
fn to_proto(&self, project_id: u64, cx: &AppContext) -> proto::PrepareRename { fn to_proto(&self, project_id: u64, cx: &AppContext) -> proto::PrepareRename {
let buffer_id = self.buffer.read(cx).remote_id(); let buffer = &self.buffer.read(cx);
let buffer_id = buffer.remote_id();
proto::PrepareRename { proto::PrepareRename {
project_id, project_id,
buffer_id, buffer_id,
position: None, position: Some(language::proto::serialize_anchor(
&buffer.anchor_before(self.position),
)),
} }
} }
@ -93,10 +97,15 @@ impl LspCommand for PrepareRename {
self, self,
message: proto::PrepareRenameResponse, message: proto::PrepareRenameResponse,
_: ModelHandle<Project>, _: ModelHandle<Project>,
_: AsyncAppContext, mut cx: AsyncAppContext,
) -> LocalBoxFuture<'static, Result<Option<Range<Anchor>>>> { ) -> LocalBoxFuture<'static, Result<Option<Range<Anchor>>>> {
async move { async move {
if message.can_rename { if message.can_rename {
self.buffer
.update(&mut cx, |buffer, _| {
buffer.wait_for_version(message.version.into())
})
.await;
let start = message.start.and_then(deserialize_anchor); let start = message.start.and_then(deserialize_anchor);
let end = message.end.and_then(deserialize_anchor); let end = message.end.and_then(deserialize_anchor);
Ok(start.zip(end).map(|(start, end)| start..end)) Ok(start.zip(end).map(|(start, end)| start..end))
@ -127,11 +136,14 @@ impl LspCommand for PerformRename {
} }
fn to_proto(&self, project_id: u64, cx: &AppContext) -> proto::PerformRename { fn to_proto(&self, project_id: u64, cx: &AppContext) -> proto::PerformRename {
let buffer_id = self.buffer.read(cx).remote_id(); let buffer = &self.buffer.read(cx);
let buffer_id = buffer.remote_id();
proto::PerformRename { proto::PerformRename {
project_id, project_id,
buffer_id, buffer_id,
position: None, position: Some(language::proto::serialize_anchor(
&buffer.anchor_before(self.position),
)),
new_name: self.new_name.clone(), new_name: self.new_name.clone(),
} }
} }
@ -158,7 +170,7 @@ impl LspCommand for PerformRename {
Project::deserialize_workspace_edit( Project::deserialize_workspace_edit(
project, project,
edit, edit,
false, self.push_to_history,
language_name, language_name,
language_server, language_server,
&mut cx, &mut cx,
@ -183,7 +195,7 @@ impl LspCommand for PerformRename {
.ok_or_else(|| anyhow!("missing transaction"))?; .ok_or_else(|| anyhow!("missing transaction"))?;
project project
.update(&mut cx, |project, cx| { .update(&mut cx, |project, cx| {
project.deserialize_project_transaction(message, false, cx) project.deserialize_project_transaction(message, self.push_to_history, cx)
}) })
.await .await
} }

View File

@ -184,6 +184,8 @@ impl Project {
client.add_entity_request_handler(Self::handle_get_code_actions); client.add_entity_request_handler(Self::handle_get_code_actions);
client.add_entity_request_handler(Self::handle_get_completions); client.add_entity_request_handler(Self::handle_get_completions);
client.add_entity_request_handler(Self::handle_get_definition); client.add_entity_request_handler(Self::handle_get_definition);
client.add_entity_request_handler(Self::handle_prepare_rename);
client.add_entity_request_handler(Self::handle_perform_rename);
client.add_entity_request_handler(Self::handle_open_buffer); client.add_entity_request_handler(Self::handle_open_buffer);
client.add_entity_request_handler(Self::handle_save_buffer); client.add_entity_request_handler(Self::handle_save_buffer);
} }
@ -1835,6 +1837,7 @@ impl Project {
buffer: ModelHandle<Buffer>, buffer: ModelHandle<Buffer>,
position: T, position: T,
new_name: String, new_name: String,
push_to_history: bool,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Task<Result<ProjectTransaction>> { ) -> Task<Result<ProjectTransaction>> {
let position = position.to_point_utf16(buffer.read(cx)); let position = position.to_point_utf16(buffer.read(cx));
@ -1844,6 +1847,7 @@ impl Project {
buffer, buffer,
position, position,
new_name, new_name,
push_to_history,
}, },
cx, cx,
) )
@ -2615,6 +2619,87 @@ impl Project {
}) })
} }
async fn handle_prepare_rename(
this: ModelHandle<Self>,
envelope: TypedEnvelope<proto::PrepareRename>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::PrepareRenameResponse> {
let sender_id = envelope.original_sender_id()?;
let position = envelope
.payload
.position
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("invalid position"))?;
let (prepare_rename, version) = this.update(&mut cx, |this, cx| {
let buffer_handle = this
.shared_buffers
.get(&sender_id)
.and_then(|shared_buffers| shared_buffers.get(&envelope.payload.buffer_id).cloned())
.ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
let buffer = buffer_handle.read(cx);
let version = buffer.version();
if buffer.can_resolve(&position) {
Ok((this.prepare_rename(buffer_handle, position, cx), version))
} else {
Err(anyhow!("cannot resolve position"))
}
})?;
let range = prepare_rename.await?;
Ok(proto::PrepareRenameResponse {
can_rename: range.is_some(),
start: range
.as_ref()
.map(|range| language::proto::serialize_anchor(&range.start)),
end: range
.as_ref()
.map(|range| language::proto::serialize_anchor(&range.end)),
version: (&version).into(),
})
}
async fn handle_perform_rename(
this: ModelHandle<Self>,
envelope: TypedEnvelope<proto::PerformRename>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<proto::PerformRenameResponse> {
let sender_id = envelope.original_sender_id()?;
let position = envelope
.payload
.position
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("invalid position"))?;
let perform_rename = this.update(&mut cx, |this, cx| {
let buffer_handle = this
.shared_buffers
.get(&sender_id)
.and_then(|shared_buffers| shared_buffers.get(&envelope.payload.buffer_id).cloned())
.ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
let buffer = buffer_handle.read(cx);
if buffer.can_resolve(&position) {
Ok(this.perform_rename(
buffer_handle,
position,
envelope.payload.new_name,
false,
cx,
))
} else {
Err(anyhow!("cannot resolve position"))
}
})?;
let transaction = perform_rename.await?;
let transaction = this.update(&mut cx, |this, cx| {
this.serialize_project_transaction_for_peer(transaction, sender_id, cx)
});
Ok(proto::PerformRenameResponse {
transaction: Some(transaction),
})
}
async fn handle_open_buffer( async fn handle_open_buffer(
this: ModelHandle<Self>, this: ModelHandle<Self>,
envelope: TypedEnvelope<proto::OpenBuffer>, envelope: TypedEnvelope<proto::OpenBuffer>,

View File

@ -239,6 +239,7 @@ entity_messages!(
JoinProject, JoinProject,
LeaveProject, LeaveProject,
OpenBuffer, OpenBuffer,
PerformRename,
PrepareRename, PrepareRename,
RemoveProjectCollaborator, RemoveProjectCollaborator,
SaveBuffer, SaveBuffer,

View File

@ -91,6 +91,8 @@ impl Server {
.add_request_handler(Server::apply_additional_edits_for_completion) .add_request_handler(Server::apply_additional_edits_for_completion)
.add_request_handler(Server::get_code_actions) .add_request_handler(Server::get_code_actions)
.add_request_handler(Server::apply_code_action) .add_request_handler(Server::apply_code_action)
.add_request_handler(Server::prepare_rename)
.add_request_handler(Server::perform_rename)
.add_request_handler(Server::get_channels) .add_request_handler(Server::get_channels)
.add_request_handler(Server::get_users) .add_request_handler(Server::get_users)
.add_request_handler(Server::join_channel) .add_request_handler(Server::join_channel)
@ -708,6 +710,34 @@ impl Server {
.await?) .await?)
} }
async fn prepare_rename(
self: Arc<Server>,
request: TypedEnvelope<proto::PrepareRename>,
) -> tide::Result<proto::PrepareRenameResponse> {
let host = self
.state()
.read_project(request.payload.project_id, request.sender_id)?
.host_connection_id;
Ok(self
.peer
.forward_request(request.sender_id, host, request.payload.clone())
.await?)
}
async fn perform_rename(
self: Arc<Server>,
request: TypedEnvelope<proto::PerformRename>,
) -> tide::Result<proto::PerformRenameResponse> {
let host = self
.state()
.read_project(request.payload.project_id, request.sender_id)?
.host_connection_id;
Ok(self
.peer
.forward_request(request.sender_id, host, request.payload.clone())
.await?)
}
async fn update_buffer( async fn update_buffer(
self: Arc<Server>, self: Arc<Server>,
request: TypedEnvelope<proto::UpdateBuffer>, request: TypedEnvelope<proto::UpdateBuffer>,

View File

@ -285,19 +285,31 @@ impl History {
} }
} }
fn remove_from_undo(&mut self, transaction_id: TransactionId) -> &[HistoryEntry] { fn remove_from_undo(
&mut self,
transaction_id: TransactionId,
push_redo: bool,
) -> Vec<Transaction> {
assert_eq!(self.transaction_depth, 0); assert_eq!(self.transaction_depth, 0);
let redo_stack_start_len = self.redo_stack.len(); let mut transactions = Vec::new();
if let Some(entry_ix) = self if let Some(entry_ix) = self
.undo_stack .undo_stack
.iter() .iter()
.rposition(|entry| entry.transaction.id == transaction_id) .rposition(|entry| entry.transaction.id == transaction_id)
{ {
self.redo_stack transactions.extend(
.extend(self.undo_stack.drain(entry_ix..).rev()); self.undo_stack[entry_ix..]
.iter()
.rev()
.map(|entry| entry.transaction.clone()),
);
let transactions = self.undo_stack.drain(entry_ix..).rev();
if push_redo {
self.redo_stack.extend(transactions);
}
} }
&self.redo_stack[redo_stack_start_len..] transactions
} }
fn forget(&mut self, transaction_id: TransactionId) { fn forget(&mut self, transaction_id: TransactionId) {
@ -327,19 +339,31 @@ impl History {
} }
} }
fn remove_from_redo(&mut self, transaction_id: TransactionId) -> &[HistoryEntry] { fn remove_from_redo(
&mut self,
transaction_id: TransactionId,
push_undo: bool,
) -> Vec<Transaction> {
assert_eq!(self.transaction_depth, 0); assert_eq!(self.transaction_depth, 0);
let undo_stack_start_len = self.undo_stack.len(); let mut transactions = Vec::new();
if let Some(entry_ix) = self if let Some(entry_ix) = self
.redo_stack .redo_stack
.iter() .iter()
.rposition(|entry| entry.transaction.id == transaction_id) .rposition(|entry| entry.transaction.id == transaction_id)
{ {
self.undo_stack transactions.extend(
.extend(self.redo_stack.drain(entry_ix..).rev()); self.redo_stack[entry_ix..]
.iter()
.rev()
.map(|entry| entry.transaction.clone()),
);
if push_undo {
self.undo_stack
.extend(self.redo_stack.drain(entry_ix..).rev());
}
} }
&self.undo_stack[undo_stack_start_len..] transactions
} }
} }
@ -1215,14 +1239,12 @@ impl Buffer {
} }
} }
pub fn undo_to_transaction(&mut self, transaction_id: TransactionId) -> Vec<Operation> { pub fn undo_to_transaction(
let transactions = self &mut self,
.history transaction_id: TransactionId,
.remove_from_undo(transaction_id) push_redo: bool,
.iter() ) -> Vec<Operation> {
.map(|entry| entry.transaction.clone()) let transactions = self.history.remove_from_undo(transaction_id, push_redo);
.collect::<Vec<_>>();
transactions transactions
.into_iter() .into_iter()
.map(|transaction| self.undo_or_redo(transaction).unwrap()) .map(|transaction| self.undo_or_redo(transaction).unwrap())
@ -1244,14 +1266,12 @@ impl Buffer {
} }
} }
pub fn redo_to_transaction(&mut self, transaction_id: TransactionId) -> Vec<Operation> { pub fn redo_to_transaction(
let transactions = self &mut self,
.history transaction_id: TransactionId,
.remove_from_redo(transaction_id) push_undo: bool,
.iter() ) -> Vec<Operation> {
.map(|entry| entry.transaction.clone()) let transactions = self.history.remove_from_redo(transaction_id, push_undo);
.collect::<Vec<_>>();
transactions transactions
.into_iter() .into_iter()
.map(|transaction| self.undo_or_redo(transaction).unwrap()) .map(|transaction| self.undo_or_redo(transaction).unwrap())

View File

@ -3,8 +3,9 @@ pub mod test;
use futures::Future; use futures::Future;
use std::{ use std::{
borrow::{Borrow, BorrowMut},
cmp::Ordering, cmp::Ordering,
ops::AddAssign, ops::{AddAssign, Deref, DerefMut},
pin::Pin, pin::Pin,
task::{Context, Poll}, task::{Context, Poll},
}; };
@ -123,6 +124,38 @@ where
} }
} }
pub enum CowMut<'a, T: ?Sized + ToOwned> {
Borrowed(&'a mut T),
Owned(T::Owned),
}
impl<'a, T> Deref for CowMut<'a, T>
where
T: ?Sized + ToOwned,
{
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
CowMut::Borrowed(value) => value,
CowMut::Owned(value) => value.borrow(),
}
}
}
impl<'a, T> DerefMut for CowMut<'a, T>
where
T: ?Sized + ToOwned,
T::Owned: BorrowMut<T>,
{
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
CowMut::Borrowed(value) => value,
CowMut::Owned(value) => value.borrow_mut(),
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;