mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
repl: Add notebook command/edit modes and keybindings (#51194)
- Introducing NotebookMode state and handlers (EnterEditMode, EnterCommandMode, RunAndAdvance). - Wire up UI to switch modes,focus editors appropriately, and advance selection while in command mode. - Update default and vim keymaps to use the new bindings (shift-enter runs+advances, escape enters command mode, and enter/up/down work in command mode). - Reveal and Focus the new Cell when inserted Before you mark this PR as ready for review, make sure that you have: - [x] Added a solid test coverage and/or screenshots from doing manual testing - [x] Done a self-review taking into account security and performance aspects - [x] Aligned any UI changes with the [UI checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) Release Notes: - N/A
This commit is contained in:
parent
f2ef43429f
commit
fd667f6373
6 changed files with 250 additions and 119 deletions
|
|
@ -1436,7 +1436,7 @@
|
|||
{
|
||||
"context": "NotebookEditor",
|
||||
"bindings": {
|
||||
"shift-enter": "notebook::Run",
|
||||
"shift-enter": "notebook::RunAndAdvance",
|
||||
"ctrl-enter": "notebook::Run",
|
||||
"ctrl-shift-enter": "notebook::RunAll",
|
||||
"alt-up": "notebook::MoveCellUp",
|
||||
|
|
@ -1447,11 +1447,19 @@
|
|||
"ctrl-c": "notebook::InterruptKernel",
|
||||
},
|
||||
},
|
||||
{
|
||||
"context": "NotebookEditor && notebook_mode == command",
|
||||
"bindings": {
|
||||
"enter": "notebook::EnterEditMode",
|
||||
"down": "menu::SelectNext",
|
||||
"up": "menu::SelectPrevious",
|
||||
},
|
||||
},
|
||||
{
|
||||
"context": "NotebookEditor > Editor",
|
||||
"bindings": {
|
||||
"enter": "editor::Newline",
|
||||
"shift-enter": "notebook::Run",
|
||||
"shift-enter": "notebook::RunAndAdvance",
|
||||
"ctrl-enter": "notebook::Run",
|
||||
"ctrl-shift-enter": "notebook::RunAll",
|
||||
"alt-up": "notebook::MoveCellUp",
|
||||
|
|
@ -1460,6 +1468,7 @@
|
|||
"ctrl-shift-m": "notebook::AddMarkdownBlock",
|
||||
"ctrl-shift-r": "notebook::RestartKernel",
|
||||
"ctrl-c": "notebook::InterruptKernel",
|
||||
"escape": "notebook::EnterCommandMode",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1572,7 +1572,7 @@
|
|||
{
|
||||
"context": "NotebookEditor",
|
||||
"bindings": {
|
||||
"shift-enter": "notebook::Run",
|
||||
"shift-enter": "notebook::RunAndAdvance",
|
||||
"cmd-enter": "notebook::Run",
|
||||
"cmd-shift-enter": "notebook::RunAll",
|
||||
"alt-up": "notebook::MoveCellUp",
|
||||
|
|
@ -1583,11 +1583,19 @@
|
|||
"cmd-c": "notebook::InterruptKernel",
|
||||
},
|
||||
},
|
||||
{
|
||||
"context": "NotebookEditor && notebook_mode == command",
|
||||
"bindings": {
|
||||
"enter": "notebook::EnterEditMode",
|
||||
"down": "menu::SelectNext",
|
||||
"up": "menu::SelectPrevious",
|
||||
},
|
||||
},
|
||||
{
|
||||
"context": "NotebookEditor > Editor",
|
||||
"bindings": {
|
||||
"enter": "editor::Newline",
|
||||
"shift-enter": "notebook::Run",
|
||||
"shift-enter": "notebook::RunAndAdvance",
|
||||
"cmd-enter": "notebook::Run",
|
||||
"cmd-shift-enter": "notebook::RunAll",
|
||||
"alt-up": "notebook::MoveCellUp",
|
||||
|
|
@ -1596,6 +1604,7 @@
|
|||
"cmd-shift-m": "notebook::AddMarkdownBlock",
|
||||
"cmd-shift-r": "notebook::RestartKernel",
|
||||
"cmd-c": "notebook::InterruptKernel",
|
||||
"escape": "notebook::EnterCommandMode",
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1488,7 +1488,7 @@
|
|||
{
|
||||
"context": "NotebookEditor",
|
||||
"bindings": {
|
||||
"shift-enter": "notebook::Run",
|
||||
"shift-enter": "notebook::RunAndAdvance",
|
||||
"ctrl-enter": "notebook::Run",
|
||||
"ctrl-shift-enter": "notebook::RunAll",
|
||||
"alt-up": "notebook::MoveCellUp",
|
||||
|
|
@ -1499,11 +1499,19 @@
|
|||
"ctrl-c": "notebook::InterruptKernel",
|
||||
},
|
||||
},
|
||||
{
|
||||
"context": "NotebookEditor && notebook_mode == command",
|
||||
"bindings": {
|
||||
"enter": "notebook::EnterEditMode",
|
||||
"down": "menu::SelectNext",
|
||||
"up": "menu::SelectPrevious",
|
||||
},
|
||||
},
|
||||
{
|
||||
"context": "NotebookEditor > Editor",
|
||||
"bindings": {
|
||||
"enter": "editor::Newline",
|
||||
"shift-enter": "notebook::Run",
|
||||
"shift-enter": "notebook::RunAndAdvance",
|
||||
"ctrl-enter": "notebook::Run",
|
||||
"ctrl-shift-enter": "notebook::RunAll",
|
||||
"alt-up": "notebook::MoveCellUp",
|
||||
|
|
@ -1512,6 +1520,7 @@
|
|||
"ctrl-shift-m": "notebook::AddMarkdownBlock",
|
||||
"ctrl-shift-r": "notebook::RestartKernel",
|
||||
"ctrl-c": "notebook::InterruptKernel",
|
||||
"escape": "notebook::EnterCommandMode",
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1110,10 +1110,24 @@
|
|||
},
|
||||
{
|
||||
"context": "NotebookEditor > Editor && VimControl && vim_mode == normal",
|
||||
|
||||
"bindings": {
|
||||
"j": "notebook::NotebookMoveDown",
|
||||
"k": "notebook::NotebookMoveUp",
|
||||
"escape": "notebook::EnterCommandMode",
|
||||
},
|
||||
},
|
||||
{
|
||||
"context": "NotebookEditor && notebook_mode == command",
|
||||
"bindings": {
|
||||
"j": "menu::SelectNext",
|
||||
"k": "menu::SelectPrevious",
|
||||
"g g": "menu::SelectFirst",
|
||||
"shift-g": "menu::SelectLast",
|
||||
"i": "notebook::EnterEditMode",
|
||||
"a": "notebook::EnterEditMode",
|
||||
"enter": "notebook::EnterEditMode",
|
||||
"shift-enter": "notebook::RunAndAdvance",
|
||||
"ctrl-enter": "notebook::Run",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ use feature_flags::{FeatureFlagAppExt as _, NotebookFeatureFlag};
|
|||
use futures::FutureExt;
|
||||
use futures::future::Shared;
|
||||
use gpui::{
|
||||
AnyElement, App, Entity, EventEmitter, FocusHandle, Focusable, ListScrollEvent, ListState,
|
||||
Point, Task, actions, list, prelude::*,
|
||||
AnyElement, App, Entity, EventEmitter, FocusHandle, Focusable, KeyContext, ListScrollEvent,
|
||||
ListState, Point, Task, actions, list, prelude::*,
|
||||
};
|
||||
use jupyter_protocol::JupyterKernelspec;
|
||||
use language::{Language, LanguageRegistry};
|
||||
|
|
@ -41,33 +41,18 @@ use picker::Picker;
|
|||
use runtimelib::{ExecuteRequest, JupyterMessage, JupyterMessageContent};
|
||||
use ui::PopoverMenuHandle;
|
||||
use zed_actions::editor::{MoveDown, MoveUp};
|
||||
use zed_actions::notebook::{NotebookMoveDown, NotebookMoveUp};
|
||||
use zed_actions::notebook::{
|
||||
AddCodeBlock, AddMarkdownBlock, ClearOutputs, EnterCommandMode, EnterEditMode, InterruptKernel,
|
||||
MoveCellDown, MoveCellUp, NotebookMoveDown, NotebookMoveUp, OpenNotebook, RestartKernel, Run,
|
||||
RunAll, RunAndAdvance,
|
||||
};
|
||||
|
||||
actions!(
|
||||
notebook,
|
||||
[
|
||||
/// Opens a Jupyter notebook file.
|
||||
OpenNotebook,
|
||||
/// Runs all cells in the notebook.
|
||||
RunAll,
|
||||
/// Runs the current cell.
|
||||
Run,
|
||||
/// Clears all cell outputs.
|
||||
ClearOutputs,
|
||||
/// Moves the current cell up.
|
||||
MoveCellUp,
|
||||
/// Moves the current cell down.
|
||||
MoveCellDown,
|
||||
/// Adds a new markdown cell.
|
||||
AddMarkdownBlock,
|
||||
/// Adds a new code cell.
|
||||
AddCodeBlock,
|
||||
/// Restarts the kernel.
|
||||
RestartKernel,
|
||||
/// Interrupts the current execution.
|
||||
InterruptKernel,
|
||||
]
|
||||
);
|
||||
/// Whether the notebook is in command mode (navigating cells) or edit mode (editing a cell).
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum NotebookMode {
|
||||
Command,
|
||||
Edit,
|
||||
}
|
||||
|
||||
pub(crate) const MAX_TEXT_BLOCK_WIDTH: f32 = 9999.0;
|
||||
pub(crate) const SMALL_SPACING_SIZE: f32 = 8.0;
|
||||
|
|
@ -107,6 +92,7 @@ pub struct NotebookEditor {
|
|||
remote_id: Option<ViewId>,
|
||||
cell_list: ListState,
|
||||
|
||||
notebook_mode: NotebookMode,
|
||||
selected_cell_index: usize,
|
||||
cell_order: Vec<CellId>,
|
||||
original_cell_order: Vec<CellId>,
|
||||
|
|
@ -148,18 +134,9 @@ impl NotebookEditor {
|
|||
match &cell_entity {
|
||||
Cell::Code(code_cell) => {
|
||||
let cell_id_for_focus = cell_id.clone();
|
||||
cx.subscribe(code_cell, move |this, cell, event, cx| match event {
|
||||
cx.subscribe(code_cell, move |this, _cell, event, cx| match event {
|
||||
CellEvent::Run(cell_id) => this.execute_cell(cell_id.clone(), cx),
|
||||
CellEvent::FocusedIn(_) => {
|
||||
if let Some(index) = this
|
||||
.cell_order
|
||||
.iter()
|
||||
.position(|id| id == &cell_id_for_focus)
|
||||
{
|
||||
this.selected_cell_index = index;
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
CellEvent::FocusedIn(_) => this.select_cell_by_id(&cell_id_for_focus, cx),
|
||||
})
|
||||
.detach();
|
||||
|
||||
|
|
@ -167,20 +144,12 @@ impl NotebookEditor {
|
|||
let editor = code_cell.read(cx).editor().clone();
|
||||
cx.subscribe(&editor, move |this, _editor, event, cx| {
|
||||
if let editor::EditorEvent::Focused = event {
|
||||
if let Some(index) = this
|
||||
.cell_order
|
||||
.iter()
|
||||
.position(|id| id == &cell_id_for_editor)
|
||||
{
|
||||
this.selected_cell_index = index;
|
||||
cx.notify();
|
||||
}
|
||||
this.select_cell_by_id(&cell_id_for_editor, cx);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
Cell::Markdown(markdown_cell) => {
|
||||
let cell_id_for_focus = cell_id.clone();
|
||||
cx.subscribe(
|
||||
markdown_cell,
|
||||
move |_this, cell, event: &MarkdownCellEvent, cx| {
|
||||
|
|
@ -206,14 +175,7 @@ impl NotebookEditor {
|
|||
let editor = markdown_cell.read(cx).editor().clone();
|
||||
cx.subscribe(&editor, move |this, _editor, event, cx| {
|
||||
if let editor::EditorEvent::Focused = event {
|
||||
if let Some(index) = this
|
||||
.cell_order
|
||||
.iter()
|
||||
.position(|id| id == &cell_id_for_editor)
|
||||
{
|
||||
this.selected_cell_index = index;
|
||||
cx.notify();
|
||||
}
|
||||
this.select_cell_by_id(&cell_id_for_editor, cx);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
|
@ -239,6 +201,7 @@ impl NotebookEditor {
|
|||
notebook_language,
|
||||
remote_id: None,
|
||||
cell_list,
|
||||
notebook_mode: NotebookMode::Command,
|
||||
selected_cell_index: 0,
|
||||
cell_order: cell_order.clone(),
|
||||
original_cell_order: cell_order.clone(),
|
||||
|
|
@ -385,8 +348,7 @@ impl NotebookEditor {
|
|||
let working_directory = self
|
||||
.project
|
||||
.read(cx)
|
||||
.worktrees(cx)
|
||||
.next()
|
||||
.worktree_for_id(self.worktree_id, cx)
|
||||
.map(|worktree| worktree.read(cx).abs_path().to_path_buf())
|
||||
.unwrap_or_else(std::env::temp_dir);
|
||||
let fs = self.project.read(cx).fs().clone();
|
||||
|
|
@ -590,6 +552,31 @@ impl NotebookEditor {
|
|||
}
|
||||
|
||||
fn run_current_cell(&mut self, _: &Run, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let Some(cell_id) = self.cell_order.get(self.selected_cell_index).cloned() else {
|
||||
return;
|
||||
};
|
||||
let Some(cell) = self.cell_map.get(&cell_id) else {
|
||||
return;
|
||||
};
|
||||
match cell {
|
||||
Cell::Code(_) => {
|
||||
self.execute_cell(cell_id, cx);
|
||||
}
|
||||
Cell::Markdown(markdown_cell) => {
|
||||
// for markdown, finish editing and move to next cell
|
||||
let is_editing = markdown_cell.read(cx).is_editing();
|
||||
if is_editing {
|
||||
markdown_cell.update(cx, |cell, cx| {
|
||||
cell.run(cx);
|
||||
});
|
||||
self.enter_command_mode(window, cx);
|
||||
}
|
||||
}
|
||||
Cell::Raw(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_and_advance(&mut self, _: &RunAndAdvance, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if let Some(cell_id) = self.cell_order.get(self.selected_cell_index).cloned() {
|
||||
if let Some(cell) = self.cell_map.get(&cell_id) {
|
||||
match cell {
|
||||
|
|
@ -597,25 +584,83 @@ impl NotebookEditor {
|
|||
self.execute_cell(cell_id, cx);
|
||||
}
|
||||
Cell::Markdown(markdown_cell) => {
|
||||
// for markdown, finish editing and move to next cell
|
||||
let is_editing = markdown_cell.read(cx).is_editing();
|
||||
if is_editing {
|
||||
if markdown_cell.read(cx).is_editing() {
|
||||
markdown_cell.update(cx, |cell, cx| {
|
||||
cell.run(cx);
|
||||
});
|
||||
// move to the next cell
|
||||
// Discussion can be done on this default implementation
|
||||
self.move_to_next_cell(window, cx);
|
||||
}
|
||||
}
|
||||
Cell::Raw(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let is_last_cell = self.selected_cell_index == self.cell_count().saturating_sub(1);
|
||||
if is_last_cell {
|
||||
self.add_code_block(window, cx);
|
||||
self.enter_command_mode(window, cx);
|
||||
} else {
|
||||
self.advance_in_command_mode(window, cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_edit_mode(&mut self, _: &EnterEditMode, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.notebook_mode = NotebookMode::Edit;
|
||||
if let Some(cell_id) = self.cell_order.get(self.selected_cell_index) {
|
||||
if let Some(cell) = self.cell_map.get(cell_id) {
|
||||
match cell {
|
||||
Cell::Code(code_cell) => {
|
||||
let editor = code_cell.read(cx).editor().clone();
|
||||
window.focus(&editor.focus_handle(cx), cx);
|
||||
}
|
||||
Cell::Markdown(markdown_cell) => {
|
||||
markdown_cell.update(cx, |cell, cx| {
|
||||
cell.set_editing(true);
|
||||
cx.notify();
|
||||
});
|
||||
let editor = markdown_cell.read(cx).editor().clone();
|
||||
window.focus(&editor.focus_handle(cx), cx);
|
||||
}
|
||||
Cell::Raw(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn enter_command_mode(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.notebook_mode = NotebookMode::Command;
|
||||
self.focus_handle.focus(window, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn handle_enter_command_mode(
|
||||
&mut self,
|
||||
_: &EnterCommandMode,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.enter_command_mode(window, cx);
|
||||
}
|
||||
|
||||
/// Advances to the next cell while staying in command mode (used by RunAndAdvance and shift-enter).
|
||||
fn advance_in_command_mode(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let count = self.cell_count();
|
||||
if count == 0 {
|
||||
return;
|
||||
}
|
||||
if self.selected_cell_index < count - 1 {
|
||||
self.selected_cell_index += 1;
|
||||
self.cell_list
|
||||
.scroll_to_reveal_item(self.selected_cell_index);
|
||||
}
|
||||
self.notebook_mode = NotebookMode::Command;
|
||||
self.focus_handle.focus(window, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
// Discussion can be done on this default implementation
|
||||
/// Moves focus to the next cell, or creates a new code cell if at the end
|
||||
/// Moves focus to the next cell editor (used when already in edit mode).
|
||||
fn move_to_next_cell(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if !self.cell_order.is_empty() && self.selected_cell_index < self.cell_order.len() - 1 {
|
||||
self.selected_cell_index += 1;
|
||||
|
|
@ -666,6 +711,19 @@ impl NotebookEditor {
|
|||
}
|
||||
}
|
||||
|
||||
fn insert_cell_at_current_position(&mut self, cell_id: CellId, cell: Cell) {
|
||||
let insert_index = if self.cell_order.is_empty() {
|
||||
0
|
||||
} else {
|
||||
self.selected_cell_index + 1
|
||||
};
|
||||
self.cell_order.insert(insert_index, cell_id.clone());
|
||||
self.cell_map.insert(cell_id, cell);
|
||||
self.selected_cell_index = insert_index;
|
||||
self.cell_list.splice(insert_index..insert_index, 1);
|
||||
self.cell_list.scroll_to_reveal_item(insert_index);
|
||||
}
|
||||
|
||||
fn add_markdown_block(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let new_cell_id: CellId = Uuid::new_v4().into();
|
||||
let languages = self.languages.clone();
|
||||
|
|
@ -683,16 +741,6 @@ impl NotebookEditor {
|
|||
)
|
||||
});
|
||||
|
||||
let insert_index = if self.cell_order.is_empty() {
|
||||
0
|
||||
} else {
|
||||
self.selected_cell_index + 1
|
||||
};
|
||||
self.cell_order.insert(insert_index, new_cell_id.clone());
|
||||
self.cell_map
|
||||
.insert(new_cell_id.clone(), Cell::Markdown(markdown_cell.clone()));
|
||||
self.selected_cell_index = insert_index;
|
||||
|
||||
cx.subscribe(
|
||||
&markdown_cell,
|
||||
move |_this, cell, event: &MarkdownCellEvent, cx| match event {
|
||||
|
|
@ -709,19 +757,19 @@ impl NotebookEditor {
|
|||
let editor = markdown_cell.read(cx).editor().clone();
|
||||
cx.subscribe(&editor, move |this, _editor, event, cx| {
|
||||
if let editor::EditorEvent::Focused = event {
|
||||
if let Some(index) = this
|
||||
.cell_order
|
||||
.iter()
|
||||
.position(|id| id == &cell_id_for_editor)
|
||||
{
|
||||
this.selected_cell_index = index;
|
||||
cx.notify();
|
||||
}
|
||||
this.select_cell_by_id(&cell_id_for_editor, cx);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
self.cell_list.reset(self.cell_order.len());
|
||||
self.insert_cell_at_current_position(new_cell_id, Cell::Markdown(markdown_cell.clone()));
|
||||
markdown_cell.update(cx, |cell, cx| {
|
||||
cell.set_editing(true);
|
||||
cx.notify();
|
||||
});
|
||||
let editor = markdown_cell.read(cx).editor().clone();
|
||||
window.focus(&editor.focus_handle(cx), cx);
|
||||
self.notebook_mode = NotebookMode::Edit;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
|
|
@ -742,25 +790,10 @@ impl NotebookEditor {
|
|||
)
|
||||
});
|
||||
|
||||
let insert_index = if self.cell_order.is_empty() {
|
||||
0
|
||||
} else {
|
||||
self.selected_cell_index + 1
|
||||
};
|
||||
self.cell_order.insert(insert_index, new_cell_id.clone());
|
||||
self.cell_map
|
||||
.insert(new_cell_id.clone(), Cell::Code(code_cell.clone()));
|
||||
self.selected_cell_index = insert_index;
|
||||
|
||||
let cell_id_for_run = new_cell_id.clone();
|
||||
cx.subscribe(&code_cell, move |this, _cell, event, cx| match event {
|
||||
CellEvent::Run(cell_id) => this.execute_cell(cell_id.clone(), cx),
|
||||
CellEvent::FocusedIn(_) => {
|
||||
if let Some(index) = this.cell_order.iter().position(|id| id == &cell_id_for_run) {
|
||||
this.selected_cell_index = index;
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
CellEvent::FocusedIn(_) => this.select_cell_by_id(&cell_id_for_run, cx),
|
||||
})
|
||||
.detach();
|
||||
|
||||
|
|
@ -768,19 +801,15 @@ impl NotebookEditor {
|
|||
let editor = code_cell.read(cx).editor().clone();
|
||||
cx.subscribe(&editor, move |this, _editor, event, cx| {
|
||||
if let editor::EditorEvent::Focused = event {
|
||||
if let Some(index) = this
|
||||
.cell_order
|
||||
.iter()
|
||||
.position(|id| id == &cell_id_for_editor)
|
||||
{
|
||||
this.selected_cell_index = index;
|
||||
cx.notify();
|
||||
}
|
||||
this.select_cell_by_id(&cell_id_for_editor, cx);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
self.cell_list.reset(self.cell_order.len());
|
||||
self.insert_cell_at_current_position(new_cell_id, Cell::Code(code_cell.clone()));
|
||||
let editor = code_cell.read(cx).editor().clone();
|
||||
window.focus(&editor.focus_handle(cx), cx);
|
||||
self.notebook_mode = NotebookMode::Edit;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
|
|
@ -792,6 +821,14 @@ impl NotebookEditor {
|
|||
self.selected_cell_index
|
||||
}
|
||||
|
||||
fn select_cell_by_id(&mut self, cell_id: &CellId, cx: &mut Context<Self>) {
|
||||
if let Some(index) = self.cell_order.iter().position(|id| id == cell_id) {
|
||||
self.selected_cell_index = index;
|
||||
self.notebook_mode = NotebookMode::Edit;
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_selected_index(
|
||||
&mut self,
|
||||
index: usize,
|
||||
|
|
@ -1216,9 +1253,19 @@ impl NotebookEditor {
|
|||
|
||||
impl Render for NotebookEditor {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let mut key_context = KeyContext::new_with_defaults();
|
||||
key_context.add("NotebookEditor");
|
||||
key_context.set(
|
||||
"notebook_mode",
|
||||
match self.notebook_mode {
|
||||
NotebookMode::Command => "command",
|
||||
NotebookMode::Edit => "edit",
|
||||
},
|
||||
);
|
||||
|
||||
v_flex()
|
||||
.size_full()
|
||||
.key_context("NotebookEditor")
|
||||
.key_context(key_context)
|
||||
.track_focus(&self.focus_handle)
|
||||
.on_action(cx.listener(|this, _: &OpenNotebook, window, cx| {
|
||||
this.open_notebook(&OpenNotebook, window, cx)
|
||||
|
|
@ -1229,6 +1276,9 @@ impl Render for NotebookEditor {
|
|||
.on_action(
|
||||
cx.listener(|this, _: &Run, window, cx| this.run_current_cell(&Run, window, cx)),
|
||||
)
|
||||
.on_action(
|
||||
cx.listener(|this, action, window, cx| this.run_and_advance(action, window, cx)),
|
||||
)
|
||||
.on_action(cx.listener(|this, _: &RunAll, window, cx| this.run_cells(window, cx)))
|
||||
.on_action(
|
||||
cx.listener(|this, _: &MoveCellUp, window, cx| this.move_cell_up(window, cx)),
|
||||
|
|
@ -1242,6 +1292,20 @@ impl Render for NotebookEditor {
|
|||
.on_action(
|
||||
cx.listener(|this, _: &AddCodeBlock, window, cx| this.add_code_block(window, cx)),
|
||||
)
|
||||
.on_action(
|
||||
cx.listener(|this, action, window, cx| this.enter_edit_mode(action, window, cx)),
|
||||
)
|
||||
.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) {
|
||||
|
|
|
|||
|
|
@ -795,10 +795,36 @@ pub mod notebook {
|
|||
actions!(
|
||||
notebook,
|
||||
[
|
||||
/// Move to down in cells
|
||||
/// Opens a Jupyter notebook file.
|
||||
OpenNotebook,
|
||||
/// Runs all cells in the notebook.
|
||||
RunAll,
|
||||
/// Runs the current cell and stays on it.
|
||||
Run,
|
||||
/// Runs the current cell and advances to the next cell.
|
||||
RunAndAdvance,
|
||||
/// Clears all cell outputs.
|
||||
ClearOutputs,
|
||||
/// Moves the current cell up.
|
||||
MoveCellUp,
|
||||
/// Moves the current cell down.
|
||||
MoveCellDown,
|
||||
/// Adds a new markdown cell.
|
||||
AddMarkdownBlock,
|
||||
/// Adds a new code cell.
|
||||
AddCodeBlock,
|
||||
/// Restarts the kernel.
|
||||
RestartKernel,
|
||||
/// Interrupts the current execution.
|
||||
InterruptKernel,
|
||||
/// Move down in cells.
|
||||
NotebookMoveDown,
|
||||
/// Move to up in cells
|
||||
/// Move up in cells.
|
||||
NotebookMoveUp,
|
||||
/// Enters the current cell's editor (edit mode).
|
||||
EnterEditMode,
|
||||
/// Exits the cell editor and returns to cell command mode.
|
||||
EnterCommandMode,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue