Skip to content

Commit

Permalink
Implements the go back and forward keyboard shortcuts (#2789)
Browse files Browse the repository at this point in the history
  • Loading branch information
aleDsz authored Sep 25, 2024
1 parent a7caa42 commit aed8832
Show file tree
Hide file tree
Showing 6 changed files with 353 additions and 14 deletions.
29 changes: 22 additions & 7 deletions assets/js/hooks/cell.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,9 @@ const Cell = {
if (event.type === "dispatch_queue_evaluation") {
this.handleDispatchQueueEvaluation(event.dispatch);
} else if (event.type === "jump_to_line") {
this.handleJumpToLine(event.line);
if (this.isFocused) {
this.currentEditor().moveCursorToLine(event.line, event.offset || 0);
}
}
},

Expand All @@ -173,12 +175,6 @@ const Cell = {
}
},

handleJumpToLine(line) {
if (this.isFocused) {
this.currentEditor().moveCursorToLine(line);
}
},

handleCellEditorCreated(tag, liveEditor) {
this.liveEditors[tag] = liveEditor;

Expand Down Expand Up @@ -211,10 +207,18 @@ const Cell = {
// gives it focus
if (!this.isFocused || !this.insertMode) {
this.currentEditor().blur();
} else {
this.sendCursorHistory();
}
}, 0);
});

liveEditor.onSelectionChange(() => {
if (this.isFocused) {
this.sendCursorHistory();
}
});

if (tag === "primary") {
const source = liveEditor.getSource();

Expand Down Expand Up @@ -370,6 +374,17 @@ const Cell = {
});
});
},

sendCursorHistory() {
const cursor = this.currentEditor().getCurrentCursorPosition();
if (cursor === null) return;

globalPubsub.broadcast("history", {
...cursor,
type: "navigation",
cellId: this.props.cellId,
});
},
};

export default Cell;
41 changes: 38 additions & 3 deletions assets/js/hooks/cell_editor/live_editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ export default class LiveEditor {
*/
onFocus = this._onFocus.event;

/** @private */
_onSelectionChange = new Emitter();

/**
* Registers a callback called whenever the editor changes selection.
*/
onSelectionChange = this._onSelectionChange.event;

constructor(
container,
connection,
Expand Down Expand Up @@ -166,6 +174,21 @@ export default class LiveEditor {
return node.parentElement;
}

/**
* Returns the current main cursor position.
*/
getCurrentCursorPosition() {
if (!this.isMounted()) {
return null;
}

const pos = this.view.state.selection.main.head;
const line = this.view.state.doc.lineAt(pos);
const offset = pos - line.from;

return { line: line.number, offset };
}

/**
* Focuses the editor.
*
Expand All @@ -183,11 +206,12 @@ export default class LiveEditor {
/**
* Updates editor selection such that cursor points to the given line.
*/
moveCursorToLine(lineNumber) {
moveCursorToLine(lineNumber, offset) {
const line = this.view.state.doc.line(lineNumber);
const position = line.from + offset;

this.view.dispatch({
selection: EditorSelection.single(line.from),
selection: EditorSelection.single(position),
});
}

Expand Down Expand Up @@ -308,6 +332,10 @@ export default class LiveEditor {
{ key: "Alt-Enter", run: insertBlankLineAndCloseHints },
];

const selectionChangeListener = EditorView.updateListener.of((update) =>
this.handleViewUpdate(update),
);

this.view = new EditorView({
parent: this.container,
doc: this.source,
Expand Down Expand Up @@ -369,6 +397,7 @@ export default class LiveEditor {
focus: this.handleEditorFocus.bind(this),
}),
EditorView.clickAddsSelectionRange.of((event) => event.altKey),
selectionChangeListener,
],
});
}
Expand All @@ -389,7 +418,6 @@ export default class LiveEditor {
// We dispatch escape event, but only if it is not consumed by any
// registered handler in the editor, such as closing autocompletion
// or escaping Vim insert mode

if (event.key === "Escape") {
this.container.dispatchEvent(
new CustomEvent("lb:editor_escape", { bubbles: true }),
Expand All @@ -415,6 +443,13 @@ export default class LiveEditor {
return false;
}

/** @private */
handleViewUpdate(update) {
if (!update.state.selection.eq(update.startState.selection)) {
this._onSelectionChange.dispatch();
}
}

/** @private */
completionSource(context) {
const settings = settingsStore.get();
Expand Down
52 changes: 50 additions & 2 deletions assets/js/hooks/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { leaveChannel } from "./js_view/channel";
import { isDirectlyEditable, isEvaluable } from "../lib/notebook";
import { settingsStore } from "../lib/settings";
import { LiveStore } from "../lib/live_store";
import CursorHistory from "./session/cursor_history";

/**
* A hook managing the whole session.
Expand Down Expand Up @@ -81,6 +82,7 @@ const Session = {
this.viewOptions = null;
this.keyBuffer = new KeyBuffer();
this.lastLocationReportByClientId = {};
this.cursorHistory = new CursorHistory();
this.followedClientId = null;
this.store = LiveStore.create("session");

Expand Down Expand Up @@ -161,6 +163,7 @@ const Session = {
globalPubsub.subscribe("jump_to_editor", ({ line, file }) =>
this.jumpToLine(file, line),
),
globalPubsub.subscribe("history", this.handleHistoryEvent.bind(this)),
];

this.initializeDragAndDrop();
Expand Down Expand Up @@ -304,6 +307,7 @@ const Session = {
}

const cmd = isMacOS() ? event.metaKey : event.ctrlKey;
const ctrl = event.ctrlKey;
const alt = event.altKey;
const shift = event.shiftKey;
const key = event.key;
Expand All @@ -316,7 +320,16 @@ const Session = {
event.target.closest(`[data-el-outputs-container]`)
)
) {
if (cmd && shift && !alt && key === "Enter") {
// On macOS, ctrl+alt+- becomes an em-dash, so we check for the code
if (event.code === "Minus" && ctrl && alt) {
cancelEvent(event);
this.cursorHistoryGoBack();
return;
} else if (key === "=" && ctrl && alt) {
cancelEvent(event);
this.cursorHistoryGoForward();
return;
} else if (cmd && shift && !alt && key === "Enter") {
cancelEvent(event);
this.queueFullCellsEvaluation(true);
return;
Expand Down Expand Up @@ -1227,6 +1240,8 @@ const Session = {
},

handleCellDeleted(cellId, siblingCellId) {
this.cursorHistory.removeAllFromCell(cellId);

if (this.focusedId === cellId) {
if (this.view) {
const visibleSiblingId = this.ensureVisibleFocusableEl(siblingCellId);
Expand Down Expand Up @@ -1324,6 +1339,12 @@ const Session = {
}
},

handleHistoryEvent(event) {
if (event.type === "navigation") {
this.cursorHistory.push(event.cellId, event.line, event.offset);
}
},

repositionJSViews() {
globalPubsub.broadcast("js_views", { type: "reposition" });
},
Expand Down Expand Up @@ -1447,12 +1468,39 @@ const Session = {

jumpToLine(file, line) {
const [_filename, cellId] = file.split("#cell:");

this.setFocusedEl(cellId, { scroll: false });
this.setInsertMode(true);

globalPubsub.broadcast(`cells:${cellId}`, { type: "jump_to_line", line });
},

cursorHistoryGoBack() {
if (this.cursorHistory.canGoBack()) {
const { cellId, line, offset } = this.cursorHistory.goBack();
this.setFocusedEl(cellId, { scroll: false });
this.setInsertMode(true);

globalPubsub.broadcast(`cells:${cellId}`, {
type: "jump_to_line",
line,
offset,
});
}
},

cursorHistoryGoForward() {
if (this.cursorHistory.canGoForward()) {
const { cellId, line, offset } = this.cursorHistory.goForward();
this.setFocusedEl(cellId, { scroll: false });
this.setInsertMode(true);

globalPubsub.broadcast(`cells:${cellId}`, {
type: "jump_to_line",
line,
offset,
});
}
},
};

export default Session;
Loading

0 comments on commit aed8832

Please sign in to comment.