Avoid holding borrow to editor while painting child elements

Antonio Scandurra created

Change summary

crates/editor2/src/element.rs     | 1053 +++++++++++++++++++-------------
crates/gpui2/src/elements/text.rs |    5 
2 files changed, 610 insertions(+), 448 deletions(-)

Detailed changes

crates/editor2/src/element.rs 🔗

@@ -124,6 +124,180 @@ impl EditorElement {
         }
     }
 
+    fn register_actions(&self, cx: &mut WindowContext) {
+        let view = &self.editor;
+        register_action(view, cx, Editor::move_left);
+        register_action(view, cx, Editor::move_right);
+        register_action(view, cx, Editor::move_down);
+        register_action(view, cx, Editor::move_up);
+        // on_action(cx, Editor::new_file); todo!()
+        // on_action(cx, Editor::new_file_in_direction); todo!()
+        register_action(view, cx, Editor::cancel);
+        register_action(view, cx, Editor::newline);
+        register_action(view, cx, Editor::newline_above);
+        register_action(view, cx, Editor::newline_below);
+        register_action(view, cx, Editor::backspace);
+        register_action(view, cx, Editor::delete);
+        register_action(view, cx, Editor::tab);
+        register_action(view, cx, Editor::tab_prev);
+        register_action(view, cx, Editor::indent);
+        register_action(view, cx, Editor::outdent);
+        register_action(view, cx, Editor::delete_line);
+        register_action(view, cx, Editor::join_lines);
+        register_action(view, cx, Editor::sort_lines_case_sensitive);
+        register_action(view, cx, Editor::sort_lines_case_insensitive);
+        register_action(view, cx, Editor::reverse_lines);
+        register_action(view, cx, Editor::shuffle_lines);
+        register_action(view, cx, Editor::convert_to_upper_case);
+        register_action(view, cx, Editor::convert_to_lower_case);
+        register_action(view, cx, Editor::convert_to_title_case);
+        register_action(view, cx, Editor::convert_to_snake_case);
+        register_action(view, cx, Editor::convert_to_kebab_case);
+        register_action(view, cx, Editor::convert_to_upper_camel_case);
+        register_action(view, cx, Editor::convert_to_lower_camel_case);
+        register_action(view, cx, Editor::delete_to_previous_word_start);
+        register_action(view, cx, Editor::delete_to_previous_subword_start);
+        register_action(view, cx, Editor::delete_to_next_word_end);
+        register_action(view, cx, Editor::delete_to_next_subword_end);
+        register_action(view, cx, Editor::delete_to_beginning_of_line);
+        register_action(view, cx, Editor::delete_to_end_of_line);
+        register_action(view, cx, Editor::cut_to_end_of_line);
+        register_action(view, cx, Editor::duplicate_line);
+        register_action(view, cx, Editor::move_line_up);
+        register_action(view, cx, Editor::move_line_down);
+        register_action(view, cx, Editor::transpose);
+        register_action(view, cx, Editor::cut);
+        register_action(view, cx, Editor::copy);
+        register_action(view, cx, Editor::paste);
+        register_action(view, cx, Editor::undo);
+        register_action(view, cx, Editor::redo);
+        register_action(view, cx, Editor::move_page_up);
+        register_action(view, cx, Editor::move_page_down);
+        register_action(view, cx, Editor::next_screen);
+        register_action(view, cx, Editor::scroll_cursor_top);
+        register_action(view, cx, Editor::scroll_cursor_center);
+        register_action(view, cx, Editor::scroll_cursor_bottom);
+        register_action(view, cx, |editor, _: &LineDown, cx| {
+            editor.scroll_screen(&ScrollAmount::Line(1.), cx)
+        });
+        register_action(view, cx, |editor, _: &LineUp, cx| {
+            editor.scroll_screen(&ScrollAmount::Line(-1.), cx)
+        });
+        register_action(view, cx, |editor, _: &HalfPageDown, cx| {
+            editor.scroll_screen(&ScrollAmount::Page(0.5), cx)
+        });
+        register_action(view, cx, |editor, _: &HalfPageUp, cx| {
+            editor.scroll_screen(&ScrollAmount::Page(-0.5), cx)
+        });
+        register_action(view, cx, |editor, _: &PageDown, cx| {
+            editor.scroll_screen(&ScrollAmount::Page(1.), cx)
+        });
+        register_action(view, cx, |editor, _: &PageUp, cx| {
+            editor.scroll_screen(&ScrollAmount::Page(-1.), cx)
+        });
+        register_action(view, cx, Editor::move_to_previous_word_start);
+        register_action(view, cx, Editor::move_to_previous_subword_start);
+        register_action(view, cx, Editor::move_to_next_word_end);
+        register_action(view, cx, Editor::move_to_next_subword_end);
+        register_action(view, cx, Editor::move_to_beginning_of_line);
+        register_action(view, cx, Editor::move_to_end_of_line);
+        register_action(view, cx, Editor::move_to_start_of_paragraph);
+        register_action(view, cx, Editor::move_to_end_of_paragraph);
+        register_action(view, cx, Editor::move_to_beginning);
+        register_action(view, cx, Editor::move_to_end);
+        register_action(view, cx, Editor::select_up);
+        register_action(view, cx, Editor::select_down);
+        register_action(view, cx, Editor::select_left);
+        register_action(view, cx, Editor::select_right);
+        register_action(view, cx, Editor::select_to_previous_word_start);
+        register_action(view, cx, Editor::select_to_previous_subword_start);
+        register_action(view, cx, Editor::select_to_next_word_end);
+        register_action(view, cx, Editor::select_to_next_subword_end);
+        register_action(view, cx, Editor::select_to_beginning_of_line);
+        register_action(view, cx, Editor::select_to_end_of_line);
+        register_action(view, cx, Editor::select_to_start_of_paragraph);
+        register_action(view, cx, Editor::select_to_end_of_paragraph);
+        register_action(view, cx, Editor::select_to_beginning);
+        register_action(view, cx, Editor::select_to_end);
+        register_action(view, cx, Editor::select_all);
+        register_action(view, cx, |editor, action, cx| {
+            editor.select_all_matches(action, cx).log_err();
+        });
+        register_action(view, cx, Editor::select_line);
+        register_action(view, cx, Editor::split_selection_into_lines);
+        register_action(view, cx, Editor::add_selection_above);
+        register_action(view, cx, Editor::add_selection_below);
+        register_action(view, cx, |editor, action, cx| {
+            editor.select_next(action, cx).log_err();
+        });
+        register_action(view, cx, |editor, action, cx| {
+            editor.select_previous(action, cx).log_err();
+        });
+        register_action(view, cx, Editor::toggle_comments);
+        register_action(view, cx, Editor::select_larger_syntax_node);
+        register_action(view, cx, Editor::select_smaller_syntax_node);
+        register_action(view, cx, Editor::move_to_enclosing_bracket);
+        register_action(view, cx, Editor::undo_selection);
+        register_action(view, cx, Editor::redo_selection);
+        register_action(view, cx, Editor::go_to_diagnostic);
+        register_action(view, cx, Editor::go_to_prev_diagnostic);
+        register_action(view, cx, Editor::go_to_hunk);
+        register_action(view, cx, Editor::go_to_prev_hunk);
+        register_action(view, cx, Editor::go_to_definition);
+        register_action(view, cx, Editor::go_to_definition_split);
+        register_action(view, cx, Editor::go_to_type_definition);
+        register_action(view, cx, Editor::go_to_type_definition_split);
+        register_action(view, cx, Editor::fold);
+        register_action(view, cx, Editor::fold_at);
+        register_action(view, cx, Editor::unfold_lines);
+        register_action(view, cx, Editor::unfold_at);
+        register_action(view, cx, Editor::fold_selected_ranges);
+        register_action(view, cx, Editor::show_completions);
+        register_action(view, cx, Editor::toggle_code_actions);
+        // on_action(cx, Editor::open_excerpts); todo!()
+        register_action(view, cx, Editor::toggle_soft_wrap);
+        register_action(view, cx, Editor::toggle_inlay_hints);
+        register_action(view, cx, Editor::reveal_in_finder);
+        register_action(view, cx, Editor::copy_path);
+        register_action(view, cx, Editor::copy_relative_path);
+        register_action(view, cx, Editor::copy_highlight_json);
+        register_action(view, cx, |editor, action, cx| {
+            editor
+                .format(action, cx)
+                .map(|task| task.detach_and_log_err(cx));
+        });
+        register_action(view, cx, Editor::restart_language_server);
+        register_action(view, cx, Editor::show_character_palette);
+        // on_action(cx, Editor::confirm_completion); todo!()
+        register_action(view, cx, |editor, action, cx| {
+            editor
+                .confirm_code_action(action, cx)
+                .map(|task| task.detach_and_log_err(cx));
+        });
+        register_action(view, cx, |editor, action, cx| {
+            editor
+                .rename(action, cx)
+                .map(|task| task.detach_and_log_err(cx));
+        });
+        register_action(view, cx, |editor, action, cx| {
+            editor
+                .confirm_rename(action, cx)
+                .map(|task| task.detach_and_log_err(cx));
+        });
+        register_action(view, cx, |editor, action, cx| {
+            editor
+                .find_all_references(action, cx)
+                .map(|task| task.detach_and_log_err(cx));
+        });
+        register_action(view, cx, Editor::next_copilot_suggestion);
+        register_action(view, cx, Editor::previous_copilot_suggestion);
+        register_action(view, cx, Editor::copilot_suggest);
+        register_action(view, cx, Editor::context_menu_first);
+        register_action(view, cx, Editor::context_menu_prev);
+        register_action(view, cx, Editor::context_menu_next);
+        register_action(view, cx, Editor::context_menu_last);
+    }
+
     fn mouse_down(
         editor: &mut Editor,
         event: &MouseDownEvent,
@@ -459,7 +633,6 @@ impl EditorElement {
         &mut self,
         bounds: Bounds<Pixels>,
         layout: &mut LayoutState,
-        editor: &mut Editor,
         cx: &mut WindowContext,
     ) {
         let line_height = layout.position_map.line_height;
@@ -616,14 +789,19 @@ impl EditorElement {
         &mut self,
         text_bounds: Bounds<Pixels>,
         layout: &mut LayoutState,
-        editor: &mut Editor,
         cx: &mut WindowContext,
     ) {
         let scroll_position = layout.position_map.snapshot.scroll_position();
         let start_row = layout.visible_display_row_range.start;
         let content_origin = text_bounds.origin + point(layout.gutter_margin, Pixels::ZERO);
         let line_end_overshoot = 0.15 * layout.position_map.line_height;
-        let whitespace_setting = editor.buffer.read(cx).settings_at(0, cx).show_whitespaces;
+        let whitespace_setting = self
+            .editor
+            .read(cx)
+            .buffer
+            .read(cx)
+            .settings_at(0, cx)
+            .show_whitespaces;
 
         cx.with_content_mask(
             Some(ContentMask {
@@ -748,7 +926,7 @@ impl EditorElement {
                             invisible_display_ranges.push(selection.range.clone());
                         }
 
-                        if !selection.is_local || editor.show_local_cursors(cx) {
+                        if !selection.is_local || self.editor.read(cx).show_local_cursors(cx) {
                             let cursor_position = selection.head;
                             if layout
                                 .visible_display_row_range
@@ -800,12 +978,14 @@ impl EditorElement {
                                     * layout.position_map.line_height
                                     - layout.position_map.scroll_position.y;
                                 if selection.is_newest {
-                                    editor.pixel_position_of_newest_cursor = Some(point(
-                                        text_bounds.origin.x + x + block_width / 2.,
-                                        text_bounds.origin.y
-                                            + y
-                                            + layout.position_map.line_height / 2.,
-                                    ));
+                                    self.editor.update(cx, |editor, _| {
+                                        editor.pixel_position_of_newest_cursor = Some(point(
+                                            text_bounds.origin.x + x + block_width / 2.,
+                                            text_bounds.origin.y
+                                                + y
+                                                + layout.position_map.line_height / 2.,
+                                        ))
+                                    });
                                 }
                                 cursors.push(Cursor {
                                     color: selection_style.cursor,
@@ -1217,7 +1397,6 @@ impl EditorElement {
         &mut self,
         bounds: Bounds<Pixels>,
         layout: &mut LayoutState,
-        editor: &mut Editor,
         cx: &mut WindowContext,
     ) {
         let scroll_position = layout.position_map.snapshot.scroll_position();
@@ -1237,7 +1416,7 @@ impl EditorElement {
         }
     }
 
-    fn column_pixels(&self, column: usize, cx: &ViewContext<Editor>) -> Pixels {
+    fn column_pixels(&self, column: usize, cx: &WindowContext) -> Pixels {
         let style = &self.style;
         let font_size = style.text.font_size.to_pixels(cx.rem_size());
         let layout = cx
@@ -1258,7 +1437,7 @@ impl EditorElement {
         layout.width
     }
 
-    fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &ViewContext<Editor>) -> Pixels {
+    fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &WindowContext) -> Pixels {
         let digit_count = (snapshot.max_buffer_row() as f32 + 1.).log10().floor() as usize + 1;
         self.column_pixels(digit_count, cx)
     }
@@ -1413,7 +1592,7 @@ impl EditorElement {
     }
 
     fn layout_lines(
-        &mut self,
+        &self,
         rows: Range<u32>,
         line_number_layouts: &[Option<ShapedLine>],
         snapshot: &EditorSnapshot,
@@ -1469,483 +1648,469 @@ impl EditorElement {
 
     fn compute_layout(
         &mut self,
-        editor: &mut Editor,
-        cx: &mut ViewContext<'_, Editor>,
         mut bounds: Bounds<Pixels>,
+        cx: &mut WindowContext,
     ) -> LayoutState {
-        // let mut size = constraint.max;
-        // if size.x.is_infinite() {
-        //     unimplemented!("we don't yet handle an infinite width constraint on buffer elements");
-        // }
-
-        let snapshot = editor.snapshot(cx);
-        let style = self.style.clone();
+        self.editor.update(cx, |editor, cx| {
+            // let mut size = constraint.max;
+            // if size.x.is_infinite() {
+            //     unimplemented!("we don't yet handle an infinite width constraint on buffer elements");
+            // }
+
+            let snapshot = editor.snapshot(cx);
+            let style = self.style.clone();
+
+            let font_id = cx.text_system().font_id(&style.text.font()).unwrap();
+            let font_size = style.text.font_size.to_pixels(cx.rem_size());
+            let line_height = style.text.line_height_in_pixels(cx.rem_size());
+            let em_width = cx
+                .text_system()
+                .typographic_bounds(font_id, font_size, 'm')
+                .unwrap()
+                .size
+                .width;
+            let em_advance = cx
+                .text_system()
+                .advance(font_id, font_size, 'm')
+                .unwrap()
+                .width;
+
+            let gutter_padding;
+            let gutter_width;
+            let gutter_margin;
+            if snapshot.show_gutter {
+                let descent = cx.text_system().descent(font_id, font_size).unwrap();
+
+                let gutter_padding_factor = 3.5;
+                gutter_padding = (em_width * gutter_padding_factor).round();
+                gutter_width = self.max_line_number_width(&snapshot, cx) + gutter_padding * 2.0;
+                gutter_margin = -descent;
+            } else {
+                gutter_padding = Pixels::ZERO;
+                gutter_width = Pixels::ZERO;
+                gutter_margin = Pixels::ZERO;
+            };
 
-        let font_id = cx.text_system().font_id(&style.text.font()).unwrap();
-        let font_size = style.text.font_size.to_pixels(cx.rem_size());
-        let line_height = style.text.line_height_in_pixels(cx.rem_size());
-        let em_width = cx
-            .text_system()
-            .typographic_bounds(font_id, font_size, 'm')
-            .unwrap()
-            .size
-            .width;
-        let em_advance = cx
-            .text_system()
-            .advance(font_id, font_size, 'm')
-            .unwrap()
-            .width;
-
-        let gutter_padding;
-        let gutter_width;
-        let gutter_margin;
-        if snapshot.show_gutter {
-            let descent = cx.text_system().descent(font_id, font_size).unwrap();
-
-            let gutter_padding_factor = 3.5;
-            gutter_padding = (em_width * gutter_padding_factor).round();
-            gutter_width = self.max_line_number_width(&snapshot, cx) + gutter_padding * 2.0;
-            gutter_margin = -descent;
-        } else {
-            gutter_padding = Pixels::ZERO;
-            gutter_width = Pixels::ZERO;
-            gutter_margin = Pixels::ZERO;
-        };
+            editor.gutter_width = gutter_width;
+            let text_width = bounds.size.width - gutter_width;
+            let overscroll = size(em_width, px(0.));
+            let snapshot = {
+                editor.set_visible_line_count((bounds.size.height / line_height).into(), cx);
+
+                let editor_width = text_width - gutter_margin - overscroll.width - em_width;
+                let wrap_width = match editor.soft_wrap_mode(cx) {
+                    SoftWrap::None => (MAX_LINE_LEN / 2) as f32 * em_advance,
+                    SoftWrap::EditorWidth => editor_width,
+                    SoftWrap::Column(column) => editor_width.min(column as f32 * em_advance),
+                };
 
-        editor.gutter_width = gutter_width;
-        let text_width = bounds.size.width - gutter_width;
-        let overscroll = size(em_width, px(0.));
-        let snapshot = {
-            editor.set_visible_line_count((bounds.size.height / line_height).into(), cx);
-
-            let editor_width = text_width - gutter_margin - overscroll.width - em_width;
-            let wrap_width = match editor.soft_wrap_mode(cx) {
-                SoftWrap::None => (MAX_LINE_LEN / 2) as f32 * em_advance,
-                SoftWrap::EditorWidth => editor_width,
-                SoftWrap::Column(column) => editor_width.min(column as f32 * em_advance),
+                if editor.set_wrap_width(Some(wrap_width), cx) {
+                    editor.snapshot(cx)
+                } else {
+                    snapshot
+                }
             };
 
-            if editor.set_wrap_width(Some(wrap_width), cx) {
-                editor.snapshot(cx)
+            let wrap_guides = editor
+                .wrap_guides(cx)
+                .iter()
+                .map(|(guide, active)| (self.column_pixels(*guide, cx), *active))
+                .collect::<SmallVec<[_; 2]>>();
+
+            let scroll_height = Pixels::from(snapshot.max_point().row() + 1) * line_height;
+            // todo!("this should happen during layout")
+            let editor_mode = snapshot.mode;
+            if let EditorMode::AutoHeight { max_lines } = editor_mode {
+                todo!()
+                //     size.set_y(
+                //         scroll_height
+                //             .min(constraint.max_along(Axis::Vertical))
+                //             .max(constraint.min_along(Axis::Vertical))
+                //             .max(line_height)
+                //             .min(line_height * max_lines as f32),
+                //     )
+            } else if let EditorMode::SingleLine = editor_mode {
+                bounds.size.height = line_height.min(bounds.size.height);
+            }
+            // todo!()
+            // else if size.y.is_infinite() {
+            //     //     size.set_y(scroll_height);
+            // }
+            //
+            let gutter_size = size(gutter_width, bounds.size.height);
+            let text_size = size(text_width, bounds.size.height);
+
+            let autoscroll_horizontally =
+                editor.autoscroll_vertically(bounds.size.height, line_height, cx);
+            let mut snapshot = editor.snapshot(cx);
+
+            let scroll_position = snapshot.scroll_position();
+            // The scroll position is a fractional point, the whole number of which represents
+            // the top of the window in terms of display rows.
+            let start_row = scroll_position.y as u32;
+            let height_in_lines = f32::from(bounds.size.height / line_height);
+            let max_row = snapshot.max_point().row();
+
+            // Add 1 to ensure selections bleed off screen
+            let end_row = 1 + cmp::min((scroll_position.y + height_in_lines).ceil() as u32, max_row);
+
+            let start_anchor = if start_row == 0 {
+                Anchor::min()
             } else {
                 snapshot
-            }
-        };
+                    .buffer_snapshot
+                    .anchor_before(DisplayPoint::new(start_row, 0).to_offset(&snapshot, Bias::Left))
+            };
+            let end_anchor = if end_row > max_row {
+                Anchor::max()
+            } else {
+                snapshot
+                    .buffer_snapshot
+                    .anchor_before(DisplayPoint::new(end_row, 0).to_offset(&snapshot, Bias::Right))
+            };
 
-        let wrap_guides = editor
-            .wrap_guides(cx)
-            .iter()
-            .map(|(guide, active)| (self.column_pixels(*guide, cx), *active))
-            .collect::<SmallVec<[_; 2]>>();
-
-        let scroll_height = Pixels::from(snapshot.max_point().row() + 1) * line_height;
-        // todo!("this should happen during layout")
-        let editor_mode = snapshot.mode;
-        if let EditorMode::AutoHeight { max_lines } = editor_mode {
-            todo!()
-            //     size.set_y(
-            //         scroll_height
-            //             .min(constraint.max_along(Axis::Vertical))
-            //             .max(constraint.min_along(Axis::Vertical))
-            //             .max(line_height)
-            //             .min(line_height * max_lines as f32),
-            //     )
-        } else if let EditorMode::SingleLine = editor_mode {
-            bounds.size.height = line_height.min(bounds.size.height);
-        }
-        // todo!()
-        // else if size.y.is_infinite() {
-        //     //     size.set_y(scroll_height);
-        // }
-        //
-        let gutter_size = size(gutter_width, bounds.size.height);
-        let text_size = size(text_width, bounds.size.height);
-
-        let autoscroll_horizontally =
-            editor.autoscroll_vertically(bounds.size.height, line_height, cx);
-        let mut snapshot = editor.snapshot(cx);
-
-        let scroll_position = snapshot.scroll_position();
-        // The scroll position is a fractional point, the whole number of which represents
-        // the top of the window in terms of display rows.
-        let start_row = scroll_position.y as u32;
-        let height_in_lines = f32::from(bounds.size.height / line_height);
-        let max_row = snapshot.max_point().row();
-
-        // Add 1 to ensure selections bleed off screen
-        let end_row = 1 + cmp::min((scroll_position.y + height_in_lines).ceil() as u32, max_row);
-
-        let start_anchor = if start_row == 0 {
-            Anchor::min()
-        } else {
-            snapshot
-                .buffer_snapshot
-                .anchor_before(DisplayPoint::new(start_row, 0).to_offset(&snapshot, Bias::Left))
-        };
-        let end_anchor = if end_row > max_row {
-            Anchor::max()
-        } else {
-            snapshot
-                .buffer_snapshot
-                .anchor_before(DisplayPoint::new(end_row, 0).to_offset(&snapshot, Bias::Right))
-        };
+            let mut selections: Vec<(PlayerColor, Vec<SelectionLayout>)> = Vec::new();
+            let mut active_rows = BTreeMap::new();
+            let is_singleton = editor.is_singleton(cx);
 
-        let mut selections: Vec<(PlayerColor, Vec<SelectionLayout>)> = Vec::new();
-        let mut active_rows = BTreeMap::new();
-        let is_singleton = editor.is_singleton(cx);
+            let highlighted_rows = editor.highlighted_rows();
+            let highlighted_ranges = editor.background_highlights_in_range(
+                start_anchor..end_anchor,
+                &snapshot.display_snapshot,
+                cx.theme().colors(),
+            );
 
-        let highlighted_rows = editor.highlighted_rows();
-        let highlighted_ranges = editor.background_highlights_in_range(
-            start_anchor..end_anchor,
-            &snapshot.display_snapshot,
-            cx.theme().colors(),
-        );
+            let mut newest_selection_head = None;
+
+            if editor.show_local_selections {
+                let mut local_selections: Vec<Selection<Point>> = editor
+                    .selections
+                    .disjoint_in_range(start_anchor..end_anchor, cx);
+                local_selections.extend(editor.selections.pending(cx));
+                let mut layouts = Vec::new();
+                let newest = editor.selections.newest(cx);
+                for selection in local_selections.drain(..) {
+                    let is_empty = selection.start == selection.end;
+                    let is_newest = selection == newest;
+
+                    let layout = SelectionLayout::new(
+                        selection,
+                        editor.selections.line_mode,
+                        editor.cursor_shape,
+                        &snapshot.display_snapshot,
+                        is_newest,
+                        true,
+                    );
+                    if is_newest {
+                        newest_selection_head = Some(layout.head);
+                    }
 
-        let mut newest_selection_head = None;
-
-        if editor.show_local_selections {
-            let mut local_selections: Vec<Selection<Point>> = editor
-                .selections
-                .disjoint_in_range(start_anchor..end_anchor, cx);
-            local_selections.extend(editor.selections.pending(cx));
-            let mut layouts = Vec::new();
-            let newest = editor.selections.newest(cx);
-            for selection in local_selections.drain(..) {
-                let is_empty = selection.start == selection.end;
-                let is_newest = selection == newest;
-
-                let layout = SelectionLayout::new(
-                    selection,
-                    editor.selections.line_mode,
-                    editor.cursor_shape,
-                    &snapshot.display_snapshot,
-                    is_newest,
-                    true,
-                );
-                if is_newest {
-                    newest_selection_head = Some(layout.head);
+                    for row in cmp::max(layout.active_rows.start, start_row)
+                        ..=cmp::min(layout.active_rows.end, end_row)
+                    {
+                        let contains_non_empty_selection = active_rows.entry(row).or_insert(!is_empty);
+                        *contains_non_empty_selection |= !is_empty;
+                    }
+                    layouts.push(layout);
                 }
 
-                for row in cmp::max(layout.active_rows.start, start_row)
-                    ..=cmp::min(layout.active_rows.end, end_row)
-                {
-                    let contains_non_empty_selection = active_rows.entry(row).or_insert(!is_empty);
-                    *contains_non_empty_selection |= !is_empty;
-                }
-                layouts.push(layout);
+                selections.push((style.local_player, layouts));
             }
 
-            selections.push((style.local_player, layouts));
-        }
-
-        if let Some(collaboration_hub) = &editor.collaboration_hub {
-            // When following someone, render the local selections in their color.
-            if let Some(leader_id) = editor.leader_peer_id {
-                if let Some(collaborator) = collaboration_hub.collaborators(cx).get(&leader_id) {
-                    if let Some(participant_index) = collaboration_hub
-                        .user_participant_indices(cx)
-                        .get(&collaborator.user_id)
-                    {
-                        if let Some((local_selection_style, _)) = selections.first_mut() {
-                            *local_selection_style = cx
-                                .theme()
-                                .players()
-                                .color_for_participant(participant_index.0);
+            if let Some(collaboration_hub) = &editor.collaboration_hub {
+                // When following someone, render the local selections in their color.
+                if let Some(leader_id) = editor.leader_peer_id {
+                    if let Some(collaborator) = collaboration_hub.collaborators(cx).get(&leader_id) {
+                        if let Some(participant_index) = collaboration_hub
+                            .user_participant_indices(cx)
+                            .get(&collaborator.user_id)
+                        {
+                            if let Some((local_selection_style, _)) = selections.first_mut() {
+                                *local_selection_style = cx
+                                    .theme()
+                                    .players()
+                                    .color_for_participant(participant_index.0);
+                            }
                         }
                     }
                 }
-            }
 
-            let mut remote_selections = HashMap::default();
-            for selection in snapshot.remote_selections_in_range(
-                &(start_anchor..end_anchor),
-                collaboration_hub.as_ref(),
-                cx,
-            ) {
-                let selection_style = if let Some(participant_index) = selection.participant_index {
-                    cx.theme()
-                        .players()
-                        .color_for_participant(participant_index.0)
-                } else {
-                    cx.theme().players().absent()
-                };
-
-                // Don't re-render the leader's selections, since the local selections
-                // match theirs.
-                if Some(selection.peer_id) == editor.leader_peer_id {
-                    continue;
-                }
+                let mut remote_selections = HashMap::default();
+                for selection in snapshot.remote_selections_in_range(
+                    &(start_anchor..end_anchor),
+                    collaboration_hub.as_ref(),
+                    cx,
+                ) {
+                    let selection_style = if let Some(participant_index) = selection.participant_index {
+                        cx.theme()
+                            .players()
+                            .color_for_participant(participant_index.0)
+                    } else {
+                        cx.theme().players().absent()
+                    };
 
-                remote_selections
-                    .entry(selection.replica_id)
-                    .or_insert((selection_style, Vec::new()))
-                    .1
-                    .push(SelectionLayout::new(
-                        selection.selection,
-                        selection.line_mode,
-                        selection.cursor_shape,
-                        &snapshot.display_snapshot,
-                        false,
-                        false,
-                    ));
-            }
+                    // Don't re-render the leader's selections, since the local selections
+                    // match theirs.
+                    if Some(selection.peer_id) == editor.leader_peer_id {
+                        continue;
+                    }
 
-            selections.extend(remote_selections.into_values());
-        }
+                    remote_selections
+                        .entry(selection.replica_id)
+                        .or_insert((selection_style, Vec::new()))
+                        .1
+                        .push(SelectionLayout::new(
+                            selection.selection,
+                            selection.line_mode,
+                            selection.cursor_shape,
+                            &snapshot.display_snapshot,
+                            false,
+                            false,
+                        ));
+                }
 
-        let scrollbar_settings = EditorSettings::get_global(cx).scrollbar;
-        let show_scrollbars = match scrollbar_settings.show {
-            ShowScrollbar::Auto => {
-                // Git
-                (is_singleton && scrollbar_settings.git_diff && snapshot.buffer_snapshot.has_git_diffs())
-                ||
-                // Selections
-                (is_singleton && scrollbar_settings.selections && !highlighted_ranges.is_empty())
-                // Scrollmanager
-                || editor.scroll_manager.scrollbars_visible()
+                selections.extend(remote_selections.into_values());
             }
-            ShowScrollbar::System => editor.scroll_manager.scrollbars_visible(),
-            ShowScrollbar::Always => true,
-            ShowScrollbar::Never => false,
-        };
 
-        let head_for_relative = newest_selection_head.unwrap_or_else(|| {
-            let newest = editor.selections.newest::<Point>(cx);
-            SelectionLayout::new(
-                newest,
-                editor.selections.line_mode,
-                editor.cursor_shape,
-                &snapshot.display_snapshot,
-                true,
-                true,
-            )
-            .head
-        });
-
-        let (line_numbers, fold_statuses) = self.shape_line_numbers(
-            start_row..end_row,
-            &active_rows,
-            head_for_relative,
-            is_singleton,
-            &snapshot,
-            cx,
-        );
-
-        let display_hunks = self.layout_git_gutters(start_row..end_row, &snapshot);
-
-        let scrollbar_row_range = scroll_position.y..(scroll_position.y + height_in_lines);
-
-        let mut max_visible_line_width = Pixels::ZERO;
-        let line_layouts = self.layout_lines(start_row..end_row, &line_numbers, &snapshot, cx);
-        for line_with_invisibles in &line_layouts {
-            if line_with_invisibles.line.width > max_visible_line_width {
-                max_visible_line_width = line_with_invisibles.line.width;
-            }
-        }
+            let scrollbar_settings = EditorSettings::get_global(cx).scrollbar;
+            let show_scrollbars = match scrollbar_settings.show {
+                ShowScrollbar::Auto => {
+                    // Git
+                    (is_singleton && scrollbar_settings.git_diff && snapshot.buffer_snapshot.has_git_diffs())
+                    ||
+                    // Selections
+                    (is_singleton && scrollbar_settings.selections && !highlighted_ranges.is_empty())
+                    // Scrollmanager
+                    || editor.scroll_manager.scrollbars_visible()
+                }
+                ShowScrollbar::System => editor.scroll_manager.scrollbars_visible(),
+                ShowScrollbar::Always => true,
+                ShowScrollbar::Never => false,
+            };
 
-        let longest_line_width = layout_line(snapshot.longest_row(), &snapshot, &style, cx)
-            .unwrap()
-            .width;
-        let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.width;
+            let head_for_relative = newest_selection_head.unwrap_or_else(|| {
+                let newest = editor.selections.newest::<Point>(cx);
+                SelectionLayout::new(
+                    newest,
+                    editor.selections.line_mode,
+                    editor.cursor_shape,
+                    &snapshot.display_snapshot,
+                    true,
+                    true,
+                )
+                .head
+            });
 
-        let (scroll_width, blocks) = cx.with_element_id(Some("editor_blocks"), |cx| {
-            self.layout_blocks(
+            let (line_numbers, fold_statuses) = self.shape_line_numbers(
                 start_row..end_row,
+                &active_rows,
+                head_for_relative,
+                is_singleton,
                 &snapshot,
-                bounds.size.width,
-                scroll_width,
-                gutter_padding,
-                gutter_width,
-                em_width,
-                gutter_width + gutter_margin,
-                line_height,
-                &style,
-                &line_layouts,
-                editor,
                 cx,
-            )
-        });
+            );
 
-        let scroll_max = point(
-            f32::from((scroll_width - text_size.width) / em_width).max(0.0),
-            max_row as f32,
-        );
+            let display_hunks = self.layout_git_gutters(start_row..end_row, &snapshot);
 
-        let clamped = editor.scroll_manager.clamp_scroll_left(scroll_max.x);
+            let scrollbar_row_range = scroll_position.y..(scroll_position.y + height_in_lines);
 
-        let autoscrolled = if autoscroll_horizontally {
-            editor.autoscroll_horizontally(
-                start_row,
-                text_size.width,
-                scroll_width,
-                em_width,
-                &line_layouts,
-                cx,
-            )
-        } else {
-            false
-        };
+            let mut max_visible_line_width = Pixels::ZERO;
+            let line_layouts = self.layout_lines(start_row..end_row, &line_numbers, &snapshot, cx);
+            for line_with_invisibles in &line_layouts {
+                if line_with_invisibles.line.width > max_visible_line_width {
+                    max_visible_line_width = line_with_invisibles.line.width;
+                }
+            }
 
-        if clamped || autoscrolled {
-            snapshot = editor.snapshot(cx);
-        }
+            let longest_line_width = layout_line(snapshot.longest_row(), &snapshot, &style, cx)
+                .unwrap()
+                .width;
+            let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.width;
 
-        let mut context_menu = None;
-        let mut code_actions_indicator = None;
-        if let Some(newest_selection_head) = newest_selection_head {
-            if (start_row..end_row).contains(&newest_selection_head.row()) {
-                if editor.context_menu_visible() {
-                    context_menu =
-                        editor.render_context_menu(newest_selection_head, &self.style, cx);
-                }
+            let (scroll_width, blocks) = cx.with_element_id(Some("editor_blocks"), |cx| {
+                self.layout_blocks(
+                    start_row..end_row,
+                    &snapshot,
+                    bounds.size.width,
+                    scroll_width,
+                    gutter_padding,
+                    gutter_width,
+                    em_width,
+                    gutter_width + gutter_margin,
+                    line_height,
+                    &style,
+                    &line_layouts,
+                    editor,
+                    cx,
+                )
+            });
 
-                let active = matches!(
-                    editor.context_menu.read().as_ref(),
-                    Some(crate::ContextMenu::CodeActions(_))
-                );
+            let scroll_max = point(
+                f32::from((scroll_width - text_size.width) / em_width).max(0.0),
+                max_row as f32,
+            );
 
-                code_actions_indicator = editor
-                    .render_code_actions_indicator(&style, active, cx)
-                    .map(|element| CodeActionsIndicator {
-                        row: newest_selection_head.row(),
-                        button: element,
-                    });
+            let clamped = editor.scroll_manager.clamp_scroll_left(scroll_max.x);
+
+            let autoscrolled = if autoscroll_horizontally {
+                editor.autoscroll_horizontally(
+                    start_row,
+                    text_size.width,
+                    scroll_width,
+                    em_width,
+                    &line_layouts,
+                    cx,
+                )
+            } else {
+                false
+            };
+
+            if clamped || autoscrolled {
+                snapshot = editor.snapshot(cx);
             }
-        }
 
-        let visible_rows = start_row..start_row + line_layouts.len() as u32;
-        // todo!("hover")
-        // let mut hover = editor.hover_state.render(
-        //     &snapshot,
-        //     &style,
-        //     visible_rows,
-        //     editor.workspace.as_ref().map(|(w, _)| w.clone()),
-        //     cx,
-        // );
-        // let mode = editor.mode;
-
-        let mut fold_indicators = cx.with_element_id(Some("gutter_fold_indicators"), |cx| {
-            editor.render_fold_indicators(
-                fold_statuses,
-                &style,
-                editor.gutter_hovered,
-                line_height,
-                gutter_margin,
-                cx,
-            )
-        });
+            let mut context_menu = None;
+            let mut code_actions_indicator = None;
+            if let Some(newest_selection_head) = newest_selection_head {
+                if (start_row..end_row).contains(&newest_selection_head.row()) {
+                    if editor.context_menu_visible() {
+                        context_menu =
+                            editor.render_context_menu(newest_selection_head, &self.style, cx);
+                    }
 
-        // todo!("context_menu")
-        // if let Some((_, context_menu)) = context_menu.as_mut() {
-        //     context_menu.layout(
-        //         SizeConstraint {
-        //             min: gpui::Point::<Pixels>::zero(),
-        //             max: point(
-        //                 cx.window_size().x * 0.7,
-        //                 (12. * line_height).min((size.y - line_height) / 2.),
-        //             ),
-        //         },
-        //         editor,
-        //         cx,
-        //     );
-        // }
+                    let active = matches!(
+                        editor.context_menu.read().as_ref(),
+                        Some(crate::ContextMenu::CodeActions(_))
+                    );
 
-        // todo!("hover popovers")
-        // if let Some((_, hover_popovers)) = hover.as_mut() {
-        //     for hover_popover in hover_popovers.iter_mut() {
-        //         hover_popover.layout(
-        //             SizeConstraint {
-        //                 min: gpui::Point::<Pixels>::zero(),
-        //                 max: point(
-        //                     (120. * em_width) // Default size
-        //                         .min(size.x / 2.) // Shrink to half of the editor width
-        //                         .max(MIN_POPOVER_CHARACTER_WIDTH * em_width), // Apply minimum width of 20 characters
-        //                     (16. * line_height) // Default size
-        //                         .min(size.y / 2.) // Shrink to half of the editor height
-        //                         .max(MIN_POPOVER_LINE_HEIGHT * line_height), // Apply minimum height of 4 lines
-        //                 ),
-        //             },
-        //             editor,
-        //             cx,
-        //         );
-        //     }
-        // }
+                    code_actions_indicator = editor
+                        .render_code_actions_indicator(&style, active, cx)
+                        .map(|element| CodeActionsIndicator {
+                            row: newest_selection_head.row(),
+                            button: element,
+                        });
+                }
+            }
 
-        let invisible_symbol_font_size = font_size / 2.;
-        let tab_invisible = cx
-            .text_system()
-            .shape_line(
-                "→".into(),
-                invisible_symbol_font_size,
-                &[TextRun {
-                    len: "→".len(),
-                    font: self.style.text.font(),
-                    color: cx.theme().colors().editor_invisible,
-                    background_color: None,
-                    underline: None,
-                }],
-            )
-            .unwrap();
-        let space_invisible = cx
-            .text_system()
-            .shape_line(
-                "•".into(),
-                invisible_symbol_font_size,
-                &[TextRun {
-                    len: "•".len(),
-                    font: self.style.text.font(),
-                    color: cx.theme().colors().editor_invisible,
-                    background_color: None,
-                    underline: None,
-                }],
-            )
-            .unwrap();
+            let visible_rows = start_row..start_row + line_layouts.len() as u32;
+            // todo!("hover")
+            // let mut hover = editor.hover_state.render(
+            //     &snapshot,
+            //     &style,
+            //     visible_rows,
+            //     editor.workspace.as_ref().map(|(w, _)| w.clone()),
+            //     cx,
+            // );
+            // let mode = editor.mode;
+
+            let mut fold_indicators = cx.with_element_id(Some("gutter_fold_indicators"), |cx| {
+                editor.render_fold_indicators(
+                    fold_statuses,
+                    &style,
+                    editor.gutter_hovered,
+                    line_height,
+                    gutter_margin,
+                    cx,
+                )
+            });
 
-        LayoutState {
-            mode: editor_mode,
-            position_map: Arc::new(PositionMap {
-                size: bounds.size,
-                scroll_position: point(
-                    scroll_position.x * em_width,
-                    scroll_position.y * line_height,
-                ),
-                scroll_max,
-                line_layouts,
-                line_height,
-                em_width,
-                em_advance,
-                snapshot,
-            }),
-            visible_anchor_range: start_anchor..end_anchor,
-            visible_display_row_range: start_row..end_row,
-            wrap_guides,
-            gutter_size,
-            gutter_padding,
-            text_size,
-            scrollbar_row_range,
-            show_scrollbars,
-            is_singleton,
-            max_row,
-            gutter_margin,
-            active_rows,
-            highlighted_rows,
-            highlighted_ranges,
-            line_numbers,
-            display_hunks,
-            blocks,
-            selections,
-            context_menu,
-            code_actions_indicator,
-            fold_indicators,
-            tab_invisible,
-            space_invisible,
-            // hover_popovers: hover,
-        }
+            // todo!("hover popovers")
+            // if let Some((_, hover_popovers)) = hover.as_mut() {
+            //     for hover_popover in hover_popovers.iter_mut() {
+            //         hover_popover.layout(
+            //             SizeConstraint {
+            //                 min: gpui::Point::<Pixels>::zero(),
+            //                 max: point(
+            //                     (120. * em_width) // Default size
+            //                         .min(size.x / 2.) // Shrink to half of the editor width
+            //                         .max(MIN_POPOVER_CHARACTER_WIDTH * em_width), // Apply minimum width of 20 characters
+            //                     (16. * line_height) // Default size
+            //                         .min(size.y / 2.) // Shrink to half of the editor height
+            //                         .max(MIN_POPOVER_LINE_HEIGHT * line_height), // Apply minimum height of 4 lines
+            //                 ),
+            //             },
+            //             editor,
+            //             cx,
+            //         );
+            //     }
+            // }
+
+            let invisible_symbol_font_size = font_size / 2.;
+            let tab_invisible = cx
+                .text_system()
+                .shape_line(
+                    "→".into(),
+                    invisible_symbol_font_size,
+                    &[TextRun {
+                        len: "→".len(),
+                        font: self.style.text.font(),
+                        color: cx.theme().colors().editor_invisible,
+                        background_color: None,
+                        underline: None,
+                    }],
+                )
+                .unwrap();
+            let space_invisible = cx
+                .text_system()
+                .shape_line(
+                    "•".into(),
+                    invisible_symbol_font_size,
+                    &[TextRun {
+                        len: "•".len(),
+                        font: self.style.text.font(),
+                        color: cx.theme().colors().editor_invisible,
+                        background_color: None,
+                        underline: None,
+                    }],
+                )
+                .unwrap();
+
+            LayoutState {
+                mode: editor_mode,
+                position_map: Arc::new(PositionMap {
+                    size: bounds.size,
+                    scroll_position: point(
+                        scroll_position.x * em_width,
+                        scroll_position.y * line_height,
+                    ),
+                    scroll_max,
+                    line_layouts,
+                    line_height,
+                    em_width,
+                    em_advance,
+                    snapshot,
+                }),
+                visible_anchor_range: start_anchor..end_anchor,
+                visible_display_row_range: start_row..end_row,
+                wrap_guides,
+                gutter_size,
+                gutter_padding,
+                text_size,
+                scrollbar_row_range,
+                show_scrollbars,
+                is_singleton,
+                max_row,
+                gutter_margin,
+                active_rows,
+                highlighted_rows,
+                highlighted_ranges,
+                line_numbers,
+                display_hunks,
+                blocks,
+                selections,
+                context_menu,
+                code_actions_indicator,
+                fold_indicators,
+                tab_invisible,
+                space_invisible,
+                // hover_popovers: hover,
+            }
+        })
     }
 
     #[allow(clippy::too_many_arguments)]
     fn layout_blocks(
-        &mut self,
+        &self,
         rows: Range<u32>,
         snapshot: &EditorSnapshot,
         editor_width: Pixels,

crates/gpui2/src/elements/text.rs 🔗

@@ -174,10 +174,7 @@ impl TextState {
 
                 let Some(lines) = text_system
                     .shape_text(
-                        &text,
-                        font_size,
-                        &runs[..],
-                        wrap_width, // Wrap if we know the width.
+                        &text, font_size, &runs, wrap_width, // Wrap if we know the width.
                     )
                     .log_err()
                 else {