'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var text = require('@codemirror/text'); const DefaultSplit = /\r\n?|\n/; /// Distinguishes different ways in which positions can be mapped. exports.MapMode = void 0; (function (MapMode) { /// Map a position to a valid new position, even when its context /// was deleted. MapMode[MapMode["Simple"] = 0] = "Simple"; /// Return null if deletion happens across the position. MapMode[MapMode["TrackDel"] = 1] = "TrackDel"; /// Return null if the character _before_ the position is deleted. MapMode[MapMode["TrackBefore"] = 2] = "TrackBefore"; /// Return null if the character _after_ the position is deleted. MapMode[MapMode["TrackAfter"] = 3] = "TrackAfter"; })(exports.MapMode || (exports.MapMode = {})); /// A change description is a variant of [change set](#state.ChangeSet) /// that doesn't store the inserted text. As such, it can't be /// applied, but is cheaper to store and manipulate. class ChangeDesc { // Sections are encoded as pairs of integers. The first is the // length in the current document, and the second is -1 for // unaffected sections, and the length of the replacement content // otherwise. So an insertion would be (0, n>0), a deletion (n>0, // 0), and a replacement two positive numbers. /// @internal constructor( /// @internal sections) { this.sections = sections; } /// The length of the document before the change. get length() { let result = 0; for (let i = 0; i < this.sections.length; i += 2) result += this.sections[i]; return result; } /// The length of the document after the change. get newLength() { let result = 0; for (let i = 0; i < this.sections.length; i += 2) { let ins = this.sections[i + 1]; result += ins < 0 ? this.sections[i] : ins; } return result; } /// False when there are actual changes in this set. get empty() { return this.sections.length == 0 || this.sections.length == 2 && this.sections[1] < 0; } /// Iterate over the unchanged parts left by these changes. iterGaps(f) { for (let i = 0, posA = 0, posB = 0; i < this.sections.length;) { let len = this.sections[i++], ins = this.sections[i++]; if (ins < 0) { f(posA, posB, len); posB += len; } else { posB += ins; } posA += len; } } /// Iterate over the ranges changed by these changes. (See /// [`ChangeSet.iterChanges`](#state.ChangeSet.iterChanges) for a /// variant that also provides you with the inserted text.) /// /// When `individual` is true, adjacent changes (which are kept /// separate for [position mapping](#state.ChangeDesc.mapPos)) are /// reported separately. iterChangedRanges(f, individual = false) { iterChanges(this, f, individual); } /// Get a description of the inverted form of these changes. get invertedDesc() { let sections = []; for (let i = 0; i < this.sections.length;) { let len = this.sections[i++], ins = this.sections[i++]; if (ins < 0) sections.push(len, ins); else sections.push(ins, len); } return new ChangeDesc(sections); } /// Compute the combined effect of applying another set of changes /// after this one. The length of the document after this set should /// match the length before `other`. composeDesc(other) { return this.empty ? other : other.empty ? this : composeSets(this, other); } /// Map this description, which should start with the same document /// as `other`, over another set of changes, so that it can be /// applied after it. When `before` is true, map as if the changes /// in `other` happened before the ones in `this`. mapDesc(other, before = false) { return other.empty ? this : mapSet(this, other, before); } mapPos(pos, assoc = -1, mode = exports.MapMode.Simple) { let posA = 0, posB = 0; for (let i = 0; i < this.sections.length;) { let len = this.sections[i++], ins = this.sections[i++], endA = posA + len; if (ins < 0) { if (endA > pos) return posB + (pos - posA); posB += len; } else { if (mode != exports.MapMode.Simple && endA >= pos && (mode == exports.MapMode.TrackDel && posA < pos && endA > pos || mode == exports.MapMode.TrackBefore && posA < pos || mode == exports.MapMode.TrackAfter && endA > pos)) return null; if (endA > pos || endA == pos && assoc < 0 && !len) return pos == posA || assoc < 0 ? posB : posB + ins; posB += ins; } posA = endA; } if (pos > posA) throw new RangeError(`Position ${pos} is out of range for changeset of length ${posA}`); return posB; } /// Check whether these changes touch a given range. When one of the /// changes entirely covers the range, the string `"cover"` is /// returned. touchesRange(from, to = from) { for (let i = 0, pos = 0; i < this.sections.length && pos <= to;) { let len = this.sections[i++], ins = this.sections[i++], end = pos + len; if (ins >= 0 && pos <= to && end >= from) return pos < from && end > to ? "cover" : true; pos = end; } return false; } /// @internal toString() { let result = ""; for (let i = 0; i < this.sections.length;) { let len = this.sections[i++], ins = this.sections[i++]; result += (result ? " " : "") + len + (ins >= 0 ? ":" + ins : ""); } return result; } } /// A change set represents a group of modifications to a document. It /// stores the document length, and can only be applied to documents /// with exactly that length. class ChangeSet extends ChangeDesc { /// @internal constructor(sections, /// @internal inserted) { super(sections); this.inserted = inserted; } /// Apply the changes to a document, returning the modified /// document. apply(doc) { if (this.length != doc.length) throw new RangeError("Applying change set to a document with the wrong length"); iterChanges(this, (fromA, toA, fromB, _toB, text) => doc = doc.replace(fromB, fromB + (toA - fromA), text), false); return doc; } mapDesc(other, before = false) { return mapSet(this, other, before, true); } /// Given the document as it existed _before_ the changes, return a /// change set that represents the inverse of this set, which could /// be used to go from the document created by the changes back to /// the document as it existed before the changes. invert(doc) { let sections = this.sections.slice(), inserted = []; for (let i = 0, pos = 0; i < sections.length; i += 2) { let len = sections[i], ins = sections[i + 1]; if (ins >= 0) { sections[i] = ins; sections[i + 1] = len; let index = i >> 1; while (inserted.length < index) inserted.push(text.Text.empty); inserted.push(len ? doc.slice(pos, pos + len) : text.Text.empty); } pos += len; } return new ChangeSet(sections, inserted); } /// Combine two subsequent change sets into a single set. `other` /// must start in the document produced by `this`. If `this` goes /// `docA` → `docB` and `other` represents `docB` → `docC`, the /// returned value will represent the change `docA` → `docC`. compose(other) { return this.empty ? other : other.empty ? this : composeSets(this, other, true); } /// Given another change set starting in the same document, maps this /// change set over the other, producing a new change set that can be /// applied to the document produced by applying `other`. When /// `before` is `true`, order changes as if `this` comes before /// `other`, otherwise (the default) treat `other` as coming first. /// /// Given two changes `A` and `B`, `A.compose(B.map(A))` and /// `B.compose(A.map(B, true))` will produce the same document. This /// provides a basic form of [operational /// transformation](https://en.wikipedia.org/wiki/Operational_transformation), /// and can be used for collaborative editing. map(other, before = false) { return other.empty ? this : mapSet(this, other, before, true); } /// Iterate over the changed ranges in the document, calling `f` for /// each. /// /// When `individual` is true, adjacent changes are reported /// separately. iterChanges(f, individual = false) { iterChanges(this, f, individual); } /// Get a [change description](#state.ChangeDesc) for this change /// set. get desc() { return new ChangeDesc(this.sections); } /// @internal filter(ranges) { let resultSections = [], resultInserted = [], filteredSections = []; let iter = new SectionIter(this); done: for (let i = 0, pos = 0;;) { let next = i == ranges.length ? 1e9 : ranges[i++]; while (pos < next || pos == next && iter.len == 0) { if (iter.done) break done; let len = Math.min(iter.len, next - pos); addSection(filteredSections, len, -1); let ins = iter.ins == -1 ? -1 : iter.off == 0 ? iter.ins : 0; addSection(resultSections, len, ins); if (ins > 0) addInsert(resultInserted, resultSections, iter.text); iter.forward(len); pos += len; } let end = ranges[i++]; while (pos < end) { if (iter.done) break done; let len = Math.min(iter.len, end - pos); addSection(resultSections, len, -1); addSection(filteredSections, len, iter.ins == -1 ? -1 : iter.off == 0 ? iter.ins : 0); iter.forward(len); pos += len; } } return { changes: new ChangeSet(resultSections, resultInserted), filtered: new ChangeDesc(filteredSections) }; } /// Serialize this change set to a JSON-representable value. toJSON() { let parts = []; for (let i = 0; i < this.sections.length; i += 2) { let len = this.sections[i], ins = this.sections[i + 1]; if (ins < 0) parts.push(len); else if (ins == 0) parts.push([len]); else parts.push([len].concat(this.inserted[i >> 1].toJSON())); } return parts; } /// Create a change set for the given changes, for a document of the /// given length, using `lineSep` as line separator. static of(changes, length, lineSep) { let sections = [], inserted = [], pos = 0; let total = null; function flush(force = false) { if (!force && !sections.length) return; if (pos < length) addSection(sections, length - pos, -1); let set = new ChangeSet(sections, inserted); total = total ? total.compose(set.map(total)) : set; sections = []; inserted = []; pos = 0; } function process(spec) { if (Array.isArray(spec)) { for (let sub of spec) process(sub); } else if (spec instanceof ChangeSet) { if (spec.length != length) throw new RangeError(`Mismatched change set length (got ${spec.length}, expected ${length})`); flush(); total = total ? total.compose(spec.map(total)) : spec; } else { let { from, to = from, insert } = spec; if (from > to || from < 0 || to > length) throw new RangeError(`Invalid change range ${from} to ${to} (in doc of length ${length})`); let insText = !insert ? text.Text.empty : typeof insert == "string" ? text.Text.of(insert.split(lineSep || DefaultSplit)) : insert; let insLen = insText.length; if (from == to && insLen == 0) return; if (from < pos) flush(); if (from > pos) addSection(sections, from - pos, -1); addSection(sections, to - from, insLen); addInsert(inserted, sections, insText); pos = to; } } process(changes); flush(!total); return total; } /// Create an empty changeset of the given length. static empty(length) { return new ChangeSet(length ? [length, -1] : [], []); } /// Create a changeset from its JSON representation (as produced by /// [`toJSON`](#state.ChangeSet.toJSON). static fromJSON(json) { if (!Array.isArray(json)) throw new RangeError("Invalid JSON representation of ChangeSet"); let sections = [], inserted = []; for (let i = 0; i < json.length; i++) { let part = json[i]; if (typeof part == "number") { sections.push(part, -1); } else if (!Array.isArray(part) || typeof part[0] != "number" || part.some((e, i) => i && typeof e != "string")) { throw new RangeError("Invalid JSON representation of ChangeSet"); } else if (part.length == 1) { sections.push(part[0], 0); } else { while (inserted.length < i) inserted.push(text.Text.empty); inserted[i] = text.Text.of(part.slice(1)); sections.push(part[0], inserted[i].length); } } return new ChangeSet(sections, inserted); } } function addSection(sections, len, ins, forceJoin = false) { if (len == 0 && ins <= 0) return; let last = sections.length - 2; if (last >= 0 && ins <= 0 && ins == sections[last + 1]) sections[last] += len; else if (len == 0 && sections[last] == 0) sections[last + 1] += ins; else if (forceJoin) { sections[last] += len; sections[last + 1] += ins; } else sections.push(len, ins); } function addInsert(values, sections, value) { if (value.length == 0) return; let index = (sections.length - 2) >> 1; if (index < values.length) { values[values.length - 1] = values[values.length - 1].append(value); } else { while (values.length < index) values.push(text.Text.empty); values.push(value); } } function iterChanges(desc, f, individual) { let inserted = desc.inserted; for (let posA = 0, posB = 0, i = 0; i < desc.sections.length;) { let len = desc.sections[i++], ins = desc.sections[i++]; if (ins < 0) { posA += len; posB += len; } else { let endA = posA, endB = posB, text$1 = text.Text.empty; for (;;) { endA += len; endB += ins; if (ins && inserted) text$1 = text$1.append(inserted[(i - 2) >> 1]); if (individual || i == desc.sections.length || desc.sections[i + 1] < 0) break; len = desc.sections[i++]; ins = desc.sections[i++]; } f(posA, endA, posB, endB, text$1); posA = endA; posB = endB; } } } function mapSet(setA, setB, before, mkSet = false) { let sections = [], insert = mkSet ? [] : null; let a = new SectionIter(setA), b = new SectionIter(setB); for (let posA = 0, posB = 0;;) { if (a.ins == -1) { posA += a.len; a.next(); } else if (b.ins == -1 && posB < posA) { let skip = Math.min(b.len, posA - posB); b.forward(skip); addSection(sections, skip, -1); posB += skip; } else if (b.ins >= 0 && (a.done || posB < posA || posB == posA && (b.len < a.len || b.len == a.len && !before))) { addSection(sections, b.ins, -1); while (posA > posB && !a.done && posA + a.len < posB + b.len) { posA += a.len; a.next(); } posB += b.len; b.next(); } else if (a.ins >= 0) { let len = 0, end = posA + a.len; for (;;) { if (b.ins >= 0 && posB > posA && posB + b.len < end) { len += b.ins; posB += b.len; b.next(); } else if (b.ins == -1 && posB < end) { let skip = Math.min(b.len, end - posB); len += skip; b.forward(skip); posB += skip; } else { break; } } addSection(sections, len, a.ins); if (insert) addInsert(insert, sections, a.text); posA = end; a.next(); } else if (a.done && b.done) { return insert ? new ChangeSet(sections, insert) : new ChangeDesc(sections); } else { throw new Error("Mismatched change set lengths"); } } } function composeSets(setA, setB, mkSet = false) { let sections = []; let insert = mkSet ? [] : null; let a = new SectionIter(setA), b = new SectionIter(setB); for (let open = false;;) { if (a.done && b.done) { return insert ? new ChangeSet(sections, insert) : new ChangeDesc(sections); } else if (a.ins == 0) { // Deletion in A addSection(sections, a.len, 0, open); a.next(); } else if (b.len == 0 && !b.done) { // Insertion in B addSection(sections, 0, b.ins, open); if (insert) addInsert(insert, sections, b.text); b.next(); } else if (a.done || b.done) { throw new Error("Mismatched change set lengths"); } else { let len = Math.min(a.len2, b.len), sectionLen = sections.length; if (a.ins == -1) { let insB = b.ins == -1 ? -1 : b.off ? 0 : b.ins; addSection(sections, len, insB, open); if (insert && insB) addInsert(insert, sections, b.text); } else if (b.ins == -1) { addSection(sections, a.off ? 0 : a.len, len, open); if (insert) addInsert(insert, sections, a.textBit(len)); } else { addSection(sections, a.off ? 0 : a.len, b.off ? 0 : b.ins, open); if (insert && !b.off) addInsert(insert, sections, b.text); } open = (a.ins > len || b.ins >= 0 && b.len > len) && (open || sections.length > sectionLen); a.forward2(len); b.forward(len); } } } class SectionIter { constructor(set) { this.set = set; this.i = 0; this.next(); } next() { let { sections } = this.set; if (this.i < sections.length) { this.len = sections[this.i++]; this.ins = sections[this.i++]; } else { this.len = 0; this.ins = -2; } this.off = 0; } get done() { return this.ins == -2; } get len2() { return this.ins < 0 ? this.len : this.ins; } get text() { let { inserted } = this.set, index = (this.i - 2) >> 1; return index >= inserted.length ? text.Text.empty : inserted[index]; } textBit(len) { let { inserted } = this.set, index = (this.i - 2) >> 1; return index >= inserted.length && !len ? text.Text.empty : inserted[index].slice(this.off, len == null ? undefined : this.off + len); } forward(len) { if (len == this.len) this.next(); else { this.len -= len; this.off += len; } } forward2(len) { if (this.ins == -1) this.forward(len); else if (len == this.ins) this.next(); else { this.ins -= len; this.off += len; } } } /// A single selection range. When /// [`allowMultipleSelections`](#state.EditorState^allowMultipleSelections) /// is enabled, a [selection](#state.EditorSelection) may hold /// multiple ranges. By default, selections hold exactly one range. class SelectionRange { /// @internal constructor( /// The lower boundary of the range. from, /// The upper boundary of the range. to, flags) { this.from = from; this.to = to; this.flags = flags; } /// The anchor of the range—the side that doesn't move when you /// extend it. get anchor() { return this.flags & 16 /* Inverted */ ? this.to : this.from; } /// The head of the range, which is moved when the range is /// [extended](#state.SelectionRange.extend). get head() { return this.flags & 16 /* Inverted */ ? this.from : this.to; } /// True when `anchor` and `head` are at the same position. get empty() { return this.from == this.to; } /// If this is a cursor that is explicitly associated with the /// character on one of its sides, this returns the side. -1 means /// the character before its position, 1 the character after, and 0 /// means no association. get assoc() { return this.flags & 4 /* AssocBefore */ ? -1 : this.flags & 8 /* AssocAfter */ ? 1 : 0; } /// The bidirectional text level associated with this cursor, if /// any. get bidiLevel() { let level = this.flags & 3 /* BidiLevelMask */; return level == 3 ? null : level; } /// The goal column (stored vertical offset) associated with a /// cursor. This is used to preserve the vertical position when /// [moving](#view.EditorView.moveVertically) across /// lines of different length. get goalColumn() { let value = this.flags >> 5 /* GoalColumnOffset */; return value == 33554431 /* NoGoalColumn */ ? undefined : value; } /// Map this range through a change, producing a valid range in the /// updated document. map(change, assoc = -1) { let from = change.mapPos(this.from, assoc), to = change.mapPos(this.to, assoc); return from == this.from && to == this.to ? this : new SelectionRange(from, to, this.flags); } /// Extend this range to cover at least `from` to `to`. extend(from, to = from) { if (from <= this.anchor && to >= this.anchor) return EditorSelection.range(from, to); let head = Math.abs(from - this.anchor) > Math.abs(to - this.anchor) ? from : to; return EditorSelection.range(this.anchor, head); } /// Compare this range to another range. eq(other) { return this.anchor == other.anchor && this.head == other.head; } /// Return a JSON-serializable object representing the range. toJSON() { return { anchor: this.anchor, head: this.head }; } /// Convert a JSON representation of a range to a `SelectionRange` /// instance. static fromJSON(json) { if (!json || typeof json.anchor != "number" || typeof json.head != "number") throw new RangeError("Invalid JSON representation for SelectionRange"); return EditorSelection.range(json.anchor, json.head); } } /// An editor selection holds one or more selection ranges. class EditorSelection { /// @internal constructor( /// The ranges in the selection, sorted by position. Ranges cannot /// overlap (but they may touch, if they aren't empty). ranges, /// The index of the _main_ range in the selection (which is /// usually the range that was added last). mainIndex = 0) { this.ranges = ranges; this.mainIndex = mainIndex; } /// Map a selection through a change. Used to adjust the selection /// position for changes. map(change, assoc = -1) { if (change.empty) return this; return EditorSelection.create(this.ranges.map(r => r.map(change, assoc)), this.mainIndex); } /// Compare this selection to another selection. eq(other) { if (this.ranges.length != other.ranges.length || this.mainIndex != other.mainIndex) return false; for (let i = 0; i < this.ranges.length; i++) if (!this.ranges[i].eq(other.ranges[i])) return false; return true; } /// Get the primary selection range. Usually, you should make sure /// your code applies to _all_ ranges, by using methods like /// [`changeByRange`](#state.EditorState.changeByRange). get main() { return this.ranges[this.mainIndex]; } /// Make sure the selection only has one range. Returns a selection /// holding only the main range from this selection. asSingle() { return this.ranges.length == 1 ? this : new EditorSelection([this.main]); } /// Extend this selection with an extra range. addRange(range, main = true) { return EditorSelection.create([range].concat(this.ranges), main ? 0 : this.mainIndex + 1); } /// Replace a given range with another range, and then normalize the /// selection to merge and sort ranges if necessary. replaceRange(range, which = this.mainIndex) { let ranges = this.ranges.slice(); ranges[which] = range; return EditorSelection.create(ranges, this.mainIndex); } /// Convert this selection to an object that can be serialized to /// JSON. toJSON() { return { ranges: this.ranges.map(r => r.toJSON()), main: this.mainIndex }; } /// Create a selection from a JSON representation. static fromJSON(json) { if (!json || !Array.isArray(json.ranges) || typeof json.main != "number" || json.main >= json.ranges.length) throw new RangeError("Invalid JSON representation for EditorSelection"); return new EditorSelection(json.ranges.map((r) => SelectionRange.fromJSON(r)), json.main); } /// Create a selection holding a single range. static single(anchor, head = anchor) { return new EditorSelection([EditorSelection.range(anchor, head)], 0); } /// Sort and merge the given set of ranges, creating a valid /// selection. static create(ranges, mainIndex = 0) { if (ranges.length == 0) throw new RangeError("A selection needs at least one range"); for (let pos = 0, i = 0; i < ranges.length; i++) { let range = ranges[i]; if (range.empty ? range.from <= pos : range.from < pos) return normalized(ranges.slice(), mainIndex); pos = range.to; } return new EditorSelection(ranges, mainIndex); } /// Create a cursor selection range at the given position. You can /// safely ignore the optional arguments in most situations. static cursor(pos, assoc = 0, bidiLevel, goalColumn) { return new SelectionRange(pos, pos, (assoc == 0 ? 0 : assoc < 0 ? 4 /* AssocBefore */ : 8 /* AssocAfter */) | (bidiLevel == null ? 3 : Math.min(2, bidiLevel)) | ((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 33554431 /* NoGoalColumn */) << 5 /* GoalColumnOffset */)); } /// Create a selection range. static range(anchor, head, goalColumn) { let goal = (goalColumn !== null && goalColumn !== void 0 ? goalColumn : 33554431 /* NoGoalColumn */) << 5 /* GoalColumnOffset */; return head < anchor ? new SelectionRange(head, anchor, 16 /* Inverted */ | goal) : new SelectionRange(anchor, head, goal); } } function normalized(ranges, mainIndex = 0) { let main = ranges[mainIndex]; ranges.sort((a, b) => a.from - b.from); mainIndex = ranges.indexOf(main); for (let i = 1; i < ranges.length; i++) { let range = ranges[i], prev = ranges[i - 1]; if (range.empty ? range.from <= prev.to : range.from < prev.to) { let from = prev.from, to = Math.max(range.to, prev.to); if (i <= mainIndex) mainIndex--; ranges.splice(--i, 2, range.anchor > range.head ? EditorSelection.range(to, from) : EditorSelection.range(from, to)); } } return new EditorSelection(ranges, mainIndex); } function checkSelection(selection, docLength) { for (let range of selection.ranges) if (range.to > docLength) throw new RangeError("Selection points outside of document"); } let nextID = 0; /// A facet is a labeled value that is associated with an editor /// state. It takes inputs from any number of extensions, and combines /// those into a single output value. /// /// Examples of facets are the [theme](#view.EditorView^theme) styles /// associated with an editor or the [tab /// size](#state.EditorState^tabSize) (which is reduced to a single /// value, using the input with the hightest precedence). class Facet { constructor( /// @internal combine, /// @internal compareInput, /// @internal compare, isStatic, /// @internal extensions) { this.combine = combine; this.compareInput = compareInput; this.compare = compare; this.isStatic = isStatic; this.extensions = extensions; /// @internal this.id = nextID++; this.default = combine([]); } /// Define a new facet. static define(config = {}) { return new Facet(config.combine || ((a) => a), config.compareInput || ((a, b) => a === b), config.compare || (!config.combine ? sameArray : (a, b) => a === b), !!config.static, config.enables); } /// Returns an extension that adds the given value for this facet. of(value) { return new FacetProvider([], this, 0 /* Static */, value); } /// Create an extension that computes a value for the facet from a /// state. You must take care to declare the parts of the state that /// this value depends on, since your function is only called again /// for a new state when one of those parts changed. /// /// In most cases, you'll want to use the /// [`provide`](#state.StateField^define^config.provide) option when /// defining a field instead. compute(deps, get) { if (this.isStatic) throw new Error("Can't compute a static facet"); return new FacetProvider(deps, this, 1 /* Single */, get); } /// Create an extension that computes zero or more values for this /// facet from a state. computeN(deps, get) { if (this.isStatic) throw new Error("Can't compute a static facet"); return new FacetProvider(deps, this, 2 /* Multi */, get); } from(field, get) { if (!get) get = x => x; return this.compute([field], state => get(state.field(field))); } } function sameArray(a, b) { return a == b || a.length == b.length && a.every((e, i) => e === b[i]); } class FacetProvider { constructor(dependencies, facet, type, value) { this.dependencies = dependencies; this.facet = facet; this.type = type; this.value = value; this.id = nextID++; } dynamicSlot(addresses) { var _a; let getter = this.value; let compare = this.facet.compareInput; let idx = addresses[this.id] >> 1, multi = this.type == 2 /* Multi */; let depDoc = false, depSel = false, depAddrs = []; for (let dep of this.dependencies) { if (dep == "doc") depDoc = true; else if (dep == "selection") depSel = true; else if ((((_a = addresses[dep.id]) !== null && _a !== void 0 ? _a : 1) & 1) == 0) depAddrs.push(addresses[dep.id]); } return (state, tr) => { if (!tr || tr.reconfigured) { state.values[idx] = getter(state); return 1 /* Changed */; } else { let depChanged = (depDoc && tr.docChanged) || (depSel && (tr.docChanged || tr.selection)) || depAddrs.some(addr => (ensureAddr(state, addr) & 1 /* Changed */) > 0); if (!depChanged) return 0; let newVal = getter(state), oldVal = tr.startState.values[idx]; if (multi ? compareArray(newVal, oldVal, compare) : compare(newVal, oldVal)) return 0; state.values[idx] = newVal; return 1 /* Changed */; } }; } } function compareArray(a, b, compare) { if (a.length != b.length) return false; for (let i = 0; i < a.length; i++) if (!compare(a[i], b[i])) return false; return true; } function dynamicFacetSlot(addresses, facet, providers) { let providerAddrs = providers.map(p => addresses[p.id]); let providerTypes = providers.map(p => p.type); let dynamic = providerAddrs.filter(p => !(p & 1)); let idx = addresses[facet.id] >> 1; return (state, tr) => { let oldAddr = !tr ? null : tr.reconfigured ? tr.startState.config.address[facet.id] : idx << 1; let changed = oldAddr == null; for (let dynAddr of dynamic) { if (ensureAddr(state, dynAddr) & 1 /* Changed */) changed = true; } if (!changed) return 0; let values = []; for (let i = 0; i < providerAddrs.length; i++) { let value = getAddr(state, providerAddrs[i]); if (providerTypes[i] == 2 /* Multi */) for (let val of value) values.push(val); else values.push(value); } let newVal = facet.combine(values); if (oldAddr != null && facet.compare(newVal, getAddr(tr.startState, oldAddr))) return 0; state.values[idx] = newVal; return 1 /* Changed */; }; } function maybeIndex(state, id) { let found = state.config.address[id]; return found == null ? null : found >> 1; } const initField = Facet.define({ static: true }); /// Fields can store additional information in an editor state, and /// keep it in sync with the rest of the state. class StateField { constructor( /// @internal id, createF, updateF, compareF, /// @internal spec) { this.id = id; this.createF = createF; this.updateF = updateF; this.compareF = compareF; this.spec = spec; /// @internal this.provides = undefined; } /// Define a state field. static define(config) { let field = new StateField(nextID++, config.create, config.update, config.compare || ((a, b) => a === b), config); if (config.provide) field.provides = config.provide(field); return field; } create(state) { let init = state.facet(initField).find(i => i.field == this); return ((init === null || init === void 0 ? void 0 : init.create) || this.createF)(state); } /// @internal slot(addresses) { let idx = addresses[this.id] >> 1; return (state, tr) => { if (!tr) { state.values[idx] = this.create(state); return 1 /* Changed */; } let oldVal, changed = 0; if (tr.reconfigured) { let oldIdx = maybeIndex(tr.startState, this.id); oldVal = oldIdx == null ? this.create(tr.startState) : tr.startState.values[oldIdx]; changed = 1 /* Changed */; } else { oldVal = tr.startState.values[idx]; } let value = this.updateF(oldVal, tr); if (!changed && !this.compareF(oldVal, value)) changed = 1 /* Changed */; if (changed) state.values[idx] = value; return changed; }; } /// Returns an extension that enables this field and overrides the /// way it is initialized. Can be useful when you need to provide a /// non-default starting value for the field. init(create) { return [this, initField.of({ field: this, create })]; } } const Prec_ = { fallback: 3, default: 2, extend: 1, override: 0 }; function prec(value) { return (ext) => new PrecExtension(ext, value); } /// By default extensions are registered in the order they are found /// in the flattened form of nested array that was provided. /// Individual extension values can be assigned a precedence to /// override this. Extensions that do not have a precedence set get /// the precedence of the nearest parent with a precedence, or /// [`default`](#state.Prec.default) if there is no such parent. The /// final ordering of extensions is determined by first sorting by /// precedence and then by order within each precedence. const Prec = { /// A precedence below the default precedence, which will cause /// default-precedence extensions to override it even if they are /// specified later in the extension ordering. fallback: prec(Prec_.fallback), /// The regular default precedence. default: prec(Prec_.default), /// A higher-than-default precedence. extend: prec(Prec_.extend), /// Precedence above the `default` and `extend` precedences. override: prec(Prec_.override) }; class PrecExtension { constructor(inner, prec) { this.inner = inner; this.prec = prec; } } /// Extension compartments can be used to make a configuration /// dynamic. By [wrapping](#state.Compartment.of) part of your /// configuration in a compartment, you can later /// [replace](#state.Compartment.reconfigure) that part through a /// transaction. class Compartment { /// Create an instance of this compartment to add to your [state /// configuration](#state.EditorStateConfig.extensions). of(ext) { return new CompartmentInstance(this, ext); } /// Create an [effect](#state.TransactionSpec.effects) that /// reconfigures this compartment. reconfigure(content) { return Compartment.reconfigure.of({ compartment: this, extension: content }); } } class CompartmentInstance { constructor(compartment, inner) { this.compartment = compartment; this.inner = inner; } } class Configuration { constructor(base, compartments, dynamicSlots, address, staticValues) { this.base = base; this.compartments = compartments; this.dynamicSlots = dynamicSlots; this.address = address; this.staticValues = staticValues; this.statusTemplate = []; while (this.statusTemplate.length < dynamicSlots.length) this.statusTemplate.push(0 /* Uninitialized */); } staticFacet(facet) { let addr = this.address[facet.id]; return addr == null ? facet.default : this.staticValues[addr >> 1]; } static resolve(base, compartments, oldState) { let fields = []; let facets = Object.create(null); let usedCompartments = new Set(); for (let ext of flatten(base, compartments, usedCompartments)) { if (ext instanceof StateField) fields.push(ext); else (facets[ext.facet.id] || (facets[ext.facet.id] = [])).push(ext); } let address = Object.create(null); let staticValues = []; let dynamicSlots = []; for (let field of fields) { address[field.id] = dynamicSlots.length << 1; dynamicSlots.push(a => field.slot(a)); } for (let id in facets) { let providers = facets[id], facet = providers[0].facet; if (providers.every(p => p.type == 0 /* Static */)) { address[facet.id] = (staticValues.length << 1) | 1; let value = facet.combine(providers.map(p => p.value)); let oldAddr = oldState ? oldState.config.address[facet.id] : null; if (oldAddr != null) { let oldVal = getAddr(oldState, oldAddr); if (facet.compare(value, oldVal)) value = oldVal; } staticValues.push(value); } else { for (let p of providers) { if (p.type == 0 /* Static */) { address[p.id] = (staticValues.length << 1) | 1; staticValues.push(p.value); } else { address[p.id] = dynamicSlots.length << 1; dynamicSlots.push(a => p.dynamicSlot(a)); } } address[facet.id] = dynamicSlots.length << 1; dynamicSlots.push(a => dynamicFacetSlot(a, facet, providers)); } } return new Configuration(base, removeUnused(compartments, usedCompartments), dynamicSlots.map(f => f(address)), address, staticValues); } } function removeUnused(compartments, usedCompartments) { let dropped = []; compartments.forEach((_, c) => { if (!usedCompartments.has(c)) dropped.push(c); }); if (!dropped.length) return compartments; let newCompartments = new Map(); compartments.forEach((e, c) => { if (dropped.indexOf(c) < 0) newCompartments.set(c, e); }); return newCompartments; } function flatten(extension, compartments, compartmentsSeen) { let result = [[], [], [], []]; let seen = new Map(); function inner(ext, prec) { let known = seen.get(ext); if (known != null) { if (known >= prec) return; let found = result[known].indexOf(ext); if (found > -1) result[known].splice(found, 1); if (ext instanceof CompartmentInstance) compartmentsSeen.delete(ext.compartment); } seen.set(ext, prec); if (Array.isArray(ext)) { for (let e of ext) inner(e, prec); } else if (ext instanceof CompartmentInstance) { if (compartmentsSeen.has(ext.compartment)) throw new RangeError(`Duplicate use of compartment in extensions`); compartmentsSeen.add(ext.compartment); inner(compartments.get(ext.compartment) || ext.inner, prec); } else if (ext instanceof PrecExtension) { inner(ext.inner, ext.prec); } else if (ext instanceof StateField) { result[prec].push(ext); if (ext.provides) inner(ext.provides, prec); } else if (ext instanceof FacetProvider) { result[prec].push(ext); if (ext.facet.extensions) inner(ext.facet.extensions, prec); } else { let content = ext.extension; if (!content) throw new Error(`Unrecognized extension value in extension set (${ext}). This sometimes happens because multiple instances of @codemirror/state are loaded, breaking instanceof checks.`); inner(content, prec); } } inner(extension, Prec_.default); return result.reduce((a, b) => a.concat(b)); } function ensureAddr(state, addr) { if (addr & 1) return 2 /* Computed */; let idx = addr >> 1; let status = state.status[idx]; if (status == 4 /* Computing */) throw new Error("Cyclic dependency between fields and/or facets"); if (status & 2 /* Computed */) return status; state.status[idx] = 4 /* Computing */; let changed = state.config.dynamicSlots[idx](state, state.applying); return state.status[idx] = 2 /* Computed */ | changed; } function getAddr(state, addr) { return addr & 1 ? state.config.staticValues[addr >> 1] : state.values[addr >> 1]; } const languageData = Facet.define(); const allowMultipleSelections = Facet.define({ combine: values => values.some(v => v), static: true }); const lineSeparator = Facet.define({ combine: values => values.length ? values[0] : undefined, static: true }); const changeFilter = Facet.define(); const transactionFilter = Facet.define(); const transactionExtender = Facet.define(); /// Annotations are tagged values that are used to add metadata to /// transactions in an extensible way. They should be used to model /// things that effect the entire transaction (such as its [time /// stamp](#state.Transaction^time) or information about its /// [origin](#state.Transaction^userEvent)). For effects that happen /// _alongside_ the other changes made by the transaction, [state /// effects](#state.StateEffect) are more appropriate. class Annotation { /// @internal constructor( /// The annotation type. type, /// The value of this annotation. value) { this.type = type; this.value = value; } /// Define a new type of annotation. static define() { return new AnnotationType(); } } /// Marker that identifies a type of [annotation](#state.Annotation). class AnnotationType { /// Create an instance of this annotation. of(value) { return new Annotation(this, value); } } /// Representation of a type of state effect. Defined with /// [`StateEffect.define`](#state.StateEffect^define). class StateEffectType { /// @internal constructor( // The `any` types in these function types are there to work // around TypeScript issue #37631, where the type guard on // `StateEffect.is` mysteriously stops working when these properly // have type `Value`. /// @internal map) { this.map = map; } /// Create a [state effect](#state.StateEffect) instance of this /// type. of(value) { return new StateEffect(this, value); } } /// State effects can be used to represent additional effects /// associated with a [transaction](#state.Transaction.effects). They /// are often useful to model changes to custom [state /// fields](#state.StateField), when those changes aren't implicit in /// document or selection changes. class StateEffect { /// @internal constructor( /// @internal type, /// The value of this effect. value) { this.type = type; this.value = value; } /// Map this effect through a position mapping. Will return /// `undefined` when that ends up deleting the effect. map(mapping) { let mapped = this.type.map(this.value, mapping); return mapped === undefined ? undefined : mapped == this.value ? this : new StateEffect(this.type, mapped); } /// Tells you whether this effect object is of a given /// [type](#state.StateEffectType). is(type) { return this.type == type; } /// Define a new effect type. The type parameter indicates the type /// of values that his effect holds. static define(spec = {}) { return new StateEffectType(spec.map || (v => v)); } /// Map an array of effects through a change set. static mapEffects(effects, mapping) { if (!effects.length) return effects; let result = []; for (let effect of effects) { let mapped = effect.map(mapping); if (mapped) result.push(mapped); } return result; } } /// This effect can be used to reconfigure the root extensions of /// the editor. Doing this will discard any extensions /// [appended](#state.StateEffect^appendConfig), but does not reset /// the content of [reconfigured](#state.Compartment.reconfigure) /// compartments. StateEffect.reconfigure = StateEffect.define(); /// Append extensions to the top-level configuration of the editor. StateEffect.appendConfig = StateEffect.define(); /// Changes to the editor state are grouped into transactions. /// Typically, a user action creates a single transaction, which may /// contain any number of document changes, may change the selection, /// or have other effects. Create a transaction by calling /// [`EditorState.update`](#state.EditorState.update). class Transaction { /// @internal constructor( /// The state from which the transaction starts. startState, /// The document changes made by this transaction. changes, /// The selection set by this transaction, or undefined if it /// doesn't explicitly set a selection. selection, /// The effects added to the transaction. effects, /// @internal annotations, /// Whether the selection should be scrolled into view after this /// transaction is dispatched. scrollIntoView) { this.startState = startState; this.changes = changes; this.selection = selection; this.effects = effects; this.annotations = annotations; this.scrollIntoView = scrollIntoView; /// @internal this._doc = null; /// @internal this._state = null; if (selection) checkSelection(selection, changes.newLength); if (!annotations.some((a) => a.type == Transaction.time)) this.annotations = annotations.concat(Transaction.time.of(Date.now())); } /// The new document produced by the transaction. Contrary to /// [`.state`](#state.Transaction.state)`.doc`, accessing this won't /// force the entire new state to be computed right away, so it is /// recommended that [transaction /// filters](#state.EditorState^transactionFilter) use this getter /// when they need to look at the new document. get newDoc() { return this._doc || (this._doc = this.changes.apply(this.startState.doc)); } /// The new selection produced by the transaction. If /// [`this.selection`](#state.Transaction.selection) is undefined, /// this will [map](#state.EditorSelection.map) the start state's /// current selection through the changes made by the transaction. get newSelection() { return this.selection || this.startState.selection.map(this.changes); } /// The new state created by the transaction. Computed on demand /// (but retained for subsequent access), so itis recommended not to /// access it in [transaction /// filters](#state.EditorState^transactionFilter) when possible. get state() { if (!this._state) this.startState.applyTransaction(this); return this._state; } /// Get the value of the given annotation type, if any. annotation(type) { for (let ann of this.annotations) if (ann.type == type) return ann.value; return undefined; } /// Indicates whether the transaction changed the document. get docChanged() { return !this.changes.empty; } /// Indicates whether this transaction reconfigures the state /// (through a [configuration compartment](#state.Compartment) or /// with a top-level configuration /// [effect](#state.StateEffect^reconfigure). get reconfigured() { return this.startState.config != this.state.config; } } /// Annotation used to store transaction timestamps. Transaction.time = Annotation.define(); /// Annotation used to associate a transaction with a user interface /// event. The view will set this to... /// /// - `"input"` when the user types text /// - `"delete"` when the user deletes the selection or text near the selection /// - `"keyboardselection"` when moving the selection via the keyboard /// - `"pointerselection"` when moving the selection through the pointing device /// - `"paste"` when pasting content /// - `"cut"` when cutting /// - `"drop"` when content is inserted via drag-and-drop Transaction.userEvent = Annotation.define(); /// Annotation indicating whether a transaction should be added to /// the undo history or not. Transaction.addToHistory = Annotation.define(); function joinRanges(a, b) { let result = []; for (let iA = 0, iB = 0;;) { let from, to; if (iA < a.length && (iB == b.length || b[iB] >= a[iA])) { from = a[iA++]; to = a[iA++]; } else if (iB < b.length) { from = b[iB++]; to = b[iB++]; } else return result; if (!result.length || result[result.length - 1] < from) result.push(from, to); else if (result[result.length - 1] < to) result[result.length - 1] = to; } } function mergeTransaction(a, b, sequential) { var _a; let mapForA, mapForB, changes; if (sequential) { mapForA = b.changes; mapForB = ChangeSet.empty(b.changes.length); changes = a.changes.compose(b.changes); } else { mapForA = b.changes.map(a.changes); mapForB = a.changes.mapDesc(b.changes, true); changes = a.changes.compose(mapForA); } return { changes, selection: b.selection ? b.selection.map(mapForB) : (_a = a.selection) === null || _a === void 0 ? void 0 : _a.map(mapForA), effects: StateEffect.mapEffects(a.effects, mapForA).concat(StateEffect.mapEffects(b.effects, mapForB)), annotations: a.annotations.length ? a.annotations.concat(b.annotations) : b.annotations, scrollIntoView: a.scrollIntoView || b.scrollIntoView }; } function resolveTransactionInner(state, spec, docSize) { let sel = spec.selection; return { changes: spec.changes instanceof ChangeSet ? spec.changes : ChangeSet.of(spec.changes || [], docSize, state.facet(lineSeparator)), selection: sel && (sel instanceof EditorSelection ? sel : EditorSelection.single(sel.anchor, sel.head)), effects: asArray(spec.effects), annotations: asArray(spec.annotations), scrollIntoView: !!spec.scrollIntoView }; } function resolveTransaction(state, specs, filter) { let s = resolveTransactionInner(state, specs.length ? specs[0] : {}, state.doc.length); if (specs.length && specs[0].filter === false) filter = false; for (let i = 1; i < specs.length; i++) { if (specs[i].filter === false) filter = false; let seq = !!specs[i].sequential; s = mergeTransaction(s, resolveTransactionInner(state, specs[i], seq ? s.changes.newLength : state.doc.length), seq); } let tr = new Transaction(state, s.changes, s.selection, s.effects, s.annotations, s.scrollIntoView); return extendTransaction(filter ? filterTransaction(tr) : tr); } // Finish a transaction by applying filters if necessary. function filterTransaction(tr) { let state = tr.startState; // Change filters let result = true; for (let filter of state.facet(changeFilter)) { let value = filter(tr); if (value === false) { result = false; break; } if (Array.isArray(value)) result = result === true ? value : joinRanges(result, value); } if (result !== true) { let changes, back; if (result === false) { back = tr.changes.invertedDesc; changes = ChangeSet.empty(state.doc.length); } else { let filtered = tr.changes.filter(result); changes = filtered.changes; back = filtered.filtered.invertedDesc; } tr = new Transaction(state, changes, tr.selection && tr.selection.map(back), StateEffect.mapEffects(tr.effects, back), tr.annotations, tr.scrollIntoView); } // Transaction filters let filters = state.facet(transactionFilter); for (let i = filters.length - 1; i >= 0; i--) { let filtered = filters[i](tr); if (filtered instanceof Transaction) tr = filtered; else if (Array.isArray(filtered) && filtered.length == 1 && filtered[0] instanceof Transaction) tr = filtered[0]; else tr = resolveTransaction(state, asArray(filtered), false); } return tr; } function extendTransaction(tr) { let state = tr.startState, extenders = state.facet(transactionExtender), spec = tr; for (let i = extenders.length - 1; i >= 0; i--) { let extension = extenders[i](tr); if (extension && Object.keys(extension).length) spec = mergeTransaction(tr, resolveTransactionInner(state, extension, tr.changes.newLength), true); } return spec == tr ? tr : new Transaction(state, tr.changes, tr.selection, spec.effects, spec.annotations, spec.scrollIntoView); } const none = []; function asArray(value) { return value == null ? none : Array.isArray(value) ? value : [value]; } /// The categories produced by a [character /// categorizer](#state.EditorState.charCategorizer). These are used /// do things like selecting by word. exports.CharCategory = void 0; (function (CharCategory) { /// Word characters. CharCategory[CharCategory["Word"] = 0] = "Word"; /// Whitespace. CharCategory[CharCategory["Space"] = 1] = "Space"; /// Anything else. CharCategory[CharCategory["Other"] = 2] = "Other"; })(exports.CharCategory || (exports.CharCategory = {})); const nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; let wordChar; try { wordChar = new RegExp("[\\p{Alphabetic}\\p{Number}_]", "u"); } catch (_) { } function hasWordChar(str) { if (wordChar) return wordChar.test(str); for (let i = 0; i < str.length; i++) { let ch = str[i]; if (/\w/.test(ch) || ch > "\x80" && (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))) return true; } return false; } function makeCategorizer(wordChars) { return (char) => { if (!/\S/.test(char)) return exports.CharCategory.Space; if (hasWordChar(char)) return exports.CharCategory.Word; for (let i = 0; i < wordChars.length; i++) if (char.indexOf(wordChars[i]) > -1) return exports.CharCategory.Word; return exports.CharCategory.Other; }; } /// The editor state class is a persistent (immutable) data structure. /// To update a state, you [create](#state.EditorState.update) a /// [transaction](#state.Transaction), which produces a _new_ state /// instance, without modifying the original object. /// /// As such, _never_ mutate properties of a state directly. That'll /// just break things. class EditorState { /// @internal constructor( /// @internal config, /// The current document. doc, /// The current selection. selection, tr = null) { this.config = config; this.doc = doc; this.selection = selection; /// @internal this.applying = null; this.status = config.statusTemplate.slice(); if (tr && tr.startState.config == config) { this.values = tr.startState.values.slice(); } else { this.values = config.dynamicSlots.map(_ => null); // Copy over old values for shared facets/fields if this is a reconfigure if (tr) for (let id in config.address) { let cur = config.address[id], prev = tr.startState.config.address[id]; if (prev != null && (cur & 1) == 0) this.values[cur >> 1] = getAddr(tr.startState, prev); } } this.applying = tr; // Fill in the computed state immediately, so that further queries // for it made during the update return this state if (tr) tr._state = this; for (let i = 0; i < this.config.dynamicSlots.length; i++) ensureAddr(this, i << 1); this.applying = null; } field(field, require = true) { let addr = this.config.address[field.id]; if (addr == null) { if (require) throw new RangeError("Field is not present in this state"); return undefined; } ensureAddr(this, addr); return getAddr(this, addr); } /// Create a [transaction](#state.Transaction) that updates this /// state. Any number of [transaction specs](#state.TransactionSpec) /// can be passed. Unless /// [`sequential`](#state.TransactionSpec.sequential) is set, the /// [changes](#state.TransactionSpec.changes) (if any) of each spec /// are assumed to start in the _current_ document (not the document /// produced by previous specs), and its /// [selection](#state.TransactionSpec.selection) and /// [effects](#state.TransactionSpec.effects) are assumed to refer /// to the document created by its _own_ changes. The resulting /// transaction contains the combined effect of all the different /// specs. For [selection](#state.TransactionSpec.selection), later /// specs take precedence over earlier ones. update(...specs) { return resolveTransaction(this, specs, true); } /// @internal applyTransaction(tr) { let conf = this.config, { base, compartments } = conf; for (let effect of tr.effects) { if (effect.is(Compartment.reconfigure)) { if (conf) { compartments = new Map; conf.compartments.forEach((val, key) => compartments.set(key, val)); conf = null; } compartments.set(effect.value.compartment, effect.value.extension); } else if (effect.is(StateEffect.reconfigure)) { conf = null; base = effect.value; } else if (effect.is(StateEffect.appendConfig)) { conf = null; base = asArray(base).concat(effect.value); } } new EditorState(conf || Configuration.resolve(base, compartments, this), tr.newDoc, tr.newSelection, tr); } /// Create a [transaction spec](#state.TransactionSpec) that /// replaces every selection range with the given content. replaceSelection(text) { if (typeof text == "string") text = this.toText(text); return this.changeByRange(range => ({ changes: { from: range.from, to: range.to, insert: text }, range: EditorSelection.cursor(range.from + text.length) })); } /// Create a set of changes and a new selection by running the given /// function for each range in the active selection. The function /// can return an optional set of changes (in the coordinate space /// of the start document), plus an updated range (in the coordinate /// space of the document produced by the call's own changes). This /// method will merge all the changes and ranges into a single /// changeset and selection, and return it as a [transaction /// spec](#state.TransactionSpec), which can be passed to /// [`update`](#state.EditorState.update). changeByRange(f) { let sel = this.selection; let result1 = f(sel.ranges[0]); let changes = this.changes(result1.changes), ranges = [result1.range]; let effects = asArray(result1.effects); for (let i = 1; i < sel.ranges.length; i++) { let result = f(sel.ranges[i]); let newChanges = this.changes(result.changes), newMapped = newChanges.map(changes); for (let j = 0; j < i; j++) ranges[j] = ranges[j].map(newMapped); let mapBy = changes.mapDesc(newChanges, true); ranges.push(result.range.map(mapBy)); changes = changes.compose(newMapped); effects = StateEffect.mapEffects(effects, newMapped).concat(StateEffect.mapEffects(asArray(result.effects), mapBy)); } return { changes, selection: EditorSelection.create(ranges, sel.mainIndex), effects }; } /// Create a [change set](#state.ChangeSet) from the given change /// description, taking the state's document length and line /// separator into account. changes(spec = []) { if (spec instanceof ChangeSet) return spec; return ChangeSet.of(spec, this.doc.length, this.facet(EditorState.lineSeparator)); } /// Using the state's [line /// separator](#state.EditorState^lineSeparator), create a /// [`Text`](#text.Text) instance from the given string. toText(string) { return text.Text.of(string.split(this.facet(EditorState.lineSeparator) || DefaultSplit)); } /// Return the given range of the document as a string. sliceDoc(from = 0, to = this.doc.length) { return this.doc.sliceString(from, to, this.lineBreak); } /// Get the value of a state [facet](#state.Facet). facet(facet) { let addr = this.config.address[facet.id]; if (addr == null) return facet.default; ensureAddr(this, addr); return getAddr(this, addr); } /// Convert this state to a JSON-serializable object. When custom /// fields should be serialized, you can pass them in as an object /// mapping property names (in the resulting object, which should /// not use `doc` or `selection`) to fields. toJSON(fields) { let result = { doc: this.sliceDoc(), selection: this.selection.toJSON() }; if (fields) for (let prop in fields) result[prop] = fields[prop].spec.toJSON(this.field(fields[prop]), this); return result; } /// Deserialize a state from its JSON representation. When custom /// fields should be deserialized, pass the same object you passed /// to [`toJSON`](#state.EditorState.toJSON) when serializing as /// third argument. static fromJSON(json, config = {}, fields) { if (!json || typeof json.doc != "string") throw new RangeError("Invalid JSON representation for EditorState"); let fieldInit = []; if (fields) for (let prop in fields) { let field = fields[prop], value = json[prop]; fieldInit.push(field.init(state => field.spec.fromJSON(value, state))); } return EditorState.create({ doc: json.doc, selection: EditorSelection.fromJSON(json.selection), extensions: config.extensions ? fieldInit.concat([config.extensions]) : fieldInit }); } /// Create a new state. You'll usually only need this when /// initializing an editor—updated states are created by applying /// transactions. static create(config = {}) { let configuration = Configuration.resolve(config.extensions || [], new Map); let doc = config.doc instanceof text.Text ? config.doc : text.Text.of((config.doc || "").split(configuration.staticFacet(EditorState.lineSeparator) || DefaultSplit)); let selection = !config.selection ? EditorSelection.single(0) : config.selection instanceof EditorSelection ? config.selection : EditorSelection.single(config.selection.anchor, config.selection.head); checkSelection(selection, doc.length); if (!configuration.staticFacet(allowMultipleSelections)) selection = selection.asSingle(); return new EditorState(configuration, doc, selection); } /// The size (in columns) of a tab in the document, determined by /// the [`tabSize`](#state.EditorState^tabSize) facet. get tabSize() { return this.facet(EditorState.tabSize); } /// Get the proper [line-break](#state.EditorState^lineSeparator) /// string for this state. get lineBreak() { return this.facet(EditorState.lineSeparator) || "\n"; } /// Look up a translation for the given phrase (via the /// [`phrases`](#state.EditorState^phrases) facet), or return the /// original string if no translation is found. phrase(phrase) { for (let map of this.facet(EditorState.phrases)) if (Object.prototype.hasOwnProperty.call(map, phrase)) return map[phrase]; return phrase; } /// Find the values for a given language data field, provided by the /// the [`languageData`](#state.EditorState^languageData) facet. languageDataAt(name, pos) { let values = []; for (let provider of this.facet(languageData)) { for (let result of provider(this, pos)) { if (Object.prototype.hasOwnProperty.call(result, name)) values.push(result[name]); } } return values; } /// Return a function that can categorize strings (expected to /// represent a single [grapheme cluster](#text.findClusterBreak)) /// into one of: /// /// - Word (contains an alphanumeric character or a character /// explicitly listed in the local language's `"wordChars"` /// language data, which should be a string) /// - Space (contains only whitespace) /// - Other (anything else) charCategorizer(at) { return makeCategorizer(this.languageDataAt("wordChars", at).join("")); } } /// A facet that, when enabled, causes the editor to allow multiple /// ranges to be selected. Be careful though, because by default the /// editor relies on the native DOM selection, which cannot handle /// multiple selections. An extension like /// [`drawSelection`](#view.drawSelection) can be used to make /// secondary selections visible to the user. EditorState.allowMultipleSelections = allowMultipleSelections; /// Configures the tab size to use in this state. The first /// (highest-precedence) value of the facet is used. If no value is /// given, this defaults to 4. EditorState.tabSize = Facet.define({ combine: values => values.length ? values[0] : 4 }); /// The line separator to use. By default, any of `"\n"`, `"\r\n"` /// and `"\r"` is treated as a separator when splitting lines, and /// lines are joined with `"\n"`. /// /// When you configure a value here, only that precise separator /// will be used, allowing you to round-trip documents through the /// editor without normalizing line separators. EditorState.lineSeparator = lineSeparator; /// Registers translation phrases. The /// [`phrase`](#state.EditorState.phrase) method will look through /// all objects registered with this facet to find translations for /// its argument. EditorState.phrases = Facet.define(); /// A facet used to register [language /// data](#state.EditorState.languageDataAt) providers. EditorState.languageData = languageData; /// Facet used to register change filters, which are called for each /// transaction (unless explicitly /// [disabled](#state.TransactionSpec.filter)), and can suppress /// part of the transaction's changes. /// /// Such a function can return `true` to indicate that it doesn't /// want to do anything, `false` to completely stop the changes in /// the transaction, or a set of ranges in which changes should be /// suppressed. Such ranges are represented as an array of numbers, /// with each pair of two number indicating the start and end of a /// range. So for example `[10, 20, 100, 110]` suppresses changes /// between 10 and 20, and between 100 and 110. EditorState.changeFilter = changeFilter; /// Facet used to register a hook that gets a chance to update or /// replace transaction specs before they are applied. This will /// only be applied for transactions that don't have /// [`filter`](#state.TransactionSpec.filter) set to `false`. You /// can either return a single (possibly the input transaction), or /// an array of specs (which will be combined in the same way as the /// arguments to [`EditorState.update`](#state.EditorState.update)). /// /// When possible, it is recommended to avoid accessing /// [`Transaction.state`](#state.Transaction.state) in a filter, /// since it will force creation of a state that will then be /// discarded again, if the transaction is actually filtered. /// /// (This functionality should be used with care. Indiscriminately /// modifying transaction is likely to break something or degrade /// the user experience.) EditorState.transactionFilter = transactionFilter; /// This is a more limited form of /// [`transactionFilter`](#state.EditorState^transactionFilter), /// which can only add /// [annotations](#state.TransactionSpec.annotations) and /// [effects](#state.TransactionSpec.effects). _But_, this type /// of filter runs even the transaction has disabled regular /// [filtering](#state.TransactionSpec.filter), making it suitable /// for effects that don't need to touch the changes or selection, /// but do want to process every transaction. /// /// Extenders run _after_ filters, when both are applied. EditorState.transactionExtender = transactionExtender; Compartment.reconfigure = StateEffect.define(); /// Utility function for combining behaviors to fill in a config /// object from an array of provided configs. Will, by default, error /// when a field gets two values that aren't `===`-equal, but you can /// provide combine functions per field to do something else. function combineConfig(configs, defaults, // Should hold only the optional properties of Config, but I haven't managed to express that combine = {}) { let result = {}; for (let config of configs) for (let key of Object.keys(config)) { let value = config[key], current = result[key]; if (current === undefined) result[key] = value; else if (current === value || value === undefined) ; // No conflict else if (Object.hasOwnProperty.call(combine, key)) result[key] = combine[key](current, value); else throw new Error("Config merge conflict for field " + key); } for (let key in defaults) if (result[key] === undefined) result[key] = defaults[key]; return result; } Object.defineProperty(exports, 'Text', { enumerable: true, get: function () { return text.Text; } }); exports.Annotation = Annotation; exports.AnnotationType = AnnotationType; exports.ChangeDesc = ChangeDesc; exports.ChangeSet = ChangeSet; exports.Compartment = Compartment; exports.EditorSelection = EditorSelection; exports.EditorState = EditorState; exports.Facet = Facet; exports.Prec = Prec; exports.SelectionRange = SelectionRange; exports.StateEffect = StateEffect; exports.StateEffectType = StateEffectType; exports.StateField = StateField; exports.Transaction = Transaction; exports.combineConfig = combineConfig;