CodeMirror 6 editor provider for JupyterLab with comprehensive language support, themes, extensions, and collaborative editing capabilities
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Custom command functions for editor interactions, keyboard shortcuts, and special behaviors in JupyterLab context.
Collection of editor state commands designed for JupyterLab integration and context-aware behavior.
/**
* CodeMirror commands namespace
* Provides context-aware editor commands for JupyterLab integration
*/
namespace StateCommands {
/**
* Indent or insert a tab as appropriate, with completer awareness
*/
function indentMoreOrInsertTab(target: CommandTarget): boolean;
/**
* Insert new line if completer is not active
*/
function completerOrInsertNewLine(target: CommandTarget): boolean;
/**
* Prevent insertion of new line when running cell with Ctrl/Command + Enter (deprecated)
*/
function preventNewLineOnRun(target: { dom: HTMLElement }): boolean;
/**
* Insert a new line or run a cell with Ctrl/Command + Enter
*/
function insertBlankLineOnRun(target: CommandTarget): boolean;
/**
* Simplify selection but do not prevent default to allow switching to command mode
*/
function simplifySelectionAndMaybeSwitchToCommandMode(target: CommandTarget): boolean;
/**
* Prevent dedenting when launching inspection request (tooltip)
*/
function dedentIfNotLaunchingTooltip(target: CommandTarget): boolean;
}
/**
* Command target interface for state commands
*/
interface CommandTarget {
dom: HTMLElement;
state: EditorState;
dispatch: (transaction: Transaction) => void;
}Usage Examples:
import { StateCommands } from "@jupyterlab/codemirror";
import { keymap } from "@codemirror/view";
// Create keymap with JupyterLab-aware commands
const jupyterLabKeymap = keymap.of([
{
key: "Tab",
run: StateCommands.indentMoreOrInsertTab
},
{
key: "Enter",
run: StateCommands.completerOrInsertNewLine
},
{
key: "Ctrl-Enter",
run: StateCommands.insertBlankLineOnRun
},
{
key: "Escape",
run: StateCommands.simplifySelectionAndMaybeSwitchToCommandMode
},
{
key: "Shift-Tab",
run: StateCommands.dedentIfNotLaunchingTooltip
}
]);
// Use in editor configuration
const editor = new EditorView({
extensions: [
jupyterLabKeymap,
// ... other extensions
]
});Smart indentation and tab handling with context awareness.
/**
* Indent or insert a tab as appropriate
* Handles completer state and selection context
*/
function indentMoreOrInsertTab(target: CommandTarget): boolean;Behavior:
Usage Example:
// Manual command execution
const result = StateCommands.indentMoreOrInsertTab({
dom: editor.dom,
state: editor.state,
dispatch: editor.dispatch
});
if (result) {
console.log("Command handled indentation/tab");
} else {
console.log("Command deferred to other handlers");
}Context-aware newline insertion with completer and cell runner integration.
/**
* Insert new line if completer is not active
* Integrates with JupyterLab completer and cell execution
*/
function completerOrInsertNewLine(target: CommandTarget): boolean;
/**
* Insert a new line or run a cell with Ctrl/Command + Enter
*/
function insertBlankLineOnRun(target: CommandTarget): boolean;Behavior:
completerOrInsertNewLine: Defers to completer when active, otherwise inserts newlineinsertBlankLineOnRun: Defers to cell runner when in code runner context, otherwise inserts blank lineUsage Examples:
// Configure Enter key behavior
const enterKeyBinding = {
key: "Enter",
run: StateCommands.completerOrInsertNewLine
};
// Configure Ctrl+Enter for cell execution
const ctrlEnterBinding = {
key: "Ctrl-Enter",
run: StateCommands.insertBlankLineOnRun
};
// Use in keymap
const keymap = keymap.of([enterKeyBinding, ctrlEnterBinding]);Commands for managing selections and editor navigation in JupyterLab context.
/**
* Simplify selection but do not prevent default to allow switching to command mode
* Enables JupyterLab notebook cell mode switching
*/
function simplifySelectionAndMaybeSwitchToCommandMode(target: CommandTarget): boolean;
/**
* Prevent dedenting when launching inspection request (tooltip)
* Avoids interference with Shift+Tab tooltip functionality
*/
function dedentIfNotLaunchingTooltip(target: CommandTarget): boolean;Behavior:
simplifySelectionAndMaybeSwitchToCommandMode: Simplifies selection, allows notebook command mode switchingdedentIfNotLaunchingTooltip: Only dedents if not in tooltip-capable contextThe commands use CSS selectors to detect JupyterLab context and adjust behavior accordingly.
// Selectors used by commands for context detection
const CODE_RUNNER_SELECTOR = '[data-jp-code-runner]';
const TERMINAL_CODE_RUNNER_SELECTOR = '[data-jp-interaction-mode="terminal"]';
const TOOLTIP_OPENER_SELECTOR = '.jp-CodeMirrorEditor:not(.jp-mod-has-primary-selection):not(.jp-mod-in-leading-whitespace):not(.jp-mod-completer-active)';
const ACTIVE_CELL_IN_EDIT_MODE_SELECTOR = '.jp-mod-editMode .jp-Cell.jp-mod-active';
// Commands check these contexts to determine appropriate behavior
function contextAwareCommand(target: CommandTarget): boolean {
const element = target.dom;
// Check if in code runner context
if (element.closest(CODE_RUNNER_SELECTOR)) {
// Defer to code runner
return false;
}
// Check if in terminal mode
if (element.closest(TERMINAL_CODE_RUNNER_SELECTOR)) {
// Handle terminal-specific behavior
return handleTerminalMode(target);
}
// Default editor behavior
return handleDefaultMode(target);
}Creating custom commands that integrate with JupyterLab's command system.
import { StateCommands } from "@jupyterlab/codemirror";
import { EditorState, Transaction } from "@codemirror/state";
// Create custom command with JupyterLab awareness
function customJupyterLabCommand(target: CommandTarget): boolean {
const { dom, state, dispatch } = target;
// Check JupyterLab context
const isInNotebook = dom.closest('.jp-Notebook');
const isInConsole = dom.closest('.jp-CodeConsole');
const isReadOnly = state.readOnly;
if (isReadOnly) {
return false; // Don't handle read-only editors
}
if (isInNotebook) {
// Notebook-specific behavior
return handleNotebookCommand(target);
} else if (isInConsole) {
// Console-specific behavior
return handleConsoleCommand(target);
} else {
// File editor behavior
return handleFileEditorCommand(target);
}
}
function handleNotebookCommand(target: CommandTarget): boolean {
// Custom notebook cell command logic
const { state, dispatch } = target;
// Example: Auto-complete imports in Python cells
const selection = state.selection.main;
const line = state.doc.lineAt(selection.head);
const lineText = line.text;
if (lineText.startsWith('import ') && selection.head === line.to) {
// Auto-complete common imports
const completion = '\nfrom typing import List, Dict, Optional';
dispatch(state.update({
changes: { from: selection.head, insert: completion },
selection: { anchor: selection.head + completion.length }
}));
return true;
}
return false;
}
// Register custom command in keymap
const customKeymap = keymap.of([
{
key: "Ctrl-Alt-i",
run: customJupyterLabCommand
}
]);Complex command scenarios and command composition.
// Compose multiple commands
function compositeCommand(...commands: Array<(target: CommandTarget) => boolean>) {
return (target: CommandTarget): boolean => {
for (const command of commands) {
if (command(target)) {
return true; // First successful command wins
}
}
return false; // No command handled the input
};
}
// Create fallback command chain
const smartIndentCommand = compositeCommand(
StateCommands.indentMoreOrInsertTab,
(target) => {
// Fallback: always insert 2 spaces
target.dispatch(target.state.update({
changes: { from: target.state.selection.main.head, insert: " " },
selection: { anchor: target.state.selection.main.head + 2 }
}));
return true;
}
);
// Conditional command execution
function conditionalCommand(
condition: (target: CommandTarget) => boolean,
trueCommand: (target: CommandTarget) => boolean,
falseCommand?: (target: CommandTarget) => boolean
) {
return (target: CommandTarget): boolean => {
if (condition(target)) {
return trueCommand(target);
} else if (falseCommand) {
return falseCommand(target);
}
return false;
};
}
// Use conditional command
const contextSensitiveEnter = conditionalCommand(
(target) => target.dom.closest('.jp-CodeConsole') !== null,
StateCommands.insertBlankLineOnRun, // Console: run command
StateCommands.completerOrInsertNewLine // Notebook: handle completer
);Using commands within the editor configuration and extension system.
import { StateCommands } from "@jupyterlab/codemirror";
import { CodeMirrorEditor } from "@jupyterlab/codemirror";
import { keymap } from "@codemirror/view";
// Create editor with JupyterLab commands
const editor = new CodeMirrorEditor({
model,
host,
extensions: [
keymap.of([
{ key: "Tab", run: StateCommands.indentMoreOrInsertTab },
{ key: "Enter", run: StateCommands.completerOrInsertNewLine },
{ key: "Ctrl-Enter", run: StateCommands.insertBlankLineOnRun },
{ key: "Escape", run: StateCommands.simplifySelectionAndMaybeSwitchToCommandMode },
{ key: "Shift-Tab", run: StateCommands.dedentIfNotLaunchingTooltip }
])
]
});
// Execute commands programmatically
editor.execCommand(StateCommands.indentMoreOrInsertTab);
// Create command-based extension
function jupyterLabCommandsExtension() {
return keymap.of([
{ key: "Tab", run: StateCommands.indentMoreOrInsertTab },
{ key: "Enter", run: StateCommands.completerOrInsertNewLine },
{ key: "Ctrl-Enter", run: StateCommands.insertBlankLineOnRun },
{ key: "Escape", run: StateCommands.simplifySelectionAndMaybeSwitchToCommandMode },
{ key: "Shift-Tab", run: StateCommands.dedentIfNotLaunchingTooltip }
]);
}interface CommandTarget {
dom: HTMLElement;
state: EditorState;
dispatch: (transaction: Transaction) => void;
}
type StateCommand = (target: CommandTarget) => boolean;
namespace StateCommands {
function indentMoreOrInsertTab(target: CommandTarget): boolean;
function completerOrInsertNewLine(target: CommandTarget): boolean;
function preventNewLineOnRun(target: { dom: HTMLElement }): boolean;
function insertBlankLineOnRun(target: CommandTarget): boolean;
function simplifySelectionAndMaybeSwitchToCommandMode(target: CommandTarget): boolean;
function dedentIfNotLaunchingTooltip(target: CommandTarget): boolean;
}