Selection system supporting single and multiple selection ranges with cursor positioning and range operations.
The main selection class that can hold one or more selection ranges.
/**
* An editor selection holds one or more selection ranges.
*/
class EditorSelection {
/** Create a selection with a single cursor at the given position */
static single(pos: number): EditorSelection;
/** Create a selection with a single cursor */
static cursor(pos: number, assoc?: number, bidiLevel?: number, goalColumn?: number): EditorSelection;
/** Create a selection with a single range */
static range(anchor: number, head?: number, goalColumn?: number, bidiLevel?: number): EditorSelection;
/** Create a selection from an array of ranges */
static create(ranges: readonly SelectionRange[], mainIndex?: number): EditorSelection;
/** Deserialize a selection from JSON */
static fromJSON(json: any): EditorSelection;
/** The ranges in the selection, sorted by position */
readonly ranges: readonly SelectionRange[];
/** The index of the main range in the selection */
readonly mainIndex: number;
/** Get the primary selection range */
get main(): SelectionRange;
/** Map a selection through a change */
map(change: ChangeDesc, assoc?: number): EditorSelection;
/** Compare this selection to another selection */
eq(other: EditorSelection, includeAssoc?: boolean): boolean;
/** Make sure the selection only has one range */
asSingle(): EditorSelection;
/** Return a JSON-serializable representation */
toJSON(): any;
}Usage Examples:
import { EditorSelection } from "@codemirror/state";
// Create a cursor at position 5
const cursor = EditorSelection.cursor(5);
console.log(cursor.main.from); // 5
console.log(cursor.main.to); // 5 (empty range)
// Create a range selection from position 2 to 8
const range = EditorSelection.range(2, 8);
console.log(range.main.from); // 2
console.log(range.main.to); // 8
// Create multiple selections (requires allowMultipleSelections facet)
const multiSelect = EditorSelection.create([
EditorSelection.range(2, 5).main,
EditorSelection.range(10, 15).main
], 0); // Main selection is first range
// Convert to single selection
const single = multiSelect.asSingle();
console.log(single.ranges.length); // 1Individual selection range representing a single cursor or text selection.
/**
* A single selection range. When allowMultipleSelections is enabled,
* a selection may hold multiple ranges.
*/
class SelectionRange {
/** The lower boundary of the range */
readonly from: number;
/** The upper boundary of the range */
readonly to: number;
/** The anchor of the range—the side that doesn't move when you extend it */
get anchor(): number;
/** The head of the range, which is moved when the range is extended */
get head(): number;
/** True when anchor and head are at the same position */
get empty(): boolean;
/** Character association direction (-1, 0, or 1) */
get assoc(): -1 | 0 | 1;
/** The bidirectional text level associated with this cursor */
get bidiLevel(): number | null;
/** The goal column associated with a cursor for vertical motion */
get goalColumn(): number | undefined;
/** Map this range through a change */
map(change: ChangeDesc, assoc?: number): SelectionRange;
/** Extend this range to cover at least from to to */
extend(from: number, to?: number): SelectionRange;
/** Compare this range to another range */
eq(other: SelectionRange, includeAssoc?: boolean): boolean;
/** Return a JSON-serializable object representing the range */
toJSON(): any;
/** Convert a JSON representation to a SelectionRange instance */
static fromJSON(json: any): SelectionRange;
}Usage Examples:
// Create a range from position 5 to 10
const range = EditorSelection.range(5, 10).main;
console.log(range.from); // 5
console.log(range.to); // 10
console.log(range.anchor); // 5 (where selection started)
console.log(range.head); // 10 (where selection ends)
console.log(range.empty); // false
// Create a cursor (empty range)
const cursor = EditorSelection.cursor(7).main;
console.log(cursor.empty); // true
console.log(cursor.from); // 7
console.log(cursor.to); // 7
// Extend a range
const extended = range.extend(3, 12);
console.log(extended.from); // 3
console.log(extended.to); // 12
// Work with reversed selection (head before anchor)
const reversed = EditorSelection.range(10, 5).main;
console.log(reversed.from); // 5 (still lower boundary)
console.log(reversed.to); // 10 (still upper boundary)
console.log(reversed.anchor); // 10 (where selection started)
console.log(reversed.head); // 5 (where selection ends)Static methods for creating common selection types.
/**
* Create a selection with a single cursor at the given position
* @param pos The cursor position
*/
static single(pos: number): EditorSelection;
/**
* Create a selection with a single cursor
* @param pos The cursor position
* @param assoc Character association (-1, 0, 1)
* @param bidiLevel Bidirectional text level
* @param goalColumn Goal column for vertical motion
*/
static cursor(pos: number, assoc?: number, bidiLevel?: number, goalColumn?: number): EditorSelection;
/**
* Create a selection with a single range
* @param anchor The anchor position (where selection starts)
* @param head The head position (where selection ends, defaults to anchor)
* @param goalColumn Goal column for vertical motion
* @param bidiLevel Bidirectional text level
*/
static range(anchor: number, head?: number, goalColumn?: number, bidiLevel?: number): EditorSelection;
/**
* Create a selection from an array of ranges
* @param ranges Array of selection ranges
* @param mainIndex Index of the main range (defaults to ranges.length - 1)
*/
static create(ranges: readonly SelectionRange[], mainIndex?: number): EditorSelection;Usage Examples:
// Simple cursor
const cursor = EditorSelection.single(10);
// Cursor with association (prefers character before position)
const associatedCursor = EditorSelection.cursor(10, -1);
// Range selection
const selection = EditorSelection.range(5, 15);
// Multiple ranges
const ranges = [
EditorSelection.range(2, 5).main,
EditorSelection.range(8, 12).main,
EditorSelection.range(15, 20).main
];
const multiSelection = EditorSelection.create(ranges, 1); // Main is second rangeMethods for updating selections when the document changes.
/**
* Map a selection through a change. Used to adjust the selection position for changes.
* @param change The change to map through
* @param assoc Association direction for boundary positions
*/
map(change: ChangeDesc, assoc?: number): EditorSelection;Usage Examples:
import { EditorSelection, ChangeSet } from "@codemirror/state";
// Original selection from 5 to 10
const selection = EditorSelection.range(5, 10);
// Create a change that inserts "Hello " at position 3
const change = ChangeSet.of([{from: 3, insert: "Hello "}], 20);
// Map the selection through the change
const mappedSelection = selection.map(change);
console.log(mappedSelection.main.from); // 11 (5 + 6 characters inserted before)
console.log(mappedSelection.main.to); // 16 (10 + 6 characters inserted before)
// Selection that spans the change
const spanningSelection = EditorSelection.range(2, 8);
const mappedSpanning = spanningSelection.map(change);
console.log(mappedSpanning.main.from); // 2 (unchanged, before insertion)
console.log(mappedSpanning.main.to); // 14 (8 + 6 characters inserted)Methods for converting selections to and from JSON format.
/**
* Return a JSON-serializable representation of the selection
*/
toJSON(): any;
/**
* Deserialize a selection from its JSON representation
*/
static fromJSON(json: any): EditorSelection;Usage Examples:
// Serialize selection
const selection = EditorSelection.range(5, 15);
const json = selection.toJSON();
console.log(json); // { ranges: [{anchor: 5, head: 15}], main: 0 }
// Deserialize selection
const restored = EditorSelection.fromJSON(json);
console.log(restored.main.from); // 5
console.log(restored.main.to); // 15
// Multiple ranges serialization
const multiSelect = EditorSelection.create([
EditorSelection.range(2, 5).main,
EditorSelection.range(10, 15).main
]);
const multiJson = multiSelect.toJSON();
const restoredMulti = EditorSelection.fromJSON(multiJson);
console.log(restoredMulti.ranges.length); // 2/**
* Validation function for checking selection validity
*/
type SelectionValidator = (selection: EditorSelection, docLength: number) => EditorSelection;