CodeMirror Compartment: switch language and tab size in the open document

In CodeMirror 6, wrap a replaceable group of extensions in a Compartment, include it in the initial configuration, and dispatch its reconfigure() effect when the choice changes. A language menu can then change how the current source is interpreted without replacing that source or constructing a fresh editor state. Configuration example

Suppose a snippet-review tool accepts HTML and JavaScript. An author pastes a JavaScript fragment while the menu still says HTML. Correcting that menu should change language support and preserve the unsaved fragment, selection, and available editing history.

Give each setting a slot during initialization

This fragment belongs in a bundled CodeMirror 6 application with compatible packages. Add these extensions to its existing setup:

import { Compartment, EditorState } from '@codemirror/state';
import { html } from '@codemirror/lang-html';
import { javascript } from '@codemirror/lang-javascript';

const languageSlot = new Compartment();
const tabSlot = new Compartment();

const snippetOptions = [
  languageSlot.of(html()),
  tabSlot.of(EditorState.tabSize.of(4))
];

Include snippetOptions once in the initial state's extensions. Keep the application's history, keymaps, and other editing features installed. Retain these compartment objects with the editor they configure; creating new objects inside a menu handler would address different slots.

The Compartment reference documents get(state), which returns the compartment's configured extension or undefined when the slot is absent. An effect for a slot that your current configuration no longer contains cannot restore that slot by itself.

Change both choices in one transaction

With view referring to that existing editor:

function configureSnippet(kind, tabWidth) {
  if (!['html', 'javascript'].includes(kind)) {
    throw new Error('Unsupported snippet language.');
  }
  if (![2, 4, 8].includes(tabWidth)) {
    throw new Error('Choose a supported tab width.');
  }

  view.dispatch({
    effects: [
      languageSlot.reconfigure(
        kind === 'html' ? html() : javascript()
      ),
      tabSlot.reconfigure(EditorState.tabSize.of(tabWidth))
    ]
  });
}

Wire the menu's string value to a deliberate numeric tab width before calling this function. The transaction supplies configuration effects and no document changes. It neither translates JavaScript into HTML nor reformats existing indentation.

The tabSize facet controls the displayed width of a tab character. A source line containing four literal spaces still contains four spaces after choosing a tab width of eight. If the application's Tab command should insert a different indentation unit, configure that separate language setting and command policy. The CodeMirror language reference distinguishes indentation from tab display.

Preserve unrelated configuration

Use a dedicated slot for the portion that changes. Reconfiguring languageSlot should leave a separately installed search panel or history extension available. Replacing the entire state to apply a menu choice would require deliberately restoring those stateful features.

If a language support extension contributes its own helpers, replacing that support changes those helpers too. A document's characters can remain identical while completions and syntax interpretation change. The keymap guide explains shortcut precedence when a newly configured feature competes for keys. The theme guide covers a separate appearance slot.

Check the unsaved fragment after switching

In the integrated application, enter a small JavaScript function, select its name, and make one edit with history enabled. Switch from HTML to JavaScript, then change tab width. Read view.state.doc.toString() and inspect the selection; the configuration-only transaction should preserve both. Confirm that Undo still reaches the preceding text edit under the application's history policy.

Save from the document model after this check. The selected language is application metadata if your storage format needs it; changing syntax support does not add a language label to the saved source automatically.

Keep reading