Wire up rename editor

Antonio Scandurra created

Change summary

crates/editor2/src/editor.rs  | 342 ++++++++++++++++++------------------
crates/editor2/src/element.rs |  13 +
2 files changed, 182 insertions(+), 173 deletions(-)

Detailed changes

crates/editor2/src/editor.rs 🔗

@@ -40,7 +40,7 @@ use fuzzy::{StringMatch, StringMatchCandidate};
 use git::diff_hunk_to_display;
 use gpui::{
     action, actions, div, point, px, relative, rems, size, uniform_list, AnyElement, AppContext,
-    AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context,
+    AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context, Entity,
     EventEmitter, FocusHandle, FontFeatures, FontStyle, FontWeight, HighlightStyle, Hsla,
     InputHandler, KeyContext, Model, MouseButton, ParentElement, Pixels, Render,
     StatefulInteractive, StatelessInteractive, Styled, Subscription, Task, TextStyle,
@@ -100,7 +100,9 @@ use theme::{
 use ui::{v_stack, HighlightedLabel, IconButton, StyledExt, TextTooltip};
 use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
 use workspace::{
-    item::ItemEvent, searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace,
+    item::{ItemEvent, ItemHandle},
+    searchable::SearchEvent,
+    ItemNavHistory, SplitDirection, ViewId, Workspace,
 };
 
 const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
@@ -7690,183 +7692,183 @@ impl Editor {
         }
     }
 
-    // pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
-    //     use language::ToOffset as _;
+    pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
+        use language::ToOffset as _;
 
-    //     let project = self.project.clone()?;
-    //     let selection = self.selections.newest_anchor().clone();
-    //     let (cursor_buffer, cursor_buffer_position) = self
-    //         .buffer
-    //         .read(cx)
-    //         .text_anchor_for_position(selection.head(), cx)?;
-    //     let (tail_buffer, _) = self
-    //         .buffer
-    //         .read(cx)
-    //         .text_anchor_for_position(selection.tail(), cx)?;
-    //     if tail_buffer != cursor_buffer {
-    //         return None;
-    //     }
+        let project = self.project.clone()?;
+        let selection = self.selections.newest_anchor().clone();
+        let (cursor_buffer, cursor_buffer_position) = self
+            .buffer
+            .read(cx)
+            .text_anchor_for_position(selection.head(), cx)?;
+        let (tail_buffer, _) = self
+            .buffer
+            .read(cx)
+            .text_anchor_for_position(selection.tail(), cx)?;
+        if tail_buffer != cursor_buffer {
+            return None;
+        }
 
-    //     let snapshot = cursor_buffer.read(cx).snapshot();
-    //     let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
-    //     let prepare_rename = project.update(cx, |project, cx| {
-    //         project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
-    //     });
+        let snapshot = cursor_buffer.read(cx).snapshot();
+        let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
+        let prepare_rename = project.update(cx, |project, cx| {
+            project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
+        });
 
-    //     Some(cx.spawn(|this, mut cx| async move {
-    //         let rename_range = if let Some(range) = prepare_rename.await? {
-    //             Some(range)
-    //         } else {
-    //             this.update(&mut cx, |this, cx| {
-    //                 let buffer = this.buffer.read(cx).snapshot(cx);
-    //                 let mut buffer_highlights = this
-    //                     .document_highlights_for_position(selection.head(), &buffer)
-    //                     .filter(|highlight| {
-    //                         highlight.start.excerpt_id == selection.head().excerpt_id
-    //                             && highlight.end.excerpt_id == selection.head().excerpt_id
-    //                     });
-    //                 buffer_highlights
-    //                     .next()
-    //                     .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
-    //             })?
-    //         };
-    //         if let Some(rename_range) = rename_range {
-    //             let rename_buffer_range = rename_range.to_offset(&snapshot);
-    //             let cursor_offset_in_rename_range =
-    //                 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
-
-    //             this.update(&mut cx, |this, cx| {
-    //                 this.take_rename(false, cx);
-    //                 let buffer = this.buffer.read(cx).read(cx);
-    //                 let cursor_offset = selection.head().to_offset(&buffer);
-    //                 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
-    //                 let rename_end = rename_start + rename_buffer_range.len();
-    //                 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
-    //                 let mut old_highlight_id = None;
-    //                 let old_name: Arc<str> = buffer
-    //                     .chunks(rename_start..rename_end, true)
-    //                     .map(|chunk| {
-    //                         if old_highlight_id.is_none() {
-    //                             old_highlight_id = chunk.syntax_highlight_id;
-    //                         }
-    //                         chunk.text
-    //                     })
-    //                     .collect::<String>()
-    //                     .into();
-
-    //                 drop(buffer);
-
-    //                 // Position the selection in the rename editor so that it matches the current selection.
-    //                 this.show_local_selections = false;
-    //                 let rename_editor = cx.build_view(|cx| {
-    //                     let mut editor = Editor::single_line(cx);
-    //                     if let Some(old_highlight_id) = old_highlight_id {
-    //                         editor.override_text_style =
-    //                             Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
-    //                     }
-    //                     editor.buffer.update(cx, |buffer, cx| {
-    //                         buffer.edit([(0..0, old_name.clone())], None, cx)
-    //                     });
-    //                     editor.select_all(&SelectAll, cx);
-    //                     editor
-    //                 });
+        Some(cx.spawn(|this, mut cx| async move {
+            let rename_range = if let Some(range) = prepare_rename.await? {
+                Some(range)
+            } else {
+                this.update(&mut cx, |this, cx| {
+                    let buffer = this.buffer.read(cx).snapshot(cx);
+                    let mut buffer_highlights = this
+                        .document_highlights_for_position(selection.head(), &buffer)
+                        .filter(|highlight| {
+                            highlight.start.excerpt_id == selection.head().excerpt_id
+                                && highlight.end.excerpt_id == selection.head().excerpt_id
+                        });
+                    buffer_highlights
+                        .next()
+                        .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
+                })?
+            };
+            if let Some(rename_range) = rename_range {
+                let rename_buffer_range = rename_range.to_offset(&snapshot);
+                let cursor_offset_in_rename_range =
+                    cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
 
-    //                 let ranges = this
-    //                     .clear_background_highlights::<DocumentHighlightWrite>(cx)
-    //                     .into_iter()
-    //                     .flat_map(|(_, ranges)| ranges.into_iter())
-    //                     .chain(
-    //                         this.clear_background_highlights::<DocumentHighlightRead>(cx)
-    //                             .into_iter()
-    //                             .flat_map(|(_, ranges)| ranges.into_iter()),
-    //                     )
-    //                     .collect();
-
-    //                 this.highlight_text::<Rename>(
-    //                     ranges,
-    //                     HighlightStyle {
-    //                         fade_out: Some(style.rename_fade),
-    //                         ..Default::default()
-    //                     },
-    //                     cx,
-    //                 );
-    //                 cx.focus(&rename_editor);
-    //                 let block_id = this.insert_blocks(
-    //                     [BlockProperties {
-    //                         style: BlockStyle::Flex,
-    //                         position: range.start.clone(),
-    //                         height: 1,
-    //                         render: Arc::new({
-    //                             let editor = rename_editor.clone();
-    //                             move |cx: &mut BlockContext| {
-    //                                 ChildView::new(&editor, cx)
-    //                                     .contained()
-    //                                     .with_padding_left(cx.anchor_x)
-    //                                     .into_any()
-    //                             }
-    //                         }),
-    //                         disposition: BlockDisposition::Below,
-    //                     }],
-    //                     Some(Autoscroll::fit()),
-    //                     cx,
-    //                 )[0];
-    //                 this.pending_rename = Some(RenameState {
-    //                     range,
-    //                     old_name,
-    //                     editor: rename_editor,
-    //                     block_id,
-    //                 });
-    //             })?;
-    //         }
+                this.update(&mut cx, |this, cx| {
+                    this.take_rename(false, cx);
+                    let buffer = this.buffer.read(cx).read(cx);
+                    let cursor_offset = selection.head().to_offset(&buffer);
+                    let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
+                    let rename_end = rename_start + rename_buffer_range.len();
+                    let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
+                    let mut old_highlight_id = None;
+                    let old_name: Arc<str> = buffer
+                        .chunks(rename_start..rename_end, true)
+                        .map(|chunk| {
+                            if old_highlight_id.is_none() {
+                                old_highlight_id = chunk.syntax_highlight_id;
+                            }
+                            chunk.text
+                        })
+                        .collect::<String>()
+                        .into();
 
-    //         Ok(())
-    //     }))
-    // }
+                    drop(buffer);
 
-    //     pub fn confirm_rename(
-    //         workspace: &mut Workspace,
-    //         _: &ConfirmRename,
-    //         cx: &mut ViewContext<Workspace>,
-    //     ) -> Option<Task<Result<()>>> {
-    //         let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
-
-    //         let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
-    //             let rename = editor.take_rename(false, cx)?;
-    //             let buffer = editor.buffer.read(cx);
-    //             let (start_buffer, start) =
-    //                 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
-    //             let (end_buffer, end) =
-    //                 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
-    //             if start_buffer == end_buffer {
-    //                 let new_name = rename.editor.read(cx).text(cx);
-    //                 Some((start_buffer, start..end, rename.old_name, new_name))
-    //             } else {
-    //                 None
-    //             }
-    //         })?;
+                    // Position the selection in the rename editor so that it matches the current selection.
+                    this.show_local_selections = false;
+                    let rename_editor = cx.build_view(|cx| {
+                        let mut editor = Editor::single_line(cx);
+                        editor.buffer.update(cx, |buffer, cx| {
+                            buffer.edit([(0..0, old_name.clone())], None, cx)
+                        });
+                        editor.select_all(&SelectAll, cx);
+                        editor
+                    });
 
-    //         let rename = workspace.project().clone().update(cx, |project, cx| {
-    //             project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
-    //         });
+                    let ranges = this
+                        .clear_background_highlights::<DocumentHighlightWrite>(cx)
+                        .into_iter()
+                        .flat_map(|(_, ranges)| ranges.into_iter())
+                        .chain(
+                            this.clear_background_highlights::<DocumentHighlightRead>(cx)
+                                .into_iter()
+                                .flat_map(|(_, ranges)| ranges.into_iter()),
+                        )
+                        .collect();
 
-    //         let editor = editor.downgrade();
-    //         Some(cx.spawn(|workspace, mut cx| async move {
-    //             let project_transaction = rename.await?;
-    //             Self::open_project_transaction(
-    //                 &editor,
-    //                 workspace,
-    //                 project_transaction,
-    //                 format!("Rename: {} → {}", old_name, new_name),
-    //                 cx.clone(),
-    //             )
-    //             .await?;
+                    this.highlight_text::<Rename>(
+                        ranges,
+                        HighlightStyle {
+                            fade_out: Some(0.6),
+                            ..Default::default()
+                        },
+                        cx,
+                    );
+                    let rename_focus_handle = rename_editor.focus_handle(cx);
+                    cx.focus(&rename_focus_handle);
+                    let block_id = this.insert_blocks(
+                        [BlockProperties {
+                            style: BlockStyle::Flex,
+                            position: range.start.clone(),
+                            height: 1,
+                            render: Arc::new({
+                                let editor = rename_editor.clone();
+                                move |cx: &mut BlockContext| {
+                                    div().pl(cx.anchor_x).child(editor.clone()).render()
+                                }
+                            }),
+                            disposition: BlockDisposition::Below,
+                        }],
+                        Some(Autoscroll::fit()),
+                        cx,
+                    )[0];
+                    this.pending_rename = Some(RenameState {
+                        range,
+                        old_name,
+                        editor: rename_editor,
+                        block_id,
+                    });
+                })?;
+            }
 
-    //             editor.update(&mut cx, |editor, cx| {
-    //                 editor.refresh_document_highlights(cx);
-    //             })?;
-    //             Ok(())
-    //         }))
-    //     }
+            Ok(())
+        }))
+    }
+
+    pub fn confirm_rename(
+        &mut self,
+        _: &ConfirmRename,
+        cx: &mut ViewContext<Self>,
+    ) -> Option<Task<Result<()>>> {
+        let rename = self.take_rename(false, cx)?;
+        let workspace = self.workspace()?;
+        let (start_buffer, start) = self
+            .buffer
+            .read(cx)
+            .text_anchor_for_position(rename.range.start.clone(), cx)?;
+        let (end_buffer, end) = self
+            .buffer
+            .read(cx)
+            .text_anchor_for_position(rename.range.end.clone(), cx)?;
+        if start_buffer != end_buffer {
+            return None;
+        }
+
+        let buffer = start_buffer;
+        let range = start..end;
+        let old_name = rename.old_name;
+        let new_name = rename.editor.read(cx).text(cx);
+
+        let rename = workspace
+            .read(cx)
+            .project()
+            .clone()
+            .update(cx, |project, cx| {
+                project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
+            });
+        let workspace = workspace.downgrade();
+
+        Some(cx.spawn(|editor, mut cx| async move {
+            let project_transaction = rename.await?;
+            Self::open_project_transaction(
+                &editor,
+                workspace,
+                project_transaction,
+                format!("Rename: {} → {}", old_name, new_name),
+                cx.clone(),
+            )
+            .await?;
+
+            editor.update(&mut cx, |editor, cx| {
+                editor.refresh_document_highlights(cx);
+            })?;
+            Ok(())
+        }))
+    }
 
     fn take_rename(
         &mut self,

crates/editor2/src/element.rs 🔗

@@ -2007,7 +2007,6 @@ impl EditorElement {
                         anchor_x,
                         gutter_padding,
                         line_height,
-                        // scroll_x,
                         gutter_width,
                         em_width,
                         block_id,
@@ -2569,8 +2568,16 @@ impl Element<Editor> for EditorElement {
                             .confirm_code_action(action, cx)
                             .map(|task| task.detach_and_log_err(cx));
                     });
-                    // on_action(cx, Editor::rename); todo!()
-                    // on_action(cx, Editor::confirm_rename); todo!()
+                    register_action(cx, |editor, action, cx| {
+                        editor
+                            .rename(action, cx)
+                            .map(|task| task.detach_and_log_err(cx));
+                    });
+                    register_action(cx, |editor, action, cx| {
+                        editor
+                            .confirm_rename(action, cx)
+                            .map(|task| task.detach_and_log_err(cx));
+                    });
                     register_action(cx, |editor, action, cx| {
                         editor
                             .find_all_references(action, cx)