Introduce rename in `editor2` (#3331)

Antonio Scandurra created

Release Notes:

- N/A

Change summary

crates/editor2/src/display_map.rs           |   7 
crates/editor2/src/display_map/block_map.rs |   3 
crates/editor2/src/editor.rs                | 403 ++++++++++++----------
crates/editor2/src/element.rs               | 350 ++++++++++---------
crates/gpui2/src/style.rs                   |   8 
crates/gpui2/src/view.rs                    |  82 ++++
6 files changed, 484 insertions(+), 369 deletions(-)

Detailed changes

crates/editor2/src/display_map.rs 🔗

@@ -578,12 +578,7 @@ impl DisplaySnapshot {
             line.push_str(chunk.chunk);
 
             let text_style = if let Some(style) = chunk.style {
-                editor_style
-                    .text
-                    .clone()
-                    .highlight(style)
-                    .map(Cow::Owned)
-                    .unwrap_or_else(|_| Cow::Borrowed(&editor_style.text))
+                Cow::Owned(editor_style.text.clone().highlight(style))
             } else {
                 Cow::Borrowed(&editor_style.text)
             };

crates/editor2/src/display_map/block_map.rs 🔗

@@ -2,7 +2,7 @@ use super::{
     wrap_map::{self, WrapEdit, WrapPoint, WrapSnapshot},
     Highlights,
 };
-use crate::{Anchor, Editor, ExcerptId, ExcerptRange, ToPoint as _};
+use crate::{Anchor, Editor, EditorStyle, ExcerptId, ExcerptRange, ToPoint as _};
 use collections::{Bound, HashMap, HashSet};
 use gpui::{AnyElement, Pixels, ViewContext};
 use language::{BufferSnapshot, Chunk, Patch, Point};
@@ -88,6 +88,7 @@ pub struct BlockContext<'a, 'b> {
     pub em_width: Pixels,
     pub line_height: Pixels,
     pub block_id: usize,
+    pub editor_style: &'b EditorStyle,
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]

crates/editor2/src/editor.rs 🔗

@@ -39,12 +39,12 @@ use futures::FutureExt;
 use fuzzy::{StringMatch, StringMatchCandidate};
 use git::diff_hunk_to_display;
 use gpui::{
-    action, actions, div, point, prelude::*, px, relative, rems, size, uniform_list, AnyElement,
-    AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context,
-    EventEmitter, FocusHandle, FontFeatures, FontStyle, FontWeight, HighlightStyle, Hsla,
-    InputHandler, KeyContext, Model, MouseButton, ParentComponent, Pixels, Render, Styled,
-    Subscription, Task, TextStyle, UniformListScrollHandle, View, ViewContext, VisualContext,
-    WeakView, WindowContext,
+    action, actions, div, point, prelude::*, px, relative, rems, render_view, size, uniform_list,
+    AnyElement, AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem,
+    Component, Context, EventEmitter, FocusHandle, FontFeatures, FontStyle, FontWeight,
+    HighlightStyle, Hsla, InputHandler, KeyContext, Model, MouseButton, ParentComponent, Pixels,
+    Render, Styled, Subscription, Task, TextStyle, UniformListScrollHandle, View, ViewContext,
+    VisualContext, WeakView, WindowContext,
 };
 use highlight_matching_bracket::refresh_matching_bracket_highlights;
 use hover_popover::{hide_hover, HoverState};
@@ -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);
@@ -1878,10 +1880,8 @@ impl Editor {
         );
 
         let focus_handle = cx.focus_handle();
-        cx.on_focus_in(&focus_handle, Self::handle_focus_in)
-            .detach();
-        cx.on_focus_out(&focus_handle, Self::handle_focus_out)
-            .detach();
+        cx.on_focus(&focus_handle, Self::handle_focus).detach();
+        cx.on_blur(&focus_handle, Self::handle_blur).detach();
 
         let mut this = Self {
             handle: cx.view().downgrade(),
@@ -7690,183 +7690,210 @@ 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 rename_editor = rename_editor.clone();
+                                move |cx: &mut BlockContext| {
+                                    let mut text_style = cx.editor_style.text.clone();
+                                    if let Some(highlight_style) = old_highlight_id
+                                        .and_then(|h| h.style(&cx.editor_style.syntax))
+                                    {
+                                        text_style = text_style.highlight(highlight_style);
+                                    }
+                                    div()
+                                        .pl(cx.anchor_x)
+                                        .child(render_view(
+                                            &rename_editor,
+                                            EditorElement::new(
+                                                &rename_editor,
+                                                EditorStyle {
+                                                    background: cx.theme().system().transparent,
+                                                    local_player: cx.editor_style.local_player,
+                                                    text: text_style,
+                                                    scrollbar_width: cx
+                                                        .editor_style
+                                                        .scrollbar_width,
+                                                    syntax: cx.editor_style.syntax.clone(),
+                                                    diagnostic_style: cx
+                                                        .editor_style
+                                                        .diagnostic_style
+                                                        .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,
@@ -7874,6 +7901,10 @@ impl Editor {
         cx: &mut ViewContext<Self>,
     ) -> Option<RenameState> {
         let rename = self.pending_rename.take()?;
+        if rename.editor.focus_handle(cx).is_focused(cx) {
+            cx.focus(&self.focus_handle);
+        }
+
         self.remove_blocks(
             [rename.block_id].into_iter().collect(),
             Some(Autoscroll::fit()),
@@ -9172,17 +9203,13 @@ impl Editor {
         self.focus_handle.is_focused(cx)
     }
 
-    fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
-        if self.focus_handle.is_focused(cx) {
-            // todo!()
-            // let focused_event = EditorFocused(cx.handle());
-            // cx.emit_global(focused_event);
-            cx.emit(Event::Focused);
-        }
+    fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
+        cx.emit(Event::Focused);
+
         if let Some(rename) = self.pending_rename.as_ref() {
             let rename_editor_focus_handle = rename.editor.read(cx).focus_handle.clone();
             cx.focus(&rename_editor_focus_handle);
-        } else if self.focus_handle.is_focused(cx) {
+        } else {
             self.blink_manager.update(cx, BlinkManager::enable);
             self.buffer.update(cx, |buffer, cx| {
                 buffer.finalize_last_transaction(cx);
@@ -9198,7 +9225,7 @@ impl Editor {
         }
     }
 
-    fn handle_focus_out(&mut self, cx: &mut ViewContext<Self>) {
+    fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
         // todo!()
         // let blurred_event = EditorBlurred(cx.handle());
         // cx.emit_global(blurred_event);

crates/editor2/src/element.rs 🔗

@@ -2012,10 +2012,10 @@ impl EditorElement {
                         anchor_x,
                         gutter_padding,
                         line_height,
-                        // scroll_x,
                         gutter_width,
                         em_width,
                         block_id,
+                        editor_style: &self.style,
                     })
                 }
                 TransformBlock::ExcerptHeader {
@@ -2258,11 +2258,7 @@ impl LineWithInvisibles {
 
                 if !line_chunk.is_empty() && !line_exceeded_max_len {
                     let text_style = if let Some(style) = highlighted_chunk.style {
-                        text_style
-                            .clone()
-                            .highlight(style)
-                            .map(Cow::Owned)
-                            .unwrap_or_else(|_| Cow::Borrowed(text_style))
+                        Cow::Owned(text_style.clone().highlight(style))
                     } else {
                         Cow::Borrowed(text_style)
                     };
@@ -2460,168 +2456,7 @@ impl Element<Editor> for EditorElement {
             dispatch_context,
             Some(editor.focus_handle.clone()),
             |_, cx| {
-                register_action(cx, Editor::move_left);
-                register_action(cx, Editor::move_right);
-                register_action(cx, Editor::move_down);
-                register_action(cx, Editor::move_up);
-                // on_action(cx, Editor::new_file); todo!()
-                // on_action(cx, Editor::new_file_in_direction); todo!()
-                register_action(cx, Editor::cancel);
-                register_action(cx, Editor::newline);
-                register_action(cx, Editor::newline_above);
-                register_action(cx, Editor::newline_below);
-                register_action(cx, Editor::backspace);
-                register_action(cx, Editor::delete);
-                register_action(cx, Editor::tab);
-                register_action(cx, Editor::tab_prev);
-                register_action(cx, Editor::indent);
-                register_action(cx, Editor::outdent);
-                register_action(cx, Editor::delete_line);
-                register_action(cx, Editor::join_lines);
-                register_action(cx, Editor::sort_lines_case_sensitive);
-                register_action(cx, Editor::sort_lines_case_insensitive);
-                register_action(cx, Editor::reverse_lines);
-                register_action(cx, Editor::shuffle_lines);
-                register_action(cx, Editor::convert_to_upper_case);
-                register_action(cx, Editor::convert_to_lower_case);
-                register_action(cx, Editor::convert_to_title_case);
-                register_action(cx, Editor::convert_to_snake_case);
-                register_action(cx, Editor::convert_to_kebab_case);
-                register_action(cx, Editor::convert_to_upper_camel_case);
-                register_action(cx, Editor::convert_to_lower_camel_case);
-                register_action(cx, Editor::delete_to_previous_word_start);
-                register_action(cx, Editor::delete_to_previous_subword_start);
-                register_action(cx, Editor::delete_to_next_word_end);
-                register_action(cx, Editor::delete_to_next_subword_end);
-                register_action(cx, Editor::delete_to_beginning_of_line);
-                register_action(cx, Editor::delete_to_end_of_line);
-                register_action(cx, Editor::cut_to_end_of_line);
-                register_action(cx, Editor::duplicate_line);
-                register_action(cx, Editor::move_line_up);
-                register_action(cx, Editor::move_line_down);
-                register_action(cx, Editor::transpose);
-                register_action(cx, Editor::cut);
-                register_action(cx, Editor::copy);
-                register_action(cx, Editor::paste);
-                register_action(cx, Editor::undo);
-                register_action(cx, Editor::redo);
-                register_action(cx, Editor::move_page_up);
-                register_action(cx, Editor::move_page_down);
-                register_action(cx, Editor::next_screen);
-                register_action(cx, Editor::scroll_cursor_top);
-                register_action(cx, Editor::scroll_cursor_center);
-                register_action(cx, Editor::scroll_cursor_bottom);
-                register_action(cx, |editor, _: &LineDown, cx| {
-                    editor.scroll_screen(&ScrollAmount::Line(1.), cx)
-                });
-                register_action(cx, |editor, _: &LineUp, cx| {
-                    editor.scroll_screen(&ScrollAmount::Line(-1.), cx)
-                });
-                register_action(cx, |editor, _: &HalfPageDown, cx| {
-                    editor.scroll_screen(&ScrollAmount::Page(0.5), cx)
-                });
-                register_action(cx, |editor, _: &HalfPageUp, cx| {
-                    editor.scroll_screen(&ScrollAmount::Page(-0.5), cx)
-                });
-                register_action(cx, |editor, _: &PageDown, cx| {
-                    editor.scroll_screen(&ScrollAmount::Page(1.), cx)
-                });
-                register_action(cx, |editor, _: &PageUp, cx| {
-                    editor.scroll_screen(&ScrollAmount::Page(-1.), cx)
-                });
-                register_action(cx, Editor::move_to_previous_word_start);
-                register_action(cx, Editor::move_to_previous_subword_start);
-                register_action(cx, Editor::move_to_next_word_end);
-                register_action(cx, Editor::move_to_next_subword_end);
-                register_action(cx, Editor::move_to_beginning_of_line);
-                register_action(cx, Editor::move_to_end_of_line);
-                register_action(cx, Editor::move_to_start_of_paragraph);
-                register_action(cx, Editor::move_to_end_of_paragraph);
-                register_action(cx, Editor::move_to_beginning);
-                register_action(cx, Editor::move_to_end);
-                register_action(cx, Editor::select_up);
-                register_action(cx, Editor::select_down);
-                register_action(cx, Editor::select_left);
-                register_action(cx, Editor::select_right);
-                register_action(cx, Editor::select_to_previous_word_start);
-                register_action(cx, Editor::select_to_previous_subword_start);
-                register_action(cx, Editor::select_to_next_word_end);
-                register_action(cx, Editor::select_to_next_subword_end);
-                register_action(cx, Editor::select_to_beginning_of_line);
-                register_action(cx, Editor::select_to_end_of_line);
-                register_action(cx, Editor::select_to_start_of_paragraph);
-                register_action(cx, Editor::select_to_end_of_paragraph);
-                register_action(cx, Editor::select_to_beginning);
-                register_action(cx, Editor::select_to_end);
-                register_action(cx, Editor::select_all);
-                register_action(cx, |editor, action, cx| {
-                    editor.select_all_matches(action, cx).log_err();
-                });
-                register_action(cx, Editor::select_line);
-                register_action(cx, Editor::split_selection_into_lines);
-                register_action(cx, Editor::add_selection_above);
-                register_action(cx, Editor::add_selection_below);
-                register_action(cx, |editor, action, cx| {
-                    editor.select_next(action, cx).log_err();
-                });
-                register_action(cx, |editor, action, cx| {
-                    editor.select_previous(action, cx).log_err();
-                });
-                register_action(cx, Editor::toggle_comments);
-                register_action(cx, Editor::select_larger_syntax_node);
-                register_action(cx, Editor::select_smaller_syntax_node);
-                register_action(cx, Editor::move_to_enclosing_bracket);
-                register_action(cx, Editor::undo_selection);
-                register_action(cx, Editor::redo_selection);
-                register_action(cx, Editor::go_to_diagnostic);
-                register_action(cx, Editor::go_to_prev_diagnostic);
-                register_action(cx, Editor::go_to_hunk);
-                register_action(cx, Editor::go_to_prev_hunk);
-                register_action(cx, Editor::go_to_definition);
-                register_action(cx, Editor::go_to_definition_split);
-                register_action(cx, Editor::go_to_type_definition);
-                register_action(cx, Editor::go_to_type_definition_split);
-                register_action(cx, Editor::fold);
-                register_action(cx, Editor::fold_at);
-                register_action(cx, Editor::unfold_lines);
-                register_action(cx, Editor::unfold_at);
-                register_action(cx, Editor::fold_selected_ranges);
-                register_action(cx, Editor::show_completions);
-                register_action(cx, Editor::toggle_code_actions);
-                // on_action(cx, Editor::open_excerpts); todo!()
-                register_action(cx, Editor::toggle_soft_wrap);
-                register_action(cx, Editor::toggle_inlay_hints);
-                register_action(cx, Editor::reveal_in_finder);
-                register_action(cx, Editor::copy_path);
-                register_action(cx, Editor::copy_relative_path);
-                register_action(cx, Editor::copy_highlight_json);
-                register_action(cx, |editor, action, cx| {
-                    editor
-                        .format(action, cx)
-                        .map(|task| task.detach_and_log_err(cx));
-                });
-                register_action(cx, Editor::restart_language_server);
-                register_action(cx, Editor::show_character_palette);
-                // on_action(cx, Editor::confirm_completion); todo!()
-                register_action(cx, |editor, action, cx| {
-                    editor
-                        .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
-                        .find_all_references(action, cx)
-                        .map(|task| task.detach_and_log_err(cx));
-                });
-                register_action(cx, Editor::next_copilot_suggestion);
-                register_action(cx, Editor::previous_copilot_suggestion);
-                register_action(cx, Editor::copilot_suggest);
-                register_action(cx, Editor::context_menu_first);
-                register_action(cx, Editor::context_menu_prev);
-                register_action(cx, Editor::context_menu_next);
-                register_action(cx, Editor::context_menu_last);
+                register_actions(cx);
 
                 // We call with_z_index to establish a new stacking context.
                 cx.with_z_index(0, |cx| {
@@ -2652,6 +2487,12 @@ impl Element<Editor> for EditorElement {
     }
 }
 
+impl Component<Editor> for EditorElement {
+    fn render(self) -> AnyElement<Editor> {
+        AnyElement::new(self)
+    }
+}
+
 // impl EditorElement {
 //     type LayoutState = LayoutState;
 //     type PaintState = ();
@@ -4128,6 +3969,179 @@ fn scale_horizontal_mouse_autoscroll_delta(delta: Pixels) -> f32 {
 //     }
 // }
 
+fn register_actions(cx: &mut ViewContext<Editor>) {
+    register_action(cx, Editor::move_left);
+    register_action(cx, Editor::move_right);
+    register_action(cx, Editor::move_down);
+    register_action(cx, Editor::move_up);
+    // on_action(cx, Editor::new_file); todo!()
+    // on_action(cx, Editor::new_file_in_direction); todo!()
+    register_action(cx, Editor::cancel);
+    register_action(cx, Editor::newline);
+    register_action(cx, Editor::newline_above);
+    register_action(cx, Editor::newline_below);
+    register_action(cx, Editor::backspace);
+    register_action(cx, Editor::delete);
+    register_action(cx, Editor::tab);
+    register_action(cx, Editor::tab_prev);
+    register_action(cx, Editor::indent);
+    register_action(cx, Editor::outdent);
+    register_action(cx, Editor::delete_line);
+    register_action(cx, Editor::join_lines);
+    register_action(cx, Editor::sort_lines_case_sensitive);
+    register_action(cx, Editor::sort_lines_case_insensitive);
+    register_action(cx, Editor::reverse_lines);
+    register_action(cx, Editor::shuffle_lines);
+    register_action(cx, Editor::convert_to_upper_case);
+    register_action(cx, Editor::convert_to_lower_case);
+    register_action(cx, Editor::convert_to_title_case);
+    register_action(cx, Editor::convert_to_snake_case);
+    register_action(cx, Editor::convert_to_kebab_case);
+    register_action(cx, Editor::convert_to_upper_camel_case);
+    register_action(cx, Editor::convert_to_lower_camel_case);
+    register_action(cx, Editor::delete_to_previous_word_start);
+    register_action(cx, Editor::delete_to_previous_subword_start);
+    register_action(cx, Editor::delete_to_next_word_end);
+    register_action(cx, Editor::delete_to_next_subword_end);
+    register_action(cx, Editor::delete_to_beginning_of_line);
+    register_action(cx, Editor::delete_to_end_of_line);
+    register_action(cx, Editor::cut_to_end_of_line);
+    register_action(cx, Editor::duplicate_line);
+    register_action(cx, Editor::move_line_up);
+    register_action(cx, Editor::move_line_down);
+    register_action(cx, Editor::transpose);
+    register_action(cx, Editor::cut);
+    register_action(cx, Editor::copy);
+    register_action(cx, Editor::paste);
+    register_action(cx, Editor::undo);
+    register_action(cx, Editor::redo);
+    register_action(cx, Editor::move_page_up);
+    register_action(cx, Editor::move_page_down);
+    register_action(cx, Editor::next_screen);
+    register_action(cx, Editor::scroll_cursor_top);
+    register_action(cx, Editor::scroll_cursor_center);
+    register_action(cx, Editor::scroll_cursor_bottom);
+    register_action(cx, |editor, _: &LineDown, cx| {
+        editor.scroll_screen(&ScrollAmount::Line(1.), cx)
+    });
+    register_action(cx, |editor, _: &LineUp, cx| {
+        editor.scroll_screen(&ScrollAmount::Line(-1.), cx)
+    });
+    register_action(cx, |editor, _: &HalfPageDown, cx| {
+        editor.scroll_screen(&ScrollAmount::Page(0.5), cx)
+    });
+    register_action(cx, |editor, _: &HalfPageUp, cx| {
+        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx)
+    });
+    register_action(cx, |editor, _: &PageDown, cx| {
+        editor.scroll_screen(&ScrollAmount::Page(1.), cx)
+    });
+    register_action(cx, |editor, _: &PageUp, cx| {
+        editor.scroll_screen(&ScrollAmount::Page(-1.), cx)
+    });
+    register_action(cx, Editor::move_to_previous_word_start);
+    register_action(cx, Editor::move_to_previous_subword_start);
+    register_action(cx, Editor::move_to_next_word_end);
+    register_action(cx, Editor::move_to_next_subword_end);
+    register_action(cx, Editor::move_to_beginning_of_line);
+    register_action(cx, Editor::move_to_end_of_line);
+    register_action(cx, Editor::move_to_start_of_paragraph);
+    register_action(cx, Editor::move_to_end_of_paragraph);
+    register_action(cx, Editor::move_to_beginning);
+    register_action(cx, Editor::move_to_end);
+    register_action(cx, Editor::select_up);
+    register_action(cx, Editor::select_down);
+    register_action(cx, Editor::select_left);
+    register_action(cx, Editor::select_right);
+    register_action(cx, Editor::select_to_previous_word_start);
+    register_action(cx, Editor::select_to_previous_subword_start);
+    register_action(cx, Editor::select_to_next_word_end);
+    register_action(cx, Editor::select_to_next_subword_end);
+    register_action(cx, Editor::select_to_beginning_of_line);
+    register_action(cx, Editor::select_to_end_of_line);
+    register_action(cx, Editor::select_to_start_of_paragraph);
+    register_action(cx, Editor::select_to_end_of_paragraph);
+    register_action(cx, Editor::select_to_beginning);
+    register_action(cx, Editor::select_to_end);
+    register_action(cx, Editor::select_all);
+    register_action(cx, |editor, action, cx| {
+        editor.select_all_matches(action, cx).log_err();
+    });
+    register_action(cx, Editor::select_line);
+    register_action(cx, Editor::split_selection_into_lines);
+    register_action(cx, Editor::add_selection_above);
+    register_action(cx, Editor::add_selection_below);
+    register_action(cx, |editor, action, cx| {
+        editor.select_next(action, cx).log_err();
+    });
+    register_action(cx, |editor, action, cx| {
+        editor.select_previous(action, cx).log_err();
+    });
+    register_action(cx, Editor::toggle_comments);
+    register_action(cx, Editor::select_larger_syntax_node);
+    register_action(cx, Editor::select_smaller_syntax_node);
+    register_action(cx, Editor::move_to_enclosing_bracket);
+    register_action(cx, Editor::undo_selection);
+    register_action(cx, Editor::redo_selection);
+    register_action(cx, Editor::go_to_diagnostic);
+    register_action(cx, Editor::go_to_prev_diagnostic);
+    register_action(cx, Editor::go_to_hunk);
+    register_action(cx, Editor::go_to_prev_hunk);
+    register_action(cx, Editor::go_to_definition);
+    register_action(cx, Editor::go_to_definition_split);
+    register_action(cx, Editor::go_to_type_definition);
+    register_action(cx, Editor::go_to_type_definition_split);
+    register_action(cx, Editor::fold);
+    register_action(cx, Editor::fold_at);
+    register_action(cx, Editor::unfold_lines);
+    register_action(cx, Editor::unfold_at);
+    register_action(cx, Editor::fold_selected_ranges);
+    register_action(cx, Editor::show_completions);
+    register_action(cx, Editor::toggle_code_actions);
+    // on_action(cx, Editor::open_excerpts); todo!()
+    register_action(cx, Editor::toggle_soft_wrap);
+    register_action(cx, Editor::toggle_inlay_hints);
+    register_action(cx, Editor::reveal_in_finder);
+    register_action(cx, Editor::copy_path);
+    register_action(cx, Editor::copy_relative_path);
+    register_action(cx, Editor::copy_highlight_json);
+    register_action(cx, |editor, action, cx| {
+        editor
+            .format(action, cx)
+            .map(|task| task.detach_and_log_err(cx));
+    });
+    register_action(cx, Editor::restart_language_server);
+    register_action(cx, Editor::show_character_palette);
+    // on_action(cx, Editor::confirm_completion); todo!()
+    register_action(cx, |editor, action, cx| {
+        editor
+            .confirm_code_action(action, cx)
+            .map(|task| task.detach_and_log_err(cx));
+    });
+    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)
+            .map(|task| task.detach_and_log_err(cx));
+    });
+    register_action(cx, Editor::next_copilot_suggestion);
+    register_action(cx, Editor::previous_copilot_suggestion);
+    register_action(cx, Editor::copilot_suggest);
+    register_action(cx, Editor::context_menu_first);
+    register_action(cx, Editor::context_menu_prev);
+    register_action(cx, Editor::context_menu_next);
+    register_action(cx, Editor::context_menu_last);
+}
+
 fn register_action<T: Action>(
     cx: &mut ViewContext<Editor>,
     listener: impl Fn(&mut Editor, &T, &mut ViewContext<Editor>) + 'static,

crates/gpui2/src/style.rs 🔗

@@ -1,8 +1,8 @@
 use crate::{
     black, phi, point, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds, ContentMask,
     Corners, CornersRefinement, CursorStyle, DefiniteLength, Edges, EdgesRefinement, Font,
-    FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Result,
-    Rgba, SharedString, Size, SizeRefinement, Styled, TextRun, ViewContext,
+    FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba,
+    SharedString, Size, SizeRefinement, Styled, TextRun, ViewContext,
 };
 use refineable::{Cascade, Refineable};
 use smallvec::SmallVec;
@@ -157,7 +157,7 @@ impl Default for TextStyle {
 }
 
 impl TextStyle {
-    pub fn highlight(mut self, style: HighlightStyle) -> Result<Self> {
+    pub fn highlight(mut self, style: HighlightStyle) -> Self {
         if let Some(weight) = style.font_weight {
             self.font_weight = weight;
         }
@@ -177,7 +177,7 @@ impl TextStyle {
             self.underline = Some(underline);
         }
 
-        Ok(self)
+        self
     }
 
     pub fn font(&self) -> Font {

crates/gpui2/src/view.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{
     private::Sealed, AnyBox, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace,
-    Bounds, Component, Element, ElementId, Entity, EntityId, Flatten, LayoutId, Model, Pixels,
-    Size, ViewContext, VisualContext, WeakModel, WindowContext,
+    BorrowWindow, Bounds, Component, Element, ElementId, Entity, EntityId, Flatten, LayoutId,
+    Model, Pixels, Size, ViewContext, VisualContext, WeakModel, WindowContext,
 };
 use anyhow::{Context, Result};
 use std::{
@@ -281,6 +281,84 @@ where
     }
 }
 
+pub struct RenderView<C, V> {
+    view: View<V>,
+    component: Option<C>,
+}
+
+impl<C, ParentViewState, ViewState> Component<ParentViewState> for RenderView<C, ViewState>
+where
+    C: 'static + Component<ViewState>,
+    ParentViewState: 'static,
+    ViewState: 'static,
+{
+    fn render(self) -> AnyElement<ParentViewState> {
+        AnyElement::new(self)
+    }
+}
+
+impl<C, ParentViewState, ViewState> Element<ParentViewState> for RenderView<C, ViewState>
+where
+    C: 'static + Component<ViewState>,
+    ParentViewState: 'static,
+    ViewState: 'static,
+{
+    type ElementState = AnyElement<ViewState>;
+
+    fn element_id(&self) -> Option<ElementId> {
+        Some(self.view.entity_id().into())
+    }
+
+    fn initialize(
+        &mut self,
+        _: &mut ParentViewState,
+        _: Option<Self::ElementState>,
+        cx: &mut ViewContext<ParentViewState>,
+    ) -> Self::ElementState {
+        cx.with_element_id(Some(self.view.entity_id()), |cx| {
+            self.view.update(cx, |view, cx| {
+                let mut element = self.component.take().unwrap().render();
+                element.initialize(view, cx);
+                element
+            })
+        })
+    }
+
+    fn layout(
+        &mut self,
+        _: &mut ParentViewState,
+        element: &mut Self::ElementState,
+        cx: &mut ViewContext<ParentViewState>,
+    ) -> LayoutId {
+        cx.with_element_id(Some(self.view.entity_id()), |cx| {
+            self.view.update(cx, |view, cx| element.layout(view, cx))
+        })
+    }
+
+    fn paint(
+        &mut self,
+        _: Bounds<Pixels>,
+        _: &mut ParentViewState,
+        element: &mut Self::ElementState,
+        cx: &mut ViewContext<ParentViewState>,
+    ) {
+        cx.with_element_id(Some(self.view.entity_id()), |cx| {
+            self.view.update(cx, |view, cx| element.paint(view, cx))
+        })
+    }
+}
+
+pub fn render_view<C, V>(view: &View<V>, component: C) -> RenderView<C, V>
+where
+    C: 'static + Component<V>,
+    V: 'static,
+{
+    RenderView {
+        view: view.clone(),
+        component: Some(component),
+    }
+}
+
 mod any_view {
     use crate::{AnyElement, AnyView, BorrowWindow, LayoutId, Render, WindowContext};
     use std::any::Any;