CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-jupyterlab--codemirror

CodeMirror 6 editor provider for JupyterLab with comprehensive language support, themes, extensions, and collaborative editing capabilities

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

editor-commands.mddocs/

Editor Commands

Custom command functions for editor interactions, keyboard shortcuts, and special behaviors in JupyterLab context.

Capabilities

StateCommands Namespace

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
  ]
});

Indent and Tab Commands

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:

  • When completer is enabled but not at line beginning: returns false (allows completer)
  • When text is selected: performs indentation
  • When cursor is at beginning of line or in whitespace: performs indentation
  • Otherwise: inserts tab character

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");
}

Newline and Enter Commands

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 newline
  • insertBlankLineOnRun: Defers to cell runner when in code runner context, otherwise inserts blank line

Usage 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]);

Selection and Navigation Commands

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 switching
  • dedentIfNotLaunchingTooltip: Only dedents if not in tooltip-capable context

Context Detection

The 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);
}

Custom Command Creation

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
  }
]);

Advanced Command Patterns

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
);

Integration with Editor

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 }
  ]);
}

Types

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;
}

docs

editor-commands.md

editor-core.md

editor-factory.md

extension-system.md

index.md

language-support.md

mime-type-service.md

search-replace.md

special-extensions.md

theme-system.md

tile.json