Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

⌨️ Keyboard shortcut Alt-Arrow to move cells #2760

Merged
merged 4 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion frontend/common/KeyboardShortcuts.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
export let is_mac_keyboard = /Mac/.test(navigator.platform)

export let ctrl_or_cmd_name = is_mac_keyboard ? "Cmd" : "Ctrl"
export let control_name = is_mac_keyboard ? "⌃" : "Ctrl"
export let ctrl_or_cmd_name = is_mac_keyboard ? "⌘" : "Ctrl"
export let alt_or_options_name = is_mac_keyboard ? "⌥" : "Alt"
export let and = is_mac_keyboard ? " " : "+"

export let has_ctrl_or_cmd_pressed = (event) => event.ctrlKey || (is_mac_keyboard && event.metaKey)

Expand Down
39 changes: 39 additions & 0 deletions frontend/components/CellInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import {
syntaxHighlighting,
cssLanguage,
setDiagnostics,
moveLineUp,
} from "../imports/CodemirrorPlutoSetup.js"

import { markdown, html as htmlLang, javascript, sqlLang, python, julia_mixed } from "./CellInput/mixedParsers.js"
Expand All @@ -68,6 +69,7 @@ import { mod_d_command } from "./CellInput/mod_d_command.js"
import { open_bottom_right_panel } from "./BottomRightPanel.js"
import { timeout_promise } from "../common/PlutoConnection.js"
import { LastFocusWasForcedEffect, tab_help_plugin } from "./CellInput/tab_help_plugin.js"
import { moveLineDown } from "../imports/CodemirrorPlutoSetup.js"

export const ENABLE_CM_MIXED_PARSER = window.localStorage.getItem("ENABLE_CM_MIXED_PARSER") === "true"

