UNPKG

@scidian/scrimp

Version:

Customized CodeMirror editor for use with the Onsight Engine.

1,313 lines 5.21 MB
/** * @description Scrimp * @about Customized CodeMirror 6 editor for use with the Onsight Editor. * @author Stephens Nunnally <@stevinz> * @license MIT - Copyright (c) 2024 Stephens Nunnally * @source https://github.com/onsightengine/scrimp * @version v0.0.3 */ class Text { lineAt(pos) { if (pos < 0 || pos > this.length) throw new RangeError(`Invalid position ${pos} in document of length ${this.length}`); return this.lineInner(pos, false, 1, 0); } line(n) { if (n < 1 || n > this.lines) throw new RangeError(`Invalid line number ${n} in ${this.lines}-line document`); return this.lineInner(n, true, 1, 0); } replace(from, to, text) { [from, to] = clip(this, from, to); let parts = []; this.decompose(0, from, parts, 2 ); if (text.length) text.decompose(0, text.length, parts, 1 | 2 ); this.decompose(to, this.length, parts, 1 ); return TextNode.from(parts, this.length - (to - from) + text.length); } append(other) { return this.replace(this.length, this.length, other); } slice(from, to = this.length) { [from, to] = clip(this, from, to); let parts = []; this.decompose(from, to, parts, 0); return TextNode.from(parts, to - from); } eq(other) { if (other == this) return true; if (other.length != this.length || other.lines != this.lines) return false; let start = this.scanIdentical(other, 1), end = this.length - this.scanIdentical(other, -1); let a = new RawTextCursor(this), b = new RawTextCursor(other); for (let skip = start, pos = start;;) { a.next(skip); b.next(skip); skip = 0; if (a.lineBreak != b.lineBreak || a.done != b.done || a.value != b.value) return false; pos += a.value.length; if (a.done || pos >= end) return true; } } iter(dir = 1) { return new RawTextCursor(this, dir); } iterRange(from, to = this.length) { return new PartialTextCursor(this, from, to); } iterLines(from, to) { let inner; if (from == null) { inner = this.iter(); } else { if (to == null) to = this.lines + 1; let start = this.line(from).from; inner = this.iterRange(start, Math.max(start, to == this.lines + 1 ? this.length : to <= 1 ? 0 : this.line(to - 1).to)); } return new LineCursor(inner); } toString() { return this.sliceString(0); } toJSON() { let lines = []; this.flatten(lines); return lines; } constructor() { } static of(text) { if (text.length == 0) throw new RangeError("A document must have at least one line"); if (text.length == 1 && !text[0]) return Text.empty; return text.length <= 32 ? new TextLeaf(text) : TextNode.from(TextLeaf.split(text, [])); } } class TextLeaf extends Text { constructor(text, length = textLength(text)) { super(); this.text = text; this.length = length; } get lines() { return this.text.length; } get children() { return null; } lineInner(target, isLine, line, offset) { for (let i = 0;; i++) { let string = this.text[i], end = offset + string.length; if ((isLine ? line : end) >= target) return new Line(offset, end, line, string); offset = end + 1; line++; } } decompose(from, to, target, open) { let text = from <= 0 && to >= this.length ? this : new TextLeaf(sliceText(this.text, from, to), Math.min(to, this.length) - Math.max(0, from)); if (open & 1 ) { let prev = target.pop(); let joined = appendText(text.text, prev.text.slice(), 0, text.length); if (joined.length <= 32 ) { target.push(new TextLeaf(joined, prev.length + text.length)); } else { let mid = joined.length >> 1; target.push(new TextLeaf(joined.slice(0, mid)), new TextLeaf(joined.slice(mid))); } } else { target.push(text); } } replace(from, to, text) { if (!(text instanceof TextLeaf)) return super.replace(from, to, text); [from, to] = clip(this, from, to); let lines = appendText(this.text, appendText(text.text, sliceText(this.text, 0, from)), to); let newLen = this.length + text.length - (to - from); if (lines.length <= 32 ) return new TextLeaf(lines, newLen); return TextNode.from(TextLeaf.split(lines, []), newLen); } sliceString(from, to = this.length, lineSep = "\n") { [from, to] = clip(this, from, to); let result = ""; for (let pos = 0, i = 0; pos <= to && i < this.text.length; i++) { let line = this.text[i], end = pos + line.length; if (pos > from && i) result += lineSep; if (from < end && to > pos) result += line.slice(Math.max(0, from - pos), to - pos); pos = end + 1; } return result; } flatten(target) { for (let line of this.text) target.push(line); } scanIdentical() { return 0; } static split(text, target) { let part = [], len = -1; for (let line of text) { part.push(line); len += line.length + 1; if (part.length == 32 ) { target.push(new TextLeaf(part, len)); part = []; len = -1; } } if (len > -1) target.push(new TextLeaf(part, len)); return target; } } class TextNode extends Text { constructor(children, length) { super(); this.children = children; this.length = length; this.lines = 0; for (let child of children) this.lines += child.lines; } lineInner(target, isLine, line, offset) { for (let i = 0;; i++) { let child = this.children[i], end = offset + child.length, endLine = line + child.lines - 1; if ((isLine ? endLine : end) >= target) return child.lineInner(target, isLine, line, offset); offset = end + 1; line = endLine + 1; } } decompose(from, to, target, open) { for (let i = 0, pos = 0; pos <= to && i < this.children.length; i++) { let child = this.children[i], end = pos + child.length; if (from <= end && to >= pos) { let childOpen = open & ((pos <= from ? 1 : 0) | (end >= to ? 2 : 0)); if (pos >= from && end <= to && !childOpen) target.push(child); else child.decompose(from - pos, to - pos, target, childOpen); } pos = end + 1; } } replace(from, to, text) { [from, to] = clip(this, from, to); if (text.lines < this.lines) for (let i = 0, pos = 0; i < this.children.length; i++) { let child = this.children[i], end = pos + child.length; if (from >= pos && to <= end) { let updated = child.replace(from - pos, to - pos, text); let totalLines = this.lines - child.lines + updated.lines; if (updated.lines < (totalLines >> (5 - 1)) && updated.lines > (totalLines >> (5 + 1))) { let copy = this.children.slice(); copy[i] = updated; return new TextNode(copy, this.length - (to - from) + text.length); } return super.replace(pos, end, updated); } pos = end + 1; } return super.replace(from, to, text); } sliceString(from, to = this.length, lineSep = "\n") { [from, to] = clip(this, from, to); let result = ""; for (let i = 0, pos = 0; i < this.children.length && pos <= to; i++) { let child = this.children[i], end = pos + child.length; if (pos > from && i) result += lineSep; if (from < end && to > pos) result += child.sliceString(from - pos, to - pos, lineSep); pos = end + 1; } return result; } flatten(target) { for (let child of this.children) child.flatten(target); } scanIdentical(other, dir) { if (!(other instanceof TextNode)) return 0; let length = 0; let [iA, iB, eA, eB] = dir > 0 ? [0, 0, this.children.length, other.children.length] : [this.children.length - 1, other.children.length - 1, -1, -1]; for (;; iA += dir, iB += dir) { if (iA == eA || iB == eB) return length; let chA = this.children[iA], chB = other.children[iB]; if (chA != chB) return length + chA.scanIdentical(chB, dir); length += chA.length + 1; } } static from(children, length = children.reduce((l, ch) => l + ch.length + 1, -1)) { let lines = 0; for (let ch of children) lines += ch.lines; if (lines < 32 ) { let flat = []; for (let ch of children) ch.flatten(flat); return new TextLeaf(flat, length); } let chunk = Math.max(32 , lines >> 5 ), maxChunk = chunk << 1, minChunk = chunk >> 1; let chunked = [], currentLines = 0, currentLen = -1, currentChunk = []; function add(child) { let last; if (child.lines > maxChunk && child instanceof TextNode) { for (let node of child.children) add(node); } else if (child.lines > minChunk && (currentLines > minChunk || !currentLines)) { flush(); chunked.push(child); } else if (child instanceof TextLeaf && currentLines && (last = currentChunk[currentChunk.length - 1]) instanceof TextLeaf && child.lines + last.lines <= 32 ) { currentLines += child.lines; currentLen += child.length + 1; currentChunk[currentChunk.length - 1] = new TextLeaf(last.text.concat(child.text), last.length + 1 + child.length); } else { if (currentLines + child.lines > chunk) flush(); currentLines += child.lines; currentLen += child.length + 1; currentChunk.push(child); } } function flush() { if (currentLines == 0) return; chunked.push(currentChunk.length == 1 ? currentChunk[0] : TextNode.from(currentChunk, currentLen)); currentLen = -1; currentLines = currentChunk.length = 0; } for (let child of children) add(child); flush(); return chunked.length == 1 ? chunked[0] : new TextNode(chunked, length); } } Text.empty = new TextLeaf([""], 0); function textLength(text) { let length = -1; for (let line of text) length += line.length + 1; return length; } function appendText(text, target, from = 0, to = 1e9) { for (let pos = 0, i = 0, first = true; i < text.length && pos <= to; i++) { let line = text[i], end = pos + line.length; if (end >= from) { if (end > to) line = line.slice(0, to - pos); if (pos < from) line = line.slice(from - pos); if (first) { target[target.length - 1] += line; first = false; } else target.push(line); } pos = end + 1; } return target; } function sliceText(text, from, to) { return appendText(text, [""], from, to); } class RawTextCursor { constructor(text, dir = 1) { this.dir = dir; this.done = false; this.lineBreak = false; this.value = ""; this.nodes = [text]; this.offsets = [dir > 0 ? 1 : (text instanceof TextLeaf ? text.text.length : text.children.length) << 1]; } nextInner(skip, dir) { this.done = this.lineBreak = false; for (;;) { let last = this.nodes.length - 1; let top = this.nodes[last], offsetValue = this.offsets[last], offset = offsetValue >> 1; let size = top instanceof TextLeaf ? top.text.length : top.children.length; if (offset == (dir > 0 ? size : 0)) { if (last == 0) { this.done = true; this.value = ""; return this; } if (dir > 0) this.offsets[last - 1]++; this.nodes.pop(); this.offsets.pop(); } else if ((offsetValue & 1) == (dir > 0 ? 0 : 1)) { this.offsets[last] += dir; if (skip == 0) { this.lineBreak = true; this.value = "\n"; return this; } skip--; } else if (top instanceof TextLeaf) { let next = top.text[offset + (dir < 0 ? -1 : 0)]; this.offsets[last] += dir; if (next.length > Math.max(0, skip)) { this.value = skip == 0 ? next : dir > 0 ? next.slice(skip) : next.slice(0, next.length - skip); return this; } skip -= next.length; } else { let next = top.children[offset + (dir < 0 ? -1 : 0)]; if (skip > next.length) { skip -= next.length; this.offsets[last] += dir; } else { if (dir < 0) this.offsets[last]--; this.nodes.push(next); this.offsets.push(dir > 0 ? 1 : (next instanceof TextLeaf ? next.text.length : next.children.length) << 1); } } } } next(skip = 0) { if (skip < 0) { this.nextInner(-skip, (-this.dir)); skip = this.value.length; } return this.nextInner(skip, this.dir); } } class PartialTextCursor { constructor(text, start, end) { this.value = ""; this.done = false; this.cursor = new RawTextCursor(text, start > end ? -1 : 1); this.pos = start > end ? text.length : 0; this.from = Math.min(start, end); this.to = Math.max(start, end); } nextInner(skip, dir) { if (dir < 0 ? this.pos <= this.from : this.pos >= this.to) { this.value = ""; this.done = true; return this; } skip += Math.max(0, dir < 0 ? this.pos - this.to : this.from - this.pos); let limit = dir < 0 ? this.pos - this.from : this.to - this.pos; if (skip > limit) skip = limit; limit -= skip; let { value } = this.cursor.next(skip); this.pos += (value.length + skip) * dir; this.value = value.length <= limit ? value : dir < 0 ? value.slice(value.length - limit) : value.slice(0, limit); this.done = !this.value; return this; } next(skip = 0) { if (skip < 0) skip = Math.max(skip, this.from - this.pos); else if (skip > 0) skip = Math.min(skip, this.to - this.pos); return this.nextInner(skip, this.cursor.dir); } get lineBreak() { return this.cursor.lineBreak && this.value != ""; } } class LineCursor { constructor(inner) { this.inner = inner; this.afterBreak = true; this.value = ""; this.done = false; } next(skip = 0) { let { done, lineBreak, value } = this.inner.next(skip); if (done && this.afterBreak) { this.value = ""; this.afterBreak = false; } else if (done) { this.done = true; this.value = ""; } else if (lineBreak) { if (this.afterBreak) { this.value = ""; } else { this.afterBreak = true; this.next(); } } else { this.value = value; this.afterBreak = false; } return this; } get lineBreak() { return false; } } if (typeof Symbol != "undefined") { Text.prototype[Symbol.iterator] = function () { return this.iter(); }; RawTextCursor.prototype[Symbol.iterator] = PartialTextCursor.prototype[Symbol.iterator] = LineCursor.prototype[Symbol.iterator] = function () { return this; }; } class Line { constructor( from, to, number, text) { this.from = from; this.to = to; this.number = number; this.text = text; } get length() { return this.to - this.from; } } function clip(text, from, to) { from = Math.max(0, Math.min(text.length, from)); return [from, Math.max(from, Math.min(text.length, to))]; } let extend = "lc,34,7n,7,7b,19,,,,2,,2,,,20,b,1c,l,g,,2t,7,2,6,2,2,,4,z,,u,r,2j,b,1m,9,9,,o,4,,9,,3,,5,17,3,3b,f,,w,1j,,,,4,8,4,,3,7,a,2,t,,1m,,,,2,4,8,,9,,a,2,q,,2,2,1l,,4,2,4,2,2,3,3,,u,2,3,,b,2,1l,,4,5,,2,4,,k,2,m,6,,,1m,,,2,,4,8,,7,3,a,2,u,,1n,,,,c,,9,,14,,3,,1l,3,5,3,,4,7,2,b,2,t,,1m,,2,,2,,3,,5,2,7,2,b,2,s,2,1l,2,,,2,4,8,,9,,a,2,t,,20,,4,,2,3,,,8,,29,,2,7,c,8,2q,,2,9,b,6,22,2,r,,,,,,1j,e,,5,,2,5,b,,10,9,,2u,4,,6,,2,2,2,p,2,4,3,g,4,d,,2,2,6,,f,,jj,3,qa,3,t,3,t,2,u,2,1s,2,,7,8,,2,b,9,,19,3,3b,2,y,,3a,3,4,2,9,,6,3,63,2,2,,1m,,,7,,,,,2,8,6,a,2,,1c,h,1r,4,1c,7,,,5,,14,9,c,2,w,4,2,2,,3,1k,,,2,3,,,3,1m,8,2,2,48,3,,d,,7,4,,6,,3,2,5i,1m,,5,ek,,5f,x,2da,3,3x,,2o,w,fe,6,2x,2,n9w,4,,a,w,2,28,2,7k,,3,,4,,p,2,5,,47,2,q,i,d,,12,8,p,b,1a,3,1c,,2,4,2,2,13,,1v,6,2,2,2,2,c,,8,,1b,,1f,,,3,2,2,5,2,,,16,2,8,,6m,,2,,4,,fn4,,kh,g,g,g,a6,2,gt,,6a,,45,5,1ae,3,,2,5,4,14,3,4,,4l,2,fx,4,ar,2,49,b,4w,,1i,f,1k,3,1d,4,2,2,1x,3,10,5,,8,1q,,c,2,1g,9,a,4,2,,2n,3,2,,,2,6,,4g,,3,8,l,2,1l,2,,,,,m,,e,7,3,5,5f,8,2,3,,,n,,29,,2,6,,,2,,,2,,2,6j,,2,4,6,2,,2,r,2,2d,8,2,,,2,2y,,,,2,6,,,2t,3,2,4,,5,77,9,,2,6t,,a,2,,,4,,40,4,2,2,4,,w,a,14,6,2,4,8,,9,6,2,3,1a,d,,2,ba,7,,6,,,2a,m,2,7,,2,,2,3e,6,3,,,2,,7,,,20,2,3,,,,9n,2,f0b,5,1n,7,t4,,1r,4,29,,f5k,2,43q,,,3,4,5,8,8,2,7,u,4,44,3,1iz,1j,4,1e,8,,e,,m,5,,f,11s,7,,h,2,7,,2,,5,79,7,c5,4,15s,7,31,7,240,5,gx7k,2o,3k,6o".split(",").map(s => s ? parseInt(s, 36) : 1); for (let i = 1; i < extend.length; i++) extend[i] += extend[i - 1]; function isExtendingChar(code) { for (let i = 1; i < extend.length; i += 2) if (extend[i] > code) return extend[i - 1] <= code; return false; } function isRegionalIndicator(code) { return code >= 0x1F1E6 && code <= 0x1F1FF; } const ZWJ = 0x200d; function findClusterBreak(str, pos, forward = true, includeExtending = true) { return (forward ? nextClusterBreak : prevClusterBreak)(str, pos, includeExtending); } function nextClusterBreak(str, pos, includeExtending) { if (pos == str.length) return pos; if (pos && surrogateLow(str.charCodeAt(pos)) && surrogateHigh(str.charCodeAt(pos - 1))) pos--; let prev = codePointAt(str, pos); pos += codePointSize(prev); while (pos < str.length) { let next = codePointAt(str, pos); if (prev == ZWJ || next == ZWJ || includeExtending && isExtendingChar(next)) { pos += codePointSize(next); prev = next; } else if (isRegionalIndicator(next)) { let countBefore = 0, i = pos - 2; while (i >= 0 && isRegionalIndicator(codePointAt(str, i))) { countBefore++; i -= 2; } if (countBefore % 2 == 0) break; else pos += 2; } else { break; } } return pos; } function prevClusterBreak(str, pos, includeExtending) { while (pos > 0) { let found = nextClusterBreak(str, pos - 2, includeExtending); if (found < pos) return found; pos--; } return 0; } function surrogateLow(ch) { return ch >= 0xDC00 && ch < 0xE000; } function surrogateHigh(ch) { return ch >= 0xD800 && ch < 0xDC00; } function codePointAt(str, pos) { let code0 = str.charCodeAt(pos); if (!surrogateHigh(code0) || pos + 1 == str.length) return code0; let code1 = str.charCodeAt(pos + 1); if (!surrogateLow(code1)) return code0; return ((code0 - 0xd800) << 10) + (code1 - 0xdc00) + 0x10000; } function fromCodePoint(code) { if (code <= 0xffff) return String.fromCharCode(code); code -= 0x10000; return String.fromCharCode((code >> 10) + 0xd800, (code & 1023) + 0xdc00); } function codePointSize(code) { return code < 0x10000 ? 1 : 2; } const DefaultSplit = /\r\n?|\n/; var MapMode = (function (MapMode) { MapMode[MapMode["Simple"] = 0] = "Simple"; MapMode[MapMode["TrackDel"] = 1] = "TrackDel"; MapMode[MapMode["TrackBefore"] = 2] = "TrackBefore"; MapMode[MapMode["TrackAfter"] = 3] = "TrackAfter"; return MapMode})(MapMode || (MapMode = {})); class ChangeDesc { constructor( sections) { this.sections = sections; } get length() { let result = 0; for (let i = 0; i < this.sections.length; i += 2) result += this.sections[i]; return result; } 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; } get empty() { return this.sections.length == 0 || this.sections.length == 2 && this.sections[1] < 0; } 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; } } iterChangedRanges(f, individual = false) { iterChanges(this, f, individual); } 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); } composeDesc(other) { return this.empty ? other : other.empty ? this : composeSets(this, other); } mapDesc(other, before = false) { return other.empty ? this : mapSet(this, other, before); } mapPos(pos, assoc = -1, mode = 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 != MapMode.Simple && endA >= pos && (mode == MapMode.TrackDel && posA < pos && endA > pos || mode == MapMode.TrackBefore && posA < pos || mode == 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; } 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; } 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; } toJSON() { return this.sections; } static fromJSON(json) { if (!Array.isArray(json) || json.length % 2 || json.some(a => typeof a != "number")) throw new RangeError("Invalid JSON representation of ChangeDesc"); return new ChangeDesc(json); } static create(sections) { return new ChangeDesc(sections); } } class ChangeSet extends ChangeDesc { constructor(sections, inserted) { super(sections); this.inserted = inserted; } 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); } 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.empty); inserted.push(len ? doc.slice(pos, pos + len) : Text.empty); } pos += len; } return new ChangeSet(sections, inserted); } compose(other) { return this.empty ? other : other.empty ? this : composeSets(this, other, true); } map(other, before = false) { return other.empty ? this : mapSet(this, other, before, true); } iterChanges(f, individual = false) { iterChanges(this, f, individual); } get desc() { return ChangeDesc.create(this.sections); } 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: ChangeDesc.create(filteredSections) }; } 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; } 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.empty : typeof insert == "string" ? 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; } static empty(length) { return new ChangeSet(length ? [length, -1] : [], []); } 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.empty); inserted[i] = Text.of(part.slice(1)); sections.push(part[0], inserted[i].length); } } return new ChangeSet(sections, inserted); } static createSet(sections, inserted) { 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.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 = Text.empty; for (;;) { endA += len; endB += ins; if (ins && inserted) text = text.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); 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 inserted = -1;;) { if (a.ins == -1 && b.ins == -1) { let len = Math.min(a.len, b.len); addSection(sections, len, -1); a.forward(len); b.forward(len); } else if (b.ins >= 0 && (a.ins < 0 || inserted == a.i || a.off == 0 && (b.len < a.len || b.len == a.len && !before))) { let len = b.len; addSection(sections, b.ins, -1); while (len) { let piece = Math.min(a.len, len); if (a.ins >= 0 && inserted < a.i && a.len <= piece) { addSection(sections, 0, a.ins); if (insert) addInsert(insert, sections, a.text); inserted = a.i; } a.forward(piece); len -= piece; } b.next(); } else if (a.ins >= 0) { let len = 0, left = a.len; while (left) { if (b.ins == -1) { let piece = Math.min(left, b.len); len += piece; left -= piece; b.forward(piece); } else if (b.ins == 0 && b.len < left) { left -= b.len; b.next(); } else { break; } } addSection(sections, len, inserted < a.i ? a.ins : 0); if (insert && inserted < a.i) addInsert(insert, sections, a.text); inserted = a.i; a.forward(a.len - left); } else if (a.done && b.done) { return insert ? ChangeSet.createSet(sections, insert) : ChangeDesc.create(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 ? ChangeSet.createSet(sections, insert) : ChangeDesc.create(sections); } else if (a.ins == 0) { addSection(sections, a.len, 0, open); a.next(); } else if (b.len == 0 && !b.done) { 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.empty : inserted[index]; } textBit(len) { let { inserted } = this.set, index = (this.i - 2) >> 1; return index >= inserted.length && !len ? 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; } } } class SelectionRange { constructor( from, to, flags) { this.from = from; this.to = to; this.flags = flags; } get anchor() { return this.flags & 32 ? this.to : this.from; } get head() { return this.flags & 32 ? this.from : this.to; } get empty() { return this.from == this.to; } get assoc() { return this.flags & 8 ? -1 : this.flags & 16 ? 1 : 0; } get bidiLevel() { let level = this.flags & 7 ; return level == 7 ? null : level; } get goalColumn() { let value = this.flags >> 6 ; return value == 16777215 ? undefined : value; } map(change, assoc = -1) { let from, to; if (this.empty) { from = to = change.mapPos(this.from, assoc); } else { from = change.mapPos(this.from, 1); to = change.mapPos(this.to, -1); } return from == this.from && to == this.to ? this : new SelectionRange(from, to, this.flags); } 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); } eq(other, includeAssoc = false) { return this.anchor == other.anchor && this.head == other.head && (!includeAssoc || !this.empty || this.assoc == other.assoc); } toJSON() { return { anchor: this.anchor, head: this.head }; } 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); } static create(from, to, flags) { return new SelectionRange(from, to, flags); } } class EditorSelection { constructor( ranges, mainIndex) { this.ranges = ranges; this.mainIndex = mainIndex; } map(change, assoc = -1) { if (change.empty) return this; return EditorSelection.create(this.ranges.map(r => r.map(change, assoc)), this.mainIndex); } eq(other, includeAssoc = false) { 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], includeAssoc)) return false; return true; } get main() { return this.ranges[this.mainIndex]; } asSingle() { return this.ranges.length == 1 ? this : new EditorSelection([this.main], 0); } addRange(range, main = true) { return EditorSelection.create([range].concat(this.ranges), main ? 0 : this.mainIndex + 1); } replaceRange(range, which = this.mainIndex) { let ranges = this.ranges.slice(); ranges[which] = range; return EditorSelection.create(ranges, this.mainIndex); } toJSON() { return { ranges: this.ranges.map(r => r.toJSON()), main: this.mainIndex }; } 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); } static single(anchor, head = anchor) { return new EditorSelection([EditorSelection.range(anchor, head)], 0); } 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 EditorSelection.normalized(ranges.slice(), mainIndex); pos = range.to; } return new EditorSelection(ranges, mainIndex); } static cursor(pos, assoc = 0, bidiLevel, goalColumn) { return SelectionRange.create(pos, pos, (assoc == 0 ? 0 : assoc < 0 ? 8 : 16 ) | (bidiLevel == null ? 7 : Math.min(6, bidiLevel)) | ((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 16777215 ) << 6 )); } static range(anchor, head, goalColumn, bidiLevel) { let flags = ((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 16777215 ) << 6 ) | (bidiLevel == null ? 7 : Math.min(6, bidiLevel)); return head < anchor ? SelectionRange.create(head, anchor, 32 | 16 | flags) : SelectionRange.create(anchor, head, (head > anchor ? 8 : 0) | flags); } static 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; class Facet { constructor( combine, compareInput, compare, isStatic, enables) { this.combine = combine; this.compareInput = compareInput; this.compare = compare; this.isStatic = isStatic; this.id = nextID++; this.default = combine([]); this.extensions = typeof enables == "function" ? enables(this) : enables; } get reader() { return this; } static define(config = {}) { return new Facet(config.combine || ((a) => a), config.compareInput || ((a, b) => a === b), config.compare || (!config.combine ? sameArray$1 : (a, b) => a === b), !!config.static, config.enables); } of(value) { return new FacetProvider([], this, 0 , value); } compute(deps, get) { if (this.isStatic) throw new Error("Can't compute a static facet"); return new FacetProvider(deps, this, 1 , get); } computeN(deps, get) { if (this.isStatic) throw new Error("Can't compute a static facet"); return new FacetProvider(deps, this, 2 , get); } from(field, get) { if (!get) get = x => x; return this.compute([field], state => get(state.field(field))); } } function sameArray$1(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 id = this.id, idx = addresses[id] >> 1, multi = this.type == 2 ; 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 { create(state) { state.values[idx] = getter(state); return 1 ; }, update(state, tr) { if ((depDoc && tr.docChanged) || (depSel && (tr.docChanged || tr.selection)) || ensureAll(state, depAddrs)) { let newVal = getter(state); if (multi ? !compareArray(newVal, state.values[idx], compare) : !compare(newVal, state.values[idx])) { state.values[idx] = newVal; return 1 ; } } return 0; }, reconfigure: (state, oldState) => { let newVal, oldAddr = oldState.config.address[id]; if (oldAddr != null) { let oldVal = getAddr(oldState, oldAddr); if (this.dependencies.every(dep => { return dep instanceof Facet ? oldState.facet(dep) === state.facet(dep) : dep instanceof StateField ? oldState.field(dep, false) == state.field(dep, false) : true; }) || (multi ? compareArray(newVal = getter(state), oldVal, compare) : compare(newVal = getter(state), oldVal))) { state.values[idx] = oldVal; return 0; } } else { newVal = getter(state); } state.values[idx] = newVal; return 1 ; } }; } } 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 ensureAll(state, addrs) { let changed = false; for (let addr of addrs) if (ensureAddr(state, addr) & 1 ) changed = true; return changed; } 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; function get(state) { let values = []; for (let i = 0; i < providerAddrs.length; i++) { let value = getAddr(state, providerAddrs[i]); if (providerTypes[i] == 2 ) for (let val of value) values.push(val); else values.push(value); } return facet.combine(values); } return { create(state) { for (let addr of providerAddrs) ensureAddr(state, addr); state.values[idx] = get(state); return 1 ; }, update(state, tr) { if (!ensureAll(state, dynamic)) return 0; let value = get(state); if (facet.compare(value, state.values[idx])) return 0; state.values[idx] = value; return 1 ; }, reconfigure(state, oldState) { let depChanged = ensureAll(state, providerAddrs); let oldProviders = oldState.config.facets[facet.id], oldValue = oldState.facet(facet); if (oldProviders && !depChanged && sameArray$1(providers, oldProviders)) { state.values[idx] = oldValue; re