zudo-codemirror

Type to search...

to open search from anywhere

クイックリファレンス

作成2026年3月29日更新2026年3月29日Takeshi Takatsudo

CodeMirror 6 の一般的なパターン - import、エディタのセットアップ、コンテンツの操作、イベント処理。

よく使う import パターン

// Meta-package (includes basicSetup, EditorView, EditorState)
import { EditorView, basicSetup } from "codemirror";

// Core state
import { EditorState, StateField, StateEffect, Facet } from "@codemirror/state";

// Core view
import { EditorView, keymap, lineNumbers, Decoration, ViewPlugin } from "@codemirror/view";

// Commands and keymaps
import { defaultKeymap, historyKeymap, history, undo, redo } from "@codemirror/commands";

// Language support
import { javascript } from "@codemirror/lang-javascript";
import { markdown } from "@codemirror/lang-markdown";
import { html } from "@codemirror/lang-html";
import { css } from "@codemirror/lang-css";
import { json } from "@codemirror/lang-json";
import { python } from "@codemirror/lang-python";

// Search
import { search, searchKeymap, openSearchPanel } from "@codemirror/search";

// Autocomplete
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";

// Linting
import { linter, lintKeymap } from "@codemirror/lint";

// Vim mode
import { vim } from "@replit/codemirror-vim";

最小限のエディタセットアップ

import { EditorView, basicSetup } from "codemirror";
import { javascript } from "@codemirror/lang-javascript";

const view = new EditorView({
  doc: "// your code here\n",
  extensions: [basicSetup, javascript()],
  parent: document.getElementById("editor"),
});

basicSetup を使わないセットアップ

どの機能を含めるかを完全に制御したい場合:

import { EditorView, keymap, lineNumbers, highlightActiveLine } from "@codemirror/view";
import { EditorState } from "@codemirror/state";
import { defaultKeymap, historyKeymap, history } from "@codemirror/commands";
import { javascript } from "@codemirror/lang-javascript";
import { syntaxHighlighting, defaultHighlightStyle } from "@codemirror/language";

const view = new EditorView({
  state: EditorState.create({
    doc: "// your code here\n",
    extensions: [
      lineNumbers(),
      highlightActiveLine(),
      history(),
      syntaxHighlighting(defaultHighlightStyle),
      keymap.of([...defaultKeymap, ...historyKeymap]),
      javascript(),
    ],
  }),
  parent: document.getElementById("editor"),
});

エディタのコンテンツを取得する

// Get the full document as a string
const content = view.state.doc.toString();

// Get a specific line (1-indexed)
const line = view.state.doc.line(1);
console.log(line.text); // line content
console.log(line.from); // start position
console.log(line.to);   // end position

// Get the number of lines
const lineCount = view.state.doc.lines;

// Get text in a range
const slice = view.state.sliceDoc(0, 100);

エディタのコンテンツを設定する

// Replace the entire document
view.dispatch({
  changes: {
    from: 0,
    to: view.state.doc.length,
    insert: "new content here",
  },
});

// Insert text at a position
view.dispatch({
  changes: { from: 10, insert: "inserted text" },
});

// Delete a range
view.dispatch({
  changes: { from: 5, to: 15 },
});

// Replace a range
view.dispatch({
  changes: { from: 5, to: 15, insert: "replacement" },
});

// Multiple changes in one transaction
view.dispatch({
  changes: [
    { from: 0, to: 5, insert: "AAA" },
    { from: 20, to: 25, insert: "BBB" },
  ],
});

⚠️ Warning

単一の Transaction で複数の変更を dispatch する場合、changes 配列内の位置は元のドキュメント(変更が適用される前)の位置を指します。CodeMirror が自動的に位置をマッピングします。

エディタの状態全体を置き換える

エディタを完全にリセットして新しい状態にする(新しいドキュメント、新しい Extension、すべてを新しく)場合:

view.setState(
  EditorState.create({
    doc: "brand new document",
    extensions: [basicSetup, javascript()],
  })
);

変更をリッスンする

updateListener を使う方法

const view = new EditorView({
  doc: "",
  extensions: [
    basicSetup,
    EditorView.updateListener.of((update) => {
      if (update.docChanged) {
        console.log("Document changed:", update.state.doc.toString());
      }
    }),
  ],
  parent: document.getElementById("editor"),
});

ViewPlugin を使う方法

View へのアクセスが必要なより複雑なロジックの場合:

import { ViewPlugin, ViewUpdate } from "@codemirror/view";

const changeTracker = ViewPlugin.fromClass(
  class {
    update(update: ViewUpdate) {
      if (update.docChanged) {
        // React to document changes
        console.log("New length:", update.state.doc.length);
      }
      if (update.selectionSet) {
        // React to selection changes
        const { from, to } = update.state.selection.main;
        console.log("Selection:", from, to);
      }
    }
  }
);