Expand Down Expand Up @@ -551,6 +553,40 @@ export const CellInput = ({
return false
}

const keyMapMoveLine = (/** @type {EditorView} */ cm, direction) => {
if (cm.state.facet(EditorState.readOnly)) {
return false
}

const selection = cm.state.selection.main
const all_is_selected = selection.anchor === 0 && selection.head === cm.state.doc.length
console.log({ all_is_selected })

if (all_is_selected || cm.state.doc.lines === 1) {
pluto_actions.move_remote_cells([cell_id], pluto_actions.get_notebook().cell_order.indexOf(cell_id) + (direction === -1 ? -1 : 2))

// workaround for https://github.com/preactjs/preact/issues/4235
// but the crollintoview behaviour is nice, also when the preact issue is fixed.
requestIdleCallback(() => {
cm.dispatch({
// TODO: remove me after fix
selection: {
anchor: 0,
head: cm.state.doc.length,
},

// TODO: keep me after fix
scrollIntoView: true,
})
// TODO: remove me after fix
cm.focus()
})
return true
} else {
return direction === 1 ? moveLineDown(cm) : moveLineUp(cm)
}
}

const plutoKeyMaps = [
{ key: "Shift-Enter", run: keyMapSubmit },
{ key: "Ctrl-Enter", mac: "Cmd-Enter", run: keyMapRun },
Expand All @@ -565,6 +601,8 @@ export const CellInput = ({
{ key: "Ctrl-Delete", run: keyMapDelete },
{ key: "Backspace", run: keyMapBackspace },
{ key: "Ctrl-Backspace", run: keyMapBackspace },
{ key: "Alt-ArrowUp", run: (x) => keyMapMoveLine(x, -1) },
{ key: "Alt-ArrowDown", run: (x) => keyMapMoveLine(x, 1) },

mod_d_command,
]
Expand Down Expand Up @@ -643,6 +681,7 @@ export const CellInput = ({
// Remove selection on blur
EditorView.domEventHandlers({
blur: (event, view) => {
console.warn("blur")
// it turns out that this condition is true *exactly* if and only if the blur event was triggered by blurring the window
let caused_by_window_blur = document.activeElement === view.contentDOM

Expand Down
59 changes: 44 additions & 15 deletions frontend/components/Editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@ import { ExportBanner } from "./ExportBanner.js"
import { Popup } from "./Popup.js"

import { slice_utf8, length_utf8 } from "../common/UnicodeTools.js"
import { has_ctrl_or_cmd_pressed, ctrl_or_cmd_name, is_mac_keyboard, in_textarea_or_input } from "../common/KeyboardShortcuts.js"
import {
has_ctrl_or_cmd_pressed,
ctrl_or_cmd_name,
is_mac_keyboard,
in_textarea_or_input,
and,
control_name,
alt_or_options_name,
} from "../common/KeyboardShortcuts.js"
import { PlutoActionsContext, PlutoBondsContext, PlutoJSInitializingContext, SetWithEmptyCallback } from "../common/PlutoContext.js"
import { BackendLaunchPhase, count_stat } from "../common/Binder.js"
import { setup_mathjax } from "../common/SetupMathJax.js"
Expand Down Expand Up @@ -328,7 +336,7 @@ export class Editor extends Component {
export_menu_open: false,

last_created_cell: null,
selected_cells: [],
selected_cells: /** @type {string[]} */ ([]),

extended_components: {
CustomHeader: null,
Expand Down Expand Up @@ -517,7 +525,8 @@ export class Editor extends Component {
this.client.send("interrupt_all", {}, { notebook_id: this.state.notebook.notebook_id }, false)
},
move_remote_cells: (cell_ids, new_index) => {
update_notebook((notebook) => {
return update_notebook((notebook) => {
new_index = Math.max(0, new_index)
let before = notebook.cell_order.slice(0, new_index).filter((x) => !cell_ids.includes(x))
let after = notebook.cell_order.slice(new_index, Infinity).filter((x) => !cell_ids.includes(x))
notebook.cell_order = [...before, ...cell_ids, ...after]
Expand Down Expand Up @@ -1164,6 +1173,20 @@ patch: ${JSON.stringify(
this.run_selected = () => {
return this.actions.set_and_run_multiple(this.state.selected_cells)
}
this.move_selected = (/** @type {KeyboardEvent} */ e, /** @type {1|-1} */ delta) => {
if (this.state.selected_cells.length > 0) {
const current_indices = this.state.selected_cells.map((id) => this.state.notebook.cell_order.indexOf(id))
const new_index = (delta > 0 ? Math.max : Math.min)(...current_indices) + (delta === -1 ? -1 : 2)

e.preventDefault()
return this.actions.move_remote_cells(this.state.selected_cells, new_index).then(
// scroll into view
() => {
document.getElementById((delta > 0 ? _.last : _.first)(this.state.selected_cells) ?? "")?.scrollIntoView({ block: "nearest" })
}
)
}
}

this.serialize_selected = (cell_id = null) => {
const cells_to_serialize = cell_id == null || this.state.selected_cells.includes(cell_id) ? this.state.selected_cells : [cell_id]
Expand Down Expand Up @@ -1221,29 +1244,35 @@ patch: ${JSON.stringify(
}
} else if (e.key === "Enter" && e.shiftKey) {
this.run_selected()
} else if (e.key === "ArrowUp" && e.altKey) {
this.move_selected(e, -1)
} else if (e.key === "ArrowDown" && e.altKey) {
this.move_selected(e, 1)
} else if ((e.key === "?" && has_ctrl_or_cmd_pressed(e)) || e.key === "F1") {
// On mac "cmd+shift+?" is used by chrome, so that is why this needs to be ctrl as well on mac
// Also pressing "ctrl+shift" on mac causes the key to show up as "/", this madness
// I hope we can find a better solution for this later - Dral
alert(
`Shortcuts 🎹

Shift+Enter: run cell
${ctrl_or_cmd_name}+Enter: run cell and add cell below
⇧${and}Enter: run cell
${ctrl_or_cmd_name}${and}Enter: run cell and add cell below
${ctrl_or_cmd_name}${and}S: submit all changes
Delete or Backspace: delete empty cell

PageUp or fn+Up: jump to cell above
PageDown or fn+Down: jump to cell below

${ctrl_or_cmd_name}+Q: interrupt notebook
${ctrl_or_cmd_name}+S: submit all changes
page up or fn${and}↑: jump to cell above
page down or fn${and}↓: jump to cell below
${alt_or_options_name}${and}↑: move line/cell up
${alt_or_options_name}${and}↓: move line/cell down


Select multiple cells by dragging a selection box from the space between cells.
${ctrl_or_cmd_name}+C: copy selected cells
${ctrl_or_cmd_name}+X: cut selected cells
${ctrl_or_cmd_name}+V: paste selected cells

Ctrl+M: toggle markdown
${ctrl_or_cmd_name}${and}C: copy selected cells
${ctrl_or_cmd_name}${and}X: cut selected cells
${ctrl_or_cmd_name}${and}V: paste selected cells

${control_name}${and}M: toggle markdown
${ctrl_or_cmd_name}${and}Q: interrupt notebook

The notebook file saves every time you run a cell.`
)
Expand Down
10 changes: 9 additions & 1 deletion frontend/imports/CodemirrorPlutoSetup.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5542,6 +5542,14 @@ Default key bindings for the undo history.
*/
declare const historyKeymap: readonly KeyBinding[];
/**
Move the selected lines up one line.
*/
declare const moveLineUp: StateCommand;
/**
Move the selected lines down one line.
*/
declare const moveLineDown: StateCommand;
/**
Add a [unit](https://codemirror.net/6/docs/ref/#language.indentUnit) of indentation to all selected
lines.
*/
Expand Down Expand Up @@ -6878,4 +6886,4 @@ Get this editor's collaborative editing client ID.
*/
declare function getClientID(state: EditorState): string;

export { Annotation, ChangeSet, Compartment, Decoration, Diagnostic, EditorSelection, EditorState, EditorView, Facet, HighlightStyle, NodeProp, PostgreSQL, SelectionRange, StateEffect, StateField, Text, Tooltip, Transaction, TreeCursor, ViewPlugin, ViewUpdate, WidgetType, index_d as autocomplete, bracketMatching, closeBrackets, closeBracketsKeymap, collab, combineConfig, completionKeymap, css, cssLanguage, defaultHighlightStyle, defaultKeymap, drawSelection, foldGutter, foldKeymap, getClientID, getSyncedVersion, highlightSelectionMatches, highlightSpecialChars, history, historyKeymap, html, htmlLanguage, indentLess, indentMore, indentOnInput, indentUnit, javascript, javascriptLanguage, julia as julia_andrey, keymap, lineNumbers, linter, markdown, markdownLanguage, parseCode, parseMixed, placeholder, python, pythonLanguage, receiveUpdates, rectangularSelection, searchKeymap, selectNextOccurrence, sendableUpdates, setDiagnostics, showTooltip, sql, syntaxHighlighting, syntaxTree, syntaxTreeAvailable, tags, tooltips };
export { Annotation, ChangeSet, Compartment, Decoration, Diagnostic, EditorSelection, EditorState, EditorView, Facet, HighlightStyle, NodeProp, PostgreSQL, SelectionRange, StateEffect, StateField, Text, Tooltip, Transaction, TreeCursor, ViewPlugin, ViewUpdate, WidgetType, index_d as autocomplete, bracketMatching, closeBrackets, closeBracketsKeymap, collab, combineConfig, completionKeymap, css, cssLanguage, defaultHighlightStyle, defaultKeymap, drawSelection, foldGutter, foldKeymap, getClientID, getSyncedVersion, highlightSelectionMatches, highlightSpecialChars, history, historyKeymap, html, htmlLanguage, indentLess, indentMore, indentOnInput, indentUnit, javascript, javascriptLanguage, julia as julia_andrey, keymap, lineNumbers, linter, markdown, markdownLanguage, moveLineDown, moveLineUp, parseCode, parseMixed, placeholder, python, pythonLanguage, receiveUpdates, rectangularSelection, searchKeymap, selectNextOccurrence, sendableUpdates, setDiagnostics, showTooltip, sql, syntaxHighlighting, syntaxTree, syntaxTreeAvailable, tags, tooltips };
6 changes: 5 additions & 1 deletion frontend/imports/CodemirrorPlutoSetup.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
defaultKeymap,
indentMore,
indentLess,
moveLineUp,
moveLineDown,
tags,
HighlightStyle,
lineNumbers,
Expand Down Expand Up @@ -62,7 +64,7 @@ import {
linter,
setDiagnostics,
//@ts-ignore
} from "https://cdn.jsdelivr.net/gh/JuliaPluto/codemirror-pluto-setup@1234.1.0/dist/index.es.min.js"
} from "https://cdn.jsdelivr.net/gh/JuliaPluto/codemirror-pluto-setup@1234.2.0/dist/index.es.min.js"

export {
linter,
Expand All @@ -79,6 +81,8 @@ export {
defaultKeymap,
indentMore,
indentLess,
moveLineUp,
moveLineDown,
tags,
HighlightStyle,
lineNumbers,
Expand Down