diff --git a/crates/repl/src/notebook/cell.rs b/crates/repl/src/notebook/cell.rs index cb8f1d51103fca83cb92718e51a80c42f1e6be62..c4c651b50b564558887768d1f37f9e3cdf6a517c 100644 --- a/crates/repl/src/notebook/cell.rs +++ b/crates/repl/src/notebook/cell.rs @@ -98,6 +98,11 @@ pub enum Cell { Raw(Entity), } +pub(crate) enum MovementDirection { + Start, + End, +} + fn convert_outputs( outputs: &Vec, 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, + 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> { + 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, diff --git a/crates/repl/src/notebook/notebook_ui.rs b/crates/repl/src/notebook/notebook_ui.rs index a04c124c3d5ab56128bc5440d9999fadd74caa8f..1cb876046dd3800a156eda43b7c580b847cd9a47 100644 --- a/crates/repl/src/notebook/notebook_ui.rs +++ b/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, project: Entity, worktree_id: project::WorktreeId, - focus_handle: FocusHandle, notebook_item: Entity, notebook_language: Shared>>>, - remote_id: Option, cell_list: ListState, - notebook_mode: NotebookMode, selected_cell_index: usize, cell_order: Vec, @@ -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) -> 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, ) { @@ -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, ) { @@ -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);