Undo temporary edits before performing rename or canceling it

Antonio Scandurra created

Also, wire up remote renames.

Change summary

crates/editor/src/editor.rs       | 76 ++++++++++++++++++++--------
crates/editor/src/multi_buffer.rs | 60 ++++++++++++++++++++++
crates/language/src/buffer.rs     |  6 +
crates/project/src/lsp_command.rs | 26 +++++++--
crates/project/src/project.rs     | 85 +++++++++++++++++++++++++++++++++
crates/rpc/src/proto.rs           |  1 
crates/server/src/rpc.rs          | 30 +++++++++++
crates/text/src/text.rs           | 72 +++++++++++++++++----------
crates/util/src/lib.rs            | 35 +++++++++++++
9 files changed, 331 insertions(+), 60 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

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

crates/editor/src/multi_buffer.rs 🔗

@@ -27,6 +27,7 @@ use text::{
     AnchorRangeExt as _, Edit, Point, PointUtf16, TextSummary,
 };
 use theme::SyntaxTheme;
+use util::CowMut;
 
 const NEWLINES: &'static [u8] = &[b'\n'; u8::MAX as usize];
 
@@ -58,6 +59,7 @@ pub enum CharKind {
     Word,
 }
 
+#[derive(Clone)]
 struct Transaction {
     id: TransactionId,
     buffer_transactions: HashMap<usize, text::TransactionId>,
@@ -564,7 +566,7 @@ impl MultiBuffer {
                         if let Some(entry) = buffer.peek_undo_stack() {
                             *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
     }
 
+    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> {
         if let Some(buffer) = self.as_singleton() {
             return buffer.update(cx, |buffer, cx| buffer.redo(cx));
@@ -591,7 +622,7 @@ impl MultiBuffer {
                         if let Some(entry) = buffer.peek_redo_stack() {
                             *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> {
         assert_eq!(self.transaction_depth, 0);
         if let Some(transaction) = self.redo_stack.pop() {

crates/language/src/buffer.rs 🔗

@@ -1738,12 +1738,13 @@ impl Buffer {
     pub fn undo_to_transaction(
         &mut self,
         transaction_id: TransactionId,
+        push_redo: bool,
         cx: &mut ModelContext<Self>,
     ) -> bool {
         let was_dirty = self.is_dirty();
         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();
         for operation in operations {
             self.send_operation(Operation::Buffer(operation), cx);
@@ -1770,12 +1771,13 @@ impl Buffer {
     pub fn redo_to_transaction(
         &mut self,
         transaction_id: TransactionId,
+        push_undo: bool,
         cx: &mut ModelContext<Self>,
     ) -> bool {
         let was_dirty = self.is_dirty();
         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();
         for operation in operations {
             self.send_operation(Operation::Buffer(operation), cx);

crates/project/src/lsp_command.rs 🔗

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

crates/project/src/project.rs 🔗

@@ -184,6 +184,8 @@ impl Project {
         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_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_save_buffer);
     }
@@ -1835,6 +1837,7 @@ impl Project {
         buffer: ModelHandle<Buffer>,
         position: T,
         new_name: String,
+        push_to_history: bool,
         cx: &mut ModelContext<Self>,
     ) -> Task<Result<ProjectTransaction>> {
         let position = position.to_point_utf16(buffer.read(cx));
@@ -1844,6 +1847,7 @@ impl Project {
                 buffer,
                 position,
                 new_name,
+                push_to_history,
             },
             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(
         this: ModelHandle<Self>,
         envelope: TypedEnvelope<proto::OpenBuffer>,

crates/rpc/src/proto.rs 🔗

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

crates/server/src/rpc.rs 🔗

@@ -91,6 +91,8 @@ impl Server {
             .add_request_handler(Server::apply_additional_edits_for_completion)
             .add_request_handler(Server::get_code_actions)
             .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_users)
             .add_request_handler(Server::join_channel)
@@ -708,6 +710,34 @@ impl Server {
             .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(
         self: Arc<Server>,
         request: TypedEnvelope<proto::UpdateBuffer>,

crates/text/src/text.rs 🔗

@@ -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);
 
-        let redo_stack_start_len = self.redo_stack.len();
+        let mut transactions = Vec::new();
         if let Some(entry_ix) = self
             .undo_stack
             .iter()
             .rposition(|entry| entry.transaction.id == transaction_id)
         {
-            self.redo_stack
-                .extend(self.undo_stack.drain(entry_ix..).rev());
+            transactions.extend(
+                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) {
@@ -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);
 
-        let undo_stack_start_len = self.undo_stack.len();
+        let mut transactions = Vec::new();
         if let Some(entry_ix) = self
             .redo_stack
             .iter()
             .rposition(|entry| entry.transaction.id == transaction_id)
         {
-            self.undo_stack
-                .extend(self.redo_stack.drain(entry_ix..).rev());
+            transactions.extend(
+                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> {
-        let transactions = self
-            .history
-            .remove_from_undo(transaction_id)
-            .iter()
-            .map(|entry| entry.transaction.clone())
-            .collect::<Vec<_>>();
-
+    pub fn undo_to_transaction(
+        &mut self,
+        transaction_id: TransactionId,
+        push_redo: bool,
+    ) -> Vec<Operation> {
+        let transactions = self.history.remove_from_undo(transaction_id, push_redo);
         transactions
             .into_iter()
             .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> {
-        let transactions = self
-            .history
-            .remove_from_redo(transaction_id)
-            .iter()
-            .map(|entry| entry.transaction.clone())
-            .collect::<Vec<_>>();
-
+    pub fn redo_to_transaction(
+        &mut self,
+        transaction_id: TransactionId,
+        push_undo: bool,
+    ) -> Vec<Operation> {
+        let transactions = self.history.remove_from_redo(transaction_id, push_undo);
         transactions
             .into_iter()
             .map(|transaction| self.undo_or_redo(transaction).unwrap())

crates/util/src/lib.rs 🔗

@@ -3,8 +3,9 @@ pub mod test;
 
 use futures::Future;
 use std::{
+    borrow::{Borrow, BorrowMut},
     cmp::Ordering,
-    ops::AddAssign,
+    ops::{AddAssign, Deref, DerefMut},
     pin::Pin,
     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)]
 mod tests {
     use super::*;