The @codemirror/view package provides the DOM view component for the CodeMirror code editor, implementing the visual rendering and user interaction layer. It handles DOM manipulation, event processing, input handling, decorations, themes, tooltips, panels, gutters, and various visual features like syntax highlighting, cursors, selections, and scroll management.
npm install @codemirror/viewimport { EditorView, ViewPlugin, Decoration } from "@codemirror/view";For CommonJS:
const { EditorView, ViewPlugin, Decoration } = require("@codemirror/view");import { EditorView } from "@codemirror/view";
import { EditorState } from "@codemirror/state";
// Create a basic editor
const state = EditorState.create({
doc: "Hello, CodeMirror!",
});
const view = new EditorView({
state,
parent: document.getElementById("editor")
});
// Access editor content
console.log(view.state.doc.toString());
// Dispatch changes
view.dispatch({
changes: { from: 0, to: view.state.doc.length, insert: "New content" }
});CodeMirror View is built around several key components:
Main EditorView class that provides the foundation for all editor functionality. Manages DOM rendering, viewport optimization, coordinate mapping, and state synchronization.
class EditorView {
constructor(config?: EditorViewConfig);
// Core state and properties
readonly state: EditorState;
readonly viewport: {from: number, to: number};
readonly visibleRanges: readonly {from: number, to: number}[];
readonly dom: HTMLElement;
readonly contentDOM: HTMLElement;
readonly scrollDOM: HTMLElement;
readonly root: DocumentOrShadowRoot;
readonly inView: boolean;
readonly hasFocus: boolean;
readonly composing: boolean;
readonly compositionStarted: boolean;
// Layout and measurement properties
readonly defaultCharacterWidth: number;
readonly defaultLineHeight: number;
readonly textDirection: Direction;
readonly lineWrapping: boolean;
readonly contentHeight: number;
readonly scaleX: number;
readonly scaleY: number;
readonly themeClasses: string;
readonly documentTop: number;
readonly documentPadding: {top: number, bottom: number};
readonly viewportLineBlocks: readonly BlockInfo[];
// State management and updates
dispatch(tr: Transaction): void;
dispatch(trs: readonly Transaction[]): void;
dispatch(...specs: TransactionSpec[]): void;
update(transactions: readonly Transaction[]): void;
setState(newState: EditorState): void;
// Lifecycle
focus(): void;
destroy(): void;
// Coordinate mapping
posAtCoords(coords: {x: number, y: number}, precise: false): number;
posAtCoords(coords: {x: number, y: number}): number | null;
coordsAtPos(pos: number, side?: -1 | 1): Rect | null;
coordsForChar(pos: number): Rect | null;
domAtPos(pos: number): {node: Node, offset: number};
posAtDOM(node: Node, offset?: number): number;
// Block and line information
lineBlockAt(pos: number): BlockInfo;
elementAtHeight(height: number): BlockInfo;
textDirectionAt(pos: number): Direction;
bidiSpans(line: Line): readonly BidiSpan[];
// Movement and navigation
moveByChar(start: SelectionRange, forward: boolean, by?: Function): SelectionRange;
moveByGroup(start: SelectionRange, forward: boolean): SelectionRange;
moveToLineBoundary(start: SelectionRange, forward: boolean, includeWrap?: boolean): SelectionRange;
moveVertically(start: SelectionRange, forward: boolean, distance?: number): SelectionRange;
visualLineSide(line: Line, end: boolean): SelectionRange;
// Scrolling and view management
scrollSnapshot(): StateEffect<unknown>;
setRoot(root: Document | ShadowRoot): void;
setTabFocusMode(to?: boolean | number): void;
// Plugin and measurement
plugin<T extends PluginValue>(plugin: ViewPlugin<T>): T | null;
requestMeasure<T>(request?: MeasureRequest<T>): void;
// Static methods
static scrollIntoView(pos: number | SelectionRange, options?: ScrollIntoViewOptions): StateEffect<unknown>;
static theme(spec: {[selector: string]: StyleSpec}, options?: {dark?: boolean}): Extension;
static baseTheme(spec: {[selector: string]: StyleSpec}): Extension;
static domEventHandlers(handlers: DOMEventHandlers): Extension;
static domEventObservers(observers: DOMEventHandlers): Extension;
static findFromDOM(dom: HTMLElement): EditorView | null;
// Static facets
static readonly styleModule: Facet<StyleModule>;
static readonly inputHandler: Facet<InputHandler>;
static readonly clipboardInputFilter: Facet<ClipboardFilter>;
static readonly clipboardOutputFilter: Facet<ClipboardFilter>;
static readonly scrollHandler: Facet<ScrollHandler>;
static readonly focusChangeEffect: Facet<FocusChangeEffect>;
static readonly perLineTextDirection: Facet<boolean, boolean>;
static readonly exceptionSink: Facet<ExceptionSink>;
static readonly updateListener: Facet<UpdateListener>;
static readonly editable: Facet<boolean, boolean>;
static readonly mouseSelectionStyle: Facet<MouseSelectionStyle>;
static readonly dragMovesSelection: Facet<DragMovesSelection>;
static readonly clickAddsSelectionRange: Facet<ClickAddsSelectionRange>;
static readonly decorations: Facet<DecorationSource>;
static readonly outerDecorations: Facet<DecorationSource>;
static readonly atomicRanges: Facet<AtomicRangesSource>;
static readonly bidiIsolatedRanges: Facet<DecorationSource>;
static readonly scrollMargins: Facet<ScrollMarginsSource>;
static readonly darkTheme: Facet<boolean, boolean>;
static readonly cspNonce: Facet<string>;
static readonly contentAttributes: Facet<AttrSource>;
static readonly editorAttributes: Facet<AttrSource>;
static readonly lineWrapping: Extension;
static readonly announce: Facet<string>;
}
interface EditorViewConfig extends EditorStateConfig {
state?: EditorState;
parent?: Element | DocumentFragment;
root?: Document | ShadowRoot;
scrollTo?: StateEffect<any>;
dispatchTransactions?: (trs: readonly Transaction[], view: EditorView) => void;
dispatch?: (tr: Transaction, view: EditorView) => void;
}Plugin architecture for extending editor functionality with lifecycle management, event handling, and state integration.
class ViewPlugin<V extends PluginValue, Arg = undefined> {
static define<V extends PluginValue>(
create: (view: EditorView) => V,
spec?: PluginSpec<V>
): ViewPlugin<V>;
static fromClass<V extends PluginValue>(
cls: {new(view: EditorView): V},
spec?: PluginSpec<V>
): ViewPlugin<V>;
readonly extension: Arg extends undefined ? Extension : null;
of(arg: Arg): Extension;
readonly id: number;
readonly create: (view: EditorView, arg?: Arg) => V;
readonly domEventHandlers: DOMEventHandlers<V> | undefined;
readonly domEventObservers: DOMEventHandlers<V> | undefined;
}
interface PluginValue {
update?(update: ViewUpdate): void;
destroy?(): void;
docViewUpdate?(view: EditorView): void;
}
interface PluginSpec<V extends PluginValue> {
eventHandlers?: DOMEventHandlers<V>;
eventObservers?: DOMEventHandlers<V>;
provide?: (plugin: ViewPlugin<V>) => Extension;
decorations?: (value: V) => DecorationSet;
}
class ViewUpdate {
readonly view: EditorView;
readonly state: EditorState;
readonly transactions: readonly Transaction[];
readonly changes: ChangeSet;
readonly changedRanges: readonly ChangedRange[];
readonly startState: EditorState;
readonly docChanged: boolean;
readonly focusChanged: boolean;
readonly selectionSet: boolean;
readonly viewportChanged: boolean;
readonly viewportMoved: boolean;
readonly heightChanged: boolean;
readonly geometryChanged: boolean;
readonly empty: boolean;
static create(view: EditorView, state: EditorState, transactions: readonly Transaction[]): ViewUpdate;
}
type Command = (target: EditorView) => boolean;
function logException(state: EditorState, exception: any, context?: string): void;Flexible decoration system for styling text ranges, adding widgets, replacing content, and creating custom visual elements.
abstract class Decoration extends RangeValue {
static mark(spec: MarkDecorationSpec): Decoration;
static widget(spec: WidgetDecorationSpec): Decoration;
static replace(spec: ReplaceDecorationSpec): Decoration;
static line(spec: LineDecorationSpec): Decoration;
static set(of: Range<Decoration> | readonly Range<Decoration>[], sort?: boolean): DecorationSet;
static none: DecorationSet;
readonly spec: any;
readonly widget: WidgetType | null;
readonly heightRelevant: boolean;
hasHeight(): boolean;
abstract eq(other: Decoration): boolean;
range(from: number, to?: number): Range<Decoration>;
}
class DecorationSet extends RangeSet<Decoration> {
static readonly empty: DecorationSet;
static create(view: EditorView, decorations: Range<Decoration>[]): DecorationSet;
static of<T>(ranges: readonly Range<T>[] | Range<T>, sort?: boolean): RangeSet<T>;
update(mapping: ChangeDesc, filterFrom?: number, filterTo?: number): DecorationSet;
map(mapping: ChangeDesc, start?: number, end?: number): DecorationSet;
}
abstract class WidgetType {
abstract toDOM(view: EditorView): HTMLElement;
abstract eq(other: WidgetType): boolean;
updateDOM?(dom: HTMLElement, view: EditorView): boolean;
coordsAt?(dom: HTMLElement, pos: number, side: number): Rect | null;
ignoreEvent?(event: Event): boolean;
destroy?(dom: HTMLElement): void;
readonly estimatedHeight: number;
get lineBreaks(): number;
get isHidden(): boolean;
get editable(): boolean;
compare(other: WidgetType): boolean;
}Flexible key binding system with support for multi-stroke keys, platform-specific modifiers, and context-sensitive commands.
interface KeyBinding {
key: string;
run: Command;
shift?: Command;
scope?: string;
preventDefault?: boolean;
stopPropagation?: boolean;
mac?: string;
win?: string;
linux?: string;
any?: Command;
}
const keymap: Facet<readonly KeyBinding[]>;
function runScopeHandlers(view: EditorView, event: KeyboardEvent, scope: string): boolean;Rich tooltip system with positioning, hover support, and custom content rendering.
interface Tooltip {
pos: number;
end?: number;
create(view: EditorView): TooltipView;
above?: boolean;
strictSide?: boolean;
arrow?: boolean;
clip?: boolean;
}
interface TooltipView {
dom: HTMLElement;
update?(update: ViewUpdate): void;
destroy?(): void;
mount?(view: EditorView): void;
positioned?(): void;
overlap?: boolean;
resize?: boolean;
offset?: {x: number, y: number};
getCoords?(pos: number): {x: number, y: number};
}
interface TooltipConfig {
position?: "absolute" | "fixed";
parent?: () => HTMLElement;
tooltipSpace?: (view: EditorView) => {left: number, top: number, right: number, bottom: number};
}
interface HoverTooltipOptions {
hideOn?: (view: EditorView, pos: number, side: -1 | 1) => boolean;
hideOnChange?: boolean;
hoverTime?: number;
}
type HoverTooltipSource = (view: EditorView, pos: number, side: -1 | 1) =>
Tooltip | readonly Tooltip[] | null | Promise<Tooltip | readonly Tooltip[] | null>;
const showTooltip: Facet<Tooltip | null>;
function tooltips(config?: TooltipConfig): Extension;
function hoverTooltip(source: HoverTooltipSource, options?: HoverTooltipOptions): Extension;
function getTooltip(view: EditorView, tooltip: Tooltip): TooltipView | null;
function hasHoverTooltips(state: EditorState): boolean;
function closeHoverTooltips(view: EditorView): boolean;
function repositionTooltips(view: EditorView): void;UI panel system for adding persistent interface elements above or below the editor content.
interface Panel {
dom: HTMLElement;
mount?(): void;
update?(update: ViewUpdate): void;
destroy?(): void;
top?: boolean;
}
interface PanelConfig {
topContainer?: (view: EditorView) => HTMLElement;
bottomContainer?: (view: EditorView) => HTMLElement;
}
type PanelConstructor = (view: EditorView) => Panel;
const showPanel: Facet<Panel | null>;
function panels(config?: PanelConfig): Extension;
function getPanel(view: EditorView, panel: PanelConstructor): Panel | null;Customizable gutter system for line numbers, folding controls, breakpoints, and other line-based information.
abstract class GutterMarker {
toDOM?(view: EditorView): Node;
abstract eq(other: GutterMarker): boolean;
destroy?(dom: Node): void;
readonly elementClass: string;
readonly domAtPos: boolean;
}
interface GutterConfig {
class?: string;
renderEmptyElements?: boolean;
markers?: (view: EditorView) => (RangeSet<GutterMarker> | readonly RangeSet<GutterMarker>[]);
lineMarker?: (view: EditorView, line: BlockInfo, otherMarkers: readonly GutterMarker[]) => GutterMarker | null;
widgetMarker?: (view: EditorView, widget: WidgetType, block: BlockInfo) => GutterMarker | null;
lineMarkerChange?: null | ((update: ViewUpdate) => boolean);
initialSpacer?: null | ((view: EditorView) => GutterMarker);
updateSpacer?: null | ((spacer: GutterMarker, update: ViewUpdate) => GutterMarker);
domEventHandlers?: DOMEventHandlers<GutterMarker>;
side?: "before" | "after";
}
interface LineNumberConfig {
formatNumber?: (lineNo: number, state: EditorState) => string;
domEventHandlers?: DOMEventHandlers<LineNumberMarker>;
}
function lineNumbers(config?: LineNumberConfig): Extension;
function gutter(config: GutterConfig): Extension;
function gutters(config?: {fixed?: boolean}): Extension;
function highlightActiveLineGutter(): Extension;
function gutterLineClass(className: string): Extension;
function gutterWidgetClass(className: string): Extension;
function lineNumberWidgetMarker(marker: GutterMarker, side: -1 | 1): Extension;
const lineNumberMarkers: Facet<RangeSet<GutterMarker>>;Position mapping between document coordinates and screen coordinates, with block-based layout information.
interface BlockInfo {
from: number;
to: number;
length: number;
height: number;
top: number;
type: BlockType;
}
interface Rect {
left: number;
right: number;
top: number;
bottom: number;
}
// Methods on EditorView
posAtCoords(coords: {x: number, y: number}, precise?: boolean): number | null;
coordsAtPos(pos: number, side?: -1 | 1): Rect | null;
lineBlockAt(pos: number): BlockInfo;Support for bidirectional text rendering with proper Unicode handling and visual cursor movement.
interface BidiSpan {
from: number;
to: number;
level: number;
dir: Direction;
}
enum Direction {
LTR = 0,
RTL = 1
}Positioned overlay system for creating custom visual elements that float above or below the editor content.
function layer(config: LayerConfig): Extension;
interface LayerMarker {
draw(): HTMLElement | null;
update?(dom: HTMLElement, view: EditorView): boolean;
eq(other: LayerMarker): boolean;
destroy?(dom: HTMLElement): void;
}
class RectangleMarker implements LayerMarker {
constructor(className: string, rect: Rect);
draw(): HTMLElement;
eq(other: LayerMarker): boolean;
}Automatic text matching and decoration system for finding and styling patterns in the editor content.
class MatchDecorator {
constructor(config: MatchDecoratorConfig);
createDeco(view: EditorView): DecorationSet;
}
interface MatchDecoratorConfig {
regexp: RegExp;
decoration?: Decoration | ((match: RegExpExecArray, view: EditorView, pos: number) => Decoration);
decorate?: (add: (from: number, to: number, decoration: Decoration) => void, from: number, to: number, match: RegExpExecArray, view: EditorView) => void;
boundary?: RegExp;
}Simple dialog system for displaying modal or inline dialogs within the editor interface.
const showDialog: Facet<DialogSource | null>;
function getDialog(view: EditorView): DialogSource | null;
type DialogSource = (view: EditorView) => {dom: HTMLElement, pos?: number} | null;Collection of ready-to-use extensions for common editor features and visual enhancements.
function drawSelection(): Extension;
function getDrawSelectionConfig(state: EditorState): DrawSelectionConfig;
function dropCursor(config?: DropCursorConfig): Extension;
function highlightSpecialChars(config?: SpecialCharConfig): Extension;
function scrollPastEnd(): Extension;
function highlightActiveLine(): Extension;
function placeholder(content: string | HTMLElement): Extension;
function rectangularSelection(options?: RectangularSelectionOptions): Extension;
function crosshairCursor(options?: CrosshairCursorOptions): Extension;
function highlightWhitespace(config?: HighlightWhitespaceOptions): Extension;
function highlightTrailingWhitespace(config?: HighlightTrailingWhitespaceOptions): Extension;Request-based DOM measurement system for efficient layout operations.
interface MeasureRequest<T> {
read(view: EditorView): T;
write?(measure: T, view: EditorView): void;
key?: any;
}Information about document changes and their mapping.
class ChangedRange {
constructor(
readonly fromA: number,
readonly toA: number,
readonly fromB: number,
readonly toB: number
);
join(other: ChangedRange): ChangedRange;
addToSet(set: ChangedRange[]): ChangedRange[];
static extendWithRanges(
diff: readonly ChangedRange[],
ranges: number[]
): readonly ChangedRange[];
}Scroll target representation for programmatic scrolling.
class ScrollTarget {
constructor(
readonly range: SelectionRange,
readonly y?: ScrollStrategy,
readonly x?: ScrollStrategy,
readonly yMargin?: number,
readonly xMargin?: number,
readonly isSnapshot?: boolean
);
map(changes: ChangeDesc): ScrollTarget;
clip(state: EditorState): ScrollTarget;
}
enum ScrollStrategy {
Start = 0,
Center = 1,
End = 2,
Nearest = 3
}Simple modal and inline dialog system.
const showDialog: Facet<DialogSource | null>;
function getDialog(view: EditorView): DialogSource | null;
type DialogSource = (view: EditorView) => {dom: HTMLElement, pos?: number} | null;Automatic pattern matching and decoration system.
class MatchDecorator {
constructor(config: MatchDecoratorConfig);
createDeco(view: EditorView): DecorationSet;
}
interface MatchDecoratorConfig {
regexp: RegExp;
decoration?: Decoration | ((match: RegExpExecArray, view: EditorView, pos: number) => Decoration);
decorate?: (add: (from: number, to: number, decoration: Decoration) => void, from: number, to: number, match: RegExpExecArray, view: EditorView) => void;
boundary?: RegExp;
}interface DOMEventMap {
[eventName: string]: (view: EditorView, event: Event) => boolean;
}
type DOMEventHandlers<This = any> = {
[K in keyof HTMLElementEventMap]?: (this: This, event: HTMLElementEventMap[K], view: EditorView) => boolean;
};
interface MouseSelectionStyle {
get(event: MouseEvent, extend: boolean, multiple: boolean): Selection;
update(selection: Selection, event: MouseEvent): Selection;
}
type AttrSource = Attrs | ((view: EditorView) => Attrs | null);
type Attrs = {[name: string]: string};
type DecorationSource = DecorationSet | ((view: EditorView) => DecorationSet);
type AtomicRangesSource = (view: EditorView) => RangeSet<any>;
type ScrollMarginsSource = (view: EditorView) => Partial<Rect> | null;
type InputHandler = (view: EditorView, from: number, to: number, text: string, insert: () => Transaction) => boolean;
type FocusChangeEffect = (state: EditorState, focusing: boolean) => StateEffect<any> | null;
type ScrollHandler = (view: EditorView, range: SelectionRange, options: ScrollIntoViewOptions) => boolean;
type ExceptionSink = (exception: any) => void;
type UpdateListener = (update: ViewUpdate) => void;
type ClipboardFilter = (text: string, view: EditorView) => string | null;
type DragMovesSelection = (event: MouseEvent) => boolean;
type ClickAddsSelectionRange = (event: MouseEvent) => boolean;
enum UpdateFlag {
Focus = 1,
Height = 2,
Viewport = 4,
ViewportMoved = 8,
Geometry = 16
}
interface ScrollIntoViewOptions {
y?: ScrollStrategy;
x?: ScrollStrategy;
yMargin?: number;
xMargin?: number;
}
function logException(state: EditorState, exception: any, context?: string): void;