repl: Do some cleanup (#54362)

Finn Evers created

This removes some code duplication as well as the minimap from the code
cells

Release Notes:

- N/A

Change summary

crates/repl/src/notebook/cell.rs        |  56 +++++++
crates/repl/src/notebook/notebook_ui.rs | 204 +++++++++-----------------
2 files changed, 124 insertions(+), 136 deletions(-)

Detailed changes

crates/repl/src/notebook/cell.rs 🔗

@@ -98,6 +98,11 @@ pub enum Cell {
     Raw(Entity<RawCell>),
 }
 
+pub(crate) enum MovementDirection {
+    Start,
+    End,
+}
+
 fn convert_outputs(
     outputs: &Vec<nbformat::v4::Output>,
     window: &mut Window,
@@ -223,6 +228,52 @@ impl Cell {
             })),
         }
     }
+
+    pub(crate) fn move_to(&self, direction: MovementDirection, window: &mut Window, cx: &mut App) {
+        fn move_in_editor(
+            editor: &Entity<Editor>,
+            direction: MovementDirection,
+            window: &mut Window,
+            cx: &mut App,
+        ) {
+            editor.update(cx, |editor, cx| {
+                match direction {
+                    MovementDirection::Start => {
+                        editor.move_to_beginning(&Default::default(), window, cx);
+                    }
+                    MovementDirection::End => {
+                        editor.move_to_end(&Default::default(), window, cx);
+                    }
+                }
+                editor.focus_handle(cx).focus(window, cx);
+            })
+        }
+
+        match self {
+            Cell::Code(cell) => {
+                cell.update(cx, |cell, cx| {
+                    move_in_editor(&cell.editor, direction, window, cx)
+                });
+            }
+            Cell::Markdown(cell) => {
+                cell.update(cx, |cell, cx| {
+                    cell.set_editing(true);
+                    move_in_editor(&cell.editor, direction, window, cx);
+
+                    cx.notify();
+                });
+            }
+            _ => {}
+        }
+    }
+
+    pub(crate) fn editor<'a>(&'a self, cx: &'a App) -> Option<&'a Entity<Editor>> {
+        match self {
+            Cell::Code(cell) => Some(cell.read(cx).editor()),
+            Cell::Markdown(cell) => Some(cell.read(cx).editor()),
+            _ => None,
+        }
+    }
 }
 
 pub trait RenderableCell: Render {
@@ -620,7 +671,7 @@ impl CodeCell {
         let buffer = cx.new(|cx| Buffer::local(source.clone(), cx));
         let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
 
-        let editor_view = cx.new(|cx| {
+        let editor = cx.new(|cx| {
             let mut editor = Editor::new(
                 EditorMode::Full {
                     scale_ui_elements_with_buffer_font_size: false,
@@ -643,6 +694,7 @@ impl CodeCell {
             };
 
             editor.disable_mouse_wheel_zoom();
+            editor.disable_scrollbars_and_minimap(window, cx);
             editor.set_show_gutter(false, cx);
             editor.set_text_style_refinement(refinement);
             editor.set_use_modal_editing(true);
@@ -661,7 +713,7 @@ impl CodeCell {
             metadata,
             execution_count: None,
             source,
-            editor: editor_view,
+            editor,
             outputs: Vec::new(),
             selected: false,
             cell_position: None,

crates/repl/src/notebook/notebook_ui.rs 🔗

@@ -35,6 +35,7 @@ use crate::kernels::{
     Kernel, KernelSession, KernelSpecification, KernelStatus, LocalKernelSpecification,
     NativeRunningKernel, RemoteRunningKernel, SshRunningKernel, WslRunningKernel,
 };
+use crate::notebook::MovementDirection;
 use crate::repl_store::ReplStore;
 
 use picker::Picker;
@@ -54,6 +55,12 @@ pub(crate) enum NotebookMode {
     Edit,
 }
 
+#[derive(PartialEq, Eq)]
+enum SelectionMode {
+    SelectOnly,
+    SelectAndMove,
+}
+
 pub(crate) const MAX_TEXT_BLOCK_WIDTH: f32 = 9999.0;
 pub(crate) const SMALL_SPACING_SIZE: f32 = 8.0;
 pub(crate) const MEDIUM_SPACING_SIZE: f32 = 12.0;
@@ -84,14 +91,11 @@ pub struct NotebookEditor {
     languages: Arc<LanguageRegistry>,
     project: Entity<Project>,
     worktree_id: project::WorktreeId,
-
     focus_handle: FocusHandle,
     notebook_item: Entity<NotebookItem>,
     notebook_language: Shared<Task<Option<Arc<Language>>>>,
-
     remote_id: Option<ViewId>,
     cell_list: ListState,
-
     notebook_mode: NotebookMode,
     selected_cell_index: usize,
     cell_order: Vec<CellId>,
@@ -541,6 +545,12 @@ impl NotebookEditor {
         }
     }
 
+    fn get_selected_cell(&self) -> Option<&Cell> {
+        self.cell_order
+            .get(self.selected_cell_index)
+            .and_then(|cell_id| self.cell_map.get(cell_id))
+    }
+
     fn has_outputs(&self, window: &mut Window, cx: &mut Context<Self>) -> bool {
         self.cell_map.values().any(|cell| {
             if let Cell::Code(code_cell) = cell {
@@ -865,9 +875,10 @@ impl NotebookEditor {
         }
     }
 
-    pub fn select_next(
+    fn select_next(
         &mut self,
         _: &menu::SelectNext,
+        selection_mode: SelectionMode,
         window: &mut Window,
         cx: &mut Context<Self>,
     ) {
@@ -880,13 +891,21 @@ impl NotebookEditor {
                 index + 1
             };
             self.set_selected_index(ix, true, window, cx);
+
+            if selection_mode == SelectionMode::SelectAndMove
+                && let Some(cell) = self.get_selected_cell()
+            {
+                cell.move_to(MovementDirection::Start, window, cx);
+            }
+
             cx.notify();
         }
     }
 
-    pub fn select_previous(
+    fn select_previous(
         &mut self,
         _: &menu::SelectPrevious,
+        selection_mode: SelectionMode,
         window: &mut Window,
         cx: &mut Context<Self>,
     ) {
@@ -895,6 +914,13 @@ impl NotebookEditor {
             let index = self.selected_index();
             let ix = if index == 0 { 0 } else { index - 1 };
             self.set_selected_index(ix, true, window, cx);
+
+            if selection_mode == SelectionMode::SelectAndMove
+                && let Some(cell) = self.get_selected_cell()
+            {
+                cell.move_to(MovementDirection::End, window, cx);
+            }
+
             cx.notify();
         }
     }
@@ -1316,82 +1342,37 @@ impl Render for NotebookEditor {
             .on_action(cx.listener(|this, action, window, cx| {
                 this.handle_enter_command_mode(action, window, cx)
             }))
-            .on_action(cx.listener(|this, action, window, cx| this.select_next(action, window, cx)))
-            .on_action(
-                cx.listener(|this, action, window, cx| this.select_previous(action, window, cx)),
-            )
-            .on_action(
-                cx.listener(|this, action, window, cx| this.select_first(action, window, cx)),
-            )
-            .on_action(cx.listener(|this, action, window, cx| this.select_last(action, window, cx)))
-            .on_action(cx.listener(|this, _: &MoveUp, window, cx| {
-                this.select_previous(&menu::SelectPrevious, window, cx);
-                if let Some(cell_id) = this.cell_order.get(this.selected_cell_index) {
-                    if let Some(cell) = this.cell_map.get(cell_id) {
-                        match cell {
-                            Cell::Code(cell) => {
-                                let editor = cell.read(cx).editor().clone();
-                                editor.update(cx, |editor, cx| {
-                                    editor.move_to_end(&Default::default(), window, cx);
-                                });
-                                editor.focus_handle(cx).focus(window, cx);
-                            }
-                            Cell::Markdown(cell) => {
-                                cell.update(cx, |cell, cx| {
-                                    cell.set_editing(true);
-                                    cx.notify();
-                                });
-                                let editor = cell.read(cx).editor().clone();
-                                editor.update(cx, |editor, cx| {
-                                    editor.move_to_end(&Default::default(), window, cx);
-                                });
-                                editor.focus_handle(cx).focus(window, cx);
-                            }
-                            _ => {}
-                        }
-                    }
-                }
+            .on_action(cx.listener(|this, action, window, cx| {
+                this.select_next(action, SelectionMode::SelectOnly, window, cx)
+            }))
+            .on_action(cx.listener(|this, action, window, cx| {
+                this.select_previous(action, SelectionMode::SelectOnly, window, cx)
             }))
+            .on_action(cx.listener(Self::select_first))
+            .on_action(cx.listener(Self::select_last))
             .on_action(cx.listener(|this, _: &MoveDown, window, cx| {
-                this.select_next(&menu::SelectNext, window, cx);
-                if let Some(cell_id) = this.cell_order.get(this.selected_cell_index) {
-                    if let Some(cell) = this.cell_map.get(cell_id) {
-                        match cell {
-                            Cell::Code(cell) => {
-                                let editor = cell.read(cx).editor().clone();
-                                editor.update(cx, |editor, cx| {
-                                    editor.move_to_beginning(&Default::default(), window, cx);
-                                });
-                                editor.focus_handle(cx).focus(window, cx);
-                            }
-                            Cell::Markdown(cell) => {
-                                cell.update(cx, |cell, cx| {
-                                    cell.set_editing(true);
-                                    cx.notify();
-                                });
-                                let editor = cell.read(cx).editor().clone();
-                                editor.update(cx, |editor, cx| {
-                                    editor.move_to_beginning(&Default::default(), window, cx);
-                                });
-                                editor.focus_handle(cx).focus(window, cx);
-                            }
-                            _ => {}
-                        }
-                    }
-                }
+                this.select_next(
+                    &Default::default(),
+                    SelectionMode::SelectAndMove,
+                    window,
+                    cx,
+                );
+            }))
+            .on_action(cx.listener(|this, _: &MoveUp, window, cx| {
+                this.select_previous(
+                    &Default::default(),
+                    SelectionMode::SelectAndMove,
+                    window,
+                    cx,
+                );
             }))
             .on_action(cx.listener(|this, _: &NotebookMoveDown, window, cx| {
-                let Some(cell_id) = this.cell_order.get(this.selected_cell_index) else {
-                    return;
-                };
-                let Some(cell) = this.cell_map.get(cell_id) else {
+                let Some(cell) = this.get_selected_cell() else {
                     return;
                 };
 
-                let editor = match cell {
-                    Cell::Code(cell) => cell.read(cx).editor().clone(),
-                    Cell::Markdown(cell) => cell.read(cx).editor().clone(),
-                    _ => return,
+                let Some(editor) = cell.editor(cx).cloned() else {
+                    return;
                 };
 
                 let is_at_last_line = editor.update(cx, |editor, cx| {
@@ -1409,32 +1390,12 @@ impl Render for NotebookEditor {
                 });
 
                 if is_at_last_line {
-                    this.select_next(&menu::SelectNext, window, cx);
-                    if let Some(cell_id) = this.cell_order.get(this.selected_cell_index) {
-                        if let Some(cell) = this.cell_map.get(cell_id) {
-                            match cell {
-                                Cell::Code(cell) => {
-                                    let editor = cell.read(cx).editor().clone();
-                                    editor.update(cx, |editor, cx| {
-                                        editor.move_to_beginning(&Default::default(), window, cx);
-                                    });
-                                    editor.focus_handle(cx).focus(window, cx);
-                                }
-                                Cell::Markdown(cell) => {
-                                    cell.update(cx, |cell, cx| {
-                                        cell.set_editing(true);
-                                        cx.notify();
-                                    });
-                                    let editor = cell.read(cx).editor().clone();
-                                    editor.update(cx, |editor, cx| {
-                                        editor.move_to_beginning(&Default::default(), window, cx);
-                                    });
-                                    editor.focus_handle(cx).focus(window, cx);
-                                }
-                                _ => {}
-                            }
-                        }
-                    }
+                    this.select_next(
+                        &Default::default(),
+                        SelectionMode::SelectAndMove,
+                        window,
+                        cx,
+                    );
                 } else {
                     editor.update(cx, |editor, cx| {
                         editor.move_down(&Default::default(), window, cx);
@@ -1442,17 +1403,12 @@ impl Render for NotebookEditor {
                 }
             }))
             .on_action(cx.listener(|this, _: &NotebookMoveUp, window, cx| {
-                let Some(cell_id) = this.cell_order.get(this.selected_cell_index) else {
-                    return;
-                };
-                let Some(cell) = this.cell_map.get(cell_id) else {
+                let Some(cell) = this.get_selected_cell() else {
                     return;
                 };
 
-                let editor = match cell {
-                    Cell::Code(cell) => cell.read(cx).editor().clone(),
-                    Cell::Markdown(cell) => cell.read(cx).editor().clone(),
-                    _ => return,
+                let Some(editor) = cell.editor(cx).cloned() else {
+                    return;
                 };
 
                 let is_at_first_line = editor.update(cx, |editor, cx| {
@@ -1469,32 +1425,12 @@ impl Render for NotebookEditor {
                 });
 
                 if is_at_first_line {
-                    this.select_previous(&menu::SelectPrevious, window, cx);
-                    if let Some(cell_id) = this.cell_order.get(this.selected_cell_index) {
-                        if let Some(cell) = this.cell_map.get(cell_id) {
-                            match cell {
-                                Cell::Code(cell) => {
-                                    let editor = cell.read(cx).editor().clone();
-                                    editor.update(cx, |editor, cx| {
-                                        editor.move_to_end(&Default::default(), window, cx);
-                                    });
-                                    editor.focus_handle(cx).focus(window, cx);
-                                }
-                                Cell::Markdown(cell) => {
-                                    cell.update(cx, |cell, cx| {
-                                        cell.set_editing(true);
-                                        cx.notify();
-                                    });
-                                    let editor = cell.read(cx).editor().clone();
-                                    editor.update(cx, |editor, cx| {
-                                        editor.move_to_end(&Default::default(), window, cx);
-                                    });
-                                    editor.focus_handle(cx).focus(window, cx);
-                                }
-                                _ => {}
-                            }
-                        }
-                    }
+                    this.select_previous(
+                        &Default::default(),
+                        SelectionMode::SelectAndMove,
+                        window,
+                        cx,
+                    );
                 } else {
                     editor.update(cx, |editor, cx| {
                         editor.move_up(&Default::default(), window, cx);