エディタにフォーカスする

// Focus the editor
view.focus();

// Check if the editor has focus
const hasFocus = view.hasFocus;

選択範囲

// Get the current selection
const { from, to } = view.state.selection.main;

// Get the selected text
const selectedText = view.state.sliceDoc(from, to);

// Set the cursor position (collapsed selection)
view.dispatch({
  selection: { anchor: 0 },
});

// Set a selection range
view.dispatch({
  selection: { anchor: 0, head: 10 },
});

// Set cursor position and scroll it into view
view.dispatch({
  selection: { anchor: 0 },
  scrollIntoView: true,
});

Extension の追加と削除

CodeMirror には addExtension / removeExtension の API はありません。代わりに、Compartment を使用してエディタの一部を動的に再設定します。

Compartment を使う方法

import { Compartment } from "@codemirror/state";

// Create a compartment for the language
const languageConf = new Compartment();

const view = new EditorView({
  doc: "",
  extensions: [
    basicSetup,
    languageConf.of(javascript()),
  ],
  parent: document.getElementById("editor"),
});

// Later: switch the language to Python
import { python } from "@codemirror/lang-python";

view.dispatch({
  effects: languageConf.reconfigure(python()),
});

// Remove the extension by reconfiguring with an empty array
view.dispatch({
  effects: languageConf.reconfigure([]),
});

Extension のオン/オフを切り替える

import { Compartment } from "@codemirror/state";
import { lineNumbers } from "@codemirror/view";

const lineNumberConf = new Compartment();
let lineNumbersEnabled = true;

const view = new EditorView({
  doc: "",
  extensions: [
    basicSetup,
    lineNumberConf.of(lineNumbers()),
  ],
  parent: document.getElementById("editor"),
});

function toggleLineNumbers() {
  lineNumbersEnabled = !lineNumbersEnabled;
  view.dispatch({
    effects: lineNumberConf.reconfigure(
      lineNumbersEnabled ? lineNumbers() : []
    ),
  });
}

Transaction の dispatch

Transaction はエディタの状態を更新する唯一の方法です。view.dispatch() を呼び出すたびに、Transaction が作成され適用されます。

// Simple document change
view.dispatch({
  changes: { from: 0, insert: "Hello " },
});

// Change with selection update
view.dispatch({
  changes: { from: 0, insert: "Hello " },
  selection: { anchor: 6 },
});

// Add an annotation to a transaction
import { Annotation } from "@codemirror/state";

const externalChange = Annotation.define<boolean>();

view.dispatch({
  changes: { from: 0, to: view.state.doc.length, insert: "reset" },
  annotations: externalChange.of(true),
});

// Multiple sequential changes (applied as separate transactions)
view.dispatch({ changes: { from: 0, insert: "A" } });
view.dispatch({ changes: { from: 0, insert: "B" } });

読み取り専用モード

import { EditorState } from "@codemirror/state";
import { Compartment } from "@codemirror/state";

const readOnlyConf = new Compartment();

const view = new EditorView({
  doc: "read-only content",
  extensions: [
    basicSetup,
    readOnlyConf.of(EditorState.readOnly.of(true)),
  ],
  parent: document.getElementById("editor"),
});

// Toggle read-only
view.dispatch({
  effects: readOnlyConf.reconfigure(
    EditorState.readOnly.of(false)
  ),
});

エディタの破棄

view.destroy();

これにより、エディタが DOM から削除され、イベントリスナーがクリーンアップされます。destroy() を呼び出した後は、EditorView インスタンスを使用しないでください。

テーマ

import { EditorView } from "@codemirror/view";

const myTheme = EditorView.theme({
  "&": {
    fontSize: "14px",
    backgroundColor: "#1e1e1e",
  },
  ".cm-content": {
    fontFamily: "'Fira Code', monospace",
    color: "#d4d4d4",
  },
  ".cm-gutters": {
    backgroundColor: "#252525",
    color: "#858585",
    border: "none",
  },
  ".cm-activeLine": {
    backgroundColor: "#2a2a2a",
  },
  "&.cm-focused .cm-cursor": {
    borderLeftColor: "#ffffff",
  },
});

const view = new EditorView({
  doc: "",
  extensions: [basicSetup, myTheme],
  parent: document.getElementById("editor"),
});

テーマセレクタでは & でエディタの外側の要素(.cm-editor)を参照します。すべての CSS プロパティは標準的なキャメルケース名を使用します。

DOM イベント

const view = new EditorView({
  doc: "",
  extensions: [
    basicSetup,
    EditorView.domEventHandlers({
      keydown(event, view) {
        if (event.key === "Escape") {
          console.log("Escape pressed");
          // Return true to prevent further handling
          return false;
        }
      },
      blur(event, view) {
        console.log("Editor lost focus");
      },
    }),
  ],
  parent: document.getElementById("editor"),
});

Revision History