diff --git a/thinks/static/thinks/code-editor.mjs b/thinks/static/thinks/code-editor.mjs index 508a14f..715e75f 100644 --- a/thinks/static/thinks/code-editor.mjs +++ b/thinks/static/thinks/code-editor.mjs @@ -1,3 +1,84 @@ +// These are filled with ranges (rangeFrom[i] up to but not including +// rangeTo[i]) of code points that count as extending characters. +let rangeFrom = [], rangeTo = [] + +;(() => { + // Compressed representation of the Grapheme_Cluster_Break=Extend + // information from + // http://www.unicode.org/Public/16.0.0/ucd/auxiliary/GraphemeBreakProperty.txt. + // Each pair of elements represents a range, as an offet from the + // previous range and a length. Numbers are in base-36, with the empty + // string being a shorthand for 1. + let numbers = "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 = 0, n = 0; i < numbers.length; i++) + (i % 2 ? rangeTo : rangeFrom).push(n = n + numbers[i]); +})(); + +function isExtendingChar(code) { + if (code < 768) return false + for (let from = 0, to = rangeFrom.length;;) { + let mid = (from + to) >> 1; + if (code < rangeFrom[mid]) to = mid; + else if (code >= rangeTo[mid]) from = mid + 1; + else return true + if (from == to) return false + } +} + +function isRegionalIndicator(code) { + return code >= 0x1F1E6 && code <= 0x1F1FF +} + +const ZWJ = 0x200d; + +function findClusterBreak$1(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 is in the middle of a surrogate pair, move to its start + if (pos && surrogateLow$1(str.charCodeAt(pos)) && surrogateHigh$1(str.charCodeAt(pos - 1))) pos--; + let prev = codePointAt$1(str, pos); + pos += codePointSize$1(prev); + while (pos < str.length) { + let next = codePointAt$1(str, pos); + if (prev == ZWJ || next == ZWJ || includeExtending && isExtendingChar(next)) { + pos += codePointSize$1(next); + prev = next; + } else if (isRegionalIndicator(next)) { + let countBefore = 0, i = pos - 2; + while (i >= 0 && isRegionalIndicator(codePointAt$1(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 codePointAt$1(str, pos) { + let code0 = str.charCodeAt(pos); + if (!surrogateHigh$1(code0) || pos + 1 == str.length) return code0 + let code1 = str.charCodeAt(pos + 1); + if (!surrogateLow$1(code1)) return code0 + return ((code0 - 0xd800) << 10) + (code1 - 0xdc00) + 0x10000 +} + +function surrogateLow$1(ch) { return ch >= 0xDC00 && ch < 0xE000 } +function surrogateHigh$1(ch) { return ch >= 0xD800 && ch < 0xDC00 } +function codePointSize$1(code) { return code < 0x10000 ? 1 : 2 } + /** The data structure for documents. @nonabstract */ @@ -560,26 +641,6 @@ function clip(text, from, to) { return [from, Math.max(from, Math.min(text.length, to))]; } -// Compressed representation of the Grapheme_Cluster_Break=Extend -// information from -// http://www.unicode.org/Public/13.0.0/ucd/auxiliary/GraphemeBreakProperty.txt. -// Each pair of elements represents a range, as an offet from the -// previous range and a length. Numbers are in base-36, with the empty -// string being a shorthand for 1. -let extend = /*@__PURE__*/"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); -// Convert offsets into absolute values -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; /** Returns a next grapheme cluster break _after_ (not equal to) `pos`, if `forward` is true, or before otherwise. Returns `pos` @@ -589,47 +650,7 @@ Moves across surrogate pairs, extending characters (when joiners, and flag emoji. */ 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 is in the middle of a surrogate pair, move to its start - 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; + return findClusterBreak$1(str, pos, forward, includeExtending); } function surrogateLow(ch) { return ch >= 0xDC00 && ch < 0xE000; } function surrogateHigh(ch) { return ch >= 0xD800 && ch < 0xDC00; } @@ -659,7 +680,7 @@ function fromCodePoint(code) { return String.fromCharCode((code >> 10) + 0xd800, (code & 1023) + 0xdc00); } /** -The amount of positions a character takes up a JavaScript string. +The amount of positions a character takes up in a JavaScript string. */ function codePointSize(code) { return code < 0x10000 ? 1 : 2; } @@ -788,7 +809,7 @@ class ChangeDesc { 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`. + in `this` happened before the ones in `other`. */ mapDesc(other, before = false) { return other.empty ? this : mapSet(this, other, before); } mapPos(pos, assoc = -1, mode = MapMode.Simple) { @@ -1091,7 +1112,7 @@ function addSection(sections, len, ins, forceJoin = false) { let last = sections.length - 2; if (last >= 0 && ins <= 0 && ins == sections[last + 1]) sections[last] += len; - else if (len == 0 && sections[last] == 0) + else if (last >= 0 && len == 0 && sections[last] == 0) sections[last + 1] += ins; else if (forceJoin) { sections[last] += len; @@ -1149,7 +1170,10 @@ function mapSet(setA, setB, before, mkSet = false) { // content has been inserted already, and refers to the section // index. for (let inserted = -1;;) { - if (a.ins == -1 && b.ins == -1) { + if (a.done && b.len || b.done && a.len) { + throw new Error("Mismatched change set lengths"); + } + else if (a.ins == -1 && b.ins == -1) { // Move across ranges skipped by both sets. let len = Math.min(a.len, b.len); addSection(sections, len, -1); @@ -1829,6 +1853,11 @@ class StateField { return 1 /* SlotStatus.Changed */; }, reconfigure: (state, oldState) => { + let init = state.facet(initField), oldInit = oldState.facet(initField), reInit; + if ((reInit = init.find(i => i.field == this)) && reInit != oldInit.find(i => i.field == this)) { + state.values[idx] = reInit.create(state); + return 1 /* SlotStatus.Changed */; + } if (oldState.config.address[this.id] != null) { state.values[idx] = oldState.field(this); return 0; @@ -3841,7 +3870,7 @@ function compare(a, startA, b, startB, length, comparator) { let endB = startB + length; let pos = startB, dPos = startB - startA; for (;;) { - let diff = (a.to + dPos) - b.to || a.endSide - b.endSide; + let dEnd = (a.to + dPos) - b.to, diff = dEnd || a.endSide - b.endSide; let end = diff < 0 ? a.to + dPos : b.to, clipEnd = Math.min(end, endB); if (a.point || b.point) { if (!(a.point && b.point && (a.point == b.point || a.point.eq(b.point)) && @@ -3854,6 +3883,8 @@ function compare(a, startA, b, startB, length, comparator) { } if (end > endB) break; + if ((dEnd || a.openEnd != b.openEnd) && comparator.boundChange) + comparator.boundChange(end); pos = end; if (diff <= 0) a.next(); @@ -3895,7 +3926,7 @@ taking extending characters and tab size into account. */ function countColumn(string, tabSize, to = string.length) { let n = 0; - for (let i = 0; i < to;) { + for (let i = 0; i < to && i < string.length;) { if (string.charCodeAt(i) == 9) { n += tabSize - (n % tabSize); i++; @@ -4235,12 +4266,6 @@ function getSelection(root) { function contains(dom, node) { return node ? dom == node || dom.contains(node.nodeType != 1 ? node.parentNode : node) : false; } -function deepActiveElement(doc) { - let elt = doc.activeElement; - while (elt && elt.shadowRoot) - elt = elt.shadowRoot.activeElement; - return elt; -} function hasSelection(dom, selection) { if (!selection.anchorNode) return false; @@ -4276,6 +4301,9 @@ function domIndex(node) { return index; } } +function isBlockElement(node) { + return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName); +} function scanFor(node, off, targetNode, targetOff, dir) { for (;;) { if (node == targetNode && off == targetOff) @@ -4308,6 +4336,12 @@ function flattenRect(rect, left) { return { left: x, right: x, top: rect.top, bottom: rect.bottom }; } function windowRect(win) { + let vp = win.visualViewport; + if (vp) + return { + left: 0, right: vp.width, + top: 0, bottom: vp.height + }; return { left: 0, right: win.innerWidth, top: 0, bottom: win.innerHeight }; } @@ -4416,15 +4450,17 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) { } } } -function scrollableParent(dom) { - let doc = dom.ownerDocument; +function scrollableParents(dom) { + let doc = dom.ownerDocument, x, y; for (let cur = dom.parentNode; cur;) { - if (cur == doc.body) { + if (cur == doc.body || (x && y)) { break; } else if (cur.nodeType == 1) { - if (cur.scrollHeight > cur.clientHeight || cur.scrollWidth > cur.clientWidth) - return cur; + if (!y && cur.scrollHeight > cur.clientHeight) + y = cur; + if (!x && cur.scrollWidth > cur.clientWidth) + x = cur; cur = cur.assignedSlot || cur.parentNode; } else if (cur.nodeType == 11) { @@ -4434,7 +4470,7 @@ function scrollableParent(dom) { break; } } - return null; + return { x, y }; } class DOMSelectionState { constructor() { @@ -4497,8 +4533,10 @@ function textRange(node, from, to = from) { range.setStart(node, from); return range; } -function dispatchKey(elt, name, code) { +function dispatchKey(elt, name, code, mods) { let options = { key: name, code: name, keyCode: code, which: code, cancelable: true }; + if (mods) + ({ altKey: options.altKey, ctrlKey: options.ctrlKey, shiftKey: options.shiftKey, metaKey: options.metaKey } = mods); let down = new KeyboardEvent("keydown", options); down.synthetic = true; elt.dispatchEvent(down); @@ -4549,6 +4587,46 @@ function atElementStart(doc, selection) { function isScrolledToBottom(elt) { return elt.scrollTop > Math.max(1, elt.scrollHeight - elt.clientHeight - 4); } +function textNodeBefore(startNode, startOffset) { + for (let node = startNode, offset = startOffset;;) { + if (node.nodeType == 3 && offset > 0) { + return { node: node, offset: offset }; + } + else if (node.nodeType == 1 && offset > 0) { + if (node.contentEditable == "false") + return null; + node = node.childNodes[offset - 1]; + offset = maxOffset(node); + } + else if (node.parentNode && !isBlockElement(node)) { + offset = domIndex(node); + node = node.parentNode; + } + else { + return null; + } + } +} +function textNodeAfter(startNode, startOffset) { + for (let node = startNode, offset = startOffset;;) { + if (node.nodeType == 3 && offset < node.nodeValue.length) { + return { node: node, offset: offset }; + } + else if (node.nodeType == 1 && offset < node.childNodes.length) { + if (node.contentEditable == "false") + return null; + node = node.childNodes[offset]; + offset = 0; + } + else if (node.parentNode && !isBlockElement(node)) { + offset = domIndex(node) + 1; + node = node.parentNode; + } + else { + return null; + } + } +} class DOMPos { constructor(node, offset, precise = true) { @@ -4729,7 +4807,10 @@ class ContentView { if (child.parent == this && children.indexOf(child) < 0) child.destroy(); } - this.children.splice(from, to - from, ...children); + if (children.length < 250) + this.children.splice(from, to - from, ...children); + else + this.children = [].concat(this.children.slice(0, from), children, this.children.slice(to)); for (let i = 0; i < children.length; i++) children[i].setParent(this); } @@ -4909,7 +4990,7 @@ var browser = { android: /*@__PURE__*//Android\b/.test(nav.userAgent), webkit, safari, - webkit_version: webkit ? +(/*@__PURE__*//\bAppleWebKit\/(\d+)/.exec(navigator.userAgent) || [0, 0])[1] : 0, + webkit_version: webkit ? +(/*@__PURE__*//\bAppleWebKit\/(\d+)/.exec(nav.userAgent) || [0, 0])[1] : 0, tabSize: doc.documentElement.style.tabSize != null ? "tab-size" : "-moz-tab-size" }; @@ -5271,14 +5352,14 @@ function combineAttrs(source, target) { } return target; } -const noAttrs = /*@__PURE__*/Object.create(null); +const noAttrs$1 = /*@__PURE__*/Object.create(null); function attrsEq(a, b, ignore) { if (a == b) return true; if (!a) - a = noAttrs; + a = noAttrs$1; if (!b) - b = noAttrs; + b = noAttrs$1; let keysA = Object.keys(a), keysB = Object.keys(b); if (keysA.length - (ignore && keysA.indexOf(ignore) > -1 ? 1 : 0) != keysB.length - (ignore && keysB.indexOf(ignore) > -1 ? 1 : 0)) @@ -5320,237 +5401,6 @@ function getAttrs(dom) { return attrs; } -class LineView extends ContentView { - constructor() { - super(...arguments); - this.children = []; - this.length = 0; - this.prevAttrs = undefined; - this.attrs = null; - this.breakAfter = 0; - } - // Consumes source - merge(from, to, source, hasStart, openStart, openEnd) { - if (source) { - if (!(source instanceof LineView)) - return false; - if (!this.dom) - source.transferDOM(this); // Reuse source.dom when appropriate - } - if (hasStart) - this.setDeco(source ? source.attrs : null); - mergeChildrenInto(this, from, to, source ? source.children.slice() : [], openStart, openEnd); - return true; - } - split(at) { - let end = new LineView; - end.breakAfter = this.breakAfter; - if (this.length == 0) - return end; - let { i, off } = this.childPos(at); - if (off) { - end.append(this.children[i].split(off), 0); - this.children[i].merge(off, this.children[i].length, null, false, 0, 0); - i++; - } - for (let j = i; j < this.children.length; j++) - end.append(this.children[j], 0); - while (i > 0 && this.children[i - 1].length == 0) - this.children[--i].destroy(); - this.children.length = i; - this.markDirty(); - this.length = at; - return end; - } - transferDOM(other) { - if (!this.dom) - return; - this.markDirty(); - other.setDOM(this.dom); - other.prevAttrs = this.prevAttrs === undefined ? this.attrs : this.prevAttrs; - this.prevAttrs = undefined; - this.dom = null; - } - setDeco(attrs) { - if (!attrsEq(this.attrs, attrs)) { - if (this.dom) { - this.prevAttrs = this.attrs; - this.markDirty(); - } - this.attrs = attrs; - } - } - append(child, openStart) { - joinInlineInto(this, child, openStart); - } - // Only called when building a line view in ContentBuilder - addLineDeco(deco) { - let attrs = deco.spec.attributes, cls = deco.spec.class; - if (attrs) - this.attrs = combineAttrs(attrs, this.attrs || {}); - if (cls) - this.attrs = combineAttrs({ class: cls }, this.attrs || {}); - } - domAtPos(pos) { - return inlineDOMAtPos(this, pos); - } - reuseDOM(node) { - if (node.nodeName == "DIV") { - this.setDOM(node); - this.flags |= 4 /* ViewFlag.AttrsDirty */ | 2 /* ViewFlag.NodeDirty */; - } - } - sync(view, track) { - var _a; - if (!this.dom) { - this.setDOM(document.createElement("div")); - this.dom.className = "cm-line"; - this.prevAttrs = this.attrs ? null : undefined; - } - else if (this.flags & 4 /* ViewFlag.AttrsDirty */) { - clearAttributes(this.dom); - this.dom.className = "cm-line"; - this.prevAttrs = this.attrs ? null : undefined; - } - if (this.prevAttrs !== undefined) { - updateAttrs(this.dom, this.prevAttrs, this.attrs); - this.dom.classList.add("cm-line"); - this.prevAttrs = undefined; - } - super.sync(view, track); - let last = this.dom.lastChild; - while (last && ContentView.get(last) instanceof MarkView) - last = last.lastChild; - if (!last || !this.length || - last.nodeName != "BR" && ((_a = ContentView.get(last)) === null || _a === void 0 ? void 0 : _a.isEditable) == false && - (!browser.ios || !this.children.some(ch => ch instanceof TextView))) { - let hack = document.createElement("BR"); - hack.cmIgnore = true; - this.dom.appendChild(hack); - } - } - measureTextSize() { - if (this.children.length == 0 || this.length > 20) - return null; - let totalWidth = 0, textHeight; - for (let child of this.children) { - if (!(child instanceof TextView) || /[^ -~]/.test(child.text)) - return null; - let rects = clientRectsFor(child.dom); - if (rects.length != 1) - return null; - totalWidth += rects[0].width; - textHeight = rects[0].height; - } - return !totalWidth ? null : { - lineHeight: this.dom.getBoundingClientRect().height, - charWidth: totalWidth / this.length, - textHeight - }; - } - coordsAt(pos, side) { - let rect = coordsInChildren(this, pos, side); - // Correct rectangle height for empty lines when the returned - // height is larger than the text height. - if (!this.children.length && rect && this.parent) { - let { heightOracle } = this.parent.view.viewState, height = rect.bottom - rect.top; - if (Math.abs(height - heightOracle.lineHeight) < 2 && heightOracle.textHeight < height) { - let dist = (height - heightOracle.textHeight) / 2; - return { top: rect.top + dist, bottom: rect.bottom - dist, left: rect.left, right: rect.left }; - } - } - return rect; - } - become(_other) { return false; } - covers() { return true; } - static find(docView, pos) { - for (let i = 0, off = 0; i < docView.children.length; i++) { - let block = docView.children[i], end = off + block.length; - if (end >= pos) { - if (block instanceof LineView) - return block; - if (end > pos) - break; - } - off = end + block.breakAfter; - } - return null; - } -} -class BlockWidgetView extends ContentView { - constructor(widget, length, deco) { - super(); - this.widget = widget; - this.length = length; - this.deco = deco; - this.breakAfter = 0; - this.prevWidget = null; - } - merge(from, to, source, _takeDeco, openStart, openEnd) { - if (source && (!(source instanceof BlockWidgetView) || !this.widget.compare(source.widget) || - from > 0 && openStart <= 0 || to < this.length && openEnd <= 0)) - return false; - this.length = from + (source ? source.length : 0) + (this.length - to); - return true; - } - domAtPos(pos) { - return pos == 0 ? DOMPos.before(this.dom) : DOMPos.after(this.dom, pos == this.length); - } - split(at) { - let len = this.length - at; - this.length = at; - let end = new BlockWidgetView(this.widget, len, this.deco); - end.breakAfter = this.breakAfter; - return end; - } - get children() { return noChildren; } - sync(view) { - if (!this.dom || !this.widget.updateDOM(this.dom, view)) { - if (this.dom && this.prevWidget) - this.prevWidget.destroy(this.dom); - this.prevWidget = null; - this.setDOM(this.widget.toDOM(view)); - if (!this.widget.editable) - this.dom.contentEditable = "false"; - } - } - get overrideDOMText() { - return this.parent ? this.parent.view.state.doc.slice(this.posAtStart, this.posAtEnd) : Text.empty; - } - domBoundsAround() { return null; } - become(other) { - if (other instanceof BlockWidgetView && - other.widget.constructor == this.widget.constructor) { - if (!other.widget.compare(this.widget)) - this.markDirty(true); - if (this.dom && !this.prevWidget) - this.prevWidget = this.widget; - this.widget = other.widget; - this.length = other.length; - this.deco = other.deco; - this.breakAfter = other.breakAfter; - return true; - } - return false; - } - ignoreMutation() { return true; } - ignoreEvent(event) { return this.widget.ignoreEvent(event); } - get isEditable() { return false; } - get isWidget() { return true; } - coordsAt(pos, side) { - return this.widget.coordsAt(this.dom, pos, side); - } - destroy() { - super.destroy(); - if (this.dom) - this.widget.destroy(this.dom); - } - covers(side) { - let { startSide, endSide } = this.deco; - return startSide == endSide ? false : side < 0 ? startSide < 0 : endSide > 0; - } -} - /** Widgets added to the content are described by subclasses of this class. Using a description object like that makes it possible to @@ -5835,6 +5685,265 @@ function addRange(from, to, ranges, margin = 0) { ranges.push(from, to); } +class LineView extends ContentView { + constructor() { + super(...arguments); + this.children = []; + this.length = 0; + this.prevAttrs = undefined; + this.attrs = null; + this.breakAfter = 0; + } + // Consumes source + merge(from, to, source, hasStart, openStart, openEnd) { + if (source) { + if (!(source instanceof LineView)) + return false; + if (!this.dom) + source.transferDOM(this); // Reuse source.dom when appropriate + } + if (hasStart) + this.setDeco(source ? source.attrs : null); + mergeChildrenInto(this, from, to, source ? source.children.slice() : [], openStart, openEnd); + return true; + } + split(at) { + let end = new LineView; + end.breakAfter = this.breakAfter; + if (this.length == 0) + return end; + let { i, off } = this.childPos(at); + if (off) { + end.append(this.children[i].split(off), 0); + this.children[i].merge(off, this.children[i].length, null, false, 0, 0); + i++; + } + for (let j = i; j < this.children.length; j++) + end.append(this.children[j], 0); + while (i > 0 && this.children[i - 1].length == 0) + this.children[--i].destroy(); + this.children.length = i; + this.markDirty(); + this.length = at; + return end; + } + transferDOM(other) { + if (!this.dom) + return; + this.markDirty(); + other.setDOM(this.dom); + other.prevAttrs = this.prevAttrs === undefined ? this.attrs : this.prevAttrs; + this.prevAttrs = undefined; + this.dom = null; + } + setDeco(attrs) { + if (!attrsEq(this.attrs, attrs)) { + if (this.dom) { + this.prevAttrs = this.attrs; + this.markDirty(); + } + this.attrs = attrs; + } + } + append(child, openStart) { + joinInlineInto(this, child, openStart); + } + // Only called when building a line view in ContentBuilder + addLineDeco(deco) { + let attrs = deco.spec.attributes, cls = deco.spec.class; + if (attrs) + this.attrs = combineAttrs(attrs, this.attrs || {}); + if (cls) + this.attrs = combineAttrs({ class: cls }, this.attrs || {}); + } + domAtPos(pos) { + return inlineDOMAtPos(this, pos); + } + reuseDOM(node) { + if (node.nodeName == "DIV") { + this.setDOM(node); + this.flags |= 4 /* ViewFlag.AttrsDirty */ | 2 /* ViewFlag.NodeDirty */; + } + } + sync(view, track) { + var _a; + if (!this.dom) { + this.setDOM(document.createElement("div")); + this.dom.className = "cm-line"; + this.prevAttrs = this.attrs ? null : undefined; + } + else if (this.flags & 4 /* ViewFlag.AttrsDirty */) { + clearAttributes(this.dom); + this.dom.className = "cm-line"; + this.prevAttrs = this.attrs ? null : undefined; + } + if (this.prevAttrs !== undefined) { + updateAttrs(this.dom, this.prevAttrs, this.attrs); + this.dom.classList.add("cm-line"); + this.prevAttrs = undefined; + } + super.sync(view, track); + let last = this.dom.lastChild; + while (last && ContentView.get(last) instanceof MarkView) + last = last.lastChild; + if (!last || !this.length || + last.nodeName != "BR" && ((_a = ContentView.get(last)) === null || _a === void 0 ? void 0 : _a.isEditable) == false && + (!browser.ios || !this.children.some(ch => ch instanceof TextView))) { + let hack = document.createElement("BR"); + hack.cmIgnore = true; + this.dom.appendChild(hack); + } + } + measureTextSize() { + if (this.children.length == 0 || this.length > 20) + return null; + let totalWidth = 0, textHeight; + for (let child of this.children) { + if (!(child instanceof TextView) || /[^ -~]/.test(child.text)) + return null; + let rects = clientRectsFor(child.dom); + if (rects.length != 1) + return null; + totalWidth += rects[0].width; + textHeight = rects[0].height; + } + return !totalWidth ? null : { + lineHeight: this.dom.getBoundingClientRect().height, + charWidth: totalWidth / this.length, + textHeight + }; + } + coordsAt(pos, side) { + let rect = coordsInChildren(this, pos, side); + // Correct rectangle height for empty lines when the returned + // height is larger than the text height. + if (!this.children.length && rect && this.parent) { + let { heightOracle } = this.parent.view.viewState, height = rect.bottom - rect.top; + if (Math.abs(height - heightOracle.lineHeight) < 2 && heightOracle.textHeight < height) { + let dist = (height - heightOracle.textHeight) / 2; + return { top: rect.top + dist, bottom: rect.bottom - dist, left: rect.left, right: rect.left }; + } + } + return rect; + } + become(other) { + return other instanceof LineView && this.children.length == 0 && other.children.length == 0 && + attrsEq(this.attrs, other.attrs) && this.breakAfter == other.breakAfter; + } + covers() { return true; } + static find(docView, pos) { + for (let i = 0, off = 0; i < docView.children.length; i++) { + let block = docView.children[i], end = off + block.length; + if (end >= pos) { + if (block instanceof LineView) + return block; + if (end > pos) + break; + } + off = end + block.breakAfter; + } + return null; + } +} +class BlockWidgetView extends ContentView { + constructor(widget, length, deco) { + super(); + this.widget = widget; + this.length = length; + this.deco = deco; + this.breakAfter = 0; + this.prevWidget = null; + } + merge(from, to, source, _takeDeco, openStart, openEnd) { + if (source && (!(source instanceof BlockWidgetView) || !this.widget.compare(source.widget) || + from > 0 && openStart <= 0 || to < this.length && openEnd <= 0)) + return false; + this.length = from + (source ? source.length : 0) + (this.length - to); + return true; + } + domAtPos(pos) { + return pos == 0 ? DOMPos.before(this.dom) : DOMPos.after(this.dom, pos == this.length); + } + split(at) { + let len = this.length - at; + this.length = at; + let end = new BlockWidgetView(this.widget, len, this.deco); + end.breakAfter = this.breakAfter; + return end; + } + get children() { return noChildren; } + sync(view) { + if (!this.dom || !this.widget.updateDOM(this.dom, view)) { + if (this.dom && this.prevWidget) + this.prevWidget.destroy(this.dom); + this.prevWidget = null; + this.setDOM(this.widget.toDOM(view)); + if (!this.widget.editable) + this.dom.contentEditable = "false"; + } + } + get overrideDOMText() { + return this.parent ? this.parent.view.state.doc.slice(this.posAtStart, this.posAtEnd) : Text.empty; + } + domBoundsAround() { return null; } + become(other) { + if (other instanceof BlockWidgetView && + other.widget.constructor == this.widget.constructor) { + if (!other.widget.compare(this.widget)) + this.markDirty(true); + if (this.dom && !this.prevWidget) + this.prevWidget = this.widget; + this.widget = other.widget; + this.length = other.length; + this.deco = other.deco; + this.breakAfter = other.breakAfter; + return true; + } + return false; + } + ignoreMutation() { return true; } + ignoreEvent(event) { return this.widget.ignoreEvent(event); } + get isEditable() { return false; } + get isWidget() { return true; } + coordsAt(pos, side) { + let custom = this.widget.coordsAt(this.dom, pos, side); + if (custom) + return custom; + if (this.widget instanceof BlockGapWidget) + return null; + return flattenRect(this.dom.getBoundingClientRect(), this.length ? pos == 0 : side <= 0); + } + destroy() { + super.destroy(); + if (this.dom) + this.widget.destroy(this.dom); + } + covers(side) { + let { startSide, endSide } = this.deco; + return startSide == endSide ? false : side < 0 ? startSide < 0 : endSide > 0; + } +} +class BlockGapWidget extends WidgetType { + constructor(height) { + super(); + this.height = height; + } + toDOM() { + let elt = document.createElement("div"); + elt.className = "cm-gap"; + this.updateDOM(elt); + return elt; + } + eq(other) { return other.height == this.height; } + updateDOM(elt) { + elt.style.height = this.height + "px"; + return true; + } + get editable() { return true; } + get estimatedHeight() { return this.height; } + ignoreEvent() { return false; } +} + class ContentBuilder { constructor(doc, pos, end, disallowBlockEffectsFor) { this.doc = doc; @@ -5940,10 +6049,10 @@ class ContentBuilder { if (deco.block) { if (deco.startSide > 0 && !this.posCovered()) this.getLine(); - this.addBlockWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len, deco)); + this.addBlockWidget(new BlockWidgetView(deco.widget || NullWidget.block, len, deco)); } else { - let view = WidgetView.create(deco.widget || new NullWidget("span"), len, len ? 0 : deco.startSide); + let view = WidgetView.create(deco.widget || NullWidget.inline, len, len ? 0 : deco.startSide); let cursorBefore = this.atCursorPos && !view.isEditable && openStart <= active.length && (from < to || deco.startSide > 0); let cursorAfter = !view.isEditable && (from < to || openStart > active.length || deco.startSide <= 0); @@ -6004,6 +6113,8 @@ class NullWidget extends WidgetType { updateDOM(elt) { return elt.nodeName.toLowerCase() == this.tag; } get isHidden() { return true; } } +NullWidget.inline = /*@__PURE__*/new NullWidget("span"); +NullWidget.block = /*@__PURE__*/new NullWidget("div"); /** Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection). @@ -6495,12 +6606,15 @@ const exceptionSink = /*@__PURE__*/Facet.define(); const updateListener = /*@__PURE__*/Facet.define(); const inputHandler$1 = /*@__PURE__*/Facet.define(); const focusChangeEffect = /*@__PURE__*/Facet.define(); +const clipboardInputFilter = /*@__PURE__*/Facet.define(); +const clipboardOutputFilter = /*@__PURE__*/Facet.define(); const perLineTextDirection = /*@__PURE__*/Facet.define({ combine: values => values.some(x => x) }); const nativeSelectionHidden = /*@__PURE__*/Facet.define({ combine: values => values.some(x => x) }); +const scrollHandler = /*@__PURE__*/Facet.define(); class ScrollTarget { constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5, // This data structure is abused to also store precise scroll @@ -6527,6 +6641,7 @@ class ScrollTarget { } } const scrollIntoView$1 = /*@__PURE__*/StateEffect.define({ map: (t, ch) => t.map(ch) }); +const setEditContextFormatting = /*@__PURE__*/StateEffect.define(); /** Log or report an unhandled exception in client code. Should probably only be used by extension code that allows client code to @@ -6820,6 +6935,15 @@ class ViewUpdate { return (this.flags & 4 /* UpdateFlag.Viewport */) > 0; } /** + Returns true when + [`viewportChanged`](https://codemirror.net/6/docs/ref/#view.ViewUpdate.viewportChanged) is true + and the viewport change is not just the result of mapping it in + response to document changes. + */ + get viewportMoved() { + return (this.flags & 8 /* UpdateFlag.ViewportMoved */) > 0; + } + /** Indicates whether the height of a block element in the editor changed in this update. */ @@ -6831,7 +6955,7 @@ class ViewUpdate { editor, or elements within the editor, changed. */ get geometryChanged() { - return this.docChanged || (this.flags & (8 /* UpdateFlag.Geometry */ | 2 /* UpdateFlag.Height */)) > 0; + return this.docChanged || (this.flags & (16 /* UpdateFlag.Geometry */ | 2 /* UpdateFlag.Height */)) > 0; } /** True when this update indicates a focus change. @@ -6863,10 +6987,12 @@ class DocView extends ContentView { super(); this.view = view; this.decorations = []; - this.dynamicDecorationMap = []; + this.dynamicDecorationMap = [false]; this.domChanged = null; this.hasComposition = null; this.markedForComposition = new Set; + this.editContextFormatting = Decoration.none; + this.lastCompositionAfterCursor = false; // Track a minimum width for the editor. When measuring sizes in // measureVisibleLineHeights, this is updated to point at the width // of a given element and its extent in the document. When a change @@ -6904,8 +7030,9 @@ class DocView extends ContentView { this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1); } } + this.updateEditContextFormatting(update); let readCompositionAt = -1; - if (this.view.inputState.composing >= 0) { + if (this.view.inputState.composing >= 0 && !this.view.observer.editContext) { if ((_a = this.domChanged) === null || _a === void 0 ? void 0 : _a.newSel) readCompositionAt = this.domChanged.newSel.head; else if (!touchesComposition(update.changes, this.hasComposition) && !update.selectionSet) @@ -7013,6 +7140,14 @@ class DocView extends ContentView { if (composition) this.fixCompositionDOM(composition); } + updateEditContextFormatting(update) { + this.editContextFormatting = this.editContextFormatting.map(update.changes); + for (let tr of update.transactions) + for (let effect of tr.effects) + if (effect.is(setEditContextFormatting)) { + this.editContextFormatting = effect.value; + } + } compositionView(composition) { let cur = new TextView(composition.text.nodeValue); cur.flags |= 8 /* ViewFlag.Composition */; @@ -7045,7 +7180,7 @@ class DocView extends ContentView { if (mustRead || !this.view.observer.selectionRange.focusNode) this.view.observer.readSelectionRange(); let activeElt = this.view.root.activeElement, focused = activeElt == this.dom; - let selectionNotFocus = !focused && + let selectionNotFocus = !focused && !(this.view.state.facet(editable) || this.dom.tabIndex > -1) && hasSelection(this.dom, this.view.observer.selectionRange) && !(activeElt && this.dom.contains(activeElt)); if (!(focused || fromPointer || selectionNotFocus)) return; @@ -7083,7 +7218,7 @@ class DocView extends ContentView { if (browser.gecko) { let nextTo = nextToUneditable(anchor.node, anchor.offset); if (nextTo && nextTo != (1 /* NextTo.Before */ | 2 /* NextTo.After */)) { - let text = nearbyTextNode(anchor.node, anchor.offset, nextTo == 1 /* NextTo.Before */ ? 1 : -1); + let text = (nextTo == 1 /* NextTo.Before */ ? textNodeBefore : textNodeAfter)(anchor.node, anchor.offset); if (text) anchor = new DOMPos(text.node, text.offset); } @@ -7218,6 +7353,12 @@ class DocView extends ContentView { best = child; bestPos = start; } + else if (best && start == pos && end == pos && child instanceof BlockWidgetView && Math.abs(side) < 2) { + if (child.deco.startSide < 0) + break; + else if (i) + best = null; + } off = start; } return best ? best.coordsAt(pos - bestPos, side) : null; @@ -7338,8 +7479,9 @@ class DocView extends ContentView { return Decoration.set(deco); } updateDeco() { - let allDeco = this.view.state.facet(decorations).map((d, i) => { - let dynamic = this.dynamicDecorationMap[i] = typeof d == "function"; + let i = 1; + let allDeco = this.view.state.facet(decorations).map(d => { + let dynamic = this.dynamicDecorationMap[i++] = typeof d == "function"; return dynamic ? d(this.view) : d; }); let dynamicOuter = false, outerDeco = this.view.state.facet(outerDecorations).map((d, i) => { @@ -7349,16 +7491,18 @@ class DocView extends ContentView { return dynamic ? d(this.view) : d; }); if (outerDeco.length) { - this.dynamicDecorationMap[allDeco.length] = dynamicOuter; + this.dynamicDecorationMap[i++] = dynamicOuter; allDeco.push(RangeSet.join(outerDeco)); } - for (let i = allDeco.length; i < allDeco.length + 3; i++) - this.dynamicDecorationMap[i] = false; - return this.decorations = [ + this.decorations = [ + this.editContextFormatting, ...allDeco, this.computeBlockGapDeco(), this.view.viewState.lineGapDeco ]; + while (i < this.decorations.length) + this.dynamicDecorationMap[i++] = false; + return this.decorations; } scrollIntoView(target) { if (target.isSnapshot) { @@ -7367,6 +7511,15 @@ class DocView extends ContentView { this.view.scrollDOM.scrollLeft = target.xMargin; return; } + for (let handler of this.view.state.facet(scrollHandler)) { + try { + if (handler(this.view, target.range, target)) + return true; + } + catch (e) { + logException(this.view.state, e, "scroll handler"); + } + } let { range } = target; let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other; if (!rect) @@ -7388,29 +7541,25 @@ function betweenUneditable(pos) { (pos.offset == 0 || pos.node.childNodes[pos.offset - 1].contentEditable == "false") && (pos.offset == pos.node.childNodes.length || pos.node.childNodes[pos.offset].contentEditable == "false"); } -class BlockGapWidget extends WidgetType { - constructor(height) { - super(); - this.height = height; - } - toDOM() { - let elt = document.createElement("div"); - elt.className = "cm-gap"; - this.updateDOM(elt); - return elt; - } - eq(other) { return other.height == this.height; } - updateDOM(elt) { - elt.style.height = this.height + "px"; - return true; - } - get editable() { return true; } - get estimatedHeight() { return this.height; } - ignoreEvent() { return false; } -} function findCompositionNode(view, headPos) { let sel = view.observer.selectionRange; - let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0); + if (!sel.focusNode) + return null; + let textBefore = textNodeBefore(sel.focusNode, sel.focusOffset); + let textAfter = textNodeAfter(sel.focusNode, sel.focusOffset); + let textNode = textBefore || textAfter; + if (textAfter && textBefore && textAfter.node != textBefore.node) { + let descAfter = ContentView.get(textAfter.node); + if (!descAfter || descAfter instanceof TextView && descAfter.text != textAfter.node.nodeValue) { + textNode = textAfter; + } + else if (view.docView.lastCompositionAfterCursor) { + let descBefore = ContentView.get(textBefore.node); + if (!(!descBefore || descBefore instanceof TextView && descBefore.text != textBefore.node.nodeValue)) + textNode = textAfter; + } + } + view.docView.lastCompositionAfterCursor = textNode != textBefore; if (!textNode) return null; let from = headPos - textNode.offset; @@ -7445,33 +7594,6 @@ function findCompositionRange(view, changes, headPos) { return null; } } -function nearbyTextNode(startNode, startOffset, side) { - if (side <= 0) - for (let node = startNode, offset = startOffset;;) { - if (node.nodeType == 3) - return { node: node, offset: offset }; - if (node.nodeType == 1 && offset > 0) { - node = node.childNodes[offset - 1]; - offset = maxOffset(node); - } - else { - break; - } - } - if (side >= 0) - for (let node = startNode, offset = startOffset;;) { - if (node.nodeType == 3) - return { node: node, offset: offset }; - if (node.nodeType == 1 && offset < node.childNodes.length && side >= 0) { - node = node.childNodes[offset]; - offset = 0; - } - else { - break; - } - } - return null; -} function nextToUneditable(node, offset) { if (node.nodeType != 1) return 0; @@ -7484,6 +7606,7 @@ let DecorationComparator$1 = class DecorationComparator { } compareRange(from, to) { addRange(from, to, this.changes); } comparePoint(from, to) { addRange(from, to, this.changes); } + boundChange(pos) { addRange(pos, pos, this.changes); } }; function findChangedDeco(a, b, diff) { let comp = new DecorationComparator$1; @@ -7709,6 +7832,11 @@ function posAtCoords(view, coords, precise, bias = -1) { node = undefined; } } + // Chrome will return offsets into elements without child + // nodes, which will lead to a null deref below, so clip the + // offset to the node size. + if (node) + offset = Math.min(maxOffset(node), offset); } // No luck, do our own (potentially expensive) search if (!node || !view.docView.dom.contains(node)) { @@ -7876,7 +8004,388 @@ function skipAtoms(view, oldPos, pos) { return newPos == pos.from ? pos : EditorSelection.cursor(newPos, newPos < pos.from ? 1 : -1); } -// This will also be where dragging info and such goes +const LineBreakPlaceholder = "\uffff"; +class DOMReader { + constructor(points, state) { + this.points = points; + this.text = ""; + this.lineSeparator = state.facet(EditorState.lineSeparator); + } + append(text) { + this.text += text; + } + lineBreak() { + this.text += LineBreakPlaceholder; + } + readRange(start, end) { + if (!start) + return this; + let parent = start.parentNode; + for (let cur = start;;) { + this.findPointBefore(parent, cur); + let oldLen = this.text.length; + this.readNode(cur); + let next = cur.nextSibling; + if (next == end) + break; + let view = ContentView.get(cur), nextView = ContentView.get(next); + if (view && nextView ? view.breakAfter : + (view ? view.breakAfter : isBlockElement(cur)) || + (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen)) + this.lineBreak(); + cur = next; + } + this.findPointBefore(parent, end); + return this; + } + readTextNode(node) { + let text = node.nodeValue; + for (let point of this.points) + if (point.node == node) + point.pos = this.text.length + Math.min(point.offset, text.length); + for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) { + let nextBreak = -1, breakSize = 1, m; + if (this.lineSeparator) { + nextBreak = text.indexOf(this.lineSeparator, off); + breakSize = this.lineSeparator.length; + } + else if (m = re.exec(text)) { + nextBreak = m.index; + breakSize = m[0].length; + } + this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak)); + if (nextBreak < 0) + break; + this.lineBreak(); + if (breakSize > 1) + for (let point of this.points) + if (point.node == node && point.pos > this.text.length) + point.pos -= breakSize - 1; + off = nextBreak + breakSize; + } + } + readNode(node) { + if (node.cmIgnore) + return; + let view = ContentView.get(node); + let fromView = view && view.overrideDOMText; + if (fromView != null) { + this.findPointInside(node, fromView.length); + for (let i = fromView.iter(); !i.next().done;) { + if (i.lineBreak) + this.lineBreak(); + else + this.append(i.value); + } + } + else if (node.nodeType == 3) { + this.readTextNode(node); + } + else if (node.nodeName == "BR") { + if (node.nextSibling) + this.lineBreak(); + } + else if (node.nodeType == 1) { + this.readRange(node.firstChild, null); + } + } + findPointBefore(node, next) { + for (let point of this.points) + if (point.node == node && node.childNodes[point.offset] == next) + point.pos = this.text.length; + } + findPointInside(node, length) { + for (let point of this.points) + if (node.nodeType == 3 ? point.node == node : node.contains(point.node)) + point.pos = this.text.length + (isAtEnd(node, point.node, point.offset) ? length : 0); + } +} +function isAtEnd(parent, node, offset) { + for (;;) { + if (!node || offset < maxOffset(node)) + return false; + if (node == parent) + return true; + offset = domIndex(node) + 1; + node = node.parentNode; + } +} +class DOMPoint { + constructor(node, offset) { + this.node = node; + this.offset = offset; + this.pos = -1; + } +} + +class DOMChange { + constructor(view, start, end, typeOver) { + this.typeOver = typeOver; + this.bounds = null; + this.text = ""; + this.domChanged = start > -1; + let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView; + if (view.state.readOnly && start > -1) { + // Ignore changes when the editor is read-only + this.newSel = null; + } + else if (start > -1 && (this.bounds = view.docView.domBoundsAround(start, end, 0))) { + let selPoints = iHead || iAnchor ? [] : selectionPoints(view); + let reader = new DOMReader(selPoints, view.state); + reader.readRange(this.bounds.startDOM, this.bounds.endDOM); + this.text = reader.text; + this.newSel = selectionFromPoints(selPoints, this.bounds.from); + } + else { + let domSel = view.observer.selectionRange; + let head = iHead && iHead.node == domSel.focusNode && iHead.offset == domSel.focusOffset || + !contains(view.contentDOM, domSel.focusNode) + ? view.state.selection.main.head + : view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset); + let anchor = iAnchor && iAnchor.node == domSel.anchorNode && iAnchor.offset == domSel.anchorOffset || + !contains(view.contentDOM, domSel.anchorNode) + ? view.state.selection.main.anchor + : view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset); + // iOS will refuse to select the block gaps when doing + // select-all. + // Chrome will put the selection *inside* them, confusing + // posFromDOM + let vp = view.viewport; + if ((browser.ios || browser.chrome) && view.state.selection.main.empty && head != anchor && + (vp.from > 0 || vp.to < view.state.doc.length)) { + let from = Math.min(head, anchor), to = Math.max(head, anchor); + let offFrom = vp.from - from, offTo = vp.to - to; + if ((offFrom == 0 || offFrom == 1 || from == 0) && (offTo == 0 || offTo == -1 || to == view.state.doc.length)) { + head = 0; + anchor = view.state.doc.length; + } + } + this.newSel = EditorSelection.single(anchor, head); + } + } +} +function applyDOMChange(view, domChange) { + let change; + let { newSel } = domChange, sel = view.state.selection.main; + let lastKey = view.inputState.lastKeyTime > Date.now() - 100 ? view.inputState.lastKeyCode : -1; + if (domChange.bounds) { + let { from, to } = domChange.bounds; + let preferredPos = sel.from, preferredSide = null; + // Prefer anchoring to end when Backspace is pressed (or, on + // Android, when something was deleted) + if (lastKey === 8 || browser.android && domChange.text.length < to - from) { + preferredPos = sel.to; + preferredSide = "end"; + } + let diff = findDiff(view.state.doc.sliceString(from, to, LineBreakPlaceholder), domChange.text, preferredPos - from, preferredSide); + if (diff) { + // Chrome inserts two newlines when pressing shift-enter at the + // end of a line. DomChange drops one of those. + if (browser.chrome && lastKey == 13 && + diff.toB == diff.from + 2 && domChange.text.slice(diff.from, diff.toB) == LineBreakPlaceholder + LineBreakPlaceholder) + diff.toB--; + change = { from: from + diff.from, to: from + diff.toA, + insert: Text.of(domChange.text.slice(diff.from, diff.toB).split(LineBreakPlaceholder)) }; + } + } + else if (newSel && (!view.hasFocus && view.state.facet(editable) || newSel.main.eq(sel))) { + newSel = null; + } + if (!change && !newSel) + return false; + if (!change && domChange.typeOver && !sel.empty && newSel && newSel.main.empty) { + // Heuristic to notice typing over a selected character + change = { from: sel.from, to: sel.to, insert: view.state.doc.slice(sel.from, sel.to) }; + } + else if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 && + /^\. ?$/.test(change.insert.toString()) && view.contentDOM.getAttribute("autocorrect") == "off") { + // Detect insert-period-on-double-space Mac and Android behavior, + // and transform it into a regular space insert. + if (newSel && change.insert.length == 2) + newSel = EditorSelection.single(newSel.main.anchor - 1, newSel.main.head - 1); + change = { from: change.from, to: change.to, insert: Text.of([change.insert.toString().replace(".", " ")]) }; + } + else if (change && change.from >= sel.from && change.to <= sel.to && + (change.from != sel.from || change.to != sel.to) && + (sel.to - sel.from) - (change.to - change.from) <= 4) { + // If the change is inside the selection and covers most of it, + // assume it is a selection replace (with identical characters at + // the start/end not included in the diff) + change = { + from: sel.from, to: sel.to, + insert: view.state.doc.slice(sel.from, change.from).append(change.insert).append(view.state.doc.slice(change.to, sel.to)) + }; + } + else if (browser.chrome && change && change.from == change.to && change.from == sel.head && + change.insert.toString() == "\n " && view.lineWrapping) { + // In Chrome, if you insert a space at the start of a wrapped + // line, it will actually insert a newline and a space, causing a + // bogus new line to be created in CodeMirror (#968) + if (newSel) + newSel = EditorSelection.single(newSel.main.anchor - 1, newSel.main.head - 1); + change = { from: sel.from, to: sel.to, insert: Text.of([" "]) }; + } + if (change) { + return applyDOMChangeInner(view, change, newSel, lastKey); + } + else if (newSel && !newSel.main.eq(sel)) { + let scrollIntoView = false, userEvent = "select"; + if (view.inputState.lastSelectionTime > Date.now() - 50) { + if (view.inputState.lastSelectionOrigin == "select") + scrollIntoView = true; + userEvent = view.inputState.lastSelectionOrigin; + } + view.dispatch({ selection: newSel, scrollIntoView, userEvent }); + return true; + } + else { + return false; + } +} +function applyDOMChangeInner(view, change, newSel, lastKey = -1) { + if (browser.ios && view.inputState.flushIOSKey(change)) + return true; + let sel = view.state.selection.main; + // Android browsers don't fire reasonable key events for enter, + // backspace, or delete. So this detects changes that look like + // they're caused by those keys, and reinterprets them as key + // events. (Some of these keys are also handled by beforeinput + // events and the pendingAndroidKey mechanism, but that's not + // reliable in all situations.) + if (browser.android && + ((change.to == sel.to && + // GBoard will sometimes remove a space it just inserted + // after a completion when you press enter + (change.from == sel.from || change.from == sel.from - 1 && view.state.sliceDoc(change.from, sel.from) == " ") && + change.insert.length == 1 && change.insert.lines == 2 && + dispatchKey(view.contentDOM, "Enter", 13)) || + ((change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 || + lastKey == 8 && change.insert.length < change.to - change.from && change.to > sel.head) && + dispatchKey(view.contentDOM, "Backspace", 8)) || + (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 && + dispatchKey(view.contentDOM, "Delete", 46)))) + return true; + let text = change.insert.toString(); + if (view.inputState.composing >= 0) + view.inputState.composing++; + let defaultTr; + let defaultInsert = () => defaultTr || (defaultTr = applyDefaultInsert(view, change, newSel)); + if (!view.state.facet(inputHandler$1).some(h => h(view, change.from, change.to, text, defaultInsert))) + view.dispatch(defaultInsert()); + return true; +} +function applyDefaultInsert(view, change, newSel) { + let tr, startState = view.state, sel = startState.selection.main; + if (change.from >= sel.from && change.to <= sel.to && change.to - change.from >= (sel.to - sel.from) / 3 && + (!newSel || newSel.main.empty && newSel.main.from == change.from + change.insert.length) && + view.inputState.composing < 0) { + let before = sel.from < change.from ? startState.sliceDoc(sel.from, change.from) : ""; + let after = sel.to > change.to ? startState.sliceDoc(change.to, sel.to) : ""; + tr = startState.replaceSelection(view.state.toText(before + change.insert.sliceString(0, undefined, view.state.lineBreak) + after)); + } + else { + let changes = startState.changes(change); + let mainSel = newSel && newSel.main.to <= changes.newLength ? newSel.main : undefined; + // Try to apply a composition change to all cursors + if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 && + change.to <= sel.to && change.to >= sel.to - 10) { + let replaced = view.state.sliceDoc(change.from, change.to); + let compositionRange, composition = newSel && findCompositionNode(view, newSel.main.head); + if (composition) { + let dLen = change.insert.length - (change.to - change.from); + compositionRange = { from: composition.from, to: composition.to - dLen }; + } + else { + compositionRange = view.state.doc.lineAt(sel.head); + } + let offset = sel.to - change.to, size = sel.to - sel.from; + tr = startState.changeByRange(range => { + if (range.from == sel.from && range.to == sel.to) + return { changes, range: mainSel || range.map(changes) }; + let to = range.to - offset, from = to - replaced.length; + if (range.to - range.from != size || view.state.sliceDoc(from, to) != replaced || + // Unfortunately, there's no way to make multiple + // changes in the same node work without aborting + // composition, so cursors in the composition range are + // ignored. + range.to >= compositionRange.from && range.from <= compositionRange.to) + return { range }; + let rangeChanges = startState.changes({ from, to, insert: change.insert }), selOff = range.to - sel.to; + return { + changes: rangeChanges, + range: !mainSel ? range.map(rangeChanges) : + EditorSelection.range(Math.max(0, mainSel.anchor + selOff), Math.max(0, mainSel.head + selOff)) + }; + }); + } + else { + tr = { + changes, + selection: mainSel && startState.selection.replaceRange(mainSel) + }; + } + } + let userEvent = "input.type"; + if (view.composing || + view.inputState.compositionPendingChange && view.inputState.compositionEndedAt > Date.now() - 50) { + view.inputState.compositionPendingChange = false; + userEvent += ".compose"; + if (view.inputState.compositionFirstChange) { + userEvent += ".start"; + view.inputState.compositionFirstChange = false; + } + } + return startState.update(tr, { userEvent, scrollIntoView: true }); +} +function findDiff(a, b, preferredPos, preferredSide) { + let minLen = Math.min(a.length, b.length); + let from = 0; + while (from < minLen && a.charCodeAt(from) == b.charCodeAt(from)) + from++; + if (from == minLen && a.length == b.length) + return null; + let toA = a.length, toB = b.length; + while (toA > 0 && toB > 0 && a.charCodeAt(toA - 1) == b.charCodeAt(toB - 1)) { + toA--; + toB--; + } + if (preferredSide == "end") { + let adjust = Math.max(0, from - Math.min(toA, toB)); + preferredPos -= toA + adjust - from; + } + if (toA < from && a.length < b.length) { + let move = preferredPos <= from && preferredPos >= toA ? from - preferredPos : 0; + from -= move; + toB = from + (toB - toA); + toA = from; + } + else if (toB < from) { + let move = preferredPos <= from && preferredPos >= toB ? from - preferredPos : 0; + from -= move; + toA = from + (toA - toB); + toB = from; + } + return { from, toA, toB }; +} +function selectionPoints(view) { + let result = []; + if (view.root.activeElement != view.contentDOM) + return result; + let { anchorNode, anchorOffset, focusNode, focusOffset } = view.observer.selectionRange; + if (anchorNode) { + result.push(new DOMPoint(anchorNode, anchorOffset)); + if (focusNode != anchorNode || focusOffset != anchorOffset) + result.push(new DOMPoint(focusNode, focusOffset)); + } + return result; +} +function selectionFromPoints(points, base) { + if (points.length == 0) + return null; + let anchor = points[0].pos, head = points.length == 2 ? points[1].pos : anchor; + return anchor > -1 && head > -1 ? EditorSelection.single(anchor + base, head + base) : null; +} + class InputState { setSelectionOrigin(origin) { this.lastSelectionOrigin = origin; @@ -7894,9 +8403,16 @@ class InputState { // (after which we retroactively handle them and reset the DOM) to // avoid messing up the virtual keyboard state. this.pendingIOSKey = undefined; + /** + When enabled (>-1), tab presses are not given to key handlers, + leaving the browser's default behavior. If >0, the mode expires + at that timestamp, and any other keypress clears it. + Esc enables temporary tab focus mode for two seconds when not + otherwise handled. + */ + this.tabFocusMode = -1; this.lastSelectionOrigin = null; this.lastSelectionTime = 0; - this.lastEscPress = 0; this.lastContextMenu = 0; this.scrollHandlers = []; this.handlers = Object.create(null); @@ -7976,10 +8492,10 @@ class InputState { // Must always run, even if a custom handler handled the event this.lastKeyCode = event.keyCode; this.lastKeyTime = Date.now(); - if (event.keyCode == 9 && Date.now() < this.lastEscPress + 2000) + if (event.keyCode == 9 && this.tabFocusMode > -1 && (!this.tabFocusMode || Date.now() <= this.tabFocusMode)) return true; - if (event.keyCode != 27 && modifierCodes.indexOf(event.keyCode) < 0) - this.view.inputState.lastEscPress = 0; + if (this.tabFocusMode > 0 && event.keyCode != 27 && modifierCodes.indexOf(event.keyCode) < 0) + this.tabFocusMode = -1; // Chrome for Android usually doesn't fire proper key events, but // occasionally does, usually surrounded by a bunch of complicated // composition changes. When an enter or backspace key event is @@ -8007,12 +8523,15 @@ class InputState { this.view.observer.forceFlush(); return false; } - flushIOSKey() { + flushIOSKey(change) { let key = this.pendingIOSKey; if (!key) return false; + // This looks like an autocorrection before Enter + if (key.key == "Enter" && change && change.from < change.to && /^\S+$/.test(change.insert.toString())) + return false; this.pendingIOSKey = undefined; - return dispatchKey(this.view.contentDOM, key.key, key.keyCode); + return dispatchKey(this.view.contentDOM, key.key, key.keyCode, key instanceof KeyboardEvent ? key : undefined); } ignoreDuringComposition(event) { if (!/^key/.test(event.type)) @@ -8037,6 +8556,7 @@ class InputState { this.mouseSelection = mouseSelection; } update(update) { + this.view.observer.update(update); if (this.mouseSelection) this.mouseSelection.update(update); if (this.draggedContent && update.docChanged) @@ -8110,7 +8630,7 @@ class MouseSelection { this.scrollSpeed = { x: 0, y: 0 }; this.scrolling = -1; this.lastEvent = startEvent; - this.scrollParent = scrollableParent(view.contentDOM); + this.scrollParents = scrollableParents(view.contentDOM); this.atoms = view.state.facet(atomicRanges).map(f => f(view)); let doc = view.contentDOM.ownerDocument; doc.addEventListener("mousemove", this.move = this.move.bind(this)); @@ -8126,24 +8646,26 @@ class MouseSelection { this.select(event); } move(event) { - var _a; if (event.buttons == 0) return this.destroy(); if (this.dragging || this.dragging == null && dist(this.startEvent, event) < 10) return; this.select(this.lastEvent = event); let sx = 0, sy = 0; - let rect = ((_a = this.scrollParent) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) - || { left: 0, top: 0, right: this.view.win.innerWidth, bottom: this.view.win.innerHeight }; + let left = 0, top = 0, right = this.view.win.innerWidth, bottom = this.view.win.innerHeight; + if (this.scrollParents.x) + ({ left, right } = this.scrollParents.x.getBoundingClientRect()); + if (this.scrollParents.y) + ({ top, bottom } = this.scrollParents.y.getBoundingClientRect()); let margins = getScrollMargins(this.view); - if (event.clientX - margins.left <= rect.left + dragScrollMargin) - sx = -dragScrollSpeed(rect.left - event.clientX); - else if (event.clientX + margins.right >= rect.right - dragScrollMargin) - sx = dragScrollSpeed(event.clientX - rect.right); - if (event.clientY - margins.top <= rect.top + dragScrollMargin) - sy = -dragScrollSpeed(rect.top - event.clientY); - else if (event.clientY + margins.bottom >= rect.bottom - dragScrollMargin) - sy = dragScrollSpeed(event.clientY - rect.bottom); + if (event.clientX - margins.left <= left + dragScrollMargin) + sx = -dragScrollSpeed(left - event.clientX); + else if (event.clientX + margins.right >= right - dragScrollMargin) + sx = dragScrollSpeed(event.clientX - right); + if (event.clientY - margins.top <= top + dragScrollMargin) + sy = -dragScrollSpeed(top - event.clientY); + else if (event.clientY + margins.bottom >= bottom - dragScrollMargin) + sy = dragScrollSpeed(event.clientY - bottom); this.setScrollSpeed(sx, sy); } up(event) { @@ -8172,13 +8694,17 @@ class MouseSelection { } } scroll() { - if (this.scrollParent) { - this.scrollParent.scrollLeft += this.scrollSpeed.x; - this.scrollParent.scrollTop += this.scrollSpeed.y; + let { x, y } = this.scrollSpeed; + if (x && this.scrollParents.x) { + this.scrollParents.x.scrollLeft += x; + x = 0; } - else { - this.view.win.scrollBy(this.scrollSpeed.x, this.scrollSpeed.y); + if (y && this.scrollParents.y) { + this.scrollParents.y.scrollTop += y; + y = 0; } + if (x || y) + this.view.win.scrollBy(x, y); if (this.dragging === false) this.select(this.lastEvent); } @@ -8215,7 +8741,9 @@ class MouseSelection { this.mustSelect = false; } update(update) { - if (this.style.update(update)) + if (update.transactions.some(tr => tr.isUserEvent("input.type"))) + this.destroy(); + else if (this.style.update(update)) setTimeout(() => this.select(this.lastEvent), 20); } } @@ -8275,7 +8803,13 @@ function capturePaste(view) { doPaste(view, target.value); }, 50); } +function textFilter(state, facet, text) { + for (let filter of state.facet(facet)) + text = filter(text, state); + return text; +} function doPaste(view, input) { + input = textFilter(view.state, clipboardInputFilter, input); let { state } = view, changes, i = 1, text = state.toText(input); let byLine = text.lines == state.selection.ranges.length; let linewise = lastLinewiseCopy != null && state.selection.ranges.every(r => r.empty) && lastLinewiseCopy == text.toString(); @@ -8312,8 +8846,8 @@ observers.scroll = view => { }; handlers.keydown = (view, event) => { view.inputState.setSelectionOrigin("select"); - if (event.keyCode == 27) - view.inputState.lastEscPress = Date.now(); + if (event.keyCode == 27 && view.inputState.tabFocusMode != 0) + view.inputState.tabFocusMode = Date.now() + 2000; return false; }; observers.touchstart = (view, e) => { @@ -8339,7 +8873,12 @@ handlers.mousedown = (view, event) => { let mustFocus = !view.hasFocus; view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus)); if (mustFocus) - view.observer.ignore(() => focusPreventScroll(view.contentDOM)); + view.observer.ignore(() => { + focusPreventScroll(view.contentDOM); + let active = view.root.activeElement; + if (active && !active.contains(view.contentDOM)) + active.blur(); + }); let mouseSel = view.inputState.mouseSelection; if (mouseSel) { mouseSel.start(event); @@ -8363,8 +8902,7 @@ function rangeForClick(view, pos, bias, type) { return EditorSelection.range(from, to); } } -let insideY = (y, rect) => y >= rect.top && y <= rect.bottom; -let inside = (x, y, rect) => insideY(y, rect) && x >= rect.left && x <= rect.right; +let inside = (x, y, rect) => y >= rect.top && y <= rect.bottom && x >= rect.left && x <= rect.right; // Try to determine, for the given coordinates, associated with the // given position, whether they are related to the element before or // the element after the position. @@ -8386,8 +8924,8 @@ function findPositionSide(view, pos, x, y) { if (after && inside(x, y, after)) return 1; // This is probably a line wrap point. Pick before if the point is - // beside it. - return before && insideY(y, before) ? -1 : 1; + // above its bottom. + return before && before.bottom >= y ? -1 : 1; } function queryPos(view, event) { let pos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false); @@ -8456,7 +8994,7 @@ handlers.dragstart = (view, event) => { inputState.mouseSelection.dragging = true; inputState.draggedContent = range; if (event.dataTransfer) { - event.dataTransfer.setData("Text", view.state.sliceDoc(range.from, range.to)); + event.dataTransfer.setData("Text", textFilter(view.state, clipboardOutputFilter, view.state.sliceDoc(range.from, range.to))); event.dataTransfer.effectAllowed = "copyMove"; } return false; @@ -8466,6 +9004,7 @@ handlers.dragend = view => { return false; }; function dropText(view, event, text, direct) { + text = textFilter(view.state, clipboardInputFilter, text); if (!text) return; let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false); @@ -8521,7 +9060,7 @@ handlers.paste = (view, event) => { view.observer.flush(); let data = brokenClipboardAPI ? null : event.clipboardData; if (data) { - doPaste(view, data.getData("text/plain") || data.getData("text/uri-text")); + doPaste(view, data.getData("text/plain") || data.getData("text/uri-list")); return true; } else { @@ -8566,7 +9105,7 @@ function copiedRange(state) { } linewise = true; } - return { text: content.join(state.lineBreak), ranges, linewise }; + return { text: textFilter(state, clipboardOutputFilter, content.join(state.lineBreak)), ranges, linewise }; } let lastLinewiseCopy = null; handlers.copy = handlers.cut = (view, event) => { @@ -8627,6 +9166,8 @@ observers.blur = view => { updateForFocusChange(view); }; observers.compositionstart = observers.compositionupdate = view => { + if (view.observer.editContext) + return; // Composition handled by edit context if (view.inputState.compositionFirstChange == null) view.inputState.compositionFirstChange = true; if (view.inputState.composing < 0) { @@ -8635,6 +9176,8 @@ observers.compositionstart = observers.compositionupdate = view => { } }; observers.compositionend = view => { + if (view.observer.editContext) + return; // Composition handled by edit context view.inputState.composing = -1; view.inputState.compositionEndedAt = Date.now(); view.inputState.compositionPendingKey = true; @@ -8662,7 +9205,18 @@ observers.contextmenu = view => { view.inputState.lastContextMenu = Date.now(); }; handlers.beforeinput = (view, event) => { - var _a; + var _a, _b; + // In EditContext mode, we must handle insertReplacementText events + // directly, to make spell checking corrections work + if (event.inputType == "insertReplacementText" && view.observer.editContext) { + let text = (_a = event.dataTransfer) === null || _a === void 0 ? void 0 : _a.getData("text/plain"), ranges = event.getTargetRanges(); + if (text && ranges.length) { + let r = ranges[0]; + let from = view.posAtDOM(r.startContainer, r.startOffset), to = view.posAtDOM(r.endContainer, r.endOffset); + applyDOMChangeInner(view, { from, to, insert: view.state.toText(text) }, null); + return true; + } + } // Because Chrome Android doesn't fire useful key events, use // beforeinput to detect backspace (and possibly enter and delete, // but those usually don't even seem to fire beforeinput events at @@ -8674,7 +9228,7 @@ handlers.beforeinput = (view, event) => { if (browser.chrome && browser.android && (pending = PendingKeys.find(key => key.inputType == event.inputType))) { view.observer.delayAndroidKey(pending.key, pending.keyCode); if (pending.key == "Backspace" || pending.key == "Delete") { - let startViewHeight = ((_a = window.visualViewport) === null || _a === void 0 ? void 0 : _a.height) || 0; + let startViewHeight = ((_b = window.visualViewport) === null || _b === void 0 ? void 0 : _b.height) || 0; setTimeout(() => { var _a; // Backspacing near uneditable nodes on Chrome Android sometimes @@ -8687,6 +9241,16 @@ handlers.beforeinput = (view, event) => { }, 100); } } + if (browser.ios && event.inputType == "deleteContentForward") { + // For some reason, DOM changes (and beforeinput) happen _before_ + // the key event for ctrl-d on iOS when using an external + // keyboard. + view.observer.flushSoon(); + } + // Safari will occasionally forget to fire compositionend at the end of a dead-key composition + if (browser.safari && event.inputType == "insertText" && view.inputState.composing >= 0) { + setTimeout(() => observers.compositionend(view, event), 20); + } return false; }; const appliedFirefoxHack = /*@__PURE__*/new Set; @@ -8703,6 +9267,9 @@ function firefoxCopyCutHack(doc) { } const wrappingWhiteSpace = ["pre-wrap", "normal", "pre-line", "break-spaces"]; +// Used to track, during updateHeight, if any actual heights changed +let heightChangeFlag = false; +function clearHeightChangeFlag() { heightChangeFlag = false; } class HeightOracle { constructor(lineWrapping) { this.lineWrapping = lineWrapping; @@ -8712,8 +9279,6 @@ class HeightOracle { this.charWidth = 7; this.textHeight = 14; // The height of the actual font (font-size) this.lineLength = 30; - // Used to track, during updateHeight, if any actual heights changed - this.heightChanged = false; } heightForGap(from, to) { let lines = this.doc.lineAt(to).number - this.doc.lineAt(from).number + 1; @@ -8871,10 +9436,10 @@ class HeightMap { } get outdated() { return (this.flags & 2 /* Flag.Outdated */) > 0; } set outdated(value) { this.flags = (value ? 2 /* Flag.Outdated */ : 0) | (this.flags & ~2 /* Flag.Outdated */); } - setHeight(oracle, height) { + setHeight(height) { if (this.height != height) { if (Math.abs(this.height - height) > Epsilon) - oracle.heightChanged = true; + heightChangeFlag = true; this.height = height; } } @@ -8905,7 +9470,7 @@ class HeightMap { fromB += start.from - fromA; fromA = start.from; let nodes = NodeBuilder.build(oracle.setDoc(doc), decorations, fromB, toB); - me = me.replace(fromA, toA, nodes); + me = replace(me, me.replace(fromA, toA, nodes)); } return me.updateHeight(oracle, 0); } @@ -8965,6 +9530,13 @@ class HeightMap { return new HeightMapBranch(HeightMap.of(nodes.slice(0, i)), brk, HeightMap.of(nodes.slice(j))); } } +function replace(old, val) { + if (old == val) + return old; + if (old.constructor != val.constructor) + heightChangeFlag = true; + return val; +} HeightMap.prototype.size = 1; class HeightMapBlock extends HeightMap { constructor(length, height, deco) { @@ -8983,7 +9555,7 @@ class HeightMapBlock extends HeightMap { } updateHeight(oracle, offset = 0, _force = false, measured) { if (measured && measured.from <= offset && measured.more) - this.setHeight(oracle, measured.heights[measured.index++]); + this.setHeight(measured.heights[measured.index++]); this.outdated = false; return this; } @@ -9017,9 +9589,9 @@ class HeightMapText extends HeightMapBlock { } updateHeight(oracle, offset = 0, force = false, measured) { if (measured && measured.from <= offset && measured.more) - this.setHeight(oracle, measured.heights[measured.index++]); + this.setHeight(measured.heights[measured.index++]); else if (force || this.outdated) - this.setHeight(oracle, Math.max(this.widgetHeight, oracle.heightForLine(this.length - this.collapsed)) + + this.setHeight(Math.max(this.widgetHeight, oracle.heightForLine(this.length - this.collapsed)) + this.breaks * oracle.lineHeight); this.outdated = false; return this; @@ -9048,7 +9620,8 @@ class HeightMapGap extends HeightMap { blockAt(height, oracle, top, offset) { let { firstLine, lastLine, perLine, perChar } = this.heightMetrics(oracle, offset); if (oracle.lineWrapping) { - let guess = offset + Math.round(Math.max(0, Math.min(1, (height - top) / this.height)) * this.length); + let guess = offset + (height < oracle.lineHeight ? 0 + : Math.round(Math.max(0, Math.min(1, (height - top) / this.height)) * this.length)); let line = oracle.doc.lineAt(guess), lineHeight = perLine + line.length * perChar; let lineTop = Math.max(top, height - lineHeight / 2); return new BlockInfo(line.from, line.length, lineTop, lineHeight, 0); @@ -9141,11 +9714,11 @@ class HeightMapGap extends HeightMap { let result = HeightMap.of(nodes); if (singleHeight < 0 || Math.abs(result.height - this.height) >= Epsilon || Math.abs(singleHeight - this.heightMetrics(oracle, offset).perLine) >= Epsilon) - oracle.heightChanged = true; - return result; + heightChangeFlag = true; + return replace(this, result); } else if (force || this.outdated) { - this.setHeight(oracle, oracle.heightForGap(offset, offset + this.length)); + this.setHeight(oracle.heightForGap(offset, offset + this.length)); this.outdated = false; } return this; @@ -9243,9 +9816,9 @@ class HeightMapBranch extends HeightMap { balanced(left, right) { if (left.size > 2 * right.size || right.size > 2 * left.size) return HeightMap.of(this.break ? [left, null, right] : [left, right]); - this.left = left; - this.right = right; - this.height = left.height + right.height; + this.left = replace(this.left, left); + this.right = replace(this.right, right); + this.setHeight(left.height + right.height); this.outdated = left.outdated || right.outdated; this.size = left.size + right.size; this.length = left.length + this.break + right.length; @@ -9430,7 +10003,7 @@ function visiblePixelRange(dom, paddingTop) { left = Math.max(left, parentRect.left); right = Math.min(right, parentRect.right); top = Math.max(top, parentRect.top); - bottom = parent == dom.parentNode ? parentRect.bottom : Math.min(bottom, parentRect.bottom); + bottom = Math.min(parent == dom.parentNode ? win.innerHeight : bottom, parentRect.bottom); } parent = style.position == "absolute" || style.position == "fixed" ? elt.offsetParent : elt.parentNode; } @@ -9444,6 +10017,11 @@ function visiblePixelRange(dom, paddingTop) { return { left: left - rect.left, right: Math.max(left, right) - rect.left, top: top - (rect.top + paddingTop), bottom: Math.max(top, bottom) - (rect.top + paddingTop) }; } +function inWindow(elt) { + let rect = elt.getBoundingClientRect(), win = elt.ownerDocument.defaultView || window; + return rect.left < win.innerWidth && rect.right > 0 && + rect.top < win.innerHeight && rect.bottom > 0; +} function fullPixelRange(dom, paddingTop) { let rect = dom.getBoundingClientRect(); return { left: 0, right: rect.right - rect.left, @@ -9453,10 +10031,11 @@ function fullPixelRange(dom, paddingTop) { // lines within the viewport, as a kludge to keep the editor // responsive when a ridiculously long line is loaded into it. class LineGap { - constructor(from, to, size) { + constructor(from, to, size, displaySize) { this.from = from; this.to = to; this.size = size; + this.displaySize = displaySize; } static same(a, b) { if (a.length != b.length) @@ -9470,7 +10049,7 @@ class LineGap { } draw(viewState, wrapping) { return Decoration.replace({ - widget: new LineGapWidget(this.size * (wrapping ? viewState.scaleY : viewState.scaleX), wrapping) + widget: new LineGapWidget(this.displaySize * (wrapping ? viewState.scaleY : viewState.scaleX), wrapping) }).range(this.from, this.to); } } @@ -9508,7 +10087,7 @@ class ViewState { this.editorHeight = 0; // scrollDOM.clientHeight, unscaled this.editorWidth = 0; // scrollDOM.clientWidth, unscaled this.scrollTop = 0; // Last seen scrollDOM.scrollTop, scaled - this.scrolledToBottom = true; + this.scrolledToBottom = false; // The CSS-transformation scale of the editor (transformed size / // concrete size) this.scaleX = 1; @@ -9542,9 +10121,12 @@ class ViewState { this.heightOracle = new HeightOracle(guessWrapping); this.stateDeco = state.facet(decorations).filter(d => typeof d != "function"); this.heightMap = HeightMap.empty().applyChanges(this.stateDeco, Text.empty, this.heightOracle.setDoc(state.doc), [new ChangedRange(0, 0, 0, state.doc.length)]); - this.viewport = this.getViewport(0, null); + for (let i = 0; i < 2; i++) { + this.viewport = this.getViewport(0, null); + if (!this.updateForViewport()) + break; + } this.updateViewportLines(); - this.updateForViewport(); this.lineGaps = this.ensureLineGaps([]); this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(this, false))); this.computeVisibleRanges(); @@ -9559,13 +10141,18 @@ class ViewState { } } this.viewports = viewports.sort((a, b) => a.from - b.from); + return this.updateScaler(); + } + updateScaler() { + let scaler = this.scaler; this.scaler = this.heightMap.height <= 7000000 /* VP.MaxDOMHeight */ ? IdScaler : new BigScaler(this.heightOracle, this.heightMap, this.viewports); + return scaler.eq(this.scaler) ? 0 : 2 /* UpdateFlag.Height */; } updateViewportLines() { this.viewportLines = []; this.heightMap.forEachLine(this.viewport.from, this.viewport.to, this.heightOracle.setDoc(this.state.doc), 0, 0, block => { - this.viewportLines.push(this.scaler.scale == 1 ? block : scaleBlock(block, this.scaler)); + this.viewportLines.push(scaleBlock(block, this.scaler)); }); } update(update, scrollTarget = null) { @@ -9576,8 +10163,9 @@ class ViewState { let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(prevDeco, this.stateDeco, update ? update.changes : ChangeSet.empty(this.state.doc.length))); let prevHeight = this.heightMap.height; let scrollAnchor = this.scrolledToBottom ? null : this.scrollAnchorAt(this.scrollTop); + clearHeightChangeFlag(); this.heightMap = this.heightMap.applyChanges(this.stateDeco, update.startState.doc, this.heightOracle.setDoc(this.state.doc), heightChanges); - if (this.heightMap.height != prevHeight) + if (this.heightMap.height != prevHeight || heightChangeFlag) update.flags |= 2 /* UpdateFlag.Height */; if (scrollAnchor) { this.scrollAnchorPos = update.changes.mapPos(scrollAnchor.from, -1); @@ -9591,15 +10179,14 @@ class ViewState { if (scrollTarget && (scrollTarget.range.head < viewport.from || scrollTarget.range.head > viewport.to) || !this.viewportIsAppropriate(viewport)) viewport = this.getViewport(0, scrollTarget); - let updateLines = !update.changes.empty || (update.flags & 2 /* UpdateFlag.Height */) || - viewport.from != this.viewport.from || viewport.to != this.viewport.to; + let viewportChange = viewport.from != this.viewport.from || viewport.to != this.viewport.to; this.viewport = viewport; - this.updateForViewport(); - if (updateLines) + update.flags |= this.updateForViewport(); + if (viewportChange || !update.changes.empty || (update.flags & 2 /* UpdateFlag.Height */)) this.updateViewportLines(); if (this.lineGaps.length || this.viewport.to - this.viewport.from > (2000 /* LG.Margin */ << 1)) this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes))); - update.flags |= this.computeVisibleRanges(); + update.flags |= this.computeVisibleRanges(update.changes); if (scrollTarget) this.scrollTarget = scrollTarget; if (!this.mustEnforceCursorAssoc && update.selectionSet && update.view.lineWrapping && @@ -9620,10 +10207,11 @@ class ViewState { let result = 0, bias = 0; if (domRect.width && domRect.height) { let { scaleX, scaleY } = getScale(dom, domRect); - if (this.scaleX != scaleX || this.scaleY != scaleY) { + if (scaleX > .005 && Math.abs(this.scaleX - scaleX) > .005 || + scaleY > .005 && Math.abs(this.scaleY - scaleY) > .005) { this.scaleX = scaleX; this.scaleY = scaleY; - result |= 8 /* UpdateFlag.Geometry */; + result |= 16 /* UpdateFlag.Geometry */; refresh = measureContent = true; } } @@ -9633,13 +10221,13 @@ class ViewState { if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) { this.paddingTop = paddingTop; this.paddingBottom = paddingBottom; - result |= 8 /* UpdateFlag.Geometry */ | 2 /* UpdateFlag.Height */; + result |= 16 /* UpdateFlag.Geometry */ | 2 /* UpdateFlag.Height */; } if (this.editorWidth != view.scrollDOM.clientWidth) { if (oracle.lineWrapping) measureContent = true; this.editorWidth = view.scrollDOM.clientWidth; - result |= 8 /* UpdateFlag.Geometry */; + result |= 16 /* UpdateFlag.Geometry */; } let scrollTop = view.scrollDOM.scrollTop * this.scaleY; if (this.scrollTop != scrollTop) { @@ -9657,13 +10245,13 @@ class ViewState { if (inView) measureContent = true; } - if (!this.inView && !this.scrollTarget) + if (!this.inView && !this.scrollTarget && !inWindow(view.dom)) return 0; let contentWidth = domRect.width; if (this.contentDOMWidth != contentWidth || this.editorHeight != view.scrollDOM.clientHeight) { this.contentDOMWidth = domRect.width; this.editorHeight = view.scrollDOM.clientHeight; - result |= 8 /* UpdateFlag.Geometry */; + result |= 16 /* UpdateFlag.Geometry */; } if (measureContent) { let lineHeights = view.docView.measureVisibleLineHeights(this.viewport); @@ -9674,27 +10262,30 @@ class ViewState { refresh = lineHeight > 0 && oracle.refresh(whiteSpace, lineHeight, charWidth, textHeight, contentWidth / charWidth, lineHeights); if (refresh) { view.docView.minWidth = 0; - result |= 8 /* UpdateFlag.Geometry */; + result |= 16 /* UpdateFlag.Geometry */; } } if (dTop > 0 && dBottom > 0) bias = Math.max(dTop, dBottom); else if (dTop < 0 && dBottom < 0) bias = Math.min(dTop, dBottom); - oracle.heightChanged = false; + clearHeightChangeFlag(); for (let vp of this.viewports) { let heights = vp.from == this.viewport.from ? lineHeights : view.docView.measureVisibleLineHeights(vp); this.heightMap = (refresh ? HeightMap.empty().applyChanges(this.stateDeco, Text.empty, this.heightOracle, [new ChangedRange(0, 0, 0, view.state.doc.length)]) : this.heightMap).updateHeight(oracle, 0, refresh, new MeasuredHeights(vp.from, heights)); } - if (oracle.heightChanged) + if (heightChangeFlag) result |= 2 /* UpdateFlag.Height */; } let viewportChange = !this.viewportIsAppropriate(this.viewport, bias) || this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to); - if (viewportChange) + if (viewportChange) { + if (result & 2 /* UpdateFlag.Height */) + result |= this.updateScaler(); this.viewport = this.getViewport(bias, this.scrollTarget); - this.updateForViewport(); + result |= this.updateForViewport(); + } if ((result & 2 /* UpdateFlag.Height */) || viewportChange) this.updateViewportLines(); if (this.lineGaps.length || this.viewport.to - this.viewport.from > (2000 /* LG.Margin */ << 1)) @@ -9760,7 +10351,7 @@ class ViewState { let mapped = []; for (let gap of gaps) if (!changes.touchesRange(gap.from, gap.to)) - mapped.push(new LineGap(changes.mapPos(gap.from), changes.mapPos(gap.to), gap.size)); + mapped.push(new LineGap(changes.mapPos(gap.from), changes.mapPos(gap.to), gap.size, gap.displaySize)); return mapped; } // Computes positions in the viewport where the start or end of a @@ -9801,16 +10392,18 @@ class ViewState { if (lineStart > from) to = lineStart; } - gap = new LineGap(from, to, this.gapSize(line, from, to, structure)); + let size = this.gapSize(line, from, to, structure); + let displaySize = wrapping || size < 2000000 /* VP.MaxHorizGap */ ? size : 2000000 /* VP.MaxHorizGap */; + gap = new LineGap(from, to, size, displaySize); } gaps.push(gap); }; - for (let line of this.viewportLines) { - if (line.length < doubleMargin) - continue; + let checkLine = (line) => { + if (line.length < doubleMargin || line.type != BlockType.Text) + return; let structure = lineStructure(line.from, line.to, this.stateDeco); if (structure.total < doubleMargin) - continue; + return; let target = this.scrollTarget ? this.scrollTarget.range.head : null; let viewFrom, viewTo; if (wrapping) { @@ -9832,16 +10425,24 @@ class ViewState { else { let totalWidth = structure.total * this.heightOracle.charWidth; let marginWidth = margin * this.heightOracle.charWidth; + let horizOffset = 0; + if (totalWidth > 2000000 /* VP.MaxHorizGap */) + for (let old of current) { + if (old.from >= line.from && old.from < line.to && old.size != old.displaySize && + old.from * this.heightOracle.charWidth + horizOffset < this.pixelViewport.left) + horizOffset = old.size - old.displaySize; + } + let pxLeft = this.pixelViewport.left + horizOffset, pxRight = this.pixelViewport.right + horizOffset; let left, right; if (target != null) { let targetFrac = findFraction(structure, target); - let spaceFrac = ((this.pixelViewport.right - this.pixelViewport.left) / 2 + marginWidth) / totalWidth; + let spaceFrac = ((pxRight - pxLeft) / 2 + marginWidth) / totalWidth; left = targetFrac - spaceFrac; right = targetFrac + spaceFrac; } else { - left = (this.pixelViewport.left - marginWidth) / totalWidth; - right = (this.pixelViewport.right + marginWidth) / totalWidth; + left = (pxLeft - marginWidth) / totalWidth; + right = (pxRight + marginWidth) / totalWidth; } viewFrom = findPosition(structure, left); viewTo = findPosition(structure, right); @@ -9850,6 +10451,12 @@ class ViewState { addGap(line.from, viewFrom, line, structure); if (viewTo < line.to) addGap(viewTo, line.to, line, structure); + }; + for (let line of this.viewportLines) { + if (Array.isArray(line.type)) + line.type.forEach(checkLine); + else + checkLine(line); } return gaps; } @@ -9868,7 +10475,7 @@ class ViewState { this.lineGapDeco = Decoration.set(gaps.map(gap => gap.draw(this, this.heightOracle.lineWrapping))); } } - computeVisibleRanges() { + computeVisibleRanges(changes) { let deco = this.stateDeco; if (this.lineGaps.length) deco = deco.concat(this.lineGapDeco); @@ -9877,17 +10484,32 @@ class ViewState { span(from, to) { ranges.push({ from, to }); }, point() { } }, 20); - let changed = ranges.length != this.visibleRanges.length || - this.visibleRanges.some((r, i) => r.from != ranges[i].from || r.to != ranges[i].to); + let changed = 0; + if (ranges.length != this.visibleRanges.length) { + changed = 8 /* UpdateFlag.ViewportMoved */ | 4 /* UpdateFlag.Viewport */; + } + else { + for (let i = 0; i < ranges.length && !(changed & 8 /* UpdateFlag.ViewportMoved */); i++) { + let old = this.visibleRanges[i], nw = ranges[i]; + if (old.from != nw.from || old.to != nw.to) { + changed |= 4 /* UpdateFlag.Viewport */; + if (!(changes && changes.mapPos(old.from, -1) == nw.from && changes.mapPos(old.to, 1) == nw.to)) + changed |= 8 /* UpdateFlag.ViewportMoved */; + } + } + } this.visibleRanges = ranges; - return changed ? 4 /* UpdateFlag.Viewport */ : 0; + return changed; } lineBlockAt(pos) { - return (pos >= this.viewport.from && pos <= this.viewport.to && this.viewportLines.find(b => b.from <= pos && b.to >= pos)) || + return (pos >= this.viewport.from && pos <= this.viewport.to && + this.viewportLines.find(b => b.from <= pos && b.to >= pos)) || scaleBlock(this.heightMap.lineAt(pos, QueryType$1.ByPos, this.heightOracle, 0, 0), this.scaler); } lineBlockAtHeight(height) { - return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType$1.ByHeight, this.heightOracle, 0, 0), this.scaler); + return (height >= this.viewportLines[0].top && height <= this.viewportLines[this.viewportLines.length - 1].bottom && + this.viewportLines.find(l => l.top <= height && l.bottom >= height)) || + scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType$1.ByHeight, this.heightOracle, 0, 0), this.scaler); } scrollAnchorAt(scrollTop) { let block = this.lineBlockAtHeight(scrollTop + 8); @@ -9962,7 +10584,8 @@ function find(array, f) { const IdScaler = { toDOM(n) { return n; }, fromDOM(n) { return n; }, - scale: 1 + scale: 1, + eq(other) { return other == this; } }; // When the height is too big (> VP.MaxDOMHeight), scale down the // regions outside the viewports so that the total height is @@ -10005,6 +10628,12 @@ class BigScaler { domBase = vp.domBottom; } } + eq(other) { + if (!(other instanceof BigScaler)) + return false; + return this.scale == other.scale && this.viewports.length == other.viewports.length && + this.viewports.every((vp, i) => vp.from == other.viewports[i].from && vp.to == other.viewports[i].to); + } } function scaleBlock(block, scaler) { if (scaler.scale == 1) @@ -10056,7 +10685,8 @@ const baseTheme$1$3 = /*@__PURE__*/buildTheme("." + baseThemeID, { height: "100%", overflowX: "auto", position: "relative", - zIndex: 0 + zIndex: 0, + overflowAnchor: "none", }, ".cm-content": { margin: 0, @@ -10127,7 +10757,7 @@ const baseTheme$1$3 = /*@__PURE__*/buildTheme("." + baseThemeID, { display: "none" }, "&dark .cm-cursor": { - borderLeftColor: "#444" + borderLeftColor: "#ddd" }, ".cm-dropCursor": { position: "absolute" @@ -10193,7 +10823,8 @@ const baseTheme$1$3 = /*@__PURE__*/buildTheme("." + baseThemeID, { boxSizing: "border-box", position: "sticky", left: 0, - right: 0 + right: 0, + zIndex: 300 }, "&light .cm-panels": { backgroundColor: "#f5f5f5", @@ -10225,11 +10856,9 @@ const baseTheme$1$3 = /*@__PURE__*/buildTheme("." + baseThemeID, { display: "inline-block", verticalAlign: "top", }, - ".cm-highlightSpace:before": { - content: "attr(data-display)", - position: "absolute", - pointerEvents: "none", - color: "#888" + ".cm-highlightSpace": { + backgroundImage: "radial-gradient(circle at 50% 55%, #aaa 20%, transparent 5%)", + backgroundPosition: "center", }, ".cm-highlightTab": { backgroundImage: `url('data:image/svg+xml,')`, @@ -10277,383 +10906,6 @@ const baseTheme$1$3 = /*@__PURE__*/buildTheme("." + baseThemeID, { } }, lightDarkIDs); -const LineBreakPlaceholder = "\uffff"; -class DOMReader { - constructor(points, state) { - this.points = points; - this.text = ""; - this.lineSeparator = state.facet(EditorState.lineSeparator); - } - append(text) { - this.text += text; - } - lineBreak() { - this.text += LineBreakPlaceholder; - } - readRange(start, end) { - if (!start) - return this; - let parent = start.parentNode; - for (let cur = start;;) { - this.findPointBefore(parent, cur); - let oldLen = this.text.length; - this.readNode(cur); - let next = cur.nextSibling; - if (next == end) - break; - let view = ContentView.get(cur), nextView = ContentView.get(next); - if (view && nextView ? view.breakAfter : - (view ? view.breakAfter : isBlockElement(cur)) || - (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen)) - this.lineBreak(); - cur = next; - } - this.findPointBefore(parent, end); - return this; - } - readTextNode(node) { - let text = node.nodeValue; - for (let point of this.points) - if (point.node == node) - point.pos = this.text.length + Math.min(point.offset, text.length); - for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) { - let nextBreak = -1, breakSize = 1, m; - if (this.lineSeparator) { - nextBreak = text.indexOf(this.lineSeparator, off); - breakSize = this.lineSeparator.length; - } - else if (m = re.exec(text)) { - nextBreak = m.index; - breakSize = m[0].length; - } - this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak)); - if (nextBreak < 0) - break; - this.lineBreak(); - if (breakSize > 1) - for (let point of this.points) - if (point.node == node && point.pos > this.text.length) - point.pos -= breakSize - 1; - off = nextBreak + breakSize; - } - } - readNode(node) { - if (node.cmIgnore) - return; - let view = ContentView.get(node); - let fromView = view && view.overrideDOMText; - if (fromView != null) { - this.findPointInside(node, fromView.length); - for (let i = fromView.iter(); !i.next().done;) { - if (i.lineBreak) - this.lineBreak(); - else - this.append(i.value); - } - } - else if (node.nodeType == 3) { - this.readTextNode(node); - } - else if (node.nodeName == "BR") { - if (node.nextSibling) - this.lineBreak(); - } - else if (node.nodeType == 1) { - this.readRange(node.firstChild, null); - } - } - findPointBefore(node, next) { - for (let point of this.points) - if (point.node == node && node.childNodes[point.offset] == next) - point.pos = this.text.length; - } - findPointInside(node, length) { - for (let point of this.points) - if (node.nodeType == 3 ? point.node == node : node.contains(point.node)) - point.pos = this.text.length + (isAtEnd(node, point.node, point.offset) ? length : 0); - } -} -function isAtEnd(parent, node, offset) { - for (;;) { - if (!node || offset < maxOffset(node)) - return false; - if (node == parent) - return true; - offset = domIndex(node) + 1; - node = node.parentNode; - } -} -function isBlockElement(node) { - return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName); -} -class DOMPoint { - constructor(node, offset) { - this.node = node; - this.offset = offset; - this.pos = -1; - } -} - -class DOMChange { - constructor(view, start, end, typeOver) { - this.typeOver = typeOver; - this.bounds = null; - this.text = ""; - let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView; - if (view.state.readOnly && start > -1) { - // Ignore changes when the editor is read-only - this.newSel = null; - } - else if (start > -1 && (this.bounds = view.docView.domBoundsAround(start, end, 0))) { - let selPoints = iHead || iAnchor ? [] : selectionPoints(view); - let reader = new DOMReader(selPoints, view.state); - reader.readRange(this.bounds.startDOM, this.bounds.endDOM); - this.text = reader.text; - this.newSel = selectionFromPoints(selPoints, this.bounds.from); - } - else { - let domSel = view.observer.selectionRange; - let head = iHead && iHead.node == domSel.focusNode && iHead.offset == domSel.focusOffset || - !contains(view.contentDOM, domSel.focusNode) - ? view.state.selection.main.head - : view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset); - let anchor = iAnchor && iAnchor.node == domSel.anchorNode && iAnchor.offset == domSel.anchorOffset || - !contains(view.contentDOM, domSel.anchorNode) - ? view.state.selection.main.anchor - : view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset); - // iOS will refuse to select the block gaps when doing - // select-all. - // Chrome will put the selection *inside* them, confusing - // posFromDOM - let vp = view.viewport; - if ((browser.ios || browser.chrome) && view.state.selection.main.empty && head != anchor && - (vp.from > 0 || vp.to < view.state.doc.length)) { - let from = Math.min(head, anchor), to = Math.max(head, anchor); - let offFrom = vp.from - from, offTo = vp.to - to; - if ((offFrom == 0 || offFrom == 1 || from == 0) && (offTo == 0 || offTo == -1 || to == view.state.doc.length)) { - head = 0; - anchor = view.state.doc.length; - } - } - this.newSel = EditorSelection.single(anchor, head); - } - } -} -function applyDOMChange(view, domChange) { - let change; - let { newSel } = domChange, sel = view.state.selection.main; - let lastKey = view.inputState.lastKeyTime > Date.now() - 100 ? view.inputState.lastKeyCode : -1; - if (domChange.bounds) { - let { from, to } = domChange.bounds; - let preferredPos = sel.from, preferredSide = null; - // Prefer anchoring to end when Backspace is pressed (or, on - // Android, when something was deleted) - if (lastKey === 8 || browser.android && domChange.text.length < to - from) { - preferredPos = sel.to; - preferredSide = "end"; - } - let diff = findDiff(view.state.doc.sliceString(from, to, LineBreakPlaceholder), domChange.text, preferredPos - from, preferredSide); - if (diff) { - // Chrome inserts two newlines when pressing shift-enter at the - // end of a line. DomChange drops one of those. - if (browser.chrome && lastKey == 13 && - diff.toB == diff.from + 2 && domChange.text.slice(diff.from, diff.toB) == LineBreakPlaceholder + LineBreakPlaceholder) - diff.toB--; - change = { from: from + diff.from, to: from + diff.toA, - insert: Text.of(domChange.text.slice(diff.from, diff.toB).split(LineBreakPlaceholder)) }; - } - } - else if (newSel && (!view.hasFocus && view.state.facet(editable) || newSel.main.eq(sel))) { - newSel = null; - } - if (!change && !newSel) - return false; - if (!change && domChange.typeOver && !sel.empty && newSel && newSel.main.empty) { - // Heuristic to notice typing over a selected character - change = { from: sel.from, to: sel.to, insert: view.state.doc.slice(sel.from, sel.to) }; - } - else if (change && change.from >= sel.from && change.to <= sel.to && - (change.from != sel.from || change.to != sel.to) && - (sel.to - sel.from) - (change.to - change.from) <= 4) { - // If the change is inside the selection and covers most of it, - // assume it is a selection replace (with identical characters at - // the start/end not included in the diff) - change = { - from: sel.from, to: sel.to, - insert: view.state.doc.slice(sel.from, change.from).append(change.insert).append(view.state.doc.slice(change.to, sel.to)) - }; - } - else if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 && - /^\. ?$/.test(change.insert.toString()) && view.contentDOM.getAttribute("autocorrect") == "off") { - // Detect insert-period-on-double-space Mac and Android behavior, - // and transform it into a regular space insert. - if (newSel && change.insert.length == 2) - newSel = EditorSelection.single(newSel.main.anchor - 1, newSel.main.head - 1); - change = { from: sel.from, to: sel.to, insert: Text.of([" "]) }; - } - else if (browser.chrome && change && change.from == change.to && change.from == sel.head && - change.insert.toString() == "\n " && view.lineWrapping) { - // In Chrome, if you insert a space at the start of a wrapped - // line, it will actually insert a newline and a space, causing a - // bogus new line to be created in CodeMirror (#968) - if (newSel) - newSel = EditorSelection.single(newSel.main.anchor - 1, newSel.main.head - 1); - change = { from: sel.from, to: sel.to, insert: Text.of([" "]) }; - } - if (change) { - if (browser.ios && view.inputState.flushIOSKey()) - return true; - // Android browsers don't fire reasonable key events for enter, - // backspace, or delete. So this detects changes that look like - // they're caused by those keys, and reinterprets them as key - // events. (Some of these keys are also handled by beforeinput - // events and the pendingAndroidKey mechanism, but that's not - // reliable in all situations.) - if (browser.android && - ((change.from == sel.from && change.to == sel.to && - change.insert.length == 1 && change.insert.lines == 2 && - dispatchKey(view.contentDOM, "Enter", 13)) || - ((change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 || - lastKey == 8 && change.insert.length < change.to - change.from && change.to > sel.head) && - dispatchKey(view.contentDOM, "Backspace", 8)) || - (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 && - dispatchKey(view.contentDOM, "Delete", 46)))) - return true; - let text = change.insert.toString(); - if (view.inputState.composing >= 0) - view.inputState.composing++; - let defaultTr; - let defaultInsert = () => defaultTr || (defaultTr = applyDefaultInsert(view, change, newSel)); - if (!view.state.facet(inputHandler$1).some(h => h(view, change.from, change.to, text, defaultInsert))) - view.dispatch(defaultInsert()); - return true; - } - else if (newSel && !newSel.main.eq(sel)) { - let scrollIntoView = false, userEvent = "select"; - if (view.inputState.lastSelectionTime > Date.now() - 50) { - if (view.inputState.lastSelectionOrigin == "select") - scrollIntoView = true; - userEvent = view.inputState.lastSelectionOrigin; - } - view.dispatch({ selection: newSel, scrollIntoView, userEvent }); - return true; - } - else { - return false; - } -} -function applyDefaultInsert(view, change, newSel) { - let tr, startState = view.state, sel = startState.selection.main; - if (change.from >= sel.from && change.to <= sel.to && change.to - change.from >= (sel.to - sel.from) / 3 && - (!newSel || newSel.main.empty && newSel.main.from == change.from + change.insert.length) && - view.inputState.composing < 0) { - let before = sel.from < change.from ? startState.sliceDoc(sel.from, change.from) : ""; - let after = sel.to > change.to ? startState.sliceDoc(change.to, sel.to) : ""; - tr = startState.replaceSelection(view.state.toText(before + change.insert.sliceString(0, undefined, view.state.lineBreak) + after)); - } - else { - let changes = startState.changes(change); - let mainSel = newSel && newSel.main.to <= changes.newLength ? newSel.main : undefined; - // Try to apply a composition change to all cursors - if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 && - change.to <= sel.to && change.to >= sel.to - 10) { - let replaced = view.state.sliceDoc(change.from, change.to); - let compositionRange, composition = newSel && findCompositionNode(view, newSel.main.head); - if (composition) { - let dLen = change.insert.length - (change.to - change.from); - compositionRange = { from: composition.from, to: composition.to - dLen }; - } - else { - compositionRange = view.state.doc.lineAt(sel.head); - } - let offset = sel.to - change.to, size = sel.to - sel.from; - tr = startState.changeByRange(range => { - if (range.from == sel.from && range.to == sel.to) - return { changes, range: mainSel || range.map(changes) }; - let to = range.to - offset, from = to - replaced.length; - if (range.to - range.from != size || view.state.sliceDoc(from, to) != replaced || - // Unfortunately, there's no way to make multiple - // changes in the same node work without aborting - // composition, so cursors in the composition range are - // ignored. - range.to >= compositionRange.from && range.from <= compositionRange.to) - return { range }; - let rangeChanges = startState.changes({ from, to, insert: change.insert }), selOff = range.to - sel.to; - return { - changes: rangeChanges, - range: !mainSel ? range.map(rangeChanges) : - EditorSelection.range(Math.max(0, mainSel.anchor + selOff), Math.max(0, mainSel.head + selOff)) - }; - }); - } - else { - tr = { - changes, - selection: mainSel && startState.selection.replaceRange(mainSel) - }; - } - } - let userEvent = "input.type"; - if (view.composing || - view.inputState.compositionPendingChange && view.inputState.compositionEndedAt > Date.now() - 50) { - view.inputState.compositionPendingChange = false; - userEvent += ".compose"; - if (view.inputState.compositionFirstChange) { - userEvent += ".start"; - view.inputState.compositionFirstChange = false; - } - } - return startState.update(tr, { userEvent, scrollIntoView: true }); -} -function findDiff(a, b, preferredPos, preferredSide) { - let minLen = Math.min(a.length, b.length); - let from = 0; - while (from < minLen && a.charCodeAt(from) == b.charCodeAt(from)) - from++; - if (from == minLen && a.length == b.length) - return null; - let toA = a.length, toB = b.length; - while (toA > 0 && toB > 0 && a.charCodeAt(toA - 1) == b.charCodeAt(toB - 1)) { - toA--; - toB--; - } - if (preferredSide == "end") { - let adjust = Math.max(0, from - Math.min(toA, toB)); - preferredPos -= toA + adjust - from; - } - if (toA < from && a.length < b.length) { - let move = preferredPos <= from && preferredPos >= toA ? from - preferredPos : 0; - from -= move; - toB = from + (toB - toA); - toA = from; - } - else if (toB < from) { - let move = preferredPos <= from && preferredPos >= toB ? from - preferredPos : 0; - from -= move; - toA = from + (toA - toB); - toB = from; - } - return { from, toA, toB }; -} -function selectionPoints(view) { - let result = []; - if (view.root.activeElement != view.contentDOM) - return result; - let { anchorNode, anchorOffset, focusNode, focusOffset } = view.observer.selectionRange; - if (anchorNode) { - result.push(new DOMPoint(anchorNode, anchorOffset)); - if (focusNode != anchorNode || focusOffset != anchorOffset) - result.push(new DOMPoint(focusNode, focusOffset)); - } - return result; -} -function selectionFromPoints(points, base) { - if (points.length == 0) - return null; - let anchor = points[0].pos, head = points.length == 2 ? points[1].pos : anchor; - return anchor > -1 && head > -1 ? EditorSelection.single(anchor + base, head + base) : null; -} - const observeOptions = { childList: true, characterData: true, @@ -10668,6 +10920,7 @@ class DOMObserver { constructor(view) { this.view = view; this.active = false; + this.editContext = null; // The known selection. Kept in our own object, as opposed to just // directly accessing the selection because: // - Safari doesn't report the right selection in shadow DOM @@ -10689,6 +10942,7 @@ class DOMObserver { this.intersecting = false; this.gapIntersection = null; this.gaps = []; + this.printQuery = null; // Timeout for scheduling check of the parents that need scroll handlers this.parentCheck = -1; this.dom = view.contentDOM; @@ -10711,6 +10965,13 @@ class DOMObserver { else this.flush(); }); + if (window.EditContext && view.constructor.EDIT_CONTEXT !== false && + // Chrome <126 doesn't support inverted selections in edit context (#1392) + !(browser.chrome && browser.chrome_version < 126)) { + this.editContext = new EditContextManager(view); + if (view.state.facet(editable)) + view.contentDOM.editContext = this.editContext.editContext; + } if (useCharData) this.onCharData = (event) => { this.queue.push({ target: event.target, @@ -10722,6 +10983,8 @@ class DOMObserver { this.onResize = this.onResize.bind(this); this.onPrint = this.onPrint.bind(this); this.onScroll = this.onScroll.bind(this); + if (window.matchMedia) + this.printQuery = window.matchMedia("print"); if (typeof ResizeObserver == "function") { this.resizeScroll = new ResizeObserver(() => { var _a; @@ -10759,6 +11022,8 @@ class DOMObserver { onScroll(e) { if (this.intersecting) this.flush(false); + if (this.editContext) + this.view.requestMeasure(this.editContext.measureReq); this.onScrollChanged(e); } onResize() { @@ -10768,7 +11033,9 @@ class DOMObserver { this.view.requestMeasure(); }, 50); } - onPrint() { + onPrint(event) { + if ((event.type == "change" || !event.type) && !event.matches) + return; this.view.viewState.printing = true; this.view.measure(); setTimeout(() => { @@ -10789,7 +11056,7 @@ class DOMObserver { if (!this.readSelectionRange() || this.delayedAndroidKey) return; let { view } = this, sel = this.selectionRange; - if (view.state.facet(editable) ? view.root.activeElement != this.dom : !hasSelection(view.dom, sel)) + if (view.state.facet(editable) ? view.root.activeElement != this.dom : !hasSelection(this.dom, sel)) return; let context = sel.anchorNode && view.docView.nearest(sel.anchorNode); if (context && context.ignoreEvent(event)) { @@ -10813,9 +11080,12 @@ class DOMObserver { let { view } = this; // The Selection object is broken in shadow roots in Safari. See // https://github.com/codemirror/dev/issues/414 + let selection = getSelection(view.root); + if (!selection) + return false; let range = browser.safari && view.root.nodeType == 11 && - deepActiveElement(this.dom.ownerDocument) == this.dom && - safariSelectionRangeHack(this.view) || getSelection(view.root); + view.root.activeElement == this.dom && + safariSelectionRangeHack(this.view, selection) || selection; if (!range || this.selectionRange.eq(range)) return false; let local = hasSelection(this.dom, range); @@ -11012,8 +11282,9 @@ class DOMObserver { } let startState = this.view.state; let handled = applyDOMChange(this.view, domChange); - // The view wasn't updated - if (this.view.state == startState) + // The view wasn't updated but DOM/selection changes were seen. Reset the view. + if (this.view.state == startState && + (domChange.domChanged || domChange.newSel && !domChange.newSel.main.eq(this.view.state.selection.main))) this.view.update([]); return handled; } @@ -11046,16 +11317,37 @@ class DOMObserver { } addWindowListeners(win) { win.addEventListener("resize", this.onResize); - win.addEventListener("beforeprint", this.onPrint); + if (this.printQuery) { + if (this.printQuery.addEventListener) + this.printQuery.addEventListener("change", this.onPrint); + else + this.printQuery.addListener(this.onPrint); + } + else + win.addEventListener("beforeprint", this.onPrint); win.addEventListener("scroll", this.onScroll); win.document.addEventListener("selectionchange", this.onSelectionChange); } removeWindowListeners(win) { win.removeEventListener("scroll", this.onScroll); win.removeEventListener("resize", this.onResize); - win.removeEventListener("beforeprint", this.onPrint); + if (this.printQuery) { + if (this.printQuery.removeEventListener) + this.printQuery.removeEventListener("change", this.onPrint); + else + this.printQuery.removeListener(this.onPrint); + } + else + win.removeEventListener("beforeprint", this.onPrint); win.document.removeEventListener("selectionchange", this.onSelectionChange); } + update(update) { + if (this.editContext) { + this.editContext.update(update); + if (update.startState.facet(editable) != update.state.facet(editable)) + update.view.contentDOM.editContext = update.state.facet(editable) ? this.editContext.editContext : null; + } + } destroy() { var _a, _b, _c; this.stop(); @@ -11069,6 +11361,10 @@ class DOMObserver { clearTimeout(this.resizeTimeout); this.win.cancelAnimationFrame(this.delayedFlush); this.win.cancelAnimationFrame(this.flushingAndroidKey); + if (this.editContext) { + this.view.contentDOM.editContext = null; + this.editContext.destroy(); + } } } function findChild(cView, dom, dir) { @@ -11081,8 +11377,24 @@ function findChild(cView, dom, dir) { } return null; } +function buildSelectionRangeFromRange(view, range) { + let anchorNode = range.startContainer, anchorOffset = range.startOffset; + let focusNode = range.endContainer, focusOffset = range.endOffset; + let curAnchor = view.docView.domAtPos(view.state.selection.main.anchor); + // Since such a range doesn't distinguish between anchor and head, + // use a heuristic that flips it around if its end matches the + // current anchor. + if (isEquivalentPosition(curAnchor.node, curAnchor.offset, focusNode, focusOffset)) + [anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset]; + return { anchorNode, anchorOffset, focusNode, focusOffset }; +} // Used to work around a Safari Selection/shadow DOM bug (#414) -function safariSelectionRangeHack(view) { +function safariSelectionRangeHack(view, selection) { + if (selection.getComposedRanges) { + let range = selection.getComposedRanges(view.root)[0]; + if (range) + return buildSelectionRangeFromRange(view, range); + } let found = null; // Because Safari (at least in 2018-2021) doesn't provide regular // access to the selection inside a shadowroot, we have to perform a @@ -11097,17 +11409,209 @@ function safariSelectionRangeHack(view) { view.contentDOM.addEventListener("beforeinput", read, true); view.dom.ownerDocument.execCommand("indent"); view.contentDOM.removeEventListener("beforeinput", read, true); - if (!found) - return null; - let anchorNode = found.startContainer, anchorOffset = found.startOffset; - let focusNode = found.endContainer, focusOffset = found.endOffset; - let curAnchor = view.docView.domAtPos(view.state.selection.main.anchor); - // Since such a range doesn't distinguish between anchor and head, - // use a heuristic that flips it around if its end matches the - // current anchor. - if (isEquivalentPosition(curAnchor.node, curAnchor.offset, focusNode, focusOffset)) - [anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset]; - return { anchorNode, anchorOffset, focusNode, focusOffset }; + return found ? buildSelectionRangeFromRange(view, found) : null; +} +class EditContextManager { + constructor(view) { + // The document window for which the text in the context is + // maintained. For large documents, this may be smaller than the + // editor document. This window always includes the selection head. + this.from = 0; + this.to = 0; + // When applying a transaction, this is used to compare the change + // made to the context content to the change in the transaction in + // order to make the minimal changes to the context (since touching + // that sometimes breaks series of multiple edits made for a single + // user action on some Android keyboards) + this.pendingContextChange = null; + this.handlers = Object.create(null); + // Kludge to work around the fact that EditContext does not respond + // well to having its content updated during a composition (see #1472) + this.composing = null; + this.resetRange(view.state); + let context = this.editContext = new window.EditContext({ + text: view.state.doc.sliceString(this.from, this.to), + selectionStart: this.toContextPos(Math.max(this.from, Math.min(this.to, view.state.selection.main.anchor))), + selectionEnd: this.toContextPos(view.state.selection.main.head) + }); + this.handlers.textupdate = e => { + let main = view.state.selection.main, { anchor, head } = main; + let from = this.toEditorPos(e.updateRangeStart), to = this.toEditorPos(e.updateRangeEnd); + if (view.inputState.composing >= 0 && !this.composing) + this.composing = { contextBase: e.updateRangeStart, editorBase: from, drifted: false }; + let change = { from, to, insert: Text.of(e.text.split("\n")) }; + // If the window doesn't include the anchor, assume changes + // adjacent to a side go up to the anchor. + if (change.from == this.from && anchor < this.from) + change.from = anchor; + else if (change.to == this.to && anchor > this.to) + change.to = anchor; + // Edit contexts sometimes fire empty changes + if (change.from == change.to && !change.insert.length) { + let newSel = EditorSelection.single(this.toEditorPos(e.selectionStart), this.toEditorPos(e.selectionEnd)); + if (!newSel.main.eq(main)) + view.dispatch({ selection: newSel, userEvent: "select" }); + return; + } + if ((browser.mac || browser.android) && change.from == head - 1 && + /^\. ?$/.test(e.text) && view.contentDOM.getAttribute("autocorrect") == "off") + change = { from, to, insert: Text.of([e.text.replace(".", " ")]) }; + this.pendingContextChange = change; + if (!view.state.readOnly) { + let newLen = this.to - this.from + (change.to - change.from + change.insert.length); + applyDOMChangeInner(view, change, EditorSelection.single(this.toEditorPos(e.selectionStart, newLen), this.toEditorPos(e.selectionEnd, newLen))); + } + // If the transaction didn't flush our change, revert it so + // that the context is in sync with the editor state again. + if (this.pendingContextChange) { + this.revertPending(view.state); + this.setSelection(view.state); + } + }; + this.handlers.characterboundsupdate = e => { + let rects = [], prev = null; + for (let i = this.toEditorPos(e.rangeStart), end = this.toEditorPos(e.rangeEnd); i < end; i++) { + let rect = view.coordsForChar(i); + prev = (rect && new DOMRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top)) + || prev || new DOMRect; + rects.push(prev); + } + context.updateCharacterBounds(e.rangeStart, rects); + }; + this.handlers.textformatupdate = e => { + let deco = []; + for (let format of e.getTextFormats()) { + let lineStyle = format.underlineStyle, thickness = format.underlineThickness; + if (lineStyle != "None" && thickness != "None") { + let from = this.toEditorPos(format.rangeStart), to = this.toEditorPos(format.rangeEnd); + if (from < to) { + let style = `text-decoration: underline ${lineStyle == "Dashed" ? "dashed " : lineStyle == "Squiggle" ? "wavy " : ""}${thickness == "Thin" ? 1 : 2}px`; + deco.push(Decoration.mark({ attributes: { style } }).range(from, to)); + } + } + } + view.dispatch({ effects: setEditContextFormatting.of(Decoration.set(deco)) }); + }; + this.handlers.compositionstart = () => { + if (view.inputState.composing < 0) { + view.inputState.composing = 0; + view.inputState.compositionFirstChange = true; + } + }; + this.handlers.compositionend = () => { + view.inputState.composing = -1; + view.inputState.compositionFirstChange = null; + if (this.composing) { + let { drifted } = this.composing; + this.composing = null; + if (drifted) + this.reset(view.state); + } + }; + for (let event in this.handlers) + context.addEventListener(event, this.handlers[event]); + this.measureReq = { read: view => { + this.editContext.updateControlBounds(view.contentDOM.getBoundingClientRect()); + let sel = getSelection(view.root); + if (sel && sel.rangeCount) + this.editContext.updateSelectionBounds(sel.getRangeAt(0).getBoundingClientRect()); + } }; + } + applyEdits(update) { + let off = 0, abort = false, pending = this.pendingContextChange; + update.changes.iterChanges((fromA, toA, _fromB, _toB, insert) => { + if (abort) + return; + let dLen = insert.length - (toA - fromA); + if (pending && toA >= pending.to) { + if (pending.from == fromA && pending.to == toA && pending.insert.eq(insert)) { + pending = this.pendingContextChange = null; // Match + off += dLen; + this.to += dLen; + return; + } + else { // Mismatch, revert + pending = null; + this.revertPending(update.state); + } + } + fromA += off; + toA += off; + if (toA <= this.from) { // Before the window + this.from += dLen; + this.to += dLen; + } + else if (fromA < this.to) { // Overlaps with window + if (fromA < this.from || toA > this.to || (this.to - this.from) + insert.length > 30000 /* CxVp.MaxSize */) { + abort = true; + return; + } + this.editContext.updateText(this.toContextPos(fromA), this.toContextPos(toA), insert.toString()); + this.to += dLen; + } + off += dLen; + }); + if (pending && !abort) + this.revertPending(update.state); + return !abort; + } + update(update) { + let reverted = this.pendingContextChange; + if (this.composing && (this.composing.drifted || update.transactions.some(tr => !tr.isUserEvent("input.type") && tr.changes.touchesRange(this.from, this.to)))) { + this.composing.drifted = true; + this.composing.editorBase = update.changes.mapPos(this.composing.editorBase); + } + else if (!this.applyEdits(update) || !this.rangeIsValid(update.state)) { + this.pendingContextChange = null; + this.reset(update.state); + } + else if (update.docChanged || update.selectionSet || reverted) { + this.setSelection(update.state); + } + if (update.geometryChanged || update.docChanged || update.selectionSet) + update.view.requestMeasure(this.measureReq); + } + resetRange(state) { + let { head } = state.selection.main; + this.from = Math.max(0, head - 10000 /* CxVp.Margin */); + this.to = Math.min(state.doc.length, head + 10000 /* CxVp.Margin */); + } + reset(state) { + this.resetRange(state); + this.editContext.updateText(0, this.editContext.text.length, state.doc.sliceString(this.from, this.to)); + this.setSelection(state); + } + revertPending(state) { + let pending = this.pendingContextChange; + this.pendingContextChange = null; + this.editContext.updateText(this.toContextPos(pending.from), this.toContextPos(pending.from + pending.insert.length), state.doc.sliceString(pending.from, pending.to)); + } + setSelection(state) { + let { main } = state.selection; + let start = this.toContextPos(Math.max(this.from, Math.min(this.to, main.anchor))); + let end = this.toContextPos(main.head); + if (this.editContext.selectionStart != start || this.editContext.selectionEnd != end) + this.editContext.updateSelection(start, end); + } + rangeIsValid(state) { + let { head } = state.selection.main; + return !(this.from > 0 && head - this.from < 500 /* CxVp.MinMargin */ || + this.to < state.doc.length && this.to - head < 500 /* CxVp.MinMargin */ || + this.to - this.from > 10000 /* CxVp.Margin */ * 3); + } + toEditorPos(contextPos, clipLen = this.to - this.from) { + contextPos = Math.min(contextPos, clipLen); + let c = this.composing; + return c && c.drifted ? c.editorBase + (contextPos - c.contextBase) : contextPos + this.from; + } + toContextPos(editorPos) { + let c = this.composing; + return c && c.drifted ? c.contextBase + (editorPos - c.editorBase) : editorPos - this.from; + } + destroy() { + for (let event in this.handlers) + this.editContext.removeEventListener(event, this.handlers[event]); + } } // The editor's update state machine looks something like this: @@ -11183,6 +11687,7 @@ class EditorView { view, so that the user can see the editor. */ constructor(config = {}) { + var _a; this.plugins = []; this.pluginMap = new Map; this.editorAttrs = {}; @@ -11234,6 +11739,8 @@ class EditorView { this.updateAttrs(); this.updateState = 0 /* UpdateState.Idle */; this.requestMeasure(); + if ((_a = document.fonts) === null || _a === void 0 ? void 0 : _a.ready) + document.fonts.ready.then(() => this.requestMeasure()); } dispatch(...input) { let trs = input.length == 1 && input[0] instanceof Transaction ? input @@ -11330,6 +11837,8 @@ class EditorView { this.viewState.mustMeasureContent = true; if (redrawn || attrsChanged || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent) this.requestMeasure(); + if (redrawn) + this.docViewUpdate(); if (!update.empty) for (let listener of this.state.facet(updateListener)) { try { @@ -11417,6 +11926,19 @@ class EditorView { if (prevSpecs != specs) this.inputState.ensureHandlers(this.plugins); } + docViewUpdate() { + for (let plugin of this.plugins) { + let val = plugin.value; + if (val && val.docViewUpdate) { + try { + val.docViewUpdate(this); + } + catch (e) { + logException(this.state, e, "doc view update listener"); + } + } + } + } /** @internal */ @@ -11487,6 +12009,8 @@ class EditorView { this.inputState.update(update); this.updateAttrs(); redrawn = this.docView.update(update); + if (redrawn) + this.docViewUpdate(); } for (let i = 0; i < measuring.length; i++) if (measured[i] != BadMeasure) { @@ -11549,6 +12073,7 @@ class EditorView { spellcheck: "false", autocorrect: "off", autocapitalize: "off", + writingsuggestions: "false", translate: "no", contenteditable: !this.state.facet(editable) ? "false" : "true", class: "cm-content", @@ -11682,7 +12207,7 @@ class EditorView { /** Find the line block around the given document position. A line block is a range delimited on both sides by either a - non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^replace) line breaks, or the + non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^replace) line break, or the start/end of the document. It will usually just hold a line of text, but may be broken into multiple textblocks by block widgets. @@ -11914,6 +12439,8 @@ class EditorView { calling this. */ destroy() { + if (this.root.activeElement == this.contentDOM) + this.contentDOM.blur(); for (let plugin of this.plugins) plugin.destroy(this); this.plugins = []; @@ -11951,6 +12478,25 @@ class EditorView { return scrollIntoView$1.of(new ScrollTarget(EditorSelection.cursor(ref.from), "start", "start", ref.top - scrollTop, scrollLeft, true)); } /** + Enable or disable tab-focus mode, which disables key bindings + for Tab and Shift-Tab, letting the browser's default + focus-changing behavior go through instead. This is useful to + prevent trapping keyboard users in your editor. + + Without argument, this toggles the mode. With a boolean, it + enables (true) or disables it (false). Given a number, it + temporarily enables the mode until that number of milliseconds + have passed or another non-Tab key is pressed. + */ + setTabFocusMode(to) { + if (to == null) + this.inputState.tabFocusMode = this.inputState.tabFocusMode < 0 ? 0 : -1; + else if (typeof to == "boolean") + this.inputState.tabFocusMode = to ? 0 : -1; + else if (this.inputState.tabFocusMode != 0) + this.inputState.tabFocusMode = Date.now() + to; + } + /** Returns an extension that can be used to add DOM event handlers. The value should be an object mapping event names to handler functions. For any given event, such functions are ordered by @@ -12043,6 +12589,22 @@ dispatching the custom behavior as a separate transaction. */ EditorView.inputHandler = inputHandler$1; /** +Functions provided in this facet will be used to transform text +pasted or dropped into the editor. +*/ +EditorView.clipboardInputFilter = clipboardInputFilter; +/** +Transform text copied or dragged from the editor. +*/ +EditorView.clipboardOutputFilter = clipboardOutputFilter; +/** +Scroll handlers can override how things are scrolled into view. +If they return `true`, no further handling happens for the +scrolling. If they return false, the default scroll behavior is +applied. Scroll handlers should never initiate editor updates. +*/ +EditorView.scrollHandler = scrollHandler; +/** This facet can be used to provide functions that create effects to be dispatched when the editor's focus state changes. */ @@ -12355,8 +12917,9 @@ function buildKeymap(bindings, platform = currentPlatform) { let scopeObj = bound[scope] || (bound[scope] = Object.create(null)); if (!scopeObj._any) scopeObj._any = { preventDefault: false, stopPropagation: false, run: [] }; + let { any } = b; for (let key in scopeObj) - scopeObj[key].run.push(b.any); + scopeObj[key].run.push(view => any(view, currentKeyEvent)); } let name = b[platform] || b.key; if (!name) @@ -12369,7 +12932,9 @@ function buildKeymap(bindings, platform = currentPlatform) { } return bound; } +let currentKeyEvent = null; function runHandlers(map, event, view, scope) { + currentKeyEvent = event; let name = keyName(event); let charCode = codePointAt(name, 0), isChar = codePointSize(charCode) == name.length && name != " "; let prefix = "", handled = false, prevented = false, stopPropagation = false; @@ -12386,7 +12951,7 @@ function runHandlers(map, event, view, scope) { for (let cmd of binding.run) if (!ran.has(cmd)) { ran.add(cmd); - if (cmd(view, event)) { + if (cmd(view)) { if (binding.stopPropagation) stopPropagation = true; return true; @@ -12428,6 +12993,7 @@ function runHandlers(map, event, view, scope) { handled = true; if (handled && stopPropagation) event.stopPropagation(); + currentKeyEvent = null; return handled; } @@ -12511,11 +13077,17 @@ function getBase$1(view) { let left = view.textDirection == Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth * view.scaleX; return { left: left - view.scrollDOM.scrollLeft * view.scaleX, top: rect.top - view.scrollDOM.scrollTop * view.scaleY }; } -function wrappedLine(view, pos, inside) { - let range = EditorSelection.cursor(pos); - return { from: Math.max(inside.from, view.moveToLineBoundary(range, false, true).from), - to: Math.min(inside.to, view.moveToLineBoundary(range, true, true).from), - type: BlockType.Text }; +function wrappedLine(view, pos, side, inside) { + let coords = view.coordsAtPos(pos, side * 2); + if (!coords) + return inside; + let editorRect = view.dom.getBoundingClientRect(); + let y = (coords.top + coords.bottom) / 2; + let left = view.posAtCoords({ x: editorRect.left + 1, y }); + let right = view.posAtCoords({ x: editorRect.right - 1, y }); + if (left == null || right == null) + return inside; + return { from: Math.max(inside.from, Math.min(left, right)), to: Math.min(inside.to, Math.max(left, right)) }; } function rectanglesForRange(view, className, range) { if (range.to <= view.viewport.from || range.from >= view.viewport.to) @@ -12531,10 +13103,10 @@ function rectanglesForRange(view, className, range) { let visualStart = startBlock.type == BlockType.Text ? startBlock : null; let visualEnd = endBlock.type == BlockType.Text ? endBlock : null; if (visualStart && (view.lineWrapping || startBlock.widgetLineBreaks)) - visualStart = wrappedLine(view, from, visualStart); + visualStart = wrappedLine(view, from, 1, visualStart); if (visualEnd && (view.lineWrapping || endBlock.widgetLineBreaks)) - visualEnd = wrappedLine(view, to, visualEnd); - if (visualStart && visualEnd && visualStart.from == visualEnd.from) { + visualEnd = wrappedLine(view, to, -1, visualEnd); + if (visualStart && visualEnd && visualStart.from == visualEnd.from && visualStart.to == visualEnd.to) { return pieces(drawForLine(range.from, range.to, visualStart)); } else { @@ -12549,7 +13121,7 @@ function rectanglesForRange(view, className, range) { return pieces(top).concat(between).concat(pieces(bottom)); } function piece(left, top, right, bottom) { - return new RectangleMarker(className, left - base.left, top - base.top - 0.01 /* C.Epsilon */, right - left, bottom - top + 0.01 /* C.Epsilon */); + return new RectangleMarker(className, left - base.left, top - base.top, right - left, bottom - top); } function pieces({ top, bottom, horizontal }) { let pieces = []; @@ -12635,6 +13207,10 @@ class LayerView { update.view.requestMeasure(this.measureReq); } } + docViewUpdate(view) { + if (this.layer.updateOnDocViewUpdate !== false) + view.requestMeasure(this.measureReq); + } setOrder(state) { let pos = 0, order = state.facet(layerOrder); while (pos < order.length && order[pos] != this.layer) @@ -12690,7 +13266,7 @@ function layer(config) { ]; } -const CanHidePrimary = !browser.ios; // FIXME test IE +const CanHidePrimary = !(browser.ios && browser.webkit && browser.webkit_version < 534); const selectionConfig = /*@__PURE__*/Facet.define({ combine(configs) { return combineConfig(configs, { @@ -12785,14 +13361,19 @@ const selectionLayer = /*@__PURE__*/layer({ }); const themeSpec$1 = { ".cm-line": { - "& ::selection": { backgroundColor: "transparent !important" }, - "&::selection": { backgroundColor: "transparent !important" } + "& ::selection, &::selection": { backgroundColor: "transparent !important" }, + }, + ".cm-content": { + "& :focus": { + caretColor: "initial !important", + "&::selection, & ::selection": { + backgroundColor: "Highlight !important" + } + } } }; -if (CanHidePrimary) { - themeSpec$1[".cm-line"].caretColor = "transparent !important"; - themeSpec$1[".cm-content"] = { caretColor: "transparent !important" }; -} +if (CanHidePrimary) + themeSpec$1[".cm-line"].caretColor = themeSpec$1[".cm-content"].caretColor = "transparent !important"; const hideNativeSelection$1 = /*@__PURE__*/Prec.highest(/*@__PURE__*/EditorView.theme(themeSpec$1)); const setDropCursorPos = /*@__PURE__*/StateEffect.define({ @@ -12967,12 +13548,12 @@ class MatchDecorator { let changeFrom = 1e9, changeTo = -1; if (update.docChanged) update.changes.iterChanges((_f, _t, from, to) => { - if (to > update.view.viewport.from && from < update.view.viewport.to) { + if (to >= update.view.viewport.from && from <= update.view.viewport.to) { changeFrom = Math.min(from, changeFrom); changeTo = Math.max(to, changeTo); } }); - if (update.viewportChanged || changeTo - changeFrom > 1000) + if (update.viewportMoved || changeTo - changeFrom > 1000) return this.createDeco(update.view); if (changeTo > -1) return this.updateRange(update.view, deco.map(update.changes), changeFrom, changeTo); @@ -13486,7 +14067,7 @@ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class { if (tooltip.arrow && !tooltipView.dom.querySelector(".cm-tooltip > .cm-tooltip-arrow")) { let arrow = document.createElement("div"); arrow.className = "cm-tooltip-arrow"; - tooltipView.dom.insertBefore(arrow, before); + tooltipView.dom.appendChild(arrow); } tooltipView.dom.style.position = this.position; tooltipView.dom.style.top = Outside; @@ -13512,7 +14093,6 @@ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class { clearTimeout(this.measureTimeout); } readMeasure() { - let editor = this.view.dom.getBoundingClientRect(); let scaleX = 1, scaleY = 1, makeAbsolute = false; if (this.position == "fixed" && this.manager.tooltipViews.length) { let { dom } = this.manager.tooltipViews[0]; @@ -13541,9 +14121,13 @@ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class { ({ scaleX, scaleY } = this.view.viewState); } } + let visible = this.view.scrollDOM.getBoundingClientRect(), margins = getScrollMargins(this.view); return { - editor, - parent: this.parent ? this.container.getBoundingClientRect() : editor, + visible: { + left: visible.left + margins.left, top: visible.top + margins.top, + right: visible.right - margins.right, bottom: visible.bottom - margins.bottom + }, + parent: this.parent ? this.container.getBoundingClientRect() : this.view.dom.getBoundingClientRect(), pos: this.manager.tooltips.map((t, i) => { let tv = this.manager.tooltipViews[i]; return tv.getCoords ? tv.getCoords(t.pos) : this.view.coordsAtPos(t.pos); @@ -13561,16 +14145,16 @@ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class { for (let t of this.manager.tooltipViews) t.dom.style.position = "absolute"; } - let { editor, space, scaleX, scaleY } = measured; + let { visible, space, scaleX, scaleY } = measured; let others = []; for (let i = 0; i < this.manager.tooltips.length; i++) { let tooltip = this.manager.tooltips[i], tView = this.manager.tooltipViews[i], { dom } = tView; let pos = measured.pos[i], size = measured.size[i]; // Hide tooltips that are outside of the editor. - if (!pos || pos.bottom <= Math.max(editor.top, space.top) || - pos.top >= Math.min(editor.bottom, space.bottom) || - pos.right < Math.max(editor.left, space.left) - .1 || - pos.left > Math.min(editor.right, space.right) + .1) { + if (!pos || tooltip.clip !== false && (pos.bottom <= Math.max(visible.top, space.top) || + pos.top >= Math.min(visible.bottom, space.bottom) || + pos.right < Math.max(visible.left, space.left) - .1 || + pos.left > Math.min(visible.right, space.right) + .1)) { dom.style.top = Outside; continue; } @@ -13578,13 +14162,14 @@ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class { let arrowHeight = arrow ? 7 /* Arrow.Size */ : 0; let width = size.right - size.left, height = (_a = knownHeight.get(tView)) !== null && _a !== void 0 ? _a : size.bottom - size.top; let offset = tView.offset || noOffset, ltr = this.view.textDirection == Direction.LTR; - let left = size.width > space.right - space.left ? (ltr ? space.left : space.right - size.width) - : ltr ? Math.min(pos.left - (arrow ? 14 /* Arrow.Offset */ : 0) + offset.x, space.right - width) - : Math.max(space.left, pos.left - width + (arrow ? 14 /* Arrow.Offset */ : 0) - offset.x); + let left = size.width > space.right - space.left + ? (ltr ? space.left : space.right - size.width) + : ltr ? Math.max(space.left, Math.min(pos.left - (arrow ? 14 /* Arrow.Offset */ : 0) + offset.x, space.right - width)) + : Math.min(Math.max(space.left, pos.left - width + (arrow ? 14 /* Arrow.Offset */ : 0) - offset.x), space.right - width); let above = this.above[i]; if (!tooltip.strictSide && (above - ? pos.top - (size.bottom - size.top) - offset.y < space.top - : pos.bottom + (size.bottom - size.top) + offset.y > space.bottom) && + ? pos.top - height - arrowHeight - offset.y < space.top + : pos.bottom + height + arrowHeight + offset.y > space.bottom) && above == (space.bottom - pos.bottom > pos.top - space.top)) above = this.above[i] = !above; let spaceVert = (above ? pos.top - space.top : space.bottom - pos.bottom) - arrowHeight; @@ -13607,11 +14192,11 @@ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class { top = above ? r.top - height - 2 - arrowHeight : r.bottom + arrowHeight + 2; if (this.position == "absolute") { dom.style.top = (top - measured.parent.top) / scaleY + "px"; - dom.style.left = (left - measured.parent.left) / scaleX + "px"; + setLeftStyle(dom, (left - measured.parent.left) / scaleX); } else { dom.style.top = top / scaleY + "px"; - dom.style.left = left / scaleX + "px"; + setLeftStyle(dom, left / scaleX); } if (arrow) { let arrowLeft = pos.left + (ltr ? offset.x : -offset.x) - (left + 14 /* Arrow.Offset */ - 7 /* Arrow.Size */); @@ -13642,9 +14227,14 @@ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class { scroll() { this.maybeMeasure(); } } }); +function setLeftStyle(elt, value) { + let current = parseInt(elt.style.left, 10); + if (isNaN(current) || Math.abs(value - current) > 1) + elt.style.left = value + "px"; +} const baseTheme$4 = /*@__PURE__*/EditorView.baseTheme({ ".cm-tooltip": { - zIndex: 100, + zIndex: 500, boxSizing: "border-box" }, "&light .cm-tooltip": { @@ -13908,9 +14498,14 @@ class HoverPlugin { } const tooltipMargin = 4; function isInTooltip(tooltip, event) { - let rect = tooltip.getBoundingClientRect(); - return event.clientX >= rect.left - tooltipMargin && event.clientX <= rect.right + tooltipMargin && - event.clientY >= rect.top - tooltipMargin && event.clientY <= rect.bottom + tooltipMargin; + let { left, right, top, bottom } = tooltip.getBoundingClientRect(), arrow; + if (arrow = tooltip.querySelector(".cm-tooltip-arrow")) { + let arrowRect = arrow.getBoundingClientRect(); + top = Math.min(arrowRect.top, top); + bottom = Math.max(arrowRect.bottom, bottom); + } + return event.clientX >= left - tooltipMargin && event.clientX <= right + tooltipMargin && + event.clientY >= top - tooltipMargin && event.clientY <= bottom + tooltipMargin; } function isOverRange(view, from, to, x, y, margin) { let rect = view.scrollDOM.getBoundingClientRect(); @@ -13932,6 +14527,11 @@ pointer is before the position, 1 if after the position. Note that all hover tooltips are hosted within a single tooltip container element. This allows multiple tooltips over the same range to be "merged" together without overlapping. + +The return value is a valid [editor extension](https://codemirror.net/6/docs/ref/#state.Extension) +but also provides an `active` property holding a state field that +can be used to read the currently active tooltips produced by this +extension. */ function hoverTooltip(source, options = {}) { let setHover = StateEffect.define(); @@ -13968,11 +14568,14 @@ function hoverTooltip(source, options = {}) { }, provide: f => showHoverTooltip.from(f) }); - return [ - hoverState, - ViewPlugin.define(view => new HoverPlugin(view, source, hoverState, setHover, options.hoverTime || 300 /* Hover.Time */)), - showHoverTooltipHost - ]; + return { + active: hoverState, + extension: [ + hoverState, + ViewPlugin.define(view => new HoverPlugin(view, source, hoverState, setHover, options.hoverTime || 300 /* Hover.Time */)), + showHoverTooltipHost + ] + }; } /** Get the active tooltip view for a given tooltip, if available. @@ -14190,6 +14793,11 @@ Markers given to this facet should _only_ define an in all gutters for the line). */ const gutterLineClass = /*@__PURE__*/Facet.define(); +/** +Facet used to add a class to all gutter elements next to a widget. +Should not provide widgets with a `toDOM` method. +*/ +const gutterWidgetClass = /*@__PURE__*/Facet.define(); const defaults$1 = { class: "", renderEmptyElements: false, @@ -14261,8 +14869,9 @@ const gutterView = /*@__PURE__*/ViewPlugin.fromClass(class { let vpOverlap = Math.min(vpA.to, vpB.to) - Math.max(vpA.from, vpB.from); this.syncGutters(vpOverlap < (vpB.to - vpB.from) * 0.8); } - if (update.geometryChanged) - this.dom.style.minHeight = this.view.contentHeight + "px"; + if (update.geometryChanged) { + this.dom.style.minHeight = (this.view.contentHeight / this.view.scaleY) + "px"; + } if (this.view.state.facet(unfixGutters) != !this.fixed) { this.fixed = !this.fixed; this.dom.style.position = this.fixed ? "sticky" : ""; @@ -14399,9 +15008,14 @@ class UpdateContext { this.addElement(view, line, localMarkers); } widget(view, block) { - let marker = this.gutter.config.widgetMarker(view, block.widget, block); - if (marker) - this.addElement(view, block, [marker]); + let marker = this.gutter.config.widgetMarker(view, block.widget, block), markers = marker ? [marker] : null; + for (let cls of view.state.facet(gutterWidgetClass)) { + let marker = cls(view, block.widget, block); + if (marker) + (markers || (markers = [])).push(marker); + } + if (markers) + this.addElement(view, block, markers); } finish() { let gutter = this.gutter; @@ -14537,6 +15151,10 @@ function sameMarkers(a, b) { Facet used to provide markers to the line number gutter. */ const lineNumberMarkers = /*@__PURE__*/Facet.define(); +/** +Facet used to create markers in the line number gutter next to widgets. +*/ +const lineNumberWidgetMarker = /*@__PURE__*/Facet.define(); const lineNumberConfig = /*@__PURE__*/Facet.define({ combine(values) { return combineConfig(values, { formatNumber: String, domEventHandlers: {} }, { @@ -14571,7 +15189,14 @@ const lineNumberGutter = /*@__PURE__*/activeGutters.compute([lineNumberConfig], return null; return new NumberMarker(formatNumber(view, view.state.doc.lineAt(line.from).number)); }, - widgetMarker: () => null, + widgetMarker: (view, widget, block) => { + for (let m of view.state.facet(lineNumberWidgetMarker)) { + let result = m(view, widget, block); + if (result) + return result; + } + return null; + }, lineMarkerChange: update => update.startState.facet(lineNumberConfig) != update.state.facet(lineNumberConfig), initialSpacer(view) { return new NumberMarker(formatNumber(view, maxLineNumber(view.state.doc.lines))); @@ -14876,6 +15501,52 @@ class NodeType { An empty dummy node type to use when no actual type is available. */ NodeType.none = new NodeType("", Object.create(null), 0, 8 /* NodeFlag.Anonymous */); +/** +A node set holds a collection of node types. It is used to +compactly represent trees by storing their type ids, rather than a +full pointer to the type object, in a numeric array. Each parser +[has](#lr.LRParser.nodeSet) a node set, and [tree +buffers](#common.TreeBuffer) can only store collections of nodes +from the same set. A set can have a maximum of 2**16 (65536) node +types in it, so that the ids fit into 16-bit typed array slots. +*/ +class NodeSet { + /** + Create a set with the given types. The `id` property of each + type should correspond to its position within the array. + */ + constructor( + /** + The node types in this set, by id. + */ + types) { + this.types = types; + for (let i = 0; i < types.length; i++) + if (types[i].id != i) + throw new RangeError("Node type ids should correspond to array positions when creating a node set"); + } + /** + Create a copy of this set with some node properties added. The + arguments to this method can be created with + [`NodeProp.add`](#common.NodeProp.add). + */ + extend(...props) { + let newTypes = []; + for (let type of this.types) { + let newProps = null; + for (let source of props) { + let add = source(type); + if (add) { + if (!newProps) + newProps = Object.assign({}, type.props); + newProps[add[0].id] = add[1]; + } + } + newTypes.push(newProps ? new NodeType(type.name, newProps, type.id, type.flags) : type); + } + return new NodeSet(newTypes); + } +} const CachedNode = new WeakMap(), CachedInnerNode = new WeakMap(); /** Options that control iteration. Can be combined with the `|` @@ -15270,7 +15941,7 @@ class BaseNode { return resolveNode(this, pos, side, true); } matchContext(context) { - return matchNodeContext(this, context); + return matchNodeContext(this.parent, context); } enterUnfinishedNodesBefore(pos) { let scan = this.childBefore(pos), node = this; @@ -15395,7 +16066,7 @@ function getChildren(node, type, before, after) { } } function matchNodeContext(node, context, i = context.length - 1) { - for (let p = node.parent; i >= 0; p = p.parent) { + for (let p = node; i >= 0; p = p.parent) { if (!p) return false; if (!p.type.isAnonymous) { @@ -15731,7 +16402,7 @@ class TreeCursor { */ next(enter = true) { return this.move(1, enter); } /** - Move to the next node in a last-to-first pre-order traveral. A + Move to the next node in a last-to-first pre-order traversal. A node is followed by its last child or, if it has none, its previous sibling or the previous sibling of the first parent node that has one. @@ -15807,10 +16478,10 @@ class TreeCursor { if (mustLeave && leave) leave(this); mustLeave = this.type.isAnonymous; - if (this.nextSibling()) - break; if (!depth) return; + if (this.nextSibling()) + break; this.parent(); depth--; mustLeave = true; @@ -15824,11 +16495,11 @@ class TreeCursor { */ matchContext(context) { if (!this.buffer) - return matchNodeContext(this.node, context); + return matchNodeContext(this.node.parent, context); let { buffer } = this.buffer, { types } = buffer.set; for (let i = context.length - 1, d = this.stack.length - 1; i >= 0; d--) { if (d < 0) - return matchNodeContext(this.node, context, i); + return matchNodeContext(this._tree, context, i); let type = types[buffer.buffer[this.stack[d]]]; if (!type.isAnonymous) { if (context[i] && context[i] != type.name) @@ -15850,7 +16521,7 @@ function buildTree(data) { let contextHash = 0, lookAhead = 0; function takeNode(parentStart, minPos, children, positions, inRepeat, depth) { let { id, start, end, size } = cursor; - let lookAheadAtStart = lookAhead; + let lookAheadAtStart = lookAhead, contextAtStart = contextHash; while (size < 0) { cursor.next(); if (size == -1 /* SpecialRecord.Reuse */) { @@ -15891,7 +16562,7 @@ function buildTree(data) { while (cursor.pos > endPos) { if (localInRepeat >= 0 && cursor.id == localInRepeat && cursor.size >= 0) { if (cursor.end <= lastEnd - maxBufferLength) { - makeRepeatLeaf(localChildren, localPositions, start, lastGroup, cursor.end, lastEnd, localInRepeat, lookAheadAtStart); + makeRepeatLeaf(localChildren, localPositions, start, lastGroup, cursor.end, lastEnd, localInRepeat, lookAheadAtStart, contextAtStart); lastGroup = localChildren.length; lastEnd = cursor.end; } @@ -15905,15 +16576,15 @@ function buildTree(data) { } } if (localInRepeat >= 0 && lastGroup > 0 && lastGroup < localChildren.length) - makeRepeatLeaf(localChildren, localPositions, start, lastGroup, start, lastEnd, localInRepeat, lookAheadAtStart); + makeRepeatLeaf(localChildren, localPositions, start, lastGroup, start, lastEnd, localInRepeat, lookAheadAtStart, contextAtStart); localChildren.reverse(); localPositions.reverse(); if (localInRepeat > -1 && lastGroup > 0) { - let make = makeBalanced(type); + let make = makeBalanced(type, contextAtStart); node = balanceRange(type, localChildren, localPositions, 0, localChildren.length, 0, end - start, make, make); } else { - node = makeTree(type, localChildren, localPositions, end - start, lookAheadAtStart - end); + node = makeTree(type, localChildren, localPositions, end - start, lookAheadAtStart - end, contextAtStart); } } children.push(node); @@ -15951,7 +16622,7 @@ function buildTree(data) { positions.push(start - parentStart); } } - function makeBalanced(type) { + function makeBalanced(type, contextHash) { return (children, positions, length) => { let lookAhead = 0, lastI = children.length - 1, last, lookAheadProp; if (lastI >= 0 && (last = children[lastI]) instanceof Tree) { @@ -15960,19 +16631,19 @@ function buildTree(data) { if (lookAheadProp = last.prop(NodeProp.lookAhead)) lookAhead = positions[lastI] + last.length + lookAheadProp; } - return makeTree(type, children, positions, length, lookAhead); + return makeTree(type, children, positions, length, lookAhead, contextHash); }; } - function makeRepeatLeaf(children, positions, base, i, from, to, type, lookAhead) { + function makeRepeatLeaf(children, positions, base, i, from, to, type, lookAhead, contextHash) { let localChildren = [], localPositions = []; while (children.length > i) { localChildren.push(children.pop()); localPositions.push(positions.pop() + base - from); } - children.push(makeTree(nodeSet.types[type], localChildren, localPositions, to - from, lookAhead - to)); + children.push(makeTree(nodeSet.types[type], localChildren, localPositions, to - from, lookAhead - to, contextHash)); positions.push(from - base); } - function makeTree(type, children, positions, length, lookAhead = 0, props) { + function makeTree(type, children, positions, length, lookAhead, contextHash, props) { if (contextHash) { let pair = [NodeProp.contextHash, contextHash]; props = props ? [pair].concat(props) : [pair]; @@ -16131,6 +16802,58 @@ mkTree) { divide(children, positions, from, to, 0); return (mkTop || mkTree)(localChildren, localPositions, length); } +/** +Provides a way to associate values with pieces of trees. As long +as that part of the tree is reused, the associated values can be +retrieved from an updated tree. +*/ +class NodeWeakMap { + constructor() { + this.map = new WeakMap(); + } + setBuffer(buffer, index, value) { + let inner = this.map.get(buffer); + if (!inner) + this.map.set(buffer, inner = new Map); + inner.set(index, value); + } + getBuffer(buffer, index) { + let inner = this.map.get(buffer); + return inner && inner.get(index); + } + /** + Set the value for this syntax node. + */ + set(node, value) { + if (node instanceof BufferNode) + this.setBuffer(node.context.buffer, node.index, value); + else if (node instanceof TreeNode) + this.map.set(node.tree, value); + } + /** + Retrieve value for this syntax node, if it exists in the map. + */ + get(node) { + return node instanceof BufferNode ? this.getBuffer(node.context.buffer, node.index) + : node instanceof TreeNode ? this.map.get(node.tree) : undefined; + } + /** + Set the value for the node that a cursor currently points to. + */ + cursorSet(cursor, value) { + if (cursor.buffer) + this.setBuffer(cursor.buffer.buffer, cursor.index, value); + else + this.map.set(cursor.tree, value); + } + /** + Retrieve the value for the node that a cursor currently points + to. + */ + cursorGet(cursor) { + return cursor.buffer ? this.getBuffer(cursor.buffer.buffer, cursor.index) : this.map.get(cursor.tree); + } +} /** Tree fragments are used during [incremental @@ -16306,6 +17029,10 @@ class Tag { */ constructor( /** + The optional name of the base tag @internal + */ + name, + /** The set of this tag and all its parent tags, starting with this one itself and sorted in order of decreasing specificity. */ @@ -16319,6 +17046,7 @@ class Tag { The modifiers applied to this.base @internal */ modified) { + this.name = name; this.set = set; this.base = base; this.modified = modified; @@ -16327,17 +17055,20 @@ class Tag { */ this.id = nextTagID++; } - /** - Define a new tag. If `parent` is given, the tag is treated as a - sub-tag of that parent, and - [highlighters](#highlight.tagHighlighter) that don't mention - this tag will try to fall back to the parent tag (or grandparent - tag, etc). - */ - static define(parent) { + toString() { + let { name } = this; + for (let mod of this.modified) + if (mod.name) + name = `${mod.name}(${name})`; + return name; + } + static define(nameOrParent, parent) { + let name = typeof nameOrParent == "string" ? nameOrParent : "?"; + if (nameOrParent instanceof Tag) + parent = nameOrParent; if (parent === null || parent === void 0 ? void 0 : parent.base) throw new Error("Can not derive from a modified tag"); - let tag = new Tag([], null, []); + let tag = new Tag(name, [], null, []); tag.set.push(tag); if (parent) for (let t of parent.set) @@ -16356,8 +17087,8 @@ class Tag { example `m1(m2(m3(t1)))` is a subtype of `m1(m2(t1))`, `m1(m3(t1)`, and so on. */ - static defineModifier() { - let mod = new Modifier; + static defineModifier(name) { + let mod = new Modifier(name); return (tag) => { if (tag.modified.indexOf(mod) > -1) return tag; @@ -16367,7 +17098,8 @@ class Tag { } let nextModifierID = 0; class Modifier { - constructor() { + constructor(name) { + this.name = name; this.instances = []; this.id = nextModifierID++; } @@ -16377,7 +17109,7 @@ class Modifier { let exists = mods[0].instances.find(t => t.base == base && sameArray(mods, t.modified)); if (exists) return exists; - let set = [], tag = new Tag(set, base, mods); + let set = [], tag = new Tag(base.name, set, base, mods); for (let m of mods) m.instances.push(tag); let configs = powerSet(mods); @@ -16949,7 +17681,7 @@ const tags = { */ heading6: t(heading), /** - A prose separator (such as a horizontal rule). + A prose [content](#highlight.tags.content) separator (such as a horizontal rule). */ contentSeparator: t(content), /** @@ -17022,31 +17754,31 @@ const tags = { given element is being defined. Expected to be used with the various [name](#highlight.tags.name) tags. */ - definition: Tag.defineModifier(), + definition: Tag.defineModifier("definition"), /** [Modifier](#highlight.Tag^defineModifier) that indicates that something is constant. Mostly expected to be used with [variable names](#highlight.tags.variableName). */ - constant: Tag.defineModifier(), + constant: Tag.defineModifier("constant"), /** [Modifier](#highlight.Tag^defineModifier) used to indicate that a [variable](#highlight.tags.variableName) or [property name](#highlight.tags.propertyName) is being called or defined as a function. */ - function: Tag.defineModifier(), + function: Tag.defineModifier("function"), /** [Modifier](#highlight.Tag^defineModifier) that can be applied to [names](#highlight.tags.name) to indicate that they belong to the language's standard environment. */ - standard: Tag.defineModifier(), + standard: Tag.defineModifier("standard"), /** [Modifier](#highlight.Tag^defineModifier) that indicates a given [names](#highlight.tags.name) is local to some scope. */ - local: Tag.defineModifier(), + local: Tag.defineModifier("local"), /** A generic variant [modifier](#highlight.Tag^defineModifier) that can be used to tag language-specific alternative variants of @@ -17055,8 +17787,13 @@ const tags = { [variable name](#highlight.tags.variableName) tags, since those come up a lot. */ - special: Tag.defineModifier() + special: Tag.defineModifier("special") }; +for (let name in tags) { + let val = tags[name]; + if (val instanceof Tag) + val.name = name; +} /** This is a highlighter that adds stable, predictable classes to tokens, for styling with external CSS. @@ -17145,6 +17882,19 @@ facet that stores language-specific data for that language. */ const languageDataProp = /*@__PURE__*/new NodeProp(); /** +Helper function to define a facet (to be added to the top syntax +node(s) for a language via +[`languageDataProp`](https://codemirror.net/6/docs/ref/#language.languageDataProp)), that will be +used to associate language data with the language. You +probably only need this when subclassing +[`Language`](https://codemirror.net/6/docs/ref/#language.Language). +*/ +function defineLanguageFacet(baseData) { + return Facet.define({ + combine: baseData ? values => values.concat(baseData) : undefined + }); +} +/** Syntax node prop used to register sublanguages. Should be added to the top level node type for the language. */ @@ -17272,6 +18022,34 @@ function topNodeAt(state, pos, side) { return tree; } /** +A subclass of [`Language`](https://codemirror.net/6/docs/ref/#language.Language) for use with Lezer +[LR parsers](https://lezer.codemirror.net/docs/ref#lr.LRParser) +parsers. +*/ +class LRLanguage extends Language { + constructor(data, parser, name) { + super(data, parser, [], name); + this.parser = parser; + } + /** + Define a language from a parser. + */ + static define(spec) { + let data = defineLanguageFacet(spec.languageData); + return new LRLanguage(data, spec.parser.configure({ + props: [languageDataProp.add(type => type.isTop ? data : undefined)] + }), spec.name); + } + /** + Create a new instance of this language with a reconfigured + version of its parser and optionally a new name. + */ + configure(options, name) { + return new LRLanguage(this.data, this.parser.configure(options), name || this.name); + } + get allowsNesting() { return this.parser.hasWrappers(); } +} +/** Get the syntax tree for a state, which is the current (possibly incomplete) parse tree of the active [language](https://codemirror.net/6/docs/ref/#language.Language), or the empty tree if there is no @@ -17720,6 +18498,34 @@ const language = /*@__PURE__*/Facet.define({ }) ] }); +/** +This class bundles a [language](https://codemirror.net/6/docs/ref/#language.Language) with an +optional set of supporting extensions. Language packages are +encouraged to export a function that optionally takes a +configuration object and returns a `LanguageSupport` instance, as +the main way for client code to use the package. +*/ +class LanguageSupport { + /** + Create a language support object. + */ + constructor( + /** + The language object. + */ + language, + /** + An optional set of supporting extensions. When nesting a + language in another language, the outer language is encouraged + to include the supporting extensions for its inner languages + in its own set of support extensions. + */ + support = []) { + this.language = language; + this.support = support; + this.extension = [language, support]; + } +} /** Facet that defines a way to provide a function that computes the @@ -17900,10 +18706,10 @@ const indentNodeProp = /*@__PURE__*/new NodeProp(); // Compute the indentation for a given position from the syntax tree. function syntaxIndentation(cx, ast, pos) { let stack = ast.resolveStack(pos); - let inner = stack.node.enterUnfinishedNodesBefore(pos); + let inner = ast.resolveInner(pos, -1).resolve(pos, 0).enterUnfinishedNodesBefore(pos); if (inner != stack.node) { let add = []; - for (let cur = inner; cur != stack.node; cur = cur.parent) + for (let cur = inner; cur && !(cur.from == stack.node.from && cur.type == stack.node.type); cur = cur.parent) add.push(cur); for (let i = add.length - 1; i >= 0; i--) stack = { node: add[i], next: stack }; @@ -17930,9 +18736,9 @@ function indentStrategy(tree) { let last = tree.lastChild, closed = last && close.indexOf(last.name) > -1; return cx => delimitedStrategy(cx, true, 1, undefined, closed && !ignoreClosed(cx) ? last.from : undefined); } - return tree.parent == null ? topIndent : null; + return tree.parent == null ? topIndent$1 : null; } -function topIndent() { return 0; } +function topIndent$1() { return 0; } /** Objects of this type provide context information and helper methods to indentation functions registered on syntax nodes. @@ -18026,11 +18832,29 @@ function bracketedAligned(context) { let next = tree.childAfter(pos); if (!next || next == last) return null; - if (!next.type.isSkipped) - return next.from < lineEnd ? openToken : null; + if (!next.type.isSkipped) { + if (next.from >= lineEnd) + return null; + let space = /^ */.exec(openLine.text.slice(openToken.to - openLine.from))[0].length; + return { from: openToken.from, to: openToken.to + space }; + } pos = next.to; } } +/** +An indentation strategy for delimited (usually bracketed) nodes. +Will, by default, indent one unit more than the parent's base +indent unless the line starts with a closing token. When `align` +is true and there are non-skipped nodes on the node's opening +line, the content of the node will be aligned with the end of the +opening node, like this: + + foo(bar, + baz) +*/ +function delimitedIndent({ closing, align = true, units = 1 }) { + return (context) => delimitedStrategy(context, align, units, closing); +} function delimitedStrategy(context, align, units, closing, closedAt) { let after = context.textAfter, space = after.match(/^\s*/)[0].length; let closed = closing && after.slice(space, space + closing.length) == closing || closedAt == context.pos + space; @@ -18039,6 +18863,25 @@ function delimitedStrategy(context, align, units, closing, closedAt) { return closed ? context.column(aligned.from) : context.column(aligned.to); return context.baseIndent + (closed ? 0 : context.unit * units); } +/** +An indentation strategy that aligns a node's content to its base +indentation. +*/ +const flatIndent = (context) => context.baseIndent; +/** +Creates an indentation strategy that, by default, indents +continued lines one unit more than the node's base indentation. +You can provide `except` to prevent indentation of lines that +match a pattern (for example `/^else\b/` in `if`/`else` +constructs), and you can change the amount of units used with the +`units` option. +*/ +function continuedIndent({ except, units = 1 } = {}) { + return (context) => { + let matchExcept = except && except.test(context.textAfter); + return context.baseIndent + (matchExcept ? 0 : units * context.unit); + }; +} const DontIndentBeyond = 200; /** Enables reindentation on input. When a language defines an @@ -18098,6 +18941,15 @@ that tree is foldable and return the range that can be collapsed when it is. */ const foldNodeProp = /*@__PURE__*/new NodeProp(); +/** +[Fold](https://codemirror.net/6/docs/ref/#language.foldNodeProp) function that folds everything but +the first and the last child of a syntax node. Useful for nodes +that start and end with delimiters. +*/ +function foldInside(node) { + let first = node.firstChild, last = node.lastChild; + return first && first.to < last.from ? { from: first.to, to: last.type.isError ? node.to : last.from } : null; +} function syntaxFolding(state, start, end) { let tree = syntaxTree(state); if (tree.length < end) @@ -18968,8 +19820,298 @@ class StringStream { */ current() { return this.string.slice(this.start, this.pos); } } + +function fullParser(spec) { + return { + name: spec.name || "", + token: spec.token, + blankLine: spec.blankLine || (() => { }), + startState: spec.startState || (() => true), + copyState: spec.copyState || defaultCopyState, + indent: spec.indent || (() => null), + languageData: spec.languageData || {}, + tokenTable: spec.tokenTable || noTokens + }; +} +function defaultCopyState(state) { + if (typeof state != "object") + return state; + let newState = {}; + for (let prop in state) { + let val = state[prop]; + newState[prop] = (val instanceof Array ? val.slice() : val); + } + return newState; +} +const IndentedFrom = /*@__PURE__*/new WeakMap(); +/** +A [language](https://codemirror.net/6/docs/ref/#language.Language) class based on a CodeMirror +5-style [streaming parser](https://codemirror.net/6/docs/ref/#language.StreamParser). +*/ +class StreamLanguage extends Language { + constructor(parser) { + let data = defineLanguageFacet(parser.languageData); + let p = fullParser(parser), self; + let impl = new class extends Parser { + createParse(input, fragments, ranges) { + return new Parse$1(self, input, fragments, ranges); + } + }; + super(data, impl, [], parser.name); + this.topNode = docID(data, this); + self = this; + this.streamParser = p; + this.stateAfter = new NodeProp({ perNode: true }); + this.tokenTable = parser.tokenTable ? new TokenTable(p.tokenTable) : defaultTokenTable; + } + /** + Define a stream language. + */ + static define(spec) { return new StreamLanguage(spec); } + /** + @internal + */ + getIndent(cx) { + let from = undefined; + let { overrideIndentation } = cx.options; + if (overrideIndentation) { + from = IndentedFrom.get(cx.state); + if (from != null && from < cx.pos - 1e4) + from = undefined; + } + let start = findState(this, cx.node.tree, cx.node.from, cx.node.from, from !== null && from !== void 0 ? from : cx.pos), statePos, state; + if (start) { + state = start.state; + statePos = start.pos + 1; + } + else { + state = this.streamParser.startState(cx.unit); + statePos = cx.node.from; + } + if (cx.pos - statePos > 10000 /* C.MaxIndentScanDist */) + return null; + while (statePos < cx.pos) { + let line = cx.state.doc.lineAt(statePos), end = Math.min(cx.pos, line.to); + if (line.length) { + let indentation = overrideIndentation ? overrideIndentation(line.from) : -1; + let stream = new StringStream(line.text, cx.state.tabSize, cx.unit, indentation < 0 ? undefined : indentation); + while (stream.pos < end - line.from) + readToken$1(this.streamParser.token, stream, state); + } + else { + this.streamParser.blankLine(state, cx.unit); + } + if (end == cx.pos) + break; + statePos = line.to + 1; + } + let line = cx.lineAt(cx.pos); + if (overrideIndentation && from == null) + IndentedFrom.set(cx.state, line.from); + return this.streamParser.indent(state, /^\s*(.*)/.exec(line.text)[1], cx); + } + get allowsNesting() { return false; } +} +function findState(lang, tree, off, startPos, before) { + let state = off >= startPos && off + tree.length <= before && tree.prop(lang.stateAfter); + if (state) + return { state: lang.streamParser.copyState(state), pos: off + tree.length }; + for (let i = tree.children.length - 1; i >= 0; i--) { + let child = tree.children[i], pos = off + tree.positions[i]; + let found = child instanceof Tree && pos < before && findState(lang, child, pos, startPos, before); + if (found) + return found; + } + return null; +} +function cutTree(lang, tree, from, to, inside) { + if (inside && from <= 0 && to >= tree.length) + return tree; + if (!inside && from == 0 && tree.type == lang.topNode) + inside = true; + for (let i = tree.children.length - 1; i >= 0; i--) { + let pos = tree.positions[i], child = tree.children[i], inner; + if (pos < to && child instanceof Tree) { + if (!(inner = cutTree(lang, child, from - pos, to - pos, inside))) + break; + return !inside ? inner + : new Tree(tree.type, tree.children.slice(0, i).concat(inner), tree.positions.slice(0, i + 1), pos + inner.length); + } + } + return null; +} +function findStartInFragments(lang, fragments, startPos, endPos, editorState) { + for (let f of fragments) { + let from = f.from + (f.openStart ? 25 : 0), to = f.to - (f.openEnd ? 25 : 0); + let found = from <= startPos && to > startPos && findState(lang, f.tree, 0 - f.offset, startPos, to), tree; + if (found && found.pos <= endPos && (tree = cutTree(lang, f.tree, startPos + f.offset, found.pos + f.offset, false))) + return { state: found.state, tree }; + } + return { state: lang.streamParser.startState(editorState ? getIndentUnit(editorState) : 4), tree: Tree.empty }; +} +let Parse$1 = class Parse { + constructor(lang, input, fragments, ranges) { + this.lang = lang; + this.input = input; + this.fragments = fragments; + this.ranges = ranges; + this.stoppedAt = null; + this.chunks = []; + this.chunkPos = []; + this.chunk = []; + this.chunkReused = undefined; + this.rangeIndex = 0; + this.to = ranges[ranges.length - 1].to; + let context = ParseContext.get(), from = ranges[0].from; + let { state, tree } = findStartInFragments(lang, fragments, from, this.to, context === null || context === void 0 ? void 0 : context.state); + this.state = state; + this.parsedPos = this.chunkStart = from + tree.length; + for (let i = 0; i < tree.children.length; i++) { + this.chunks.push(tree.children[i]); + this.chunkPos.push(tree.positions[i]); + } + if (context && this.parsedPos < context.viewport.from - 100000 /* C.MaxDistanceBeforeViewport */ && + ranges.some(r => r.from <= context.viewport.from && r.to >= context.viewport.from)) { + this.state = this.lang.streamParser.startState(getIndentUnit(context.state)); + context.skipUntilInView(this.parsedPos, context.viewport.from); + this.parsedPos = context.viewport.from; + } + this.moveRangeIndex(); + } + advance() { + let context = ParseContext.get(); + let parseEnd = this.stoppedAt == null ? this.to : Math.min(this.to, this.stoppedAt); + let end = Math.min(parseEnd, this.chunkStart + 2048 /* C.ChunkSize */); + if (context) + end = Math.min(end, context.viewport.to); + while (this.parsedPos < end) + this.parseLine(context); + if (this.chunkStart < this.parsedPos) + this.finishChunk(); + if (this.parsedPos >= parseEnd) + return this.finish(); + if (context && this.parsedPos >= context.viewport.to) { + context.skipUntilInView(this.parsedPos, parseEnd); + return this.finish(); + } + return null; + } + stopAt(pos) { + this.stoppedAt = pos; + } + lineAfter(pos) { + let chunk = this.input.chunk(pos); + if (!this.input.lineChunks) { + let eol = chunk.indexOf("\n"); + if (eol > -1) + chunk = chunk.slice(0, eol); + } + else if (chunk == "\n") { + chunk = ""; + } + return pos + chunk.length <= this.to ? chunk : chunk.slice(0, this.to - pos); + } + nextLine() { + let from = this.parsedPos, line = this.lineAfter(from), end = from + line.length; + for (let index = this.rangeIndex;;) { + let rangeEnd = this.ranges[index].to; + if (rangeEnd >= end) + break; + line = line.slice(0, rangeEnd - (end - line.length)); + index++; + if (index == this.ranges.length) + break; + let rangeStart = this.ranges[index].from; + let after = this.lineAfter(rangeStart); + line += after; + end = rangeStart + after.length; + } + return { line, end }; + } + skipGapsTo(pos, offset, side) { + for (;;) { + let end = this.ranges[this.rangeIndex].to, offPos = pos + offset; + if (side > 0 ? end > offPos : end >= offPos) + break; + let start = this.ranges[++this.rangeIndex].from; + offset += start - end; + } + return offset; + } + moveRangeIndex() { + while (this.ranges[this.rangeIndex].to < this.parsedPos) + this.rangeIndex++; + } + emitToken(id, from, to, offset) { + let size = 4; + if (this.ranges.length > 1) { + offset = this.skipGapsTo(from, offset, 1); + from += offset; + let len0 = this.chunk.length; + offset = this.skipGapsTo(to, offset, -1); + to += offset; + size += this.chunk.length - len0; + } + let last = this.chunk.length - 4; + if (size == 4 && last >= 0 && this.chunk[last] == id && this.chunk[last + 2] == from) + this.chunk[last + 2] = to; + else + this.chunk.push(id, from, to, size); + return offset; + } + parseLine(context) { + let { line, end } = this.nextLine(), offset = 0, { streamParser } = this.lang; + let stream = new StringStream(line, context ? context.state.tabSize : 4, context ? getIndentUnit(context.state) : 2); + if (stream.eol()) { + streamParser.blankLine(this.state, stream.indentUnit); + } + else { + while (!stream.eol()) { + let token = readToken$1(streamParser.token, stream, this.state); + if (token) + offset = this.emitToken(this.lang.tokenTable.resolve(token), this.parsedPos + stream.start, this.parsedPos + stream.pos, offset); + if (stream.start > 10000 /* C.MaxLineLength */) + break; + } + } + this.parsedPos = end; + this.moveRangeIndex(); + if (this.parsedPos < this.to) + this.parsedPos++; + } + finishChunk() { + let tree = Tree.build({ + buffer: this.chunk, + start: this.chunkStart, + length: this.parsedPos - this.chunkStart, + nodeSet, + topID: 0, + maxBufferLength: 2048 /* C.ChunkSize */, + reused: this.chunkReused + }); + tree = new Tree(tree.type, tree.children, tree.positions, tree.length, [[this.lang.stateAfter, this.lang.streamParser.copyState(this.state)]]); + this.chunks.push(tree); + this.chunkPos.push(this.chunkStart - this.ranges[0].from); + this.chunk = []; + this.chunkReused = undefined; + this.chunkStart = this.parsedPos; + } + finish() { + return new Tree(this.lang.topNode, this.chunks, this.chunkPos, this.parsedPos - this.ranges[0].from).balance(); + } +}; +function readToken$1(token, stream, state) { + stream.start = stream.pos; + for (let i = 0; i < 10; i++) { + let result = token(stream, state); + if (stream.pos > stream.start) + return result; + } + throw new Error("Stream parser failed to advance stream."); +} const noTokens = /*@__PURE__*/Object.create(null); const typeArray = [NodeType.none]; +const nodeSet = /*@__PURE__*/new NodeSet(typeArray); const warned = []; // Cache of node types by name and tags const byTag = /*@__PURE__*/Object.create(null); @@ -18989,6 +20131,16 @@ for (let [legacyName, name] of [ ["property", "propertyName"] ]) defaultTable[legacyName] = /*@__PURE__*/createTokenType(noTokens, name); +class TokenTable { + constructor(extra) { + this.extra = extra; + this.table = Object.assign(Object.create(null), defaultTable); + } + resolve(tag) { + return !tag ? 0 : this.table[tag] || (this.table[tag] = createTokenType(this.extra, tag)); + } +} +const defaultTokenTable = /*@__PURE__*/new TokenTable(noTokens); function warnForPart(part, msg) { if (warned.indexOf(part) > -1) return; @@ -19034,6 +20186,14 @@ function createTokenType(extra, tagStr) { typeArray.push(type); return type.id; } +function docID(data, lang) { + let type = NodeType.define({ id: typeArray.length, name: "Document", props: [ + languageDataProp.add(() => data), + indentNodeProp.add(() => cx => lang.getIndent(cx)) + ], top: true }); + typeArray.push(type); + return type; +} ({ rtl: /*@__PURE__*/Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "rtl" }, bidiIsolate: Direction.RTL }), ltr: /*@__PURE__*/Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "ltr" }, bidiIsolate: Direction.LTR }), @@ -19121,6 +20281,8 @@ function selectedLineRanges(state) { for (let r of state.selection.ranges) { let fromLine = state.doc.lineAt(r.from); let toLine = r.to <= fromLine.to ? fromLine : state.doc.lineAt(r.to); + if (toLine.from > fromLine.from && toLine.from == r.to) + toLine = r.to == fromLine.to + 1 ? fromLine : state.doc.lineAt(r.to - 1); let last = ranges.length - 1; if (last >= 0 && ranges[last].to > fromLine.from) ranges[last].to = toLine.to; @@ -19480,7 +20642,7 @@ class HistoryState { config.joinToEvent(tr, isAdjacent(lastEvent.changes, event.changes))) || // For compose (but not compose.start) events, always join with previous event userEvent == "input.type.compose")) { - done = updateBranch(done, done.length - 1, config.minDepth, new HistEvent(event.changes.compose(lastEvent.changes), conc(event.effects, lastEvent.effects), lastEvent.mapped, lastEvent.startSelection, none$1)); + done = updateBranch(done, done.length - 1, config.minDepth, new HistEvent(event.changes.compose(lastEvent.changes), conc(StateEffect.mapEffects(event.effects, lastEvent.changes), lastEvent.effects), lastEvent.mapped, lastEvent.startSelection, none$1)); } else { done = updateBranch(done, done.length, config.minDepth, event); @@ -19753,14 +20915,14 @@ Move the selection to the bracket matching the one it is currently on, if any. */ const cursorMatchingBracket = ({ state, dispatch }) => toMatchingBracket(state, dispatch, false); -function extendSel(view, how) { - let selection = updateSel(view.state.selection, range => { +function extendSel(target, how) { + let selection = updateSel(target.state.selection, range => { let head = how(range); return EditorSelection.range(range.anchor, head.head, head.goalColumn, head.bidiLevel || undefined); }); - if (selection.eq(view.state.selection)) + if (selection.eq(target.state.selection)) return false; - view.dispatch(setSel(view.state, selection)); + target.dispatch(setSel(target.state, selection)); return true; } function selectByChar(view, forward) { @@ -19892,17 +21054,23 @@ syntax tree. */ const selectParentSyntax = ({ state, dispatch }) => { let selection = updateSel(state.selection, range => { - var _a; - let stack = syntaxTree(state).resolveStack(range.from, 1); + let tree = syntaxTree(state), stack = tree.resolveStack(range.from, 1); + if (range.empty) { + let stackBefore = tree.resolveStack(range.from, -1); + if (stackBefore.node.from >= stack.node.from && stackBefore.node.to <= stack.node.to) + stack = stackBefore; + } for (let cur = stack; cur; cur = cur.next) { let { node } = cur; if (((node.from < range.from && node.to >= range.to) || (node.to > range.to && node.from <= range.from)) && - ((_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent)) + cur.next) return EditorSelection.range(node.to, node.from); } return range; }); + if (selection.eq(state.selection)) + return false; dispatch(setSel(state, selection)); return true; }; @@ -19965,9 +21133,9 @@ function skipAtomic(target, pos, forward) { }); return pos; } -const deleteByChar = (target, forward) => deleteBy(target, range => { +const deleteByChar = (target, forward, byIndentUnit) => deleteBy(target, range => { let pos = range.from, { state } = target, line = state.doc.lineAt(pos), before, targetPos; - if (!forward && pos > line.from && pos < line.from + 200 && + if (byIndentUnit && !forward && pos > line.from && pos < line.from + 200 && !/[^ \t]/.test(before = line.text.slice(0, pos - line.from))) { if (before[before.length - 1] == "\t") return pos - 1; @@ -19986,14 +21154,14 @@ const deleteByChar = (target, forward) => deleteBy(target, range => { return targetPos; }); /** -Delete the selection, or, for cursor selections, the character -before the cursor. +Delete the selection, or, for cursor selections, the character or +indentation unit before the cursor. */ -const deleteCharBackward = view => deleteByChar(view, false); +const deleteCharBackward = view => deleteByChar(view, false, true); /** Delete the selection or the character after the cursor. */ -const deleteCharForward = view => deleteByChar(view, true); +const deleteCharForward = view => deleteByChar(view, true, false); const deleteByGroup = (target, forward) => deleteBy(target, range => { let pos = range.head, { state } = target, line = state.doc.lineAt(pos); let categorize = state.charCategorizer(pos); @@ -20173,7 +21341,15 @@ const deleteLine = view => { to++; return { from, to }; })); - let selection = updateSel(state.selection, range => view.moveVertically(range, true)).map(changes); + let selection = updateSel(state.selection, range => { + let dist = undefined; + if (view.lineWrapping) { + let block = view.lineBlockAt(range.head), pos = view.coordsAtPos(range.head, range.assoc || 1); + if (pos) + dist = (block.bottom + view.documentTop) - pos.bottom + view.defaultLineHeight / 2; + } + return view.moveVertically(range, true, dist); + }).map(changes); view.dispatch({ changes, selection, scrollIntoView: true, userEvent: "delete.line" }); return true; }; @@ -20309,6 +21485,17 @@ const indentLess = ({ state, dispatch }) => { return true; }; /** +Enables or disables +[tab-focus mode](https://codemirror.net/6/docs/ref/#view.EditorView.setTabFocusMode). While on, this +prevents the editor's key bindings from capturing Tab or +Shift-Tab, making it possible for the user to move focus out of +the editor with the keyboard. +*/ +const toggleTabFocusMode = view => { + view.setTabFocusMode(); + return true; +}; +/** Array of key bindings containing the Emacs-style bindings that are available on macOS by default. @@ -20366,7 +21553,7 @@ property changed to `mac`.) - End: [`cursorLineBoundaryForward`](https://codemirror.net/6/docs/ref/#commands.cursorLineBoundaryForward) ([`selectLineBoundaryForward`](https://codemirror.net/6/docs/ref/#commands.selectLineBoundaryForward) with Shift) - Ctrl-Home (Cmd-Home on macOS): [`cursorDocStart`](https://codemirror.net/6/docs/ref/#commands.cursorDocStart) ([`selectDocStart`](https://codemirror.net/6/docs/ref/#commands.selectDocStart) with Shift) - Ctrl-End (Cmd-Home on macOS): [`cursorDocEnd`](https://codemirror.net/6/docs/ref/#commands.cursorDocEnd) ([`selectDocEnd`](https://codemirror.net/6/docs/ref/#commands.selectDocEnd) with Shift) - - Enter: [`insertNewlineAndIndent`](https://codemirror.net/6/docs/ref/#commands.insertNewlineAndIndent) + - Enter and Shift-Enter: [`insertNewlineAndIndent`](https://codemirror.net/6/docs/ref/#commands.insertNewlineAndIndent) - Ctrl-a (Cmd-a on macOS): [`selectAll`](https://codemirror.net/6/docs/ref/#commands.selectAll) - Backspace: [`deleteCharBackward`](https://codemirror.net/6/docs/ref/#commands.deleteCharBackward) - Delete: [`deleteCharForward`](https://codemirror.net/6/docs/ref/#commands.deleteCharForward) @@ -20394,7 +21581,7 @@ const standardKeymap = /*@__PURE__*/[ { key: "Mod-Home", run: cursorDocStart, shift: selectDocStart }, { key: "End", run: cursorLineBoundaryForward, shift: selectLineBoundaryForward, preventDefault: true }, { key: "Mod-End", run: cursorDocEnd, shift: selectDocEnd }, - { key: "Enter", run: insertNewlineAndIndent }, + { key: "Enter", run: insertNewlineAndIndent, shift: insertNewlineAndIndent }, { key: "Mod-a", run: selectAll }, { key: "Backspace", run: deleteCharBackward, shift: deleteCharBackward }, { key: "Delete", run: deleteCharForward }, @@ -20424,6 +21611,7 @@ The default keymap. Includes all bindings from - Shift-Ctrl-\\ (Shift-Cmd-\\ on macOS): [`cursorMatchingBracket`](https://codemirror.net/6/docs/ref/#commands.cursorMatchingBracket) - Ctrl-/ (Cmd-/ on macOS): [`toggleComment`](https://codemirror.net/6/docs/ref/#commands.toggleComment). - Shift-Alt-a: [`toggleBlockComment`](https://codemirror.net/6/docs/ref/#commands.toggleBlockComment). +- Ctrl-m (Alt-Shift-m on macOS): [`toggleTabFocusMode`](https://codemirror.net/6/docs/ref/#commands.toggleTabFocusMode). */ const defaultKeymap = /*@__PURE__*/[ { key: "Alt-ArrowLeft", mac: "Ctrl-ArrowLeft", run: cursorSyntaxLeft, shift: selectSyntaxLeft }, @@ -20442,7 +21630,8 @@ const defaultKeymap = /*@__PURE__*/[ { key: "Shift-Mod-k", run: deleteLine }, { key: "Shift-Mod-\\", run: cursorMatchingBracket }, { key: "Mod-/", run: toggleComment }, - { key: "Alt-A", run: toggleBlockComment } + { key: "Alt-A", run: toggleBlockComment }, + { key: "Ctrl-m", mac: "Shift-Alt-m", run: toggleTabFocusMode }, ].concat(standardKeymap); /** A binding that binds Tab to [`indentMore`](https://codemirror.net/6/docs/ref/#commands.indentMore) and @@ -20557,19 +21746,20 @@ class SearchCursor { let str = fromCodePoint(next), start = this.bufferStart + this.bufferPos; this.bufferPos += codePointSize(next); let norm = this.normalize(str); - for (let i = 0, pos = start;; i++) { - let code = norm.charCodeAt(i); - let match = this.match(code, pos, this.bufferPos + this.bufferStart); - if (i == norm.length - 1) { - if (match) { - this.value = match; - return this; + if (norm.length) + for (let i = 0, pos = start;; i++) { + let code = norm.charCodeAt(i); + let match = this.match(code, pos, this.bufferPos + this.bufferStart); + if (i == norm.length - 1) { + if (match) { + this.value = match; + return this; + } + break; } - break; + if (pos == start && i < str.length && str.charCodeAt(i) == code) + pos++; } - if (pos == start && i < str.length && str.charCodeAt(i) == code) - pos++; - } } } match(code, pos, end) { @@ -21117,9 +22307,11 @@ class StringQuery extends QueryType { } nextMatch(state, curFrom, curTo) { let cursor = stringCursor(this.spec, state, curTo, state.doc.length).nextOverlapping(); - if (cursor.done) - cursor = stringCursor(this.spec, state, 0, curFrom).nextOverlapping(); - return cursor.done ? null : cursor.value; + if (cursor.done) { + let end = Math.min(state.doc.length, curFrom + this.spec.unquoted.length); + cursor = stringCursor(this.spec, state, 0, end).nextOverlapping(); + } + return cursor.done || cursor.value.from == curFrom && cursor.value.to == curTo ? null : cursor.value; } // Searching in reverse is, rather than implementing an inverted search // cursor, done by scanning chunk after chunk forward. @@ -21137,8 +22329,10 @@ class StringQuery extends QueryType { } } prevMatch(state, curFrom, curTo) { - return this.prevMatchInRange(state, 0, curFrom) || - this.prevMatchInRange(state, curTo, state.doc.length); + let found = this.prevMatchInRange(state, 0, curFrom); + if (!found) + found = this.prevMatchInRange(state, Math.max(0, curTo - this.spec.unquoted.length), state.doc.length); + return found && (found.from != curFrom || found.to != curTo) ? found : null; } getReplacement(_result) { return this.spec.unquote(this.spec.replace); } matchAll(state, limit) { @@ -21368,9 +22562,10 @@ const replaceNext = /*@__PURE__*/searchCommand((view, { query }) => { let { state } = view, { from, to } = state.selection.main; if (state.readOnly) return false; - let next = query.nextMatch(state, from, from); - if (!next) + let match = query.nextMatch(state, from, from); + if (!match) return false; + let next = match; let changes = [], selection, replacement; let effects = []; if (next.from == from && next.to == to) { @@ -21380,7 +22575,7 @@ const replaceNext = /*@__PURE__*/searchCommand((view, { query }) => { effects.push(EditorView.announce.of(state.phrase("replaced match on line $", state.doc.lineAt(from).number) + ".")); } if (next) { - let off = changes.length == 0 || changes[0].from >= next.to ? 0 : next.to - next.from - replacement.length; + let off = changes.length == 0 || changes[0].from >= match.to ? 0 : match.to - match.from - replacement.length; selection = EditorSelection.single(next.from - off, next.to - off); effects.push(announceMatch(view, next)); effects.push(state.facet(searchConfigFacet).scrollToMatch(selection.main, view)); @@ -21695,14 +22890,27 @@ class CompletionContext { only return completions when either there is part of a completable entity before the cursor, or `explicit` is true. */ - explicit) { + explicit, + /** + The editor view. May be undefined if the context was created + in a situation where there is no such view available, such as + in synchronous updates via + [`CompletionResult.update`](https://codemirror.net/6/docs/ref/#autocomplete.CompletionResult.update) + or when called by test code. + */ + view) { this.state = state; this.pos = pos; this.explicit = explicit; + this.view = view; /** @internal */ this.abortListeners = []; + /** + @internal + */ + this.abortOnDocChange = false; } /** Get the extent, content, and (if there is a token) type of the @@ -21736,10 +22944,21 @@ class CompletionContext { Allows you to register abort handlers, which will be called when the query is [aborted](https://codemirror.net/6/docs/ref/#autocomplete.CompletionContext.aborted). + + By default, running queries will not be aborted for regular + typing or backspacing, on the assumption that they are likely to + return a result with a + [`validFor`](https://codemirror.net/6/docs/ref/#autocomplete.CompletionResult.validFor) field that + allows the result to be used after all. Passing `onDocChange: + true` will cause this query to be aborted for any document + change. */ - addEventListener(type, listener) { - if (type == "abort" && this.abortListeners) + addEventListener(type, listener, options) { + if (type == "abort" && this.abortListeners) { this.abortListeners.push(listener); + if (options && options.onDocChange) + this.abortOnDocChange = true; + } } } function toSet(chars) { @@ -21771,6 +22990,21 @@ function completeFromList(list) { return token || context.explicit ? { from: token ? token.from : context.pos, options, validFor } : null; }; } +/** +Wrap the given completion source so that it will not fire when the +cursor is in a syntax node with one of the given names. +*/ +function ifNotIn(nodes, source) { + return (context) => { + for (let pos = syntaxTree(context.state).resolveInner(context.pos, -1); pos; pos = pos.parent) { + if (nodes.indexOf(pos.name) > -1) + return null; + if (pos.type.isTop) + break; + } + return source(context); + }; +} class Option { constructor(completion, source, match, score) { this.completion = completion; @@ -21806,9 +23040,10 @@ function insertCompletionText(state, text, from, to) { if (range != main && from != to && state.sliceDoc(range.from + fromOff, range.from + toOff) != state.sliceDoc(from, to)) return { range }; + let lines = state.toText(text); return { - changes: { from: range.from + fromOff, to: to == main.from ? range.to : range.from + toOff, insert: text }, - range: EditorSelection.cursor(range.from + fromOff + text.length) + changes: { from: range.from + fromOff, to: to == main.from ? range.to : range.from + toOff, insert: lines }, + range: EditorSelection.cursor(range.from + fromOff + lines.length) }; })), { scrollIntoView: true, userEvent: "input.complete" }); } @@ -21851,7 +23086,7 @@ class FuzzyMatcher { ret(score, matched) { this.score = score; this.matched = matched; - return true; + return this; } // Matches a given word (completion) against the pattern (input). // Will return a boolean indicating whether there was a match and, @@ -21864,7 +23099,7 @@ class FuzzyMatcher { if (this.pattern.length == 0) return this.ret(-100 /* Penalty.NotFull */, []); if (word.length < this.pattern.length) - return false; + return null; let { chars, folded, any, precise, byWord } = this; // For single-character queries, only match when they occur right // at the start @@ -21875,7 +23110,7 @@ class FuzzyMatcher { else if (first == folded[0]) score += -200 /* Penalty.CaseFold */; else - return false; + return null; return this.ret(score, [0, firstSize]); } let direct = word.indexOf(this.pattern); @@ -21891,7 +23126,7 @@ class FuzzyMatcher { } // No match, exit immediately if (anyTo < len) - return false; + return null; } // This tracks the extent of the precise (non-folded, not // necessarily adjacent) match @@ -21944,7 +23179,7 @@ class FuzzyMatcher { if (byWordTo == len) return this.result(-100 /* Penalty.ByWord */ + (byWordFolded ? -200 /* Penalty.CaseFold */ : 0) + -700 /* Penalty.NotStart */ + (wordAdjacent ? 0 : -1100 /* Penalty.Gap */), byWord, word); - return chars.length == 2 ? false + return chars.length == 2 ? null : this.result((any[0] ? -700 /* Penalty.NotStart */ : 0) + -200 /* Penalty.CaseFold */ + -1100 /* Penalty.Gap */, any, word); } result(score, positions, word) { @@ -21961,11 +23196,31 @@ class FuzzyMatcher { return this.ret(score - word.length, result); } } +class StrictMatcher { + constructor(pattern) { + this.pattern = pattern; + this.matched = []; + this.score = 0; + this.folded = pattern.toLowerCase(); + } + match(word) { + if (word.length < this.pattern.length) + return null; + let start = word.slice(0, this.pattern.length); + let match = start == this.pattern ? 0 : start.toLowerCase() == this.folded ? -200 /* Penalty.CaseFold */ : null; + if (match == null) + return null; + this.matched = [0, start.length]; + this.score = match + (word.length == this.pattern.length ? 0 : -100 /* Penalty.NotFull */); + return this; + } +} const completionConfig = /*@__PURE__*/Facet.define({ combine(configs) { return combineConfig(configs, { activateOnTyping: true, + activateOnCompletion: () => false, activateOnTypingDelay: 100, selectOnOpen: true, override: null, @@ -21978,6 +23233,7 @@ const completionConfig = /*@__PURE__*/Facet.define({ icons: true, addToOptions: [], positionInfo: defaultPositionInfo, + filterStrict: false, compareCompletions: (a, b) => a.label.localeCompare(b.label), interactionDelay: 75, updateSyncTime: 100 @@ -21987,7 +23243,8 @@ const completionConfig = /*@__PURE__*/Facet.define({ icons: (a, b) => a && b, tooltipClass: (a, b) => c => joinClass(a(c), b(c)), optionClass: (a, b) => c => joinClass(a(c), b(c)), - addToOptions: (a, b) => a.concat(b) + addToOptions: (a, b) => a.concat(b), + filterStrict: (a, b) => a || b, }); } }); @@ -22242,8 +23499,8 @@ class CompletionTooltip { let selRect = sel.getBoundingClientRect(); let space = this.space; if (!space) { - let win = this.dom.ownerDocument.defaultView || window; - space = { left: 0, top: 0, right: win.innerWidth, bottom: win.innerHeight }; + let docElt = this.dom.ownerDocument.documentElement; + space = { left: 0, top: 0, right: docElt.clientWidth, bottom: docElt.clientHeight }; } if (selRect.top > Math.min(space.bottom, listRect.bottom) - 10 || selRect.bottom < Math.max(space.top, listRect.top) + 10) @@ -22268,6 +23525,11 @@ class CompletionTooltip { ul.setAttribute("role", "listbox"); ul.setAttribute("aria-expanded", "true"); ul.setAttribute("aria-label", this.view.state.phrase("Completions")); + ul.addEventListener("mousedown", e => { + // Prevent focus change when clicking the scrollbar + if (e.target == ul) + e.preventDefault(); + }); let curSection = null; for (let i = range.from; i < range.to; i++) { let { completion, match } = options[i], { section } = completion; @@ -22347,6 +23609,7 @@ function sortOptions(active, state) { sections.push(typeof section == "string" ? { name } : section); } }; + let conf = state.facet(completionConfig); for (let a of active) if (a.hasResult()) { let getMatch = a.result.getMatch; @@ -22356,11 +23619,12 @@ function sortOptions(active, state) { } } else { - let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to)); + let pattern = state.sliceDoc(a.from, a.to), match; + let matcher = conf.filterStrict ? new StrictMatcher(pattern) : new FuzzyMatcher(pattern); for (let option of a.result.options) - if (matcher.match(option.label)) { - let matched = !option.displayLabel ? matcher.matched : getMatch ? getMatch(option, matcher.matched) : []; - addOption(new Option(option, a.source, matched, matcher.score + (option.boost || 0))); + if (match = matcher.match(option.label)) { + let matched = !option.displayLabel ? match.matched : getMatch ? getMatch(option, match.matched) : []; + addOption(new Option(option, a.source, matched, match.score + (option.boost || 0))); } } } @@ -22378,7 +23642,7 @@ function sortOptions(active, state) { } } let result = [], prev = null; - let compare = state.facet(completionConfig).compareCompletions; + let compare = conf.compareCompletions; for (let opt of options.sort((a, b) => (b.score - a.score) || compare(a.completion, b.completion))) { let cur = opt.completion; if (!prev || prev.label != cur.label || prev.detail != cur.detail || @@ -22404,12 +23668,12 @@ class CompletionDialog { return selected == this.selected || selected >= this.options.length ? this : new CompletionDialog(this.options, makeAttrs(id, selected), this.tooltip, this.timestamp, selected, this.disabled); } - static build(active, state, id, prev, conf) { + static build(active, state, id, prev, conf, didSetActive) { + if (prev && !didSetActive && active.some(s => s.isPending)) + return prev.setDisabled(); let options = sortOptions(active, state); - if (!options.length) { - return prev && active.some(a => a.state == 1 /* State.Pending */) ? - new CompletionDialog(prev.options, prev.attrs, prev.tooltip, prev.timestamp, prev.selected, true) : null; - } + if (!options.length) + return prev && active.some(a => a.isPending) ? prev.setDisabled() : null; let selected = state.facet(completionConfig).selectOnOpen ? 0 : -1; if (prev && prev.selected != selected && prev.selected != -1) { let selectedValue = prev.options[prev.selected].completion; @@ -22428,6 +23692,9 @@ class CompletionDialog { map(changes) { return new CompletionDialog(this.options, this.attrs, Object.assign(Object.assign({}, this.tooltip), { pos: changes.mapPos(this.tooltip.pos) }), this.timestamp, this.selected, this.disabled); } + setDisabled() { + return new CompletionDialog(this.options, this.attrs, this.tooltip, this.timestamp, this.selected, true); + } } class CompletionState { constructor(active, id, open) { @@ -22449,15 +23716,15 @@ class CompletionState { }); if (active.length == this.active.length && active.every((a, i) => a == this.active[i])) active = this.active; - let open = this.open; + let open = this.open, didSet = tr.effects.some(e => e.is(setActiveEffect)); if (open && tr.docChanged) open = open.map(tr.changes); if (tr.selection || active.some(a => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) || - !sameResults(active, this.active)) - open = CompletionDialog.build(active, state, this.id, open, conf); - else if (open && open.disabled && !active.some(a => a.state == 1 /* State.Pending */)) + !sameResults(active, this.active) || didSet) + open = CompletionDialog.build(active, state, this.id, open, conf, didSet); + else if (open && open.disabled && !active.some(a => a.isPending)) open = null; - if (!open && active.every(a => a.state != 1 /* State.Pending */) && active.some(a => a.hasResult())) + if (!open && active.every(a => !a.isPending) && active.some(a => a.hasResult())) active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* State.Inactive */) : a); for (let effect of tr.effects) if (effect.is(setSelectedEffect)) @@ -22465,15 +23732,15 @@ class CompletionState { return active == this.active && open == this.open ? this : new CompletionState(active, this.id, open); } get tooltip() { return this.open ? this.open.tooltip : null; } - get attrs() { return this.open ? this.open.attrs : baseAttrs; } + get attrs() { return this.open ? this.open.attrs : this.active.length ? baseAttrs : noAttrs; } } function sameResults(a, b) { if (a == b) return true; for (let iA = 0, iB = 0;;) { - while (iA < a.length && !a[iA].hasResult) + while (iA < a.length && !a[iA].hasResult()) iA++; - while (iB < b.length && !b[iB].hasResult) + while (iB < b.length && !b[iB].hasResult()) iB++; let endA = iA == a.length, endB = iB == b.length; if (endA || endB) @@ -22485,6 +23752,7 @@ function sameResults(a, b) { const baseAttrs = { "aria-autocomplete": "list" }; +const noAttrs = {}; function makeAttrs(id, selected) { let result = { "aria-autocomplete": "list", @@ -22496,27 +23764,37 @@ function makeAttrs(id, selected) { return result; } const none = []; -function getUserEvent(tr) { - return tr.isUserEvent("input.type") ? "input" : tr.isUserEvent("delete.backward") ? "delete" : null; +function getUpdateType(tr, conf) { + if (tr.isUserEvent("input.complete")) { + let completion = tr.annotation(pickedCompletion); + if (completion && conf.activateOnCompletion(completion)) + return 4 /* UpdateType.Activate */ | 8 /* UpdateType.Reset */; + } + let typing = tr.isUserEvent("input.type"); + return typing && conf.activateOnTyping ? 4 /* UpdateType.Activate */ | 1 /* UpdateType.Typing */ + : typing ? 1 /* UpdateType.Typing */ + : tr.isUserEvent("delete.backward") ? 2 /* UpdateType.Backspacing */ + : tr.selection ? 8 /* UpdateType.Reset */ + : tr.docChanged ? 16 /* UpdateType.ResetIfTouching */ : 0 /* UpdateType.None */; } class ActiveSource { - constructor(source, state, explicitPos = -1) { + constructor(source, state, explicit = false) { this.source = source; this.state = state; - this.explicitPos = explicitPos; + this.explicit = explicit; } hasResult() { return false; } + get isPending() { return this.state == 1 /* State.Pending */; } update(tr, conf) { - let event = getUserEvent(tr), value = this; - if (event) - value = value.handleUserEvent(tr, event, conf); - else if (tr.docChanged) - value = value.handleChange(tr); - else if (tr.selection && value.state != 0 /* State.Inactive */) + let type = getUpdateType(tr, conf), value = this; + if ((type & 8 /* UpdateType.Reset */) || (type & 16 /* UpdateType.ResetIfTouching */) && this.touches(tr)) value = new ActiveSource(value.source, 0 /* State.Inactive */); + if ((type & 4 /* UpdateType.Activate */) && value.state == 0 /* State.Inactive */) + value = new ActiveSource(this.source, 1 /* State.Pending */); + value = value.updateFor(tr, type); for (let effect of tr.effects) { if (effect.is(startCompletionEffect)) - value = new ActiveSource(value.source, 1 /* State.Pending */, effect.value ? cur(tr.state) : -1); + value = new ActiveSource(value.source, 1 /* State.Pending */, effect.value); else if (effect.is(closeCompletionEffect)) value = new ActiveSource(value.source, 0 /* State.Inactive */); else if (effect.is(setActiveEffect)) @@ -22526,46 +23804,51 @@ class ActiveSource { } return value; } - handleUserEvent(tr, type, conf) { - return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* State.Pending */); - } - handleChange(tr) { - return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes); - } - map(changes) { - return changes.empty || this.explicitPos < 0 ? this : new ActiveSource(this.source, this.state, changes.mapPos(this.explicitPos)); + updateFor(tr, type) { return this.map(tr.changes); } + map(changes) { return this; } + touches(tr) { + return tr.changes.touchesRange(cur(tr.state)); } } class ActiveResult extends ActiveSource { - constructor(source, explicitPos, result, from, to) { - super(source, 2 /* State.Result */, explicitPos); + constructor(source, explicit, limit, result, from, to) { + super(source, 3 /* State.Result */, explicit); + this.limit = limit; this.result = result; this.from = from; this.to = to; } hasResult() { return true; } - handleUserEvent(tr, type, conf) { + updateFor(tr, type) { var _a; + if (!(type & 3 /* UpdateType.SimpleInteraction */)) + return this.map(tr.changes); + let result = this.result; + if (result.map && !tr.changes.empty) + result = result.map(result, tr.changes); let from = tr.changes.mapPos(this.from), to = tr.changes.mapPos(this.to, 1); let pos = cur(tr.state); - if ((this.explicitPos < 0 ? pos <= from : pos < this.from) || - pos > to || - type == "delete" && cur(tr.startState) == this.from) - return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* State.Pending */ : 0 /* State.Inactive */); - let explicitPos = this.explicitPos < 0 ? -1 : tr.changes.mapPos(this.explicitPos), updated; - if (checkValid(this.result.validFor, tr.state, from, to)) - return new ActiveResult(this.source, explicitPos, this.result, from, to); - if (this.result.update && - (updated = this.result.update(this.result, from, to, new CompletionContext(tr.state, pos, explicitPos >= 0)))) - return new ActiveResult(this.source, explicitPos, updated, updated.from, (_a = updated.to) !== null && _a !== void 0 ? _a : cur(tr.state)); - return new ActiveSource(this.source, 1 /* State.Pending */, explicitPos); - } - handleChange(tr) { - return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes); + if (pos > to || !result || + (type & 2 /* UpdateType.Backspacing */) && (cur(tr.startState) == this.from || pos < this.limit)) + return new ActiveSource(this.source, type & 4 /* UpdateType.Activate */ ? 1 /* State.Pending */ : 0 /* State.Inactive */); + let limit = tr.changes.mapPos(this.limit); + if (checkValid(result.validFor, tr.state, from, to)) + return new ActiveResult(this.source, this.explicit, limit, result, from, to); + if (result.update && + (result = result.update(result, from, to, new CompletionContext(tr.state, pos, false)))) + return new ActiveResult(this.source, this.explicit, limit, result, result.from, (_a = result.to) !== null && _a !== void 0 ? _a : cur(tr.state)); + return new ActiveSource(this.source, 1 /* State.Pending */, this.explicit); } map(mapping) { - return mapping.empty ? this : - new ActiveResult(this.source, this.explicitPos < 0 ? -1 : mapping.mapPos(this.explicitPos), this.result, mapping.mapPos(this.from), mapping.mapPos(this.to, 1)); + if (mapping.empty) + return this; + let result = this.result.map ? this.result.map(this.result, mapping) : this.result; + if (!result) + return new ActiveSource(this.source, 0 /* State.Inactive */); + return new ActiveResult(this.source, this.explicit, mapping.mapPos(this.limit), this.result, mapping.mapPos(this.from), mapping.mapPos(this.to, 1)); + } + touches(tr) { + return tr.changes.touchesRange(this.from, this.to); } } function checkValid(validFor, state, from, to) { @@ -22674,19 +23957,22 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class { this.pendingStart = false; this.composing = 0 /* CompositionState.None */; for (let active of view.state.field(completionState).active) - if (active.state == 1 /* State.Pending */) + if (active.isPending) this.startQuery(active); } update(update) { let cState = update.state.field(completionState); + let conf = update.state.facet(completionConfig); if (!update.selectionSet && !update.docChanged && update.startState.field(completionState) == cState) return; let doesReset = update.transactions.some(tr => { - return (tr.selection || tr.docChanged) && !getUserEvent(tr); + let type = getUpdateType(tr, conf); + return (type & 8 /* UpdateType.Reset */) || (tr.selection || tr.docChanged) && !(type & 3 /* UpdateType.SimpleInteraction */); }); for (let i = 0; i < this.running.length; i++) { let query = this.running[i]; if (doesReset || + query.context.abortOnDocChange && update.docChanged || query.updates.length + update.transactions.length > MaxUpdateCount && Date.now() - query.time > MinAbortTime) { for (let handler of query.context.abortListeners) { try { @@ -22707,12 +23993,12 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class { clearTimeout(this.debounceUpdate); if (update.transactions.some(tr => tr.effects.some(e => e.is(startCompletionEffect)))) this.pendingStart = true; - let delay = this.pendingStart ? 50 : update.state.facet(completionConfig).activateOnTypingDelay; - this.debounceUpdate = cState.active.some(a => a.state == 1 /* State.Pending */ && !this.running.some(q => q.active.source == a.source)) + let delay = this.pendingStart ? 50 : conf.activateOnTypingDelay; + this.debounceUpdate = cState.active.some(a => a.isPending && !this.running.some(q => q.active.source == a.source)) ? setTimeout(() => this.startUpdate(), delay) : -1; if (this.composing != 0 /* CompositionState.None */) for (let tr of update.transactions) { - if (getUserEvent(tr) == "input") + if (tr.isUserEvent("input.type")) this.composing = 2 /* CompositionState.Changed */; else if (this.composing == 2 /* CompositionState.Changed */ && tr.selection) this.composing = 3 /* CompositionState.ChangedAndMoved */; @@ -22723,13 +24009,15 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class { this.pendingStart = false; let { state } = this.view, cState = state.field(completionState); for (let active of cState.active) { - if (active.state == 1 /* State.Pending */ && !this.running.some(r => r.active.source == active.source)) + if (active.isPending && !this.running.some(r => r.active.source == active.source)) this.startQuery(active); } + if (this.running.length && cState.open && cState.open.disabled) + this.debounceAccept = setTimeout(() => this.accept(), this.view.state.facet(completionConfig).updateSyncTime); } startQuery(active) { let { state } = this.view, pos = cur(state); - let context = new CompletionContext(state, pos, active.explicitPos == pos); + let context = new CompletionContext(state, pos, active.explicit, this.view); let pending = new RunningQuery(active, context); this.running.push(pending); Promise.resolve(active.source(context)).then(result => { @@ -22756,14 +24044,16 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class { clearTimeout(this.debounceAccept); this.debounceAccept = -1; let updated = []; - let conf = this.view.state.facet(completionConfig); + let conf = this.view.state.facet(completionConfig), cState = this.view.state.field(completionState); for (let i = 0; i < this.running.length; i++) { let query = this.running[i]; if (query.done === undefined) continue; this.running.splice(i--, 1); if (query.done) { - let active = new ActiveResult(query.active.source, query.active.explicitPos, query.done, query.done.from, (_a = query.done.to) !== null && _a !== void 0 ? _a : cur(query.updates.length ? query.updates[0].startState : this.view.state)); + let pos = cur(query.updates.length ? query.updates[0].startState : this.view.state); + let limit = Math.min(pos, query.done.from + (query.active.explicit ? 0 : 1)); + let active = new ActiveResult(query.active.source, query.active.explicit, limit, query.done, query.done.from, (_a = query.done.to) !== null && _a !== void 0 ? _a : pos); // Replay the transactions that happened since the start of // the request and see if that preserves the result for (let tr of query.updates) @@ -22773,15 +24063,15 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class { continue; } } - let current = this.view.state.field(completionState).active.find(a => a.source == query.active.source); - if (current && current.state == 1 /* State.Pending */) { + let current = cState.active.find(a => a.source == query.active.source); + if (current && current.isPending) { if (query.done == null) { // Explicitly failed. Should clear the pending status if it // hasn't been re-set in the meantime. let active = new ActiveSource(query.active.source, 0 /* State.Inactive */); for (let tr of query.updates) active = active.update(tr, conf); - if (active.state != 1 /* State.Pending */) + if (!active.isPending) updated.push(active); } else { @@ -22790,7 +24080,7 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class { } } } - if (updated.length) + if (updated.length || cState.open && cState.open.disabled) this.view.dispatch({ effects: setActiveEffect.of(updated) }); } }, { @@ -22888,7 +24178,8 @@ const baseTheme$1 = /*@__PURE__*/EditorView.baseTheme({ padding: "3px 9px", width: "max-content", maxWidth: `${400 /* Info.Width */}px`, - boxSizing: "border-box" + boxSizing: "border-box", + whiteSpace: "pre-line" }, ".cm-completionInfo.cm-completionInfo-left": { right: "100%" }, ".cm-completionInfo.cm-completionInfo-right": { left: "100%" }, @@ -22955,6 +24246,262 @@ const baseTheme$1 = /*@__PURE__*/EditorView.baseTheme({ } }); +class FieldPos { + constructor(field, line, from, to) { + this.field = field; + this.line = line; + this.from = from; + this.to = to; + } +} +class FieldRange { + constructor(field, from, to) { + this.field = field; + this.from = from; + this.to = to; + } + map(changes) { + let from = changes.mapPos(this.from, -1, MapMode.TrackDel); + let to = changes.mapPos(this.to, 1, MapMode.TrackDel); + return from == null || to == null ? null : new FieldRange(this.field, from, to); + } +} +class Snippet { + constructor(lines, fieldPositions) { + this.lines = lines; + this.fieldPositions = fieldPositions; + } + instantiate(state, pos) { + let text = [], lineStart = [pos]; + let lineObj = state.doc.lineAt(pos), baseIndent = /^\s*/.exec(lineObj.text)[0]; + for (let line of this.lines) { + if (text.length) { + let indent = baseIndent, tabs = /^\t*/.exec(line)[0].length; + for (let i = 0; i < tabs; i++) + indent += state.facet(indentUnit); + lineStart.push(pos + indent.length - tabs); + line = indent + line.slice(tabs); + } + text.push(line); + pos += line.length + 1; + } + let ranges = this.fieldPositions.map(pos => new FieldRange(pos.field, lineStart[pos.line] + pos.from, lineStart[pos.line] + pos.to)); + return { text, ranges }; + } + static parse(template) { + let fields = []; + let lines = [], positions = [], m; + for (let line of template.split(/\r\n?|\n/)) { + while (m = /[#$]\{(?:(\d+)(?::([^}]*))?|((?:\\[{}]|[^}])*))\}/.exec(line)) { + let seq = m[1] ? +m[1] : null, rawName = m[2] || m[3] || "", found = -1; + let name = rawName.replace(/\\[{}]/g, m => m[1]); + for (let i = 0; i < fields.length; i++) { + if (seq != null ? fields[i].seq == seq : name ? fields[i].name == name : false) + found = i; + } + if (found < 0) { + let i = 0; + while (i < fields.length && (seq == null || (fields[i].seq != null && fields[i].seq < seq))) + i++; + fields.splice(i, 0, { seq, name }); + found = i; + for (let pos of positions) + if (pos.field >= found) + pos.field++; + } + positions.push(new FieldPos(found, lines.length, m.index, m.index + name.length)); + line = line.slice(0, m.index) + rawName + line.slice(m.index + m[0].length); + } + line = line.replace(/\\([{}])/g, (_, brace, index) => { + for (let pos of positions) + if (pos.line == lines.length && pos.from > index) { + pos.from--; + pos.to--; + } + return brace; + }); + lines.push(line); + } + return new Snippet(lines, positions); + } +} +let fieldMarker = /*@__PURE__*/Decoration.widget({ widget: /*@__PURE__*/new class extends WidgetType { + toDOM() { + let span = document.createElement("span"); + span.className = "cm-snippetFieldPosition"; + return span; + } + ignoreEvent() { return false; } + } }); +let fieldRange = /*@__PURE__*/Decoration.mark({ class: "cm-snippetField" }); +class ActiveSnippet { + constructor(ranges, active) { + this.ranges = ranges; + this.active = active; + this.deco = Decoration.set(ranges.map(r => (r.from == r.to ? fieldMarker : fieldRange).range(r.from, r.to))); + } + map(changes) { + let ranges = []; + for (let r of this.ranges) { + let mapped = r.map(changes); + if (!mapped) + return null; + ranges.push(mapped); + } + return new ActiveSnippet(ranges, this.active); + } + selectionInsideField(sel) { + return sel.ranges.every(range => this.ranges.some(r => r.field == this.active && r.from <= range.from && r.to >= range.to)); + } +} +const setActive = /*@__PURE__*/StateEffect.define({ + map(value, changes) { return value && value.map(changes); } +}); +const moveToField = /*@__PURE__*/StateEffect.define(); +const snippetState = /*@__PURE__*/StateField.define({ + create() { return null; }, + update(value, tr) { + for (let effect of tr.effects) { + if (effect.is(setActive)) + return effect.value; + if (effect.is(moveToField) && value) + return new ActiveSnippet(value.ranges, effect.value); + } + if (value && tr.docChanged) + value = value.map(tr.changes); + if (value && tr.selection && !value.selectionInsideField(tr.selection)) + value = null; + return value; + }, + provide: f => EditorView.decorations.from(f, val => val ? val.deco : Decoration.none) +}); +function fieldSelection(ranges, field) { + return EditorSelection.create(ranges.filter(r => r.field == field).map(r => EditorSelection.range(r.from, r.to))); +} +/** +Convert a snippet template to a function that can +[apply](https://codemirror.net/6/docs/ref/#autocomplete.Completion.apply) it. Snippets are written +using syntax like this: + + "for (let ${index} = 0; ${index} < ${end}; ${index}++) {\n\t${}\n}" + +Each `${}` placeholder (you may also use `#{}`) indicates a field +that the user can fill in. Its name, if any, will be the default +content for the field. + +When the snippet is activated by calling the returned function, +the code is inserted at the given position. Newlines in the +template are indented by the indentation of the start line, plus +one [indent unit](https://codemirror.net/6/docs/ref/#language.indentUnit) per tab character after +the newline. + +On activation, (all instances of) the first field are selected. +The user can move between fields with Tab and Shift-Tab as long as +the fields are active. Moving to the last field or moving the +cursor out of the current field deactivates the fields. + +The order of fields defaults to textual order, but you can add +numbers to placeholders (`${1}` or `${1:defaultText}`) to provide +a custom order. + +To include a literal `{` or `}` in your template, put a backslash +in front of it. This will be removed and the brace will not be +interpreted as indicating a placeholder. +*/ +function snippet(template) { + let snippet = Snippet.parse(template); + return (editor, completion, from, to) => { + let { text, ranges } = snippet.instantiate(editor.state, from); + let { main } = editor.state.selection; + let spec = { + changes: { from, to: to == main.from ? main.to : to, insert: Text.of(text) }, + scrollIntoView: true, + annotations: completion ? [pickedCompletion.of(completion), Transaction.userEvent.of("input.complete")] : undefined + }; + if (ranges.length) + spec.selection = fieldSelection(ranges, 0); + if (ranges.some(r => r.field > 0)) { + let active = new ActiveSnippet(ranges, 0); + let effects = spec.effects = [setActive.of(active)]; + if (editor.state.field(snippetState, false) === undefined) + effects.push(StateEffect.appendConfig.of([snippetState, addSnippetKeymap, snippetPointerHandler, baseTheme$1])); + } + editor.dispatch(editor.state.update(spec)); + }; +} +function moveField(dir) { + return ({ state, dispatch }) => { + let active = state.field(snippetState, false); + if (!active || dir < 0 && active.active == 0) + return false; + let next = active.active + dir, last = dir > 0 && !active.ranges.some(r => r.field == next + dir); + dispatch(state.update({ + selection: fieldSelection(active.ranges, next), + effects: setActive.of(last ? null : new ActiveSnippet(active.ranges, next)), + scrollIntoView: true + })); + return true; + }; +} +/** +A command that clears the active snippet, if any. +*/ +const clearSnippet = ({ state, dispatch }) => { + let active = state.field(snippetState, false); + if (!active) + return false; + dispatch(state.update({ effects: setActive.of(null) })); + return true; +}; +/** +Move to the next snippet field, if available. +*/ +const nextSnippetField = /*@__PURE__*/moveField(1); +/** +Move to the previous snippet field, if available. +*/ +const prevSnippetField = /*@__PURE__*/moveField(-1); +const defaultSnippetKeymap = [ + { key: "Tab", run: nextSnippetField, shift: prevSnippetField }, + { key: "Escape", run: clearSnippet } +]; +/** +A facet that can be used to configure the key bindings used by +snippets. The default binds Tab to +[`nextSnippetField`](https://codemirror.net/6/docs/ref/#autocomplete.nextSnippetField), Shift-Tab to +[`prevSnippetField`](https://codemirror.net/6/docs/ref/#autocomplete.prevSnippetField), and Escape +to [`clearSnippet`](https://codemirror.net/6/docs/ref/#autocomplete.clearSnippet). +*/ +const snippetKeymap = /*@__PURE__*/Facet.define({ + combine(maps) { return maps.length ? maps[0] : defaultSnippetKeymap; } +}); +const addSnippetKeymap = /*@__PURE__*/Prec.highest(/*@__PURE__*/keymap.compute([snippetKeymap], state => state.facet(snippetKeymap))); +/** +Create a completion from a snippet. Returns an object with the +properties from `completion`, plus an `apply` function that +applies the snippet. +*/ +function snippetCompletion(template, completion) { + return Object.assign(Object.assign({}, completion), { apply: snippet(template) }); +} +const snippetPointerHandler = /*@__PURE__*/EditorView.domEventHandlers({ + mousedown(event, view) { + let active = view.state.field(snippetState, false), pos; + if (!active || (pos = view.posAtCoords({ x: event.clientX, y: event.clientY })) == null) + return false; + let match = active.ranges.find(r => r.from <= pos && r.to >= pos); + if (!match || match.field == active.active) + return false; + view.dispatch({ + selection: fieldSelection(active.ranges, match.field), + effects: setActive.of(active.ranges.some(r => r.field > match.field) + ? new ActiveSnippet(active.ranges, match.field) : null), + scrollIntoView: true + }); + return true; + } +}); + const defaults = { brackets: ["(", "[", "{", "'", '"'], before: ")]}:;>", @@ -23004,9 +24551,9 @@ function closing(ch) { function config(state, pos) { return state.languageDataAt("closeBrackets", pos)[0] || defaults; } -const android = typeof navigator == "object" && /*@__PURE__*//Android\b/.test(navigator.userAgent); +const android$1 = typeof navigator == "object" && /*@__PURE__*//Android\b/.test(navigator.userAgent); const inputHandler = /*@__PURE__*/EditorView.inputHandler.of((view, from, to, insert) => { - if ((android ? view.composing : view.compositionStarted) || view.state.readOnly) + if ((android$1 ? view.composing : view.compositionStarted) || view.state.readOnly) return false; let sel = view.state.selection.main; if (insert.length > 2 || insert.length == 2 && codePointSize(codePointAt(insert, 0)) == 1 || @@ -23216,7 +24763,7 @@ function autocompletion(config = {}) { /** Basic keybindings for autocompletion. - - Ctrl-Space: [`startCompletion`](https://codemirror.net/6/docs/ref/#autocomplete.startCompletion) + - Ctrl-Space (and Alt-\` on macOS): [`startCompletion`](https://codemirror.net/6/docs/ref/#autocomplete.startCompletion) - Escape: [`closeCompletion`](https://codemirror.net/6/docs/ref/#autocomplete.closeCompletion) - ArrowDown: [`moveCompletionSelection`](https://codemirror.net/6/docs/ref/#autocomplete.moveCompletionSelection)`(true)` - ArrowUp: [`moveCompletionSelection`](https://codemirror.net/6/docs/ref/#autocomplete.moveCompletionSelection)`(false)` @@ -23226,6 +24773,7 @@ Basic keybindings for autocompletion. */ const completionKeymap = [ { key: "Ctrl-Space", run: startCompletion }, + { mac: "Alt-`", run: startCompletion }, { key: "Escape", run: closeCompletion }, { key: "ArrowDown", run: /*@__PURE__*/moveCompletionSelection(true) }, { key: "ArrowUp", run: /*@__PURE__*/moveCompletionSelection(false) }, @@ -23254,35 +24802,80 @@ class LintState { let diagnosticFilter = state.facet(lintConfig).markerFilter; if (diagnosticFilter) markedDiagnostics = diagnosticFilter(markedDiagnostics, state); - let ranges = Decoration.set(markedDiagnostics.map((d) => { - // For zero-length ranges or ranges covering only a line break, create a widget - return d.from == d.to || (d.from == d.to - 1 && state.doc.lineAt(d.from).to == d.from) - ? Decoration.widget({ - widget: new DiagnosticWidget(d), - diagnostic: d - }).range(d.from) - : Decoration.mark({ - attributes: { class: "cm-lintRange cm-lintRange-" + d.severity + (d.markClass ? " " + d.markClass : "") }, - diagnostic: d, - inclusive: true - }).range(d.from, d.to); - }), true); - return new LintState(ranges, panel, findDiagnostic(ranges)); + let sorted = diagnostics.slice().sort((a, b) => a.from - b.from || a.to - b.to); + let deco = new RangeSetBuilder(), active = [], pos = 0; + for (let i = 0;;) { + let next = i == sorted.length ? null : sorted[i]; + if (!next && !active.length) + break; + let from, to; + if (active.length) { + from = pos; + to = active.reduce((p, d) => Math.min(p, d.to), next && next.from > from ? next.from : 1e8); + } + else { + from = next.from; + to = next.to; + active.push(next); + i++; + } + while (i < sorted.length) { + let next = sorted[i]; + if (next.from == from && (next.to > next.from || next.to == from)) { + active.push(next); + i++; + to = Math.min(next.to, to); + } + else { + to = Math.min(next.from, to); + break; + } + } + let sev = maxSeverity(active); + if (active.some(d => d.from == d.to || (d.from == d.to - 1 && state.doc.lineAt(d.from).to == d.from))) { + deco.add(from, from, Decoration.widget({ + widget: new DiagnosticWidget(sev), + diagnostics: active.slice() + })); + } + else { + let markClass = active.reduce((c, d) => d.markClass ? c + " " + d.markClass : c, ""); + deco.add(from, to, Decoration.mark({ + class: "cm-lintRange cm-lintRange-" + sev + markClass, + diagnostics: active.slice(), + inclusiveEnd: active.some(a => a.to > to) + })); + } + pos = to; + for (let i = 0; i < active.length; i++) + if (active[i].to <= pos) + active.splice(i--, 1); + } + let set = deco.finish(); + return new LintState(set, panel, findDiagnostic(set)); } } function findDiagnostic(diagnostics, diagnostic = null, after = 0) { let found = null; diagnostics.between(after, 1e9, (from, to, { spec }) => { - if (diagnostic && spec.diagnostic != diagnostic) + if (diagnostic && spec.diagnostics.indexOf(diagnostic) < 0) return; - found = new SelectedDiagnostic(from, to, spec.diagnostic); - return false; + if (!found) + found = new SelectedDiagnostic(from, to, diagnostic || spec.diagnostics[0]); + else if (spec.diagnostics.indexOf(found.diagnostic) < 0) + return false; + else + found = new SelectedDiagnostic(found.from, to, found.diagnostic); }); return found; } function hideTooltip(tr, tooltip) { + let from = tooltip.pos, to = tooltip.end || from; + let result = tr.state.facet(lintConfig).hideOn(tr, from, to); + if (result != null) + return result; let line = tr.startState.doc.lineAt(tooltip.pos); - return !!(tr.effects.some(e => e.is(setDiagnosticsEffect)) || tr.changes.touchesRange(line.from, line.to)); + return !!(tr.effects.some(e => e.is(setDiagnosticsEffect)) || tr.changes.touchesRange(line.from, Math.max(line.to, to))); } function maybeEnableLint(state, effects) { return state.field(lintState, false) ? effects : effects.concat(StateEffect.appendConfig.of(lintExtensions)); @@ -23299,17 +24892,20 @@ const lintState = /*@__PURE__*/StateField.define({ return new LintState(Decoration.none, null, null); }, update(value, tr) { - if (tr.docChanged) { - let mapped = value.diagnostics.map(tr.changes), selected = null; + if (tr.docChanged && value.diagnostics.size) { + let mapped = value.diagnostics.map(tr.changes), selected = null, panel = value.panel; if (value.selected) { let selPos = tr.changes.mapPos(value.selected.from, 1); selected = findDiagnostic(mapped, value.selected.diagnostic, selPos) || findDiagnostic(mapped, null, selPos); } - value = new LintState(mapped, value.panel, selected); + if (!mapped.size && panel && tr.state.facet(lintConfig).autoPanel) + panel = null; + value = new LintState(mapped, panel, selected); } for (let effect of tr.effects) { if (effect.is(setDiagnosticsEffect)) { - value = LintState.init(effect.value, value.panel, tr.state); + let panel = !tr.state.facet(lintConfig).autoPanel ? value.panel : effect.value.length ? LintPanel.open : null; + value = LintState.init(effect.value, panel, tr.state); } else if (effect.is(togglePanel)) { value = new LintState(value.diagnostics, effect.value ? LintPanel.open : null, value.selected); @@ -23323,27 +24919,28 @@ const lintState = /*@__PURE__*/StateField.define({ provide: f => [showPanel.from(f, val => val.panel), EditorView.decorations.from(f, s => s.diagnostics)] }); -const activeMark = /*@__PURE__*/Decoration.mark({ class: "cm-lintRange cm-lintRange-active", inclusive: true }); +const activeMark = /*@__PURE__*/Decoration.mark({ class: "cm-lintRange cm-lintRange-active" }); function lintTooltip(view, pos, side) { let { diagnostics } = view.state.field(lintState); - let found = [], stackStart = 2e8, stackEnd = 0; + let found, start = -1, end = -1; diagnostics.between(pos - (side < 0 ? 1 : 0), pos + (side > 0 ? 1 : 0), (from, to, { spec }) => { if (pos >= from && pos <= to && (from == to || ((pos > from || side > 0) && (pos < to || side < 0)))) { - found.push(spec.diagnostic); - stackStart = Math.min(from, stackStart); - stackEnd = Math.max(to, stackEnd); + found = spec.diagnostics; + start = from; + end = to; + return false; } }); let diagnosticFilter = view.state.facet(lintConfig).tooltipFilter; - if (diagnosticFilter) + if (found && diagnosticFilter) found = diagnosticFilter(found, view.state); - if (!found.length) + if (!found) return null; return { - pos: stackStart, - end: stackEnd, - above: view.state.doc.lineAt(stackStart).to < stackEnd, + pos: start, + end: end, + above: view.state.doc.lineAt(start).to < end, create() { return { dom: diagnosticsTooltip(view, found) }; } @@ -23406,7 +25003,8 @@ const lintConfig = /*@__PURE__*/Facet.define({ delay: 750, markerFilter: null, tooltipFilter: null, - needsRefresh: null + needsRefresh: null, + hideOn: () => null, }, { needsRefresh: (a, b) => !a ? b : !b ? a : u => a(u) || b(u) })); @@ -23430,7 +25028,7 @@ function assignKeys(actions) { function renderDiagnostic(view, diagnostic, inPanel) { var _a; let keys = inPanel ? assignKeys(diagnostic.actions) : []; - return crelt("li", { class: "cm-diagnostic cm-diagnostic-" + diagnostic.severity }, crelt("span", { class: "cm-diagnosticText" }, diagnostic.renderMessage ? diagnostic.renderMessage() : diagnostic.message), (_a = diagnostic.actions) === null || _a === void 0 ? void 0 : _a.map((action, i) => { + return crelt("li", { class: "cm-diagnostic cm-diagnostic-" + diagnostic.severity }, crelt("span", { class: "cm-diagnosticText" }, diagnostic.renderMessage ? diagnostic.renderMessage(view) : diagnostic.message), (_a = diagnostic.actions) === null || _a === void 0 ? void 0 : _a.map((action, i) => { let fired = false, click = (e) => { e.preventDefault(); if (fired) @@ -23454,13 +25052,13 @@ function renderDiagnostic(view, diagnostic, inPanel) { }), diagnostic.source && crelt("div", { class: "cm-diagnosticSource" }, diagnostic.source)); } class DiagnosticWidget extends WidgetType { - constructor(diagnostic) { + constructor(sev) { super(); - this.diagnostic = diagnostic; + this.sev = sev; } - eq(other) { return other.diagnostic == this.diagnostic; } + eq(other) { return other.sev == this.sev; } toDOM() { - return crelt("span", { class: "cm-lintPoint cm-lintPoint-" + this.diagnostic.severity }); + return crelt("span", { class: "cm-lintPoint cm-lintPoint-" + this.sev }); } } class PanelItem { @@ -23543,35 +25141,41 @@ class LintPanel { update() { let { diagnostics, selected } = this.view.state.field(lintState); let i = 0, needsSync = false, newSelectedItem = null; + let seen = new Set(); diagnostics.between(0, this.view.state.doc.length, (_start, _end, { spec }) => { - let found = -1, item; - for (let j = i; j < this.items.length; j++) - if (this.items[j].diagnostic == spec.diagnostic) { - found = j; - break; - } - if (found < 0) { - item = new PanelItem(this.view, spec.diagnostic); - this.items.splice(i, 0, item); - needsSync = true; - } - else { - item = this.items[found]; - if (found > i) { - this.items.splice(i, found - i); + for (let diagnostic of spec.diagnostics) { + if (seen.has(diagnostic)) + continue; + seen.add(diagnostic); + let found = -1, item; + for (let j = i; j < this.items.length; j++) + if (this.items[j].diagnostic == diagnostic) { + found = j; + break; + } + if (found < 0) { + item = new PanelItem(this.view, diagnostic); + this.items.splice(i, 0, item); needsSync = true; } - } - if (selected && item.diagnostic == selected.diagnostic) { - if (!item.dom.hasAttribute("aria-selected")) { - item.dom.setAttribute("aria-selected", "true"); - newSelectedItem = item; + else { + item = this.items[found]; + if (found > i) { + this.items.splice(i, found - i); + needsSync = true; + } } + if (selected && item.diagnostic == selected.diagnostic) { + if (!item.dom.hasAttribute("aria-selected")) { + item.dom.setAttribute("aria-selected", "true"); + newSelectedItem = item; + } + } + else if (item.dom.hasAttribute("aria-selected")) { + item.dom.removeAttribute("aria-selected"); + } + i++; } - else if (item.dom.hasAttribute("aria-selected")) { - item.dom.removeAttribute("aria-selected"); - } - i++; }); while (i < this.items.length && !(this.items.length == 1 && this.items[0].diagnostic.from < 0)) { needsSync = true; @@ -23737,6 +25341,20 @@ const baseTheme = /*@__PURE__*/EditorView.baseTheme({ } } }); +function severityWeight(sev) { + return sev == "error" ? 4 : sev == "warning" ? 3 : sev == "info" ? 2 : 1; +} +function maxSeverity(diagnostics) { + let sev = "hint", weight = 1; + for (let d of diagnostics) { + let w = severityWeight(d.severity); + if (w > weight) { + weight = w; + sev = d.severity; + } + } + return sev; +} const lintExtensions = [ lintState, /*@__PURE__*/EditorView.decorations.compute([lintState], state => { @@ -23817,6 +25435,3309 @@ const basicSetup = /*@__PURE__*/(() => [ ]) ])(); +/** +A parse stack. These are used internally by the parser to track +parsing progress. They also provide some properties and methods +that external code such as a tokenizer can use to get information +about the parse state. +*/ +class Stack { + /** + @internal + */ + constructor( + /** + The parse that this stack is part of @internal + */ + p, + /** + Holds state, input pos, buffer index triplets for all but the + top state @internal + */ + stack, + /** + The current parse state @internal + */ + state, + // The position at which the next reduce should take place. This + // can be less than `this.pos` when skipped expressions have been + // added to the stack (which should be moved outside of the next + // reduction) + /** + @internal + */ + reducePos, + /** + The input position up to which this stack has parsed. + */ + pos, + /** + The dynamic score of the stack, including dynamic precedence + and error-recovery penalties + @internal + */ + score, + // The output buffer. Holds (type, start, end, size) quads + // representing nodes created by the parser, where `size` is + // amount of buffer array entries covered by this node. + /** + @internal + */ + buffer, + // The base offset of the buffer. When stacks are split, the split + // instance shared the buffer history with its parent up to + // `bufferBase`, which is the absolute offset (including the + // offset of previous splits) into the buffer at which this stack + // starts writing. + /** + @internal + */ + bufferBase, + /** + @internal + */ + curContext, + /** + @internal + */ + lookAhead = 0, + // A parent stack from which this was split off, if any. This is + // set up so that it always points to a stack that has some + // additional buffer content, never to a stack with an equal + // `bufferBase`. + /** + @internal + */ + parent) { + this.p = p; + this.stack = stack; + this.state = state; + this.reducePos = reducePos; + this.pos = pos; + this.score = score; + this.buffer = buffer; + this.bufferBase = bufferBase; + this.curContext = curContext; + this.lookAhead = lookAhead; + this.parent = parent; + } + /** + @internal + */ + toString() { + return `[${this.stack.filter((_, i) => i % 3 == 0).concat(this.state)}]@${this.pos}${this.score ? "!" + this.score : ""}`; + } + // Start an empty stack + /** + @internal + */ + static start(p, state, pos = 0) { + let cx = p.parser.context; + return new Stack(p, [], state, pos, pos, 0, [], 0, cx ? new StackContext(cx, cx.start) : null, 0, null); + } + /** + The stack's current [context](#lr.ContextTracker) value, if + any. Its type will depend on the context tracker's type + parameter, or it will be `null` if there is no context + tracker. + */ + get context() { return this.curContext ? this.curContext.context : null; } + // Push a state onto the stack, tracking its start position as well + // as the buffer base at that point. + /** + @internal + */ + pushState(state, start) { + this.stack.push(this.state, start, this.bufferBase + this.buffer.length); + this.state = state; + } + // Apply a reduce action + /** + @internal + */ + reduce(action) { + var _a; + let depth = action >> 19 /* Action.ReduceDepthShift */, type = action & 65535 /* Action.ValueMask */; + let { parser } = this.p; + let lookaheadRecord = this.reducePos < this.pos - 25 /* Lookahead.Margin */; + if (lookaheadRecord) + this.setLookAhead(this.pos); + let dPrec = parser.dynamicPrecedence(type); + if (dPrec) + this.score += dPrec; + if (depth == 0) { + this.pushState(parser.getGoto(this.state, type, true), this.reducePos); + // Zero-depth reductions are a special case—they add stuff to + // the stack without popping anything off. + if (type < parser.minRepeatTerm) + this.storeNode(type, this.reducePos, this.reducePos, lookaheadRecord ? 8 : 4, true); + this.reduceContext(type, this.reducePos); + return; + } + // Find the base index into `this.stack`, content after which will + // be dropped. Note that with `StayFlag` reductions we need to + // consume two extra frames (the dummy parent node for the skipped + // expression and the state that we'll be staying in, which should + // be moved to `this.state`). + let base = this.stack.length - ((depth - 1) * 3) - (action & 262144 /* Action.StayFlag */ ? 6 : 0); + let start = base ? this.stack[base - 2] : this.p.ranges[0].from, size = this.reducePos - start; + // This is a kludge to try and detect overly deep left-associative + // trees, which will not increase the parse stack depth and thus + // won't be caught by the regular stack-depth limit check. + if (size >= 2000 /* Recover.MinBigReduction */ && !((_a = this.p.parser.nodeSet.types[type]) === null || _a === void 0 ? void 0 : _a.isAnonymous)) { + if (start == this.p.lastBigReductionStart) { + this.p.bigReductionCount++; + this.p.lastBigReductionSize = size; + } + else if (this.p.lastBigReductionSize < size) { + this.p.bigReductionCount = 1; + this.p.lastBigReductionStart = start; + this.p.lastBigReductionSize = size; + } + } + let bufferBase = base ? this.stack[base - 1] : 0, count = this.bufferBase + this.buffer.length - bufferBase; + // Store normal terms or `R -> R R` repeat reductions + if (type < parser.minRepeatTerm || (action & 131072 /* Action.RepeatFlag */)) { + let pos = parser.stateFlag(this.state, 1 /* StateFlag.Skipped */) ? this.pos : this.reducePos; + this.storeNode(type, start, pos, count + 4, true); + } + if (action & 262144 /* Action.StayFlag */) { + this.state = this.stack[base]; + } + else { + let baseStateID = this.stack[base - 3]; + this.state = parser.getGoto(baseStateID, type, true); + } + while (this.stack.length > base) + this.stack.pop(); + this.reduceContext(type, start); + } + // Shift a value into the buffer + /** + @internal + */ + storeNode(term, start, end, size = 4, mustSink = false) { + if (term == 0 /* Term.Err */ && + (!this.stack.length || this.stack[this.stack.length - 1] < this.buffer.length + this.bufferBase)) { + // Try to omit/merge adjacent error nodes + let cur = this, top = this.buffer.length; + if (top == 0 && cur.parent) { + top = cur.bufferBase - cur.parent.bufferBase; + cur = cur.parent; + } + if (top > 0 && cur.buffer[top - 4] == 0 /* Term.Err */ && cur.buffer[top - 1] > -1) { + if (start == end) + return; + if (cur.buffer[top - 2] >= start) { + cur.buffer[top - 2] = end; + return; + } + } + } + if (!mustSink || this.pos == end) { // Simple case, just append + this.buffer.push(term, start, end, size); + } + else { // There may be skipped nodes that have to be moved forward + let index = this.buffer.length; + if (index > 0 && this.buffer[index - 4] != 0 /* Term.Err */) { + let mustMove = false; + for (let scan = index; scan > 0 && this.buffer[scan - 2] > end; scan -= 4) { + if (this.buffer[scan - 1] >= 0) { + mustMove = true; + break; + } + } + if (mustMove) + while (index > 0 && this.buffer[index - 2] > end) { + // Move this record forward + this.buffer[index] = this.buffer[index - 4]; + this.buffer[index + 1] = this.buffer[index - 3]; + this.buffer[index + 2] = this.buffer[index - 2]; + this.buffer[index + 3] = this.buffer[index - 1]; + index -= 4; + if (size > 4) + size -= 4; + } + } + this.buffer[index] = term; + this.buffer[index + 1] = start; + this.buffer[index + 2] = end; + this.buffer[index + 3] = size; + } + } + // Apply a shift action + /** + @internal + */ + shift(action, type, start, end) { + if (action & 131072 /* Action.GotoFlag */) { + this.pushState(action & 65535 /* Action.ValueMask */, this.pos); + } + else if ((action & 262144 /* Action.StayFlag */) == 0) { // Regular shift + let nextState = action, { parser } = this.p; + if (end > this.pos || type <= parser.maxNode) { + this.pos = end; + if (!parser.stateFlag(nextState, 1 /* StateFlag.Skipped */)) + this.reducePos = end; + } + this.pushState(nextState, start); + this.shiftContext(type, start); + if (type <= parser.maxNode) + this.buffer.push(type, start, end, 4); + } + else { // Shift-and-stay, which means this is a skipped token + this.pos = end; + this.shiftContext(type, start); + if (type <= this.p.parser.maxNode) + this.buffer.push(type, start, end, 4); + } + } + // Apply an action + /** + @internal + */ + apply(action, next, nextStart, nextEnd) { + if (action & 65536 /* Action.ReduceFlag */) + this.reduce(action); + else + this.shift(action, next, nextStart, nextEnd); + } + // Add a prebuilt (reused) node into the buffer. + /** + @internal + */ + useNode(value, next) { + let index = this.p.reused.length - 1; + if (index < 0 || this.p.reused[index] != value) { + this.p.reused.push(value); + index++; + } + let start = this.pos; + this.reducePos = this.pos = start + value.length; + this.pushState(next, start); + this.buffer.push(index, start, this.reducePos, -1 /* size == -1 means this is a reused value */); + if (this.curContext) + this.updateContext(this.curContext.tracker.reuse(this.curContext.context, value, this, this.p.stream.reset(this.pos - value.length))); + } + // Split the stack. Due to the buffer sharing and the fact + // that `this.stack` tends to stay quite shallow, this isn't very + // expensive. + /** + @internal + */ + split() { + let parent = this; + let off = parent.buffer.length; + // Because the top of the buffer (after this.pos) may be mutated + // to reorder reductions and skipped tokens, and shared buffers + // should be immutable, this copies any outstanding skipped tokens + // to the new buffer, and puts the base pointer before them. + while (off > 0 && parent.buffer[off - 2] > parent.reducePos) + off -= 4; + let buffer = parent.buffer.slice(off), base = parent.bufferBase + off; + // Make sure parent points to an actual parent with content, if there is such a parent. + while (parent && base == parent.bufferBase) + parent = parent.parent; + return new Stack(this.p, this.stack.slice(), this.state, this.reducePos, this.pos, this.score, buffer, base, this.curContext, this.lookAhead, parent); + } + // Try to recover from an error by 'deleting' (ignoring) one token. + /** + @internal + */ + recoverByDelete(next, nextEnd) { + let isNode = next <= this.p.parser.maxNode; + if (isNode) + this.storeNode(next, this.pos, nextEnd, 4); + this.storeNode(0 /* Term.Err */, this.pos, nextEnd, isNode ? 8 : 4); + this.pos = this.reducePos = nextEnd; + this.score -= 190 /* Recover.Delete */; + } + /** + Check if the given term would be able to be shifted (optionally + after some reductions) on this stack. This can be useful for + external tokenizers that want to make sure they only provide a + given token when it applies. + */ + canShift(term) { + for (let sim = new SimulatedStack(this);;) { + let action = this.p.parser.stateSlot(sim.state, 4 /* ParseState.DefaultReduce */) || this.p.parser.hasAction(sim.state, term); + if (action == 0) + return false; + if ((action & 65536 /* Action.ReduceFlag */) == 0) + return true; + sim.reduce(action); + } + } + // Apply up to Recover.MaxNext recovery actions that conceptually + // inserts some missing token or rule. + /** + @internal + */ + recoverByInsert(next) { + if (this.stack.length >= 300 /* Recover.MaxInsertStackDepth */) + return []; + let nextStates = this.p.parser.nextStates(this.state); + if (nextStates.length > 4 /* Recover.MaxNext */ << 1 || this.stack.length >= 120 /* Recover.DampenInsertStackDepth */) { + let best = []; + for (let i = 0, s; i < nextStates.length; i += 2) { + if ((s = nextStates[i + 1]) != this.state && this.p.parser.hasAction(s, next)) + best.push(nextStates[i], s); + } + if (this.stack.length < 120 /* Recover.DampenInsertStackDepth */) + for (let i = 0; best.length < 4 /* Recover.MaxNext */ << 1 && i < nextStates.length; i += 2) { + let s = nextStates[i + 1]; + if (!best.some((v, i) => (i & 1) && v == s)) + best.push(nextStates[i], s); + } + nextStates = best; + } + let result = []; + for (let i = 0; i < nextStates.length && result.length < 4 /* Recover.MaxNext */; i += 2) { + let s = nextStates[i + 1]; + if (s == this.state) + continue; + let stack = this.split(); + stack.pushState(s, this.pos); + stack.storeNode(0 /* Term.Err */, stack.pos, stack.pos, 4, true); + stack.shiftContext(nextStates[i], this.pos); + stack.reducePos = this.pos; + stack.score -= 200 /* Recover.Insert */; + result.push(stack); + } + return result; + } + // Force a reduce, if possible. Return false if that can't + // be done. + /** + @internal + */ + forceReduce() { + let { parser } = this.p; + let reduce = parser.stateSlot(this.state, 5 /* ParseState.ForcedReduce */); + if ((reduce & 65536 /* Action.ReduceFlag */) == 0) + return false; + if (!parser.validAction(this.state, reduce)) { + let depth = reduce >> 19 /* Action.ReduceDepthShift */, term = reduce & 65535 /* Action.ValueMask */; + let target = this.stack.length - depth * 3; + if (target < 0 || parser.getGoto(this.stack[target], term, false) < 0) { + let backup = this.findForcedReduction(); + if (backup == null) + return false; + reduce = backup; + } + this.storeNode(0 /* Term.Err */, this.pos, this.pos, 4, true); + this.score -= 100 /* Recover.Reduce */; + } + this.reducePos = this.pos; + this.reduce(reduce); + return true; + } + /** + Try to scan through the automaton to find some kind of reduction + that can be applied. Used when the regular ForcedReduce field + isn't a valid action. @internal + */ + findForcedReduction() { + let { parser } = this.p, seen = []; + let explore = (state, depth) => { + if (seen.includes(state)) + return; + seen.push(state); + return parser.allActions(state, (action) => { + if (action & (262144 /* Action.StayFlag */ | 131072 /* Action.GotoFlag */)) ; + else if (action & 65536 /* Action.ReduceFlag */) { + let rDepth = (action >> 19 /* Action.ReduceDepthShift */) - depth; + if (rDepth > 1) { + let term = action & 65535 /* Action.ValueMask */, target = this.stack.length - rDepth * 3; + if (target >= 0 && parser.getGoto(this.stack[target], term, false) >= 0) + return (rDepth << 19 /* Action.ReduceDepthShift */) | 65536 /* Action.ReduceFlag */ | term; + } + } + else { + let found = explore(action, depth + 1); + if (found != null) + return found; + } + }); + }; + return explore(this.state, 0); + } + /** + @internal + */ + forceAll() { + while (!this.p.parser.stateFlag(this.state, 2 /* StateFlag.Accepting */)) { + if (!this.forceReduce()) { + this.storeNode(0 /* Term.Err */, this.pos, this.pos, 4, true); + break; + } + } + return this; + } + /** + Check whether this state has no further actions (assumed to be a direct descendant of the + top state, since any other states must be able to continue + somehow). @internal + */ + get deadEnd() { + if (this.stack.length != 3) + return false; + let { parser } = this.p; + return parser.data[parser.stateSlot(this.state, 1 /* ParseState.Actions */)] == 65535 /* Seq.End */ && + !parser.stateSlot(this.state, 4 /* ParseState.DefaultReduce */); + } + /** + Restart the stack (put it back in its start state). Only safe + when this.stack.length == 3 (state is directly below the top + state). @internal + */ + restart() { + this.storeNode(0 /* Term.Err */, this.pos, this.pos, 4, true); + this.state = this.stack[0]; + this.stack.length = 0; + } + /** + @internal + */ + sameState(other) { + if (this.state != other.state || this.stack.length != other.stack.length) + return false; + for (let i = 0; i < this.stack.length; i += 3) + if (this.stack[i] != other.stack[i]) + return false; + return true; + } + /** + Get the parser used by this stack. + */ + get parser() { return this.p.parser; } + /** + Test whether a given dialect (by numeric ID, as exported from + the terms file) is enabled. + */ + dialectEnabled(dialectID) { return this.p.parser.dialect.flags[dialectID]; } + shiftContext(term, start) { + if (this.curContext) + this.updateContext(this.curContext.tracker.shift(this.curContext.context, term, this, this.p.stream.reset(start))); + } + reduceContext(term, start) { + if (this.curContext) + this.updateContext(this.curContext.tracker.reduce(this.curContext.context, term, this, this.p.stream.reset(start))); + } + /** + @internal + */ + emitContext() { + let last = this.buffer.length - 1; + if (last < 0 || this.buffer[last] != -3) + this.buffer.push(this.curContext.hash, this.pos, this.pos, -3); + } + /** + @internal + */ + emitLookAhead() { + let last = this.buffer.length - 1; + if (last < 0 || this.buffer[last] != -4) + this.buffer.push(this.lookAhead, this.pos, this.pos, -4); + } + updateContext(context) { + if (context != this.curContext.context) { + let newCx = new StackContext(this.curContext.tracker, context); + if (newCx.hash != this.curContext.hash) + this.emitContext(); + this.curContext = newCx; + } + } + /** + @internal + */ + setLookAhead(lookAhead) { + if (lookAhead > this.lookAhead) { + this.emitLookAhead(); + this.lookAhead = lookAhead; + } + } + /** + @internal + */ + close() { + if (this.curContext && this.curContext.tracker.strict) + this.emitContext(); + if (this.lookAhead > 0) + this.emitLookAhead(); + } +} +class StackContext { + constructor(tracker, context) { + this.tracker = tracker; + this.context = context; + this.hash = tracker.strict ? tracker.hash(context) : 0; + } +} +// Used to cheaply run some reductions to scan ahead without mutating +// an entire stack +class SimulatedStack { + constructor(start) { + this.start = start; + this.state = start.state; + this.stack = start.stack; + this.base = this.stack.length; + } + reduce(action) { + let term = action & 65535 /* Action.ValueMask */, depth = action >> 19 /* Action.ReduceDepthShift */; + if (depth == 0) { + if (this.stack == this.start.stack) + this.stack = this.stack.slice(); + this.stack.push(this.state, 0, 0); + this.base += 3; + } + else { + this.base -= (depth - 1) * 3; + } + let goto = this.start.p.parser.getGoto(this.stack[this.base - 3], term, true); + this.state = goto; + } +} +// This is given to `Tree.build` to build a buffer, and encapsulates +// the parent-stack-walking necessary to read the nodes. +class StackBufferCursor { + constructor(stack, pos, index) { + this.stack = stack; + this.pos = pos; + this.index = index; + this.buffer = stack.buffer; + if (this.index == 0) + this.maybeNext(); + } + static create(stack, pos = stack.bufferBase + stack.buffer.length) { + return new StackBufferCursor(stack, pos, pos - stack.bufferBase); + } + maybeNext() { + let next = this.stack.parent; + if (next != null) { + this.index = this.stack.bufferBase - next.bufferBase; + this.stack = next; + this.buffer = next.buffer; + } + } + get id() { return this.buffer[this.index - 4]; } + get start() { return this.buffer[this.index - 3]; } + get end() { return this.buffer[this.index - 2]; } + get size() { return this.buffer[this.index - 1]; } + next() { + this.index -= 4; + this.pos -= 4; + if (this.index == 0) + this.maybeNext(); + } + fork() { + return new StackBufferCursor(this.stack, this.pos, this.index); + } +} + +// See lezer-generator/src/encode.ts for comments about the encoding +// used here +function decodeArray(input, Type = Uint16Array) { + if (typeof input != "string") + return input; + let array = null; + for (let pos = 0, out = 0; pos < input.length;) { + let value = 0; + for (;;) { + let next = input.charCodeAt(pos++), stop = false; + if (next == 126 /* Encode.BigValCode */) { + value = 65535 /* Encode.BigVal */; + break; + } + if (next >= 92 /* Encode.Gap2 */) + next--; + if (next >= 34 /* Encode.Gap1 */) + next--; + let digit = next - 32 /* Encode.Start */; + if (digit >= 46 /* Encode.Base */) { + digit -= 46 /* Encode.Base */; + stop = true; + } + value += digit; + if (stop) + break; + value *= 46 /* Encode.Base */; + } + if (array) + array[out++] = value; + else + array = new Type(value); + } + return array; +} + +class CachedToken { + constructor() { + this.start = -1; + this.value = -1; + this.end = -1; + this.extended = -1; + this.lookAhead = 0; + this.mask = 0; + this.context = 0; + } +} +const nullToken = new CachedToken; +/** +[Tokenizers](#lr.ExternalTokenizer) interact with the input +through this interface. It presents the input as a stream of +characters, tracking lookahead and hiding the complexity of +[ranges](#common.Parser.parse^ranges) from tokenizer code. +*/ +class InputStream { + /** + @internal + */ + constructor( + /** + @internal + */ + input, + /** + @internal + */ + ranges) { + this.input = input; + this.ranges = ranges; + /** + @internal + */ + this.chunk = ""; + /** + @internal + */ + this.chunkOff = 0; + /** + Backup chunk + */ + this.chunk2 = ""; + this.chunk2Pos = 0; + /** + The character code of the next code unit in the input, or -1 + when the stream is at the end of the input. + */ + this.next = -1; + /** + @internal + */ + this.token = nullToken; + this.rangeIndex = 0; + this.pos = this.chunkPos = ranges[0].from; + this.range = ranges[0]; + this.end = ranges[ranges.length - 1].to; + this.readNext(); + } + /** + @internal + */ + resolveOffset(offset, assoc) { + let range = this.range, index = this.rangeIndex; + let pos = this.pos + offset; + while (pos < range.from) { + if (!index) + return null; + let next = this.ranges[--index]; + pos -= range.from - next.to; + range = next; + } + while (assoc < 0 ? pos > range.to : pos >= range.to) { + if (index == this.ranges.length - 1) + return null; + let next = this.ranges[++index]; + pos += next.from - range.to; + range = next; + } + return pos; + } + /** + @internal + */ + clipPos(pos) { + if (pos >= this.range.from && pos < this.range.to) + return pos; + for (let range of this.ranges) + if (range.to > pos) + return Math.max(pos, range.from); + return this.end; + } + /** + Look at a code unit near the stream position. `.peek(0)` equals + `.next`, `.peek(-1)` gives you the previous character, and so + on. + + Note that looking around during tokenizing creates dependencies + on potentially far-away content, which may reduce the + effectiveness incremental parsing—when looking forward—or even + cause invalid reparses when looking backward more than 25 code + units, since the library does not track lookbehind. + */ + peek(offset) { + let idx = this.chunkOff + offset, pos, result; + if (idx >= 0 && idx < this.chunk.length) { + pos = this.pos + offset; + result = this.chunk.charCodeAt(idx); + } + else { + let resolved = this.resolveOffset(offset, 1); + if (resolved == null) + return -1; + pos = resolved; + if (pos >= this.chunk2Pos && pos < this.chunk2Pos + this.chunk2.length) { + result = this.chunk2.charCodeAt(pos - this.chunk2Pos); + } + else { + let i = this.rangeIndex, range = this.range; + while (range.to <= pos) + range = this.ranges[++i]; + this.chunk2 = this.input.chunk(this.chunk2Pos = pos); + if (pos + this.chunk2.length > range.to) + this.chunk2 = this.chunk2.slice(0, range.to - pos); + result = this.chunk2.charCodeAt(0); + } + } + if (pos >= this.token.lookAhead) + this.token.lookAhead = pos + 1; + return result; + } + /** + Accept a token. By default, the end of the token is set to the + current stream position, but you can pass an offset (relative to + the stream position) to change that. + */ + acceptToken(token, endOffset = 0) { + let end = endOffset ? this.resolveOffset(endOffset, -1) : this.pos; + if (end == null || end < this.token.start) + throw new RangeError("Token end out of bounds"); + this.token.value = token; + this.token.end = end; + } + /** + Accept a token ending at a specific given position. + */ + acceptTokenTo(token, endPos) { + this.token.value = token; + this.token.end = endPos; + } + getChunk() { + if (this.pos >= this.chunk2Pos && this.pos < this.chunk2Pos + this.chunk2.length) { + let { chunk, chunkPos } = this; + this.chunk = this.chunk2; + this.chunkPos = this.chunk2Pos; + this.chunk2 = chunk; + this.chunk2Pos = chunkPos; + this.chunkOff = this.pos - this.chunkPos; + } + else { + this.chunk2 = this.chunk; + this.chunk2Pos = this.chunkPos; + let nextChunk = this.input.chunk(this.pos); + let end = this.pos + nextChunk.length; + this.chunk = end > this.range.to ? nextChunk.slice(0, this.range.to - this.pos) : nextChunk; + this.chunkPos = this.pos; + this.chunkOff = 0; + } + } + readNext() { + if (this.chunkOff >= this.chunk.length) { + this.getChunk(); + if (this.chunkOff == this.chunk.length) + return this.next = -1; + } + return this.next = this.chunk.charCodeAt(this.chunkOff); + } + /** + Move the stream forward N (defaults to 1) code units. Returns + the new value of [`next`](#lr.InputStream.next). + */ + advance(n = 1) { + this.chunkOff += n; + while (this.pos + n >= this.range.to) { + if (this.rangeIndex == this.ranges.length - 1) + return this.setDone(); + n -= this.range.to - this.pos; + this.range = this.ranges[++this.rangeIndex]; + this.pos = this.range.from; + } + this.pos += n; + if (this.pos >= this.token.lookAhead) + this.token.lookAhead = this.pos + 1; + return this.readNext(); + } + setDone() { + this.pos = this.chunkPos = this.end; + this.range = this.ranges[this.rangeIndex = this.ranges.length - 1]; + this.chunk = ""; + return this.next = -1; + } + /** + @internal + */ + reset(pos, token) { + if (token) { + this.token = token; + token.start = pos; + token.lookAhead = pos + 1; + token.value = token.extended = -1; + } + else { + this.token = nullToken; + } + if (this.pos != pos) { + this.pos = pos; + if (pos == this.end) { + this.setDone(); + return this; + } + while (pos < this.range.from) + this.range = this.ranges[--this.rangeIndex]; + while (pos >= this.range.to) + this.range = this.ranges[++this.rangeIndex]; + if (pos >= this.chunkPos && pos < this.chunkPos + this.chunk.length) { + this.chunkOff = pos - this.chunkPos; + } + else { + this.chunk = ""; + this.chunkOff = 0; + } + this.readNext(); + } + return this; + } + /** + @internal + */ + read(from, to) { + if (from >= this.chunkPos && to <= this.chunkPos + this.chunk.length) + return this.chunk.slice(from - this.chunkPos, to - this.chunkPos); + if (from >= this.chunk2Pos && to <= this.chunk2Pos + this.chunk2.length) + return this.chunk2.slice(from - this.chunk2Pos, to - this.chunk2Pos); + if (from >= this.range.from && to <= this.range.to) + return this.input.read(from, to); + let result = ""; + for (let r of this.ranges) { + if (r.from >= to) + break; + if (r.to > from) + result += this.input.read(Math.max(r.from, from), Math.min(r.to, to)); + } + return result; + } +} +/** +@internal +*/ +class TokenGroup { + constructor(data, id) { + this.data = data; + this.id = id; + } + token(input, stack) { + let { parser } = stack.p; + readToken(this.data, input, stack, this.id, parser.data, parser.tokenPrecTable); + } +} +TokenGroup.prototype.contextual = TokenGroup.prototype.fallback = TokenGroup.prototype.extend = false; +/** +@hide +*/ +class LocalTokenGroup { + constructor(data, precTable, elseToken) { + this.precTable = precTable; + this.elseToken = elseToken; + this.data = typeof data == "string" ? decodeArray(data) : data; + } + token(input, stack) { + let start = input.pos, skipped = 0; + for (;;) { + let atEof = input.next < 0, nextPos = input.resolveOffset(1, 1); + readToken(this.data, input, stack, 0, this.data, this.precTable); + if (input.token.value > -1) + break; + if (this.elseToken == null) + return; + if (!atEof) + skipped++; + if (nextPos == null) + break; + input.reset(nextPos, input.token); + } + if (skipped) { + input.reset(start, input.token); + input.acceptToken(this.elseToken, skipped); + } + } +} +LocalTokenGroup.prototype.contextual = TokenGroup.prototype.fallback = TokenGroup.prototype.extend = false; +/** +`@external tokens` declarations in the grammar should resolve to +an instance of this class. +*/ +class ExternalTokenizer { + /** + Create a tokenizer. The first argument is the function that, + given an input stream, scans for the types of tokens it + recognizes at the stream's position, and calls + [`acceptToken`](#lr.InputStream.acceptToken) when it finds + one. + */ + constructor( + /** + @internal + */ + token, options = {}) { + this.token = token; + this.contextual = !!options.contextual; + this.fallback = !!options.fallback; + this.extend = !!options.extend; + } +} +// Tokenizer data is stored a big uint16 array containing, for each +// state: +// +// - A group bitmask, indicating what token groups are reachable from +// this state, so that paths that can only lead to tokens not in +// any of the current groups can be cut off early. +// +// - The position of the end of the state's sequence of accepting +// tokens +// +// - The number of outgoing edges for the state +// +// - The accepting tokens, as (token id, group mask) pairs +// +// - The outgoing edges, as (start character, end character, state +// index) triples, with end character being exclusive +// +// This function interprets that data, running through a stream as +// long as new states with the a matching group mask can be reached, +// and updating `input.token` when it matches a token. +function readToken(data, input, stack, group, precTable, precOffset) { + let state = 0, groupMask = 1 << group, { dialect } = stack.p.parser; + scan: for (;;) { + if ((groupMask & data[state]) == 0) + break; + let accEnd = data[state + 1]; + // Check whether this state can lead to a token in the current group + // Accept tokens in this state, possibly overwriting + // lower-precedence / shorter tokens + for (let i = state + 3; i < accEnd; i += 2) + if ((data[i + 1] & groupMask) > 0) { + let term = data[i]; + if (dialect.allows(term) && + (input.token.value == -1 || input.token.value == term || + overrides(term, input.token.value, precTable, precOffset))) { + input.acceptToken(term); + break; + } + } + let next = input.next, low = 0, high = data[state + 2]; + // Special case for EOF + if (input.next < 0 && high > low && data[accEnd + high * 3 - 3] == 65535 /* Seq.End */) { + state = data[accEnd + high * 3 - 1]; + continue scan; + } + // Do a binary search on the state's edges + for (; low < high;) { + let mid = (low + high) >> 1; + let index = accEnd + mid + (mid << 1); + let from = data[index], to = data[index + 1] || 0x10000; + if (next < from) + high = mid; + else if (next >= to) + low = mid + 1; + else { + state = data[index + 2]; + input.advance(); + continue scan; + } + } + break; + } +} +function findOffset(data, start, term) { + for (let i = start, next; (next = data[i]) != 65535 /* Seq.End */; i++) + if (next == term) + return i - start; + return -1; +} +function overrides(token, prev, tableData, tableOffset) { + let iPrev = findOffset(tableData, tableOffset, prev); + return iPrev < 0 || findOffset(tableData, tableOffset, token) < iPrev; +} + +// Environment variable used to control console output +const verbose = typeof process != "undefined" && process.env && /\bparse\b/.test(process.env.LOG); +let stackIDs = null; +function cutAt(tree, pos, side) { + let cursor = tree.cursor(IterMode.IncludeAnonymous); + cursor.moveTo(pos); + for (;;) { + if (!(side < 0 ? cursor.childBefore(pos) : cursor.childAfter(pos))) + for (;;) { + if ((side < 0 ? cursor.to < pos : cursor.from > pos) && !cursor.type.isError) + return side < 0 ? Math.max(0, Math.min(cursor.to - 1, pos - 25 /* Lookahead.Margin */)) + : Math.min(tree.length, Math.max(cursor.from + 1, pos + 25 /* Lookahead.Margin */)); + if (side < 0 ? cursor.prevSibling() : cursor.nextSibling()) + break; + if (!cursor.parent()) + return side < 0 ? 0 : tree.length; + } + } +} +class FragmentCursor { + constructor(fragments, nodeSet) { + this.fragments = fragments; + this.nodeSet = nodeSet; + this.i = 0; + this.fragment = null; + this.safeFrom = -1; + this.safeTo = -1; + this.trees = []; + this.start = []; + this.index = []; + this.nextFragment(); + } + nextFragment() { + let fr = this.fragment = this.i == this.fragments.length ? null : this.fragments[this.i++]; + if (fr) { + this.safeFrom = fr.openStart ? cutAt(fr.tree, fr.from + fr.offset, 1) - fr.offset : fr.from; + this.safeTo = fr.openEnd ? cutAt(fr.tree, fr.to + fr.offset, -1) - fr.offset : fr.to; + while (this.trees.length) { + this.trees.pop(); + this.start.pop(); + this.index.pop(); + } + this.trees.push(fr.tree); + this.start.push(-fr.offset); + this.index.push(0); + this.nextStart = this.safeFrom; + } + else { + this.nextStart = 1e9; + } + } + // `pos` must be >= any previously given `pos` for this cursor + nodeAt(pos) { + if (pos < this.nextStart) + return null; + while (this.fragment && this.safeTo <= pos) + this.nextFragment(); + if (!this.fragment) + return null; + for (;;) { + let last = this.trees.length - 1; + if (last < 0) { // End of tree + this.nextFragment(); + return null; + } + let top = this.trees[last], index = this.index[last]; + if (index == top.children.length) { + this.trees.pop(); + this.start.pop(); + this.index.pop(); + continue; + } + let next = top.children[index]; + let start = this.start[last] + top.positions[index]; + if (start > pos) { + this.nextStart = start; + return null; + } + if (next instanceof Tree) { + if (start == pos) { + if (start < this.safeFrom) + return null; + let end = start + next.length; + if (end <= this.safeTo) { + let lookAhead = next.prop(NodeProp.lookAhead); + if (!lookAhead || end + lookAhead < this.fragment.to) + return next; + } + } + this.index[last]++; + if (start + next.length >= Math.max(this.safeFrom, pos)) { // Enter this node + this.trees.push(next); + this.start.push(start); + this.index.push(0); + } + } + else { + this.index[last]++; + this.nextStart = start + next.length; + } + } + } +} +class TokenCache { + constructor(parser, stream) { + this.stream = stream; + this.tokens = []; + this.mainToken = null; + this.actions = []; + this.tokens = parser.tokenizers.map(_ => new CachedToken); + } + getActions(stack) { + let actionIndex = 0; + let main = null; + let { parser } = stack.p, { tokenizers } = parser; + let mask = parser.stateSlot(stack.state, 3 /* ParseState.TokenizerMask */); + let context = stack.curContext ? stack.curContext.hash : 0; + let lookAhead = 0; + for (let i = 0; i < tokenizers.length; i++) { + if (((1 << i) & mask) == 0) + continue; + let tokenizer = tokenizers[i], token = this.tokens[i]; + if (main && !tokenizer.fallback) + continue; + if (tokenizer.contextual || token.start != stack.pos || token.mask != mask || token.context != context) { + this.updateCachedToken(token, tokenizer, stack); + token.mask = mask; + token.context = context; + } + if (token.lookAhead > token.end + 25 /* Lookahead.Margin */) + lookAhead = Math.max(token.lookAhead, lookAhead); + if (token.value != 0 /* Term.Err */) { + let startIndex = actionIndex; + if (token.extended > -1) + actionIndex = this.addActions(stack, token.extended, token.end, actionIndex); + actionIndex = this.addActions(stack, token.value, token.end, actionIndex); + if (!tokenizer.extend) { + main = token; + if (actionIndex > startIndex) + break; + } + } + } + while (this.actions.length > actionIndex) + this.actions.pop(); + if (lookAhead) + stack.setLookAhead(lookAhead); + if (!main && stack.pos == this.stream.end) { + main = new CachedToken; + main.value = stack.p.parser.eofTerm; + main.start = main.end = stack.pos; + actionIndex = this.addActions(stack, main.value, main.end, actionIndex); + } + this.mainToken = main; + return this.actions; + } + getMainToken(stack) { + if (this.mainToken) + return this.mainToken; + let main = new CachedToken, { pos, p } = stack; + main.start = pos; + main.end = Math.min(pos + 1, p.stream.end); + main.value = pos == p.stream.end ? p.parser.eofTerm : 0 /* Term.Err */; + return main; + } + updateCachedToken(token, tokenizer, stack) { + let start = this.stream.clipPos(stack.pos); + tokenizer.token(this.stream.reset(start, token), stack); + if (token.value > -1) { + let { parser } = stack.p; + for (let i = 0; i < parser.specialized.length; i++) + if (parser.specialized[i] == token.value) { + let result = parser.specializers[i](this.stream.read(token.start, token.end), stack); + if (result >= 0 && stack.p.parser.dialect.allows(result >> 1)) { + if ((result & 1) == 0 /* Specialize.Specialize */) + token.value = result >> 1; + else + token.extended = result >> 1; + break; + } + } + } + else { + token.value = 0 /* Term.Err */; + token.end = this.stream.clipPos(start + 1); + } + } + putAction(action, token, end, index) { + // Don't add duplicate actions + for (let i = 0; i < index; i += 3) + if (this.actions[i] == action) + return index; + this.actions[index++] = action; + this.actions[index++] = token; + this.actions[index++] = end; + return index; + } + addActions(stack, token, end, index) { + let { state } = stack, { parser } = stack.p, { data } = parser; + for (let set = 0; set < 2; set++) { + for (let i = parser.stateSlot(state, set ? 2 /* ParseState.Skip */ : 1 /* ParseState.Actions */);; i += 3) { + if (data[i] == 65535 /* Seq.End */) { + if (data[i + 1] == 1 /* Seq.Next */) { + i = pair(data, i + 2); + } + else { + if (index == 0 && data[i + 1] == 2 /* Seq.Other */) + index = this.putAction(pair(data, i + 2), token, end, index); + break; + } + } + if (data[i] == token) + index = this.putAction(pair(data, i + 1), token, end, index); + } + } + return index; + } +} +class Parse { + constructor(parser, input, fragments, ranges) { + this.parser = parser; + this.input = input; + this.ranges = ranges; + this.recovering = 0; + this.nextStackID = 0x2654; // ♔, ♕, ♖, ♗, ♘, ♙, ♠, ♡, ♢, ♣, ♤, ♥, ♦, ♧ + this.minStackPos = 0; + this.reused = []; + this.stoppedAt = null; + this.lastBigReductionStart = -1; + this.lastBigReductionSize = 0; + this.bigReductionCount = 0; + this.stream = new InputStream(input, ranges); + this.tokens = new TokenCache(parser, this.stream); + this.topTerm = parser.top[1]; + let { from } = ranges[0]; + this.stacks = [Stack.start(this, parser.top[0], from)]; + this.fragments = fragments.length && this.stream.end - from > parser.bufferLength * 4 + ? new FragmentCursor(fragments, parser.nodeSet) : null; + } + get parsedPos() { + return this.minStackPos; + } + // Move the parser forward. This will process all parse stacks at + // `this.pos` and try to advance them to a further position. If no + // stack for such a position is found, it'll start error-recovery. + // + // When the parse is finished, this will return a syntax tree. When + // not, it returns `null`. + advance() { + let stacks = this.stacks, pos = this.minStackPos; + // This will hold stacks beyond `pos`. + let newStacks = this.stacks = []; + let stopped, stoppedTokens; + // If a large amount of reductions happened with the same start + // position, force the stack out of that production in order to + // avoid creating a tree too deep to recurse through. + // (This is an ugly kludge, because unfortunately there is no + // straightforward, cheap way to check for this happening, due to + // the history of reductions only being available in an + // expensive-to-access format in the stack buffers.) + if (this.bigReductionCount > 300 /* Rec.MaxLeftAssociativeReductionCount */ && stacks.length == 1) { + let [s] = stacks; + while (s.forceReduce() && s.stack.length && s.stack[s.stack.length - 2] >= this.lastBigReductionStart) { } + this.bigReductionCount = this.lastBigReductionSize = 0; + } + // Keep advancing any stacks at `pos` until they either move + // forward or can't be advanced. Gather stacks that can't be + // advanced further in `stopped`. + for (let i = 0; i < stacks.length; i++) { + let stack = stacks[i]; + for (;;) { + this.tokens.mainToken = null; + if (stack.pos > pos) { + newStacks.push(stack); + } + else if (this.advanceStack(stack, newStacks, stacks)) { + continue; + } + else { + if (!stopped) { + stopped = []; + stoppedTokens = []; + } + stopped.push(stack); + let tok = this.tokens.getMainToken(stack); + stoppedTokens.push(tok.value, tok.end); + } + break; + } + } + if (!newStacks.length) { + let finished = stopped && findFinished(stopped); + if (finished) { + if (verbose) + console.log("Finish with " + this.stackID(finished)); + return this.stackToTree(finished); + } + if (this.parser.strict) { + if (verbose && stopped) + console.log("Stuck with token " + (this.tokens.mainToken ? this.parser.getName(this.tokens.mainToken.value) : "none")); + throw new SyntaxError("No parse at " + pos); + } + if (!this.recovering) + this.recovering = 5 /* Rec.Distance */; + } + if (this.recovering && stopped) { + let finished = this.stoppedAt != null && stopped[0].pos > this.stoppedAt ? stopped[0] + : this.runRecovery(stopped, stoppedTokens, newStacks); + if (finished) { + if (verbose) + console.log("Force-finish " + this.stackID(finished)); + return this.stackToTree(finished.forceAll()); + } + } + if (this.recovering) { + let maxRemaining = this.recovering == 1 ? 1 : this.recovering * 3 /* Rec.MaxRemainingPerStep */; + if (newStacks.length > maxRemaining) { + newStacks.sort((a, b) => b.score - a.score); + while (newStacks.length > maxRemaining) + newStacks.pop(); + } + if (newStacks.some(s => s.reducePos > pos)) + this.recovering--; + } + else if (newStacks.length > 1) { + // Prune stacks that are in the same state, or that have been + // running without splitting for a while, to avoid getting stuck + // with multiple successful stacks running endlessly on. + outer: for (let i = 0; i < newStacks.length - 1; i++) { + let stack = newStacks[i]; + for (let j = i + 1; j < newStacks.length; j++) { + let other = newStacks[j]; + if (stack.sameState(other) || + stack.buffer.length > 500 /* Rec.MinBufferLengthPrune */ && other.buffer.length > 500 /* Rec.MinBufferLengthPrune */) { + if (((stack.score - other.score) || (stack.buffer.length - other.buffer.length)) > 0) { + newStacks.splice(j--, 1); + } + else { + newStacks.splice(i--, 1); + continue outer; + } + } + } + } + if (newStacks.length > 12 /* Rec.MaxStackCount */) + newStacks.splice(12 /* Rec.MaxStackCount */, newStacks.length - 12 /* Rec.MaxStackCount */); + } + this.minStackPos = newStacks[0].pos; + for (let i = 1; i < newStacks.length; i++) + if (newStacks[i].pos < this.minStackPos) + this.minStackPos = newStacks[i].pos; + return null; + } + stopAt(pos) { + if (this.stoppedAt != null && this.stoppedAt < pos) + throw new RangeError("Can't move stoppedAt forward"); + this.stoppedAt = pos; + } + // Returns an updated version of the given stack, or null if the + // stack can't advance normally. When `split` and `stacks` are + // given, stacks split off by ambiguous operations will be pushed to + // `split`, or added to `stacks` if they move `pos` forward. + advanceStack(stack, stacks, split) { + let start = stack.pos, { parser } = this; + let base = verbose ? this.stackID(stack) + " -> " : ""; + if (this.stoppedAt != null && start > this.stoppedAt) + return stack.forceReduce() ? stack : null; + if (this.fragments) { + let strictCx = stack.curContext && stack.curContext.tracker.strict, cxHash = strictCx ? stack.curContext.hash : 0; + for (let cached = this.fragments.nodeAt(start); cached;) { + let match = this.parser.nodeSet.types[cached.type.id] == cached.type ? parser.getGoto(stack.state, cached.type.id) : -1; + if (match > -1 && cached.length && (!strictCx || (cached.prop(NodeProp.contextHash) || 0) == cxHash)) { + stack.useNode(cached, match); + if (verbose) + console.log(base + this.stackID(stack) + ` (via reuse of ${parser.getName(cached.type.id)})`); + return true; + } + if (!(cached instanceof Tree) || cached.children.length == 0 || cached.positions[0] > 0) + break; + let inner = cached.children[0]; + if (inner instanceof Tree && cached.positions[0] == 0) + cached = inner; + else + break; + } + } + let defaultReduce = parser.stateSlot(stack.state, 4 /* ParseState.DefaultReduce */); + if (defaultReduce > 0) { + stack.reduce(defaultReduce); + if (verbose) + console.log(base + this.stackID(stack) + ` (via always-reduce ${parser.getName(defaultReduce & 65535 /* Action.ValueMask */)})`); + return true; + } + if (stack.stack.length >= 8400 /* Rec.CutDepth */) { + while (stack.stack.length > 6000 /* Rec.CutTo */ && stack.forceReduce()) { } + } + let actions = this.tokens.getActions(stack); + for (let i = 0; i < actions.length;) { + let action = actions[i++], term = actions[i++], end = actions[i++]; + let last = i == actions.length || !split; + let localStack = last ? stack : stack.split(); + let main = this.tokens.mainToken; + localStack.apply(action, term, main ? main.start : localStack.pos, end); + if (verbose) + console.log(base + this.stackID(localStack) + ` (via ${(action & 65536 /* Action.ReduceFlag */) == 0 ? "shift" + : `reduce of ${parser.getName(action & 65535 /* Action.ValueMask */)}`} for ${parser.getName(term)} @ ${start}${localStack == stack ? "" : ", split"})`); + if (last) + return true; + else if (localStack.pos > start) + stacks.push(localStack); + else + split.push(localStack); + } + return false; + } + // Advance a given stack forward as far as it will go. Returns the + // (possibly updated) stack if it got stuck, or null if it moved + // forward and was given to `pushStackDedup`. + advanceFully(stack, newStacks) { + let pos = stack.pos; + for (;;) { + if (!this.advanceStack(stack, null, null)) + return false; + if (stack.pos > pos) { + pushStackDedup(stack, newStacks); + return true; + } + } + } + runRecovery(stacks, tokens, newStacks) { + let finished = null, restarted = false; + for (let i = 0; i < stacks.length; i++) { + let stack = stacks[i], token = tokens[i << 1], tokenEnd = tokens[(i << 1) + 1]; + let base = verbose ? this.stackID(stack) + " -> " : ""; + if (stack.deadEnd) { + if (restarted) + continue; + restarted = true; + stack.restart(); + if (verbose) + console.log(base + this.stackID(stack) + " (restarted)"); + let done = this.advanceFully(stack, newStacks); + if (done) + continue; + } + let force = stack.split(), forceBase = base; + for (let j = 0; force.forceReduce() && j < 10 /* Rec.ForceReduceLimit */; j++) { + if (verbose) + console.log(forceBase + this.stackID(force) + " (via force-reduce)"); + let done = this.advanceFully(force, newStacks); + if (done) + break; + if (verbose) + forceBase = this.stackID(force) + " -> "; + } + for (let insert of stack.recoverByInsert(token)) { + if (verbose) + console.log(base + this.stackID(insert) + " (via recover-insert)"); + this.advanceFully(insert, newStacks); + } + if (this.stream.end > stack.pos) { + if (tokenEnd == stack.pos) { + tokenEnd++; + token = 0 /* Term.Err */; + } + stack.recoverByDelete(token, tokenEnd); + if (verbose) + console.log(base + this.stackID(stack) + ` (via recover-delete ${this.parser.getName(token)})`); + pushStackDedup(stack, newStacks); + } + else if (!finished || finished.score < stack.score) { + finished = stack; + } + } + return finished; + } + // Convert the stack's buffer to a syntax tree. + stackToTree(stack) { + stack.close(); + return Tree.build({ buffer: StackBufferCursor.create(stack), + nodeSet: this.parser.nodeSet, + topID: this.topTerm, + maxBufferLength: this.parser.bufferLength, + reused: this.reused, + start: this.ranges[0].from, + length: stack.pos - this.ranges[0].from, + minRepeatType: this.parser.minRepeatTerm }); + } + stackID(stack) { + let id = (stackIDs || (stackIDs = new WeakMap)).get(stack); + if (!id) + stackIDs.set(stack, id = String.fromCodePoint(this.nextStackID++)); + return id + stack; + } +} +function pushStackDedup(stack, newStacks) { + for (let i = 0; i < newStacks.length; i++) { + let other = newStacks[i]; + if (other.pos == stack.pos && other.sameState(stack)) { + if (newStacks[i].score < stack.score) + newStacks[i] = stack; + return; + } + } + newStacks.push(stack); +} +class Dialect { + constructor(source, flags, disabled) { + this.source = source; + this.flags = flags; + this.disabled = disabled; + } + allows(term) { return !this.disabled || this.disabled[term] == 0; } +} +const id = x => x; +/** +Context trackers are used to track stateful context (such as +indentation in the Python grammar, or parent elements in the XML +grammar) needed by external tokenizers. You declare them in a +grammar file as `@context exportName from "module"`. + +Context values should be immutable, and can be updated (replaced) +on shift or reduce actions. + +The export used in a `@context` declaration should be of this +type. +*/ +class ContextTracker { + /** + Define a context tracker. + */ + constructor(spec) { + this.start = spec.start; + this.shift = spec.shift || id; + this.reduce = spec.reduce || id; + this.reuse = spec.reuse || id; + this.hash = spec.hash || (() => 0); + this.strict = spec.strict !== false; + } +} +/** +Holds the parse tables for a given grammar, as generated by +`lezer-generator`, and provides [methods](#common.Parser) to parse +content with. +*/ +class LRParser extends Parser { + /** + @internal + */ + constructor(spec) { + super(); + /** + @internal + */ + this.wrappers = []; + if (spec.version != 14 /* File.Version */) + throw new RangeError(`Parser version (${spec.version}) doesn't match runtime version (${14 /* File.Version */})`); + let nodeNames = spec.nodeNames.split(" "); + this.minRepeatTerm = nodeNames.length; + for (let i = 0; i < spec.repeatNodeCount; i++) + nodeNames.push(""); + let topTerms = Object.keys(spec.topRules).map(r => spec.topRules[r][1]); + let nodeProps = []; + for (let i = 0; i < nodeNames.length; i++) + nodeProps.push([]); + function setProp(nodeID, prop, value) { + nodeProps[nodeID].push([prop, prop.deserialize(String(value))]); + } + if (spec.nodeProps) + for (let propSpec of spec.nodeProps) { + let prop = propSpec[0]; + if (typeof prop == "string") + prop = NodeProp[prop]; + for (let i = 1; i < propSpec.length;) { + let next = propSpec[i++]; + if (next >= 0) { + setProp(next, prop, propSpec[i++]); + } + else { + let value = propSpec[i + -next]; + for (let j = -next; j > 0; j--) + setProp(propSpec[i++], prop, value); + i++; + } + } + } + this.nodeSet = new NodeSet(nodeNames.map((name, i) => NodeType.define({ + name: i >= this.minRepeatTerm ? undefined : name, + id: i, + props: nodeProps[i], + top: topTerms.indexOf(i) > -1, + error: i == 0, + skipped: spec.skippedNodes && spec.skippedNodes.indexOf(i) > -1 + }))); + if (spec.propSources) + this.nodeSet = this.nodeSet.extend(...spec.propSources); + this.strict = false; + this.bufferLength = DefaultBufferLength; + let tokenArray = decodeArray(spec.tokenData); + this.context = spec.context; + this.specializerSpecs = spec.specialized || []; + this.specialized = new Uint16Array(this.specializerSpecs.length); + for (let i = 0; i < this.specializerSpecs.length; i++) + this.specialized[i] = this.specializerSpecs[i].term; + this.specializers = this.specializerSpecs.map(getSpecializer); + this.states = decodeArray(spec.states, Uint32Array); + this.data = decodeArray(spec.stateData); + this.goto = decodeArray(spec.goto); + this.maxTerm = spec.maxTerm; + this.tokenizers = spec.tokenizers.map(value => typeof value == "number" ? new TokenGroup(tokenArray, value) : value); + this.topRules = spec.topRules; + this.dialects = spec.dialects || {}; + this.dynamicPrecedences = spec.dynamicPrecedences || null; + this.tokenPrecTable = spec.tokenPrec; + this.termNames = spec.termNames || null; + this.maxNode = this.nodeSet.types.length - 1; + this.dialect = this.parseDialect(); + this.top = this.topRules[Object.keys(this.topRules)[0]]; + } + createParse(input, fragments, ranges) { + let parse = new Parse(this, input, fragments, ranges); + for (let w of this.wrappers) + parse = w(parse, input, fragments, ranges); + return parse; + } + /** + Get a goto table entry @internal + */ + getGoto(state, term, loose = false) { + let table = this.goto; + if (term >= table[0]) + return -1; + for (let pos = table[term + 1];;) { + let groupTag = table[pos++], last = groupTag & 1; + let target = table[pos++]; + if (last && loose) + return target; + for (let end = pos + (groupTag >> 1); pos < end; pos++) + if (table[pos] == state) + return target; + if (last) + return -1; + } + } + /** + Check if this state has an action for a given terminal @internal + */ + hasAction(state, terminal) { + let data = this.data; + for (let set = 0; set < 2; set++) { + for (let i = this.stateSlot(state, set ? 2 /* ParseState.Skip */ : 1 /* ParseState.Actions */), next;; i += 3) { + if ((next = data[i]) == 65535 /* Seq.End */) { + if (data[i + 1] == 1 /* Seq.Next */) + next = data[i = pair(data, i + 2)]; + else if (data[i + 1] == 2 /* Seq.Other */) + return pair(data, i + 2); + else + break; + } + if (next == terminal || next == 0 /* Term.Err */) + return pair(data, i + 1); + } + } + return 0; + } + /** + @internal + */ + stateSlot(state, slot) { + return this.states[(state * 6 /* ParseState.Size */) + slot]; + } + /** + @internal + */ + stateFlag(state, flag) { + return (this.stateSlot(state, 0 /* ParseState.Flags */) & flag) > 0; + } + /** + @internal + */ + validAction(state, action) { + return !!this.allActions(state, a => a == action ? true : null); + } + /** + @internal + */ + allActions(state, action) { + let deflt = this.stateSlot(state, 4 /* ParseState.DefaultReduce */); + let result = deflt ? action(deflt) : undefined; + for (let i = this.stateSlot(state, 1 /* ParseState.Actions */); result == null; i += 3) { + if (this.data[i] == 65535 /* Seq.End */) { + if (this.data[i + 1] == 1 /* Seq.Next */) + i = pair(this.data, i + 2); + else + break; + } + result = action(pair(this.data, i + 1)); + } + return result; + } + /** + Get the states that can follow this one through shift actions or + goto jumps. @internal + */ + nextStates(state) { + let result = []; + for (let i = this.stateSlot(state, 1 /* ParseState.Actions */);; i += 3) { + if (this.data[i] == 65535 /* Seq.End */) { + if (this.data[i + 1] == 1 /* Seq.Next */) + i = pair(this.data, i + 2); + else + break; + } + if ((this.data[i + 2] & (65536 /* Action.ReduceFlag */ >> 16)) == 0) { + let value = this.data[i + 1]; + if (!result.some((v, i) => (i & 1) && v == value)) + result.push(this.data[i], value); + } + } + return result; + } + /** + Configure the parser. Returns a new parser instance that has the + given settings modified. Settings not provided in `config` are + kept from the original parser. + */ + configure(config) { + // Hideous reflection-based kludge to make it easy to create a + // slightly modified copy of a parser. + let copy = Object.assign(Object.create(LRParser.prototype), this); + if (config.props) + copy.nodeSet = this.nodeSet.extend(...config.props); + if (config.top) { + let info = this.topRules[config.top]; + if (!info) + throw new RangeError(`Invalid top rule name ${config.top}`); + copy.top = info; + } + if (config.tokenizers) + copy.tokenizers = this.tokenizers.map(t => { + let found = config.tokenizers.find(r => r.from == t); + return found ? found.to : t; + }); + if (config.specializers) { + copy.specializers = this.specializers.slice(); + copy.specializerSpecs = this.specializerSpecs.map((s, i) => { + let found = config.specializers.find(r => r.from == s.external); + if (!found) + return s; + let spec = Object.assign(Object.assign({}, s), { external: found.to }); + copy.specializers[i] = getSpecializer(spec); + return spec; + }); + } + if (config.contextTracker) + copy.context = config.contextTracker; + if (config.dialect) + copy.dialect = this.parseDialect(config.dialect); + if (config.strict != null) + copy.strict = config.strict; + if (config.wrap) + copy.wrappers = copy.wrappers.concat(config.wrap); + if (config.bufferLength != null) + copy.bufferLength = config.bufferLength; + return copy; + } + /** + Tells you whether any [parse wrappers](#lr.ParserConfig.wrap) + are registered for this parser. + */ + hasWrappers() { + return this.wrappers.length > 0; + } + /** + Returns the name associated with a given term. This will only + work for all terms when the parser was generated with the + `--names` option. By default, only the names of tagged terms are + stored. + */ + getName(term) { + return this.termNames ? this.termNames[term] : String(term <= this.maxNode && this.nodeSet.types[term].name || term); + } + /** + The eof term id is always allocated directly after the node + types. @internal + */ + get eofTerm() { return this.maxNode + 1; } + /** + The type of top node produced by the parser. + */ + get topNode() { return this.nodeSet.types[this.top[1]]; } + /** + @internal + */ + dynamicPrecedence(term) { + let prec = this.dynamicPrecedences; + return prec == null ? 0 : prec[term] || 0; + } + /** + @internal + */ + parseDialect(dialect) { + let values = Object.keys(this.dialects), flags = values.map(() => false); + if (dialect) + for (let part of dialect.split(" ")) { + let id = values.indexOf(part); + if (id >= 0) + flags[id] = true; + } + let disabled = null; + for (let i = 0; i < values.length; i++) + if (!flags[i]) { + for (let j = this.dialects[values[i]], id; (id = this.data[j++]) != 65535 /* Seq.End */;) + (disabled || (disabled = new Uint8Array(this.maxTerm + 1)))[id] = 1; + } + return new Dialect(dialect, flags, disabled); + } + /** + Used by the output of the parser generator. Not available to + user code. @hide + */ + static deserialize(spec) { + return new LRParser(spec); + } +} +function pair(data, off) { return data[off] | (data[off + 1] << 16); } +function findFinished(stacks) { + let best = null; + for (let stack of stacks) { + let stopped = stack.p.stoppedAt; + if ((stack.pos == stack.p.stream.end || stopped != null && stack.pos > stopped) && + stack.p.parser.stateFlag(stack.state, 2 /* StateFlag.Accepting */) && + (!best || best.score < stack.score)) + best = stack; + } + return best; +} +function getSpecializer(spec) { + if (spec.external) { + let mask = spec.extend ? 1 /* Specialize.Extend */ : 0 /* Specialize.Specialize */; + return (value, stack) => (spec.external(value, stack) << 1) | mask; + } + return spec.get; +} + +// This file was generated by lezer-generator. You probably shouldn't edit it. +const printKeyword = 1, + indent = 194, + dedent = 195, + newline$1 = 196, + blankLineStart = 197, + newlineBracketed = 198, + eof = 199, + stringContent = 200, + Escape = 2, + replacementStart = 3, + stringEnd = 201, + ParenL = 24, + ParenthesizedExpression = 25, + TupleExpression = 49, + ComprehensionExpression = 50, + BracketL = 55, + ArrayExpression = 56, + ArrayComprehensionExpression = 57, + BraceL = 59, + DictionaryExpression = 60, + DictionaryComprehensionExpression = 61, + SetExpression = 62, + SetComprehensionExpression = 63, + ArgList = 65, + subscript = 238, + String$1 = 71, + stringStart = 241, + stringStartD = 242, + stringStartL = 243, + stringStartLD = 244, + stringStartR = 245, + stringStartRD = 246, + stringStartRL = 247, + stringStartRLD = 248, + FormatString = 72, + stringStartF = 249, + stringStartFD = 250, + stringStartFL = 251, + stringStartFLD = 252, + stringStartFR = 253, + stringStartFRD = 254, + stringStartFRL = 255, + stringStartFRLD = 256, + FormatReplacement = 73, + nestedFormatReplacement = 77, + importList = 263, + TypeParamList = 112, + ParamList = 130, + SequencePattern = 151, + MappingPattern = 152, + PatternArgList = 155; + +const newline$2 = 10, carriageReturn = 13, space$1 = 32, tab = 9, hash = 35, parenOpen = 40, dot$1 = 46, + braceOpen = 123, braceClose = 125, singleQuote = 39, doubleQuote = 34, backslash = 92, + letter_o = 111, letter_x = 120, letter_N = 78, letter_u = 117, letter_U = 85; + +const bracketed = new Set([ + ParenthesizedExpression, TupleExpression, ComprehensionExpression, importList, ArgList, ParamList, + ArrayExpression, ArrayComprehensionExpression, subscript, + SetExpression, SetComprehensionExpression, FormatString, FormatReplacement, nestedFormatReplacement, + DictionaryExpression, DictionaryComprehensionExpression, + SequencePattern, MappingPattern, PatternArgList, TypeParamList +]); + +function isLineBreak(ch) { + return ch == newline$2 || ch == carriageReturn +} + +function isHex(ch) { + return ch >= 48 && ch <= 57 || ch >= 65 && ch <= 70 || ch >= 97 && ch <= 102 +} + +const newlines = new ExternalTokenizer((input, stack) => { + let prev; + if (input.next < 0) { + input.acceptToken(eof); + } else if (stack.context.flags & cx_Bracketed) { + if (isLineBreak(input.next)) input.acceptToken(newlineBracketed, 1); + } else if (((prev = input.peek(-1)) < 0 || isLineBreak(prev)) && + stack.canShift(blankLineStart)) { + let spaces = 0; + while (input.next == space$1 || input.next == tab) { input.advance(); spaces++; } + if (input.next == newline$2 || input.next == carriageReturn || input.next == hash) + input.acceptToken(blankLineStart, -spaces); + } else if (isLineBreak(input.next)) { + input.acceptToken(newline$1, 1); + } +}, {contextual: true}); + +const indentation = new ExternalTokenizer((input, stack) => { + let context = stack.context; + if (context.flags) return + let prev = input.peek(-1); + if (prev == newline$2 || prev == carriageReturn) { + let depth = 0, chars = 0; + for (;;) { + if (input.next == space$1) depth++; + else if (input.next == tab) depth += 8 - (depth % 8); + else break + input.advance(); + chars++; + } + if (depth != context.indent && + input.next != newline$2 && input.next != carriageReturn && input.next != hash) { + if (depth < context.indent) input.acceptToken(dedent, -chars); + else input.acceptToken(indent); + } + } +}); + +// Flags used in Context objects +const cx_Bracketed = 1, cx_String = 2, cx_DoubleQuote = 4, cx_Long = 8, cx_Raw = 16, cx_Format = 32; + +function Context(parent, indent, flags) { + this.parent = parent; + this.indent = indent; + this.flags = flags; + this.hash = (parent ? parent.hash + parent.hash << 8 : 0) + indent + (indent << 4) + flags + (flags << 6); +} + +const topIndent = new Context(null, 0, 0); + +function countIndent(space) { + let depth = 0; + for (let i = 0; i < space.length; i++) + depth += space.charCodeAt(i) == tab ? 8 - (depth % 8) : 1; + return depth +} + +const stringFlags = new Map([ + [stringStart, 0], + [stringStartD, cx_DoubleQuote], + [stringStartL, cx_Long], + [stringStartLD, cx_Long | cx_DoubleQuote], + [stringStartR, cx_Raw], + [stringStartRD, cx_Raw | cx_DoubleQuote], + [stringStartRL, cx_Raw | cx_Long], + [stringStartRLD, cx_Raw | cx_Long | cx_DoubleQuote], + [stringStartF, cx_Format], + [stringStartFD, cx_Format | cx_DoubleQuote], + [stringStartFL, cx_Format | cx_Long], + [stringStartFLD, cx_Format | cx_Long | cx_DoubleQuote], + [stringStartFR, cx_Format | cx_Raw], + [stringStartFRD, cx_Format | cx_Raw | cx_DoubleQuote], + [stringStartFRL, cx_Format | cx_Raw | cx_Long], + [stringStartFRLD, cx_Format | cx_Raw | cx_Long | cx_DoubleQuote] +].map(([term, flags]) => [term, flags | cx_String])); + +const trackIndent = new ContextTracker({ + start: topIndent, + reduce(context, term, _, input) { + if ((context.flags & cx_Bracketed) && bracketed.has(term) || + (term == String$1 || term == FormatString) && (context.flags & cx_String)) + return context.parent + return context + }, + shift(context, term, stack, input) { + if (term == indent) + return new Context(context, countIndent(input.read(input.pos, stack.pos)), 0) + if (term == dedent) + return context.parent + if (term == ParenL || term == BracketL || term == BraceL || term == replacementStart) + return new Context(context, 0, cx_Bracketed) + if (stringFlags.has(term)) + return new Context(context, 0, stringFlags.get(term) | (context.flags & cx_Bracketed)) + return context + }, + hash(context) { return context.hash } +}); + +const legacyPrint = new ExternalTokenizer(input => { + for (let i = 0; i < 5; i++) { + if (input.next != "print".charCodeAt(i)) return + input.advance(); + } + if (/\w/.test(String.fromCharCode(input.next))) return + for (let off = 0;; off++) { + let next = input.peek(off); + if (next == space$1 || next == tab) continue + if (next != parenOpen && next != dot$1 && next != newline$2 && next != carriageReturn && next != hash) + input.acceptToken(printKeyword); + return + } +}); + +const strings = new ExternalTokenizer((input, stack) => { + let {flags} = stack.context; + let quote = (flags & cx_DoubleQuote) ? doubleQuote : singleQuote; + let long = (flags & cx_Long) > 0; + let escapes = !(flags & cx_Raw); + let format = (flags & cx_Format) > 0; + + let start = input.pos; + for (;;) { + if (input.next < 0) { + break + } else if (format && input.next == braceOpen) { + if (input.peek(1) == braceOpen) { + input.advance(2); + } else { + if (input.pos == start) { + input.acceptToken(replacementStart, 1); + return + } + break + } + } else if (escapes && input.next == backslash) { + if (input.pos == start) { + input.advance(); + let escaped = input.next; + if (escaped >= 0) { + input.advance(); + skipEscape(input, escaped); + } + input.acceptToken(Escape); + return + } + break + } else if (input.next == quote && (!long || input.peek(1) == quote && input.peek(2) == quote)) { + if (input.pos == start) { + input.acceptToken(stringEnd, long ? 3 : 1); + return + } + break + } else if (input.next == newline$2) { + if (long) { + input.advance(); + } else if (input.pos == start) { + input.acceptToken(stringEnd); + return + } + break + } else { + input.advance(); + } + } + if (input.pos > start) input.acceptToken(stringContent); +}); + +function skipEscape(input, ch) { + if (ch == letter_o) { + for (let i = 0; i < 2 && input.next >= 48 && input.next <= 55; i++) input.advance(); + } else if (ch == letter_x) { + for (let i = 0; i < 2 && isHex(input.next); i++) input.advance(); + } else if (ch == letter_u) { + for (let i = 0; i < 4 && isHex(input.next); i++) input.advance(); + } else if (ch == letter_U) { + for (let i = 0; i < 8 && isHex(input.next); i++) input.advance(); + } else if (ch == letter_N) { + if (input.next == braceOpen) { + input.advance(); + while (input.next >= 0 && input.next != braceClose && input.next != singleQuote && + input.next != doubleQuote && input.next != newline$2) input.advance(); + if (input.next == braceClose) input.advance(); + } + } +} + +const pythonHighlighting = styleTags({ + "async \"*\" \"**\" FormatConversion FormatSpec": tags.modifier, + "for while if elif else try except finally return raise break continue with pass assert await yield match case": tags.controlKeyword, + "in not and or is del": tags.operatorKeyword, + "from def class global nonlocal lambda": tags.definitionKeyword, + import: tags.moduleKeyword, + "with as print": tags.keyword, + Boolean: tags.bool, + None: tags.null, + VariableName: tags.variableName, + "CallExpression/VariableName": tags.function(tags.variableName), + "FunctionDefinition/VariableName": tags.function(tags.definition(tags.variableName)), + "ClassDefinition/VariableName": tags.definition(tags.className), + PropertyName: tags.propertyName, + "CallExpression/MemberExpression/PropertyName": tags.function(tags.propertyName), + Comment: tags.lineComment, + Number: tags.number, + String: tags.string, + FormatString: tags.special(tags.string), + Escape: tags.escape, + UpdateOp: tags.updateOperator, + "ArithOp!": tags.arithmeticOperator, + BitOp: tags.bitwiseOperator, + CompareOp: tags.compareOperator, + AssignOp: tags.definitionOperator, + Ellipsis: tags.punctuation, + At: tags.meta, + "( )": tags.paren, + "[ ]": tags.squareBracket, + "{ }": tags.brace, + ".": tags.derefOperator, + ", ;": tags.separator +}); + +// This file was generated by lezer-generator. You probably shouldn't edit it. +const spec_identifier$1 = {__proto__:null,await:44, or:54, and:56, in:60, not:62, is:64, if:70, else:72, lambda:76, yield:94, from:96, async:102, for:104, None:162, True:164, False:164, del:178, pass:182, break:186, continue:190, return:194, raise:202, import:206, as:208, global:212, nonlocal:214, assert:218, type:223, elif:236, while:240, try:246, except:248, finally:250, with:254, def:258, class:268, match:279, case:285}; +const parser$2 = LRParser.deserialize({ + version: 14, + states: "##jO`QeOOP$}OSOOO&WQtO'#HUOOQS'#Co'#CoOOQS'#Cp'#CpO'vQdO'#CnO*UQtO'#HTOOQS'#HU'#HUOOQS'#DU'#DUOOQS'#HT'#HTO*rQdO'#D_O+VQdO'#DfO+gQdO'#DjO+zOWO'#DuO,VOWO'#DvO.[QtO'#GuOOQS'#Gu'#GuO'vQdO'#GtO0ZQtO'#GtOOQS'#Eb'#EbO0rQdO'#EcOOQS'#Gs'#GsO0|QdO'#GrOOQV'#Gr'#GrO1XQdO'#FYOOQS'#G^'#G^O1^QdO'#FXOOQV'#IS'#ISOOQV'#Gq'#GqOOQV'#Fq'#FqQ`QeOOO'vQdO'#CqO1lQdO'#C}O1sQdO'#DRO2RQdO'#HYO2cQtO'#EVO'vQdO'#EWOOQS'#EY'#EYOOQS'#E['#E[OOQS'#E^'#E^O2wQdO'#E`O3_QdO'#EdO3rQdO'#EfO3zQtO'#EfO1XQdO'#EiO0rQdO'#ElO1XQdO'#EnO0rQdO'#EtO0rQdO'#EwO4VQdO'#EyO4^QdO'#FOO4iQdO'#EzO0rQdO'#FOO1XQdO'#FQO1XQdO'#FVO4nQdO'#F[P4uOdO'#GpPOOO)CBd)CBdOOQS'#Ce'#CeOOQS'#Cf'#CfOOQS'#Cg'#CgOOQS'#Ch'#ChOOQS'#Ci'#CiOOQS'#Cj'#CjOOQS'#Cl'#ClO'vQdO,59OO'vQdO,59OO'vQdO,59OO'vQdO,59OO'vQdO,59OO'vQdO,59OO5TQdO'#DoOOQS,5:Y,5:YO5hQdO'#HdOOQS,5:],5:]O5uQ!fO,5:]O5zQtO,59YO1lQdO,59bO1lQdO,59bO1lQdO,59bO8jQdO,59bO8oQdO,59bO8vQdO,59jO8}QdO'#HTO:TQdO'#HSOOQS'#HS'#HSOOQS'#D['#D[O:lQdO,59aO'vQdO,59aO:zQdO,59aOOQS,59y,59yO;PQdO,5:RO'vQdO,5:ROOQS,5:Q,5:QO;_QdO,5:QO;dQdO,5:XO'vQdO,5:XO'vQdO,5:VOOQS,5:U,5:UO;uQdO,5:UO;zQdO,5:WOOOW'#Fy'#FyOOOOQS'#Ds'#DsOOQS1G/w1G/wOOQS1G.|1G.|O!/[QtO1G.|O!/cQtO1G.|O1lQdO1G.|O!0OQdO1G/UOOQS'#DZ'#DZO0rQdO,59tOOQS1G.{1G.{O!0VQdO1G/eO!0gQdO1G/eO!0oQdO1G/fO'vQdO'#H[O!0tQdO'#H[O!0yQtO1G.{O!1ZQdO,59iO!2aQdO,5=zO!2qQdO,5=zO!2yQdO1G/mO!3OQtO1G/mOOQS1G/l1G/lO!3`QdO,5=uO!4VQdO,5=uO0rQdO1G/qO!4tQdO1G/sO!4yQtO1G/sO!5ZQtO1G/qOOQS1G/p1G/pOOQS1G/r1G/rOOOW-E9w-E9wOOQS1G/{1G/{O!5kQdO'#HxO0rQdO'#HxO!5|QdO,5>cOOOW-E9x-E9xOOQS1G/|1G/|OOQS-E9{-E9{O!6[Q#xO1G2zO!6{QtO1G2zO'vQdO,5kOOQS1G1`1G1`O!8RQdO1G1`OOQS'#DV'#DVO0rQdO,5=qOOQS,5=q,5=qO!8WQdO'#FrO!8cQdO,59oO!8kQdO1G/XO!8uQtO,5=uOOQS1G3`1G3`OOQS,5:m,5:mO!9fQdO'#GtOOQS,5jO!;ZQdO,5>jO1XQdO,5>jO!;lQdO,5>iOOQS-E:R-E:RO!;qQdO1G0lO!;|QdO1G0lO!lO!lO!hO!=VQdO,5>hO!=hQdO'#EpO0rQdO1G0tO!=sQdO1G0tO!=xQgO1G0zO!AvQgO1G0}O!EqQdO,5>oO!E{QdO,5>oO!FTQtO,5>oO0rQdO1G1PO!F_QdO1G1PO4iQdO1G1UO!!vQdO1G1WOOQV,5;a,5;aO!FdQfO,5;aO!FiQgO1G1QO!JjQdO'#GZO4iQdO1G1QO4iQdO1G1QO!JzQdO,5>pO!KXQdO,5>pO1XQdO,5>pOOQV1G1U1G1UO!KaQdO'#FSO!KrQ!fO1G1WO!KzQdO1G1WOOQV1G1]1G1]O4iQdO1G1]O!LPQdO1G1]O!LXQdO'#F^OOQV1G1b1G1bO!#ZQtO1G1bPOOO1G2v1G2vP!L^OSO1G2vOOQS,5=},5=}OOQS'#Dp'#DpO0rQdO,5=}O!LfQdO,5=|O!LyQdO,5=|OOQS1G/u1G/uO!MRQdO,5>PO!McQdO,5>PO!MkQdO,5>PO!NOQdO,5>PO!N`QdO,5>POOQS1G3j1G3jOOQS7+$h7+$hO!8kQdO7+$pO#!RQdO1G.|O#!YQdO1G.|OOQS1G/`1G/`OOQS,5<`,5<`O'vQdO,5<`OOQS7+%P7+%PO#!aQdO7+%POOQS-E9r-E9rOOQS7+%Q7+%QO#!qQdO,5=vO'vQdO,5=vOOQS7+$g7+$gO#!vQdO7+%PO##OQdO7+%QO##TQdO1G3fOOQS7+%X7+%XO##eQdO1G3fO##mQdO7+%XOOQS,5<_,5<_O'vQdO,5<_O##rQdO1G3aOOQS-E9q-E9qO#$iQdO7+%]OOQS7+%_7+%_O#$wQdO1G3aO#%fQdO7+%_O#%kQdO1G3gO#%{QdO1G3gO#&TQdO7+%]O#&YQdO,5>dO#&sQdO,5>dO#&sQdO,5>dOOQS'#Dx'#DxO#'UO&jO'#DzO#'aO`O'#HyOOOW1G3}1G3}O#'fQdO1G3}O#'nQdO1G3}O#'yQ#xO7+(fO#(jQtO1G2UP#)TQdO'#GOOOQS,5nQdO,5sQdO1G4OOOQS-E9y-E9yO#?^QdO1G4OO<[QdO'#H{OOOO'#D{'#D{OOOO'#F|'#F|O#?oO&jO,5:fOOOW,5>e,5>eOOOW7+)i7+)iO#?zQdO7+)iO#@SQdO1G2zO#@mQdO1G2zP'vQdO'#FuO0rQdO<mO#BQQdO,5>mOOQS1G0v1G0vOOQS<rO#KgQdO,5>rOOQS,5>r,5>rO#KrQdO,5>qO#LTQdO,5>qOOQS1G1Y1G1YOOQS,5;p,5;pOOQV<VAN>VO$ dQdO<cAN>cO0rQdO1G1|O$ tQtO1G1|P$!OQdO'#FvOOQS1G2R1G2RP$!]QdO'#F{O$!jQdO7+)jO$#TQdO,5>gOOOO-E9z-E9zOOOW<tO$4pQdO,5>tO1XQdO,5vO$)cQdO,5>vOOQS1G1p1G1pO$8hQtO,5<[OOQU7+'P7+'PO$+oQdO1G/iO$)cQdO,5wO$8vQdO,5>wOOQS1G1s1G1sOOQS7+'S7+'SP$)cQdO'#GdO$9OQdO1G4bO$9YQdO1G4bO$9bQdO1G4bOOQS7+%T7+%TO$9pQdO1G1tO$:OQtO'#FaO$:VQdO,5<}OOQS,5<},5<}O$:eQdO1G4cOOQS-E:a-E:aO$)cQdO,5<|O$:lQdO,5<|O$:qQdO7+)|OOQS-E:`-E:`O$:{QdO7+)|O$)cQdO,5O~O%cOS%^OSSOS%]PQ~OPdOVaOfoOhYOopOs!POvqO!PrO!Q{O!T!SO!U!RO!XZO!][O!h`O!r`O!s`O!t`O!{tO!}uO#PvO#RwO#TxO#XyO#ZzO#^|O#_|O#a}O#c!OO#l!QO#o!TO#s!UO#u!VO#z!WO#}hO$P!XO%oRO%pRO%tSO%uWO&Z]O&[]O&]]O&^]O&_]O&`]O&a]O&b]O&c^O&d^O&e^O&f^O&g^O&h^O&i^O&j^O~O%]!YO~OV!aO_!aOa!bOh!iO!X!kO!f!mO%j![O%k!]O%l!^O%m!_O%n!_O%o!`O%p!`O%q!aO%r!aO%s!aO~Ok%xXl%xXm%xXn%xXo%xXp%xXs%xXz%xX{%xX!x%xX#g%xX%[%xX%_%xX%z%xXg%xX!T%xX!U%xX%{%xX!W%xX![%xX!Q%xX#[%xXt%xX!m%xX~P%SOfoOhYO!XZO!][O!h`O!r`O!s`O!t`O%oRO%pRO%tSO%uWO&Z]O&[]O&]]O&^]O&_]O&`]O&a]O&b]O&c^O&d^O&e^O&f^O&g^O&h^O&i^O&j^O~Oz%wX{%wX#g%wX%[%wX%_%wX%z%wX~Ok!pOl!qOm!oOn!oOo!rOp!sOs!tO!x%wX~P)pOV!zOg!|Oo0cOv0qO!PrO~P'vOV#OOo0cOv0qO!W#PO~P'vOV#SOa#TOo0cOv0qO![#UO~P'vOQ#XO%`#XO%a#ZO~OQ#^OR#[O%`#^O%a#`O~OV%iX_%iXa%iXh%iXk%iXl%iXm%iXn%iXo%iXp%iXs%iXz%iX!X%iX!f%iX%j%iX%k%iX%l%iX%m%iX%n%iX%o%iX%p%iX%q%iX%r%iX%s%iXg%iX!T%iX!U%iX~O&Z]O&[]O&]]O&^]O&_]O&`]O&a]O&b]O&c^O&d^O&e^O&f^O&g^O&h^O&i^O&j^O{%iX!x%iX#g%iX%[%iX%_%iX%z%iX%{%iX!W%iX![%iX!Q%iX#[%iXt%iX!m%iX~P,eOz#dO{%hX!x%hX#g%hX%[%hX%_%hX%z%hX~Oo0cOv0qO~P'vO#g#gO%[#iO%_#iO~O%uWO~O!T#nO#u!VO#z!WO#}hO~OopO~P'vOV#sOa#tO%uWO{wP~OV#xOo0cOv0qO!Q#yO~P'vO{#{O!x$QO%z#|O#g!yX%[!yX%_!yX~OV#xOo0cOv0qO#g#SX%[#SX%_#SX~P'vOo0cOv0qO#g#WX%[#WX%_#WX~P'vOh$WO%uWO~O!f$YO!r$YO%uWO~OV$eO~P'vO!U$gO#s$hO#u$iO~O{$jO~OV$qO~P'vOS$sO%[$rO%_$rO%c$tO~OV$}Oa$}Og%POo0cOv0qO~P'vOo0cOv0qO{%SO~P'vO&Y%UO~Oa!bOh!iO!X!kO!f!mOVba_bakbalbambanbaobapbasbazba{ba!xba#gba%[ba%_ba%jba%kba%lba%mba%nba%oba%pba%qba%rba%sba%zbagba!Tba!Uba%{ba!Wba![ba!Qba#[batba!mba~On%ZO~Oo%ZO~P'vOo0cO~P'vOk0eOl0fOm0dOn0dOo0mOp0nOs0rOg%wX!T%wX!U%wX%{%wX!W%wX![%wX!Q%wX#[%wX!m%wX~P)pO%{%]Og%vXz%vX!T%vX!U%vX!W%vX{%vX~Og%_Oz%`O!T%dO!U%cO~Og%_O~Oz%gO!T%dO!U%cO!W&SX~O!W%kO~Oz%lO{%nO!T%dO!U%cO![%}X~O![%rO~O![%sO~OQ#XO%`#XO%a%uO~OV%wOo0cOv0qO!PrO~P'vOQ#^OR#[O%`#^O%a%zO~OV!qa_!qaa!qah!qak!qal!qam!qan!qao!qap!qas!qaz!qa{!qa!X!qa!f!qa!x!qa#g!qa%[!qa%_!qa%j!qa%k!qa%l!qa%m!qa%n!qa%o!qa%p!qa%q!qa%r!qa%s!qa%z!qag!qa!T!qa!U!qa%{!qa!W!qa![!qa!Q!qa#[!qat!qa!m!qa~P#yOz%|O{%ha!x%ha#g%ha%[%ha%_%ha%z%ha~P%SOV&OOopOvqO{%ha!x%ha#g%ha%[%ha%_%ha%z%ha~P'vOz%|O{%ha!x%ha#g%ha%[%ha%_%ha%z%ha~OPdOVaOopOvqO!PrO!Q{O!{tO!}uO#PvO#RwO#TxO#XyO#ZzO#^|O#_|O#a}O#c!OO#g$zX%[$zX%_$zX~P'vO#g#gO%[&TO%_&TO~O!f&UOh&sX%[&sXz&sX#[&sX#g&sX%_&sX#Z&sXg&sX~Oh!iO%[&WO~Okealeameaneaoeapeaseazea{ea!xea#gea%[ea%_ea%zeagea!Tea!Uea%{ea!Wea![ea!Qea#[eatea!mea~P%SOsqazqa{qa#gqa%[qa%_qa%zqa~Ok!pOl!qOm!oOn!oOo!rOp!sO!xqa~PEcO%z&YOz%yX{%yX~O%uWOz%yX{%yX~Oz&]O{wX~O{&_O~Oz%lO#g%}X%[%}X%_%}Xg%}X{%}X![%}X!m%}X%z%}X~OV0lOo0cOv0qO!PrO~P'vO%z#|O#gUa%[Ua%_Ua~Oz&hO#g&PX%[&PX%_&PXn&PX~P%SOz&kO!Q&jO#g#Wa%[#Wa%_#Wa~Oz&lO#[&nO#g&rX%[&rX%_&rXg&rX~O!f$YO!r$YO#Z&qO%uWO~O#Z&qO~Oz&sO#g&tX%[&tX%_&tX~Oz&uO#g&pX%[&pX%_&pX{&pX~O!X&wO%z&xO~Oz&|On&wX~P%SOn'PO~OPdOVaOopOvqO!PrO!Q{O!{tO!}uO#PvO#RwO#TxO#XyO#ZzO#^|O#_|O#a}O#c!OO%['UO~P'vOt'YO#p'WO#q'XOP#naV#naf#nah#nao#nas#nav#na!P#na!Q#na!T#na!U#na!X#na!]#na!h#na!r#na!s#na!t#na!{#na!}#na#P#na#R#na#T#na#X#na#Z#na#^#na#_#na#a#na#c#na#l#na#o#na#s#na#u#na#z#na#}#na$P#na%X#na%o#na%p#na%t#na%u#na&Z#na&[#na&]#na&^#na&_#na&`#na&a#na&b#na&c#na&d#na&e#na&f#na&g#na&h#na&i#na&j#na%Z#na%_#na~Oz'ZO#[']O{&xX~Oh'_O!X&wO~Oh!iO{$jO!X&wO~O{'eO~P%SO%['hO%_'hO~OS'iO%['hO%_'hO~OV!aO_!aOa!bOh!iO!X!kO!f!mO%l!^O%m!_O%n!_O%o!`O%p!`O%q!aO%r!aO%s!aOkWilWimWinWioWipWisWizWi{Wi!xWi#gWi%[Wi%_Wi%jWi%zWigWi!TWi!UWi%{Wi!WWi![Wi!QWi#[WitWi!mWi~O%k!]O~P!#uO%kWi~P!#uOV!aO_!aOa!bOh!iO!X!kO!f!mO%o!`O%p!`O%q!aO%r!aO%s!aOkWilWimWinWioWipWisWizWi{Wi!xWi#gWi%[Wi%_Wi%jWi%kWi%lWi%zWigWi!TWi!UWi%{Wi!WWi![Wi!QWi#[WitWi!mWi~O%m!_O%n!_O~P!&pO%mWi%nWi~P!&pOa!bOh!iO!X!kO!f!mOkWilWimWinWioWipWisWizWi{Wi!xWi#gWi%[Wi%_Wi%jWi%kWi%lWi%mWi%nWi%oWi%pWi%zWigWi!TWi!UWi%{Wi!WWi![Wi!QWi#[WitWi!mWi~OV!aO_!aO%q!aO%r!aO%s!aO~P!)nOVWi_Wi%qWi%rWi%sWi~P!)nO!T%dO!U%cOg&VXz&VX~O%z'kO%{'kO~P,eOz'mOg&UX~Og'oO~Oz'pO{'rO!W&XX~Oo0cOv0qOz'pO{'sO!W&XX~P'vO!W'uO~Om!oOn!oOo!rOp!sOkjisjizji{ji!xji#gji%[ji%_ji%zji~Ol!qO~P!.aOlji~P!.aOk0eOl0fOm0dOn0dOo0mOp0nO~Ot'wO~P!/jOV'|Og'}Oo0cOv0qO~P'vOg'}Oz(OO~Og(QO~O!U(SO~Og(TOz(OO!T%dO!U%cO~P%SOk0eOl0fOm0dOn0dOo0mOp0nOgqa!Tqa!Uqa%{qa!Wqa![qa!Qqa#[qatqa!mqa~PEcOV'|Oo0cOv0qO!W&Sa~P'vOz(WO!W&Sa~O!W(XO~Oz(WO!T%dO!U%cO!W&Sa~P%SOV(]Oo0cOv0qO![%}a#g%}a%[%}a%_%}ag%}a{%}a!m%}a%z%}a~P'vOz(^O![%}a#g%}a%[%}a%_%}ag%}a{%}a!m%}a%z%}a~O![(aO~Oz(^O!T%dO!U%cO![%}a~P%SOz(dO!T%dO!U%cO![&Ta~P%SOz(gO{&lX![&lX!m&lX%z&lX~O{(kO![(mO!m(nO%z(jO~OV&OOopOvqO{%hi!x%hi#g%hi%[%hi%_%hi%z%hi~P'vOz(pO{%hi!x%hi#g%hi%[%hi%_%hi%z%hi~O!f&UOh&sa%[&saz&sa#[&sa#g&sa%_&sa#Z&sag&sa~O%[(uO~OV#sOa#tO%uWO~Oz&]O{wa~OopOvqO~P'vOz(^O#g%}a%[%}a%_%}ag%}a{%}a![%}a!m%}a%z%}a~P%SOz(zO#g%hX%[%hX%_%hX%z%hX~O%z#|O#gUi%[Ui%_Ui~O#g&Pa%[&Pa%_&Pan&Pa~P'vOz(}O#g&Pa%[&Pa%_&Pan&Pa~O%uWO#g&ra%[&ra%_&rag&ra~Oz)SO#g&ra%[&ra%_&rag&ra~Og)VO~OV)WOh$WO%uWO~O#Z)XO~O%uWO#g&ta%[&ta%_&ta~Oz)ZO#g&ta%[&ta%_&ta~Oo0cOv0qO#g&pa%[&pa%_&pa{&pa~P'vOz)^O#g&pa%[&pa%_&pa{&pa~OV)`Oa)`O%uWO~O%z)eO~Ot)hO#j)gOP#hiV#hif#hih#hio#his#hiv#hi!P#hi!Q#hi!T#hi!U#hi!X#hi!]#hi!h#hi!r#hi!s#hi!t#hi!{#hi!}#hi#P#hi#R#hi#T#hi#X#hi#Z#hi#^#hi#_#hi#a#hi#c#hi#l#hi#o#hi#s#hi#u#hi#z#hi#}#hi$P#hi%X#hi%o#hi%p#hi%t#hi%u#hi&Z#hi&[#hi&]#hi&^#hi&_#hi&`#hi&a#hi&b#hi&c#hi&d#hi&e#hi&f#hi&g#hi&h#hi&i#hi&j#hi%Z#hi%_#hi~Ot)iOP#kiV#kif#kih#kio#kis#kiv#ki!P#ki!Q#ki!T#ki!U#ki!X#ki!]#ki!h#ki!r#ki!s#ki!t#ki!{#ki!}#ki#P#ki#R#ki#T#ki#X#ki#Z#ki#^#ki#_#ki#a#ki#c#ki#l#ki#o#ki#s#ki#u#ki#z#ki#}#ki$P#ki%X#ki%o#ki%p#ki%t#ki%u#ki&Z#ki&[#ki&]#ki&^#ki&_#ki&`#ki&a#ki&b#ki&c#ki&d#ki&e#ki&f#ki&g#ki&h#ki&i#ki&j#ki%Z#ki%_#ki~OV)kOn&wa~P'vOz)lOn&wa~Oz)lOn&wa~P%SOn)pO~O%Y)tO~Ot)wO#p'WO#q)vOP#niV#nif#nih#nio#nis#niv#ni!P#ni!Q#ni!T#ni!U#ni!X#ni!]#ni!h#ni!r#ni!s#ni!t#ni!{#ni!}#ni#P#ni#R#ni#T#ni#X#ni#Z#ni#^#ni#_#ni#a#ni#c#ni#l#ni#o#ni#s#ni#u#ni#z#ni#}#ni$P#ni%X#ni%o#ni%p#ni%t#ni%u#ni&Z#ni&[#ni&]#ni&^#ni&_#ni&`#ni&a#ni&b#ni&c#ni&d#ni&e#ni&f#ni&g#ni&h#ni&i#ni&j#ni%Z#ni%_#ni~OV)zOo0cOv0qO{$jO~P'vOo0cOv0qO{&xa~P'vOz*OO{&xa~OV*SOa*TOg*WO%q*UO%uWO~O{$jO&{*YO~Oh'_O~Oh!iO{$jO~O%[*_O~O%[*aO%_*aO~OV$}Oa$}Oo0cOv0qOg&Ua~P'vOz*dOg&Ua~Oo0cOv0qO{*gO!W&Xa~P'vOz*hO!W&Xa~Oo0cOv0qOz*hO{*kO!W&Xa~P'vOo0cOv0qOz*hO!W&Xa~P'vOz*hO{*kO!W&Xa~Om0dOn0dOo0mOp0nOgjikjisjizji!Tji!Uji%{ji!Wji{ji![ji#gji%[ji%_ji!Qji#[jitji!mji%zji~Ol0fO~P!NkOlji~P!NkOV'|Og*pOo0cOv0qO~P'vOn*rO~Og*pOz*tO~Og*uO~OV'|Oo0cOv0qO!W&Si~P'vOz*vO!W&Si~O!W*wO~OV(]Oo0cOv0qO![%}i#g%}i%[%}i%_%}ig%}i{%}i!m%}i%z%}i~P'vOz*zO!T%dO!U%cO![&Ti~Oz*}O![%}i#g%}i%[%}i%_%}ig%}i{%}i!m%}i%z%}i~O![+OO~Oa+QOo0cOv0qO![&Ti~P'vOz*zO![&Ti~O![+SO~OV+UOo0cOv0qO{&la![&la!m&la%z&la~P'vOz+VO{&la![&la!m&la%z&la~O!]+YO&n+[O![!nX~O![+^O~O{(kO![+_O~O{(kO![+_O!m+`O~OV&OOopOvqO{%hq!x%hq#g%hq%[%hq%_%hq%z%hq~P'vOz$ri{$ri!x$ri#g$ri%[$ri%_$ri%z$ri~P%SOV&OOopOvqO~P'vOV&OOo0cOv0qO#g%ha%[%ha%_%ha%z%ha~P'vOz+aO#g%ha%[%ha%_%ha%z%ha~Oz$ia#g$ia%[$ia%_$ian$ia~P%SO#g&Pi%[&Pi%_&Pin&Pi~P'vOz+dO#g#Wq%[#Wq%_#Wq~O#[+eOz$va#g$va%[$va%_$vag$va~O%uWO#g&ri%[&ri%_&rig&ri~Oz+gO#g&ri%[&ri%_&rig&ri~OV+iOh$WO%uWO~O%uWO#g&ti%[&ti%_&ti~Oo0cOv0qO#g&pi%[&pi%_&pi{&pi~P'vO{#{Oz#eX!W#eX~Oz+mO!W&uX~O!W+oO~Ot+rO#j)gOP#hqV#hqf#hqh#hqo#hqs#hqv#hq!P#hq!Q#hq!T#hq!U#hq!X#hq!]#hq!h#hq!r#hq!s#hq!t#hq!{#hq!}#hq#P#hq#R#hq#T#hq#X#hq#Z#hq#^#hq#_#hq#a#hq#c#hq#l#hq#o#hq#s#hq#u#hq#z#hq#}#hq$P#hq%X#hq%o#hq%p#hq%t#hq%u#hq&Z#hq&[#hq&]#hq&^#hq&_#hq&`#hq&a#hq&b#hq&c#hq&d#hq&e#hq&f#hq&g#hq&h#hq&i#hq&j#hq%Z#hq%_#hq~On$|az$|a~P%SOV)kOn&wi~P'vOz+yOn&wi~Oz,TO{$jO#[,TO~O#q,VOP#nqV#nqf#nqh#nqo#nqs#nqv#nq!P#nq!Q#nq!T#nq!U#nq!X#nq!]#nq!h#nq!r#nq!s#nq!t#nq!{#nq!}#nq#P#nq#R#nq#T#nq#X#nq#Z#nq#^#nq#_#nq#a#nq#c#nq#l#nq#o#nq#s#nq#u#nq#z#nq#}#nq$P#nq%X#nq%o#nq%p#nq%t#nq%u#nq&Z#nq&[#nq&]#nq&^#nq&_#nq&`#nq&a#nq&b#nq&c#nq&d#nq&e#nq&f#nq&g#nq&h#nq&i#nq&j#nq%Z#nq%_#nq~O#[,WOz%Oa{%Oa~Oo0cOv0qO{&xi~P'vOz,YO{&xi~O{#{O%z,[Og&zXz&zX~O%uWOg&zXz&zX~Oz,`Og&yX~Og,bO~O%Y,eO~O!T%dO!U%cOg&Viz&Vi~OV$}Oa$}Oo0cOv0qOg&Ui~P'vO{,hOz$la!W$la~Oo0cOv0qO{,iOz$la!W$la~P'vOo0cOv0qO{*gO!W&Xi~P'vOz,lO!W&Xi~Oo0cOv0qOz,lO!W&Xi~P'vOz,lO{,oO!W&Xi~Og$hiz$hi!W$hi~P%SOV'|Oo0cOv0qO~P'vOn,qO~OV'|Og,rOo0cOv0qO~P'vOV'|Oo0cOv0qO!W&Sq~P'vOz$gi![$gi#g$gi%[$gi%_$gig$gi{$gi!m$gi%z$gi~P%SOV(]Oo0cOv0qO~P'vOa+QOo0cOv0qO![&Tq~P'vOz,sO![&Tq~O![,tO~OV(]Oo0cOv0qO![%}q#g%}q%[%}q%_%}qg%}q{%}q!m%}q%z%}q~P'vO{,uO~OV+UOo0cOv0qO{&li![&li!m&li%z&li~P'vOz,zO{&li![&li!m&li%z&li~O!]+YO&n+[O![!na~O{(kO![,}O~OV&OOo0cOv0qO#g%hi%[%hi%_%hi%z%hi~P'vOz-OO#g%hi%[%hi%_%hi%z%hi~O%uWO#g&rq%[&rq%_&rqg&rq~Oz-RO#g&rq%[&rq%_&rqg&rq~OV)`Oa)`O%uWO!W&ua~Oz-TO!W&ua~On$|iz$|i~P%SOV)kO~P'vOV)kOn&wq~P'vOt-XOP#myV#myf#myh#myo#mys#myv#my!P#my!Q#my!T#my!U#my!X#my!]#my!h#my!r#my!s#my!t#my!{#my!}#my#P#my#R#my#T#my#X#my#Z#my#^#my#_#my#a#my#c#my#l#my#o#my#s#my#u#my#z#my#}#my$P#my%X#my%o#my%p#my%t#my%u#my&Z#my&[#my&]#my&^#my&_#my&`#my&a#my&b#my&c#my&d#my&e#my&f#my&g#my&h#my&i#my&j#my%Z#my%_#my~O%Z-]O%_-]O~P`O#q-^OP#nyV#nyf#nyh#nyo#nys#nyv#ny!P#ny!Q#ny!T#ny!U#ny!X#ny!]#ny!h#ny!r#ny!s#ny!t#ny!{#ny!}#ny#P#ny#R#ny#T#ny#X#ny#Z#ny#^#ny#_#ny#a#ny#c#ny#l#ny#o#ny#s#ny#u#ny#z#ny#}#ny$P#ny%X#ny%o#ny%p#ny%t#ny%u#ny&Z#ny&[#ny&]#ny&^#ny&_#ny&`#ny&a#ny&b#ny&c#ny&d#ny&e#ny&f#ny&g#ny&h#ny&i#ny&j#ny%Z#ny%_#ny~Oz-aO{$jO#[-aO~Oo0cOv0qO{&xq~P'vOz-dO{&xq~O%z,[Og&zaz&za~OV*SOa*TO%q*UO%uWOg&ya~Oz-hOg&ya~O$S-lO~OV$}Oa$}Oo0cOv0qO~P'vOo0cOv0qO{-mOz$li!W$li~P'vOo0cOv0qOz$li!W$li~P'vO{-mOz$li!W$li~Oo0cOv0qO{*gO~P'vOo0cOv0qO{*gO!W&Xq~P'vOz-pO!W&Xq~Oo0cOv0qOz-pO!W&Xq~P'vOs-sO!T%dO!U%cOg&Oq!W&Oq![&Oqz&Oq~P!/jOa+QOo0cOv0qO![&Ty~P'vOz$ji![$ji~P%SOa+QOo0cOv0qO~P'vOV+UOo0cOv0qO~P'vOV+UOo0cOv0qO{&lq![&lq!m&lq%z&lq~P'vO{(kO![-xO!m-yO%z-wO~OV&OOo0cOv0qO#g%hq%[%hq%_%hq%z%hq~P'vO%uWO#g&ry%[&ry%_&ryg&ry~OV)`Oa)`O%uWO!W&ui~Ot-}OP#m!RV#m!Rf#m!Rh#m!Ro#m!Rs#m!Rv#m!R!P#m!R!Q#m!R!T#m!R!U#m!R!X#m!R!]#m!R!h#m!R!r#m!R!s#m!R!t#m!R!{#m!R!}#m!R#P#m!R#R#m!R#T#m!R#X#m!R#Z#m!R#^#m!R#_#m!R#a#m!R#c#m!R#l#m!R#o#m!R#s#m!R#u#m!R#z#m!R#}#m!R$P#m!R%X#m!R%o#m!R%p#m!R%t#m!R%u#m!R&Z#m!R&[#m!R&]#m!R&^#m!R&_#m!R&`#m!R&a#m!R&b#m!R&c#m!R&d#m!R&e#m!R&f#m!R&g#m!R&h#m!R&i#m!R&j#m!R%Z#m!R%_#m!R~Oo0cOv0qO{&xy~P'vOV*SOa*TO%q*UO%uWOg&yi~O$S-lO%Z.VO%_.VO~OV.aOh._O!X.^O!].`O!h.YO!s.[O!t.[O%p.XO%uWO&Z]O&[]O&]]O&^]O&_]O&`]O&a]O&b]O~Oo0cOv0qOz$lq!W$lq~P'vO{.fOz$lq!W$lq~Oo0cOv0qO{*gO!W&Xy~P'vOz.gO!W&Xy~Oo0cOv.kO~P'vOs-sO!T%dO!U%cOg&Oy!W&Oy![&Oyz&Oy~P!/jO{(kO![.nO~O{(kO![.nO!m.oO~OV*SOa*TO%q*UO%uWO~Oh.tO!f.rOz$TX#[$TX%j$TXg$TX~Os$TX{$TX!W$TX![$TX~P$-VO%o.vO%p.vOs$UXz$UX{$UX#[$UX%j$UX!W$UXg$UX![$UX~O!h.xO~Oz.|O#[/OO%j.yOs&|X{&|X!W&|Xg&|X~Oa/RO~P$)oOh.tOs&}Xz&}X{&}X#[&}X%j&}X!W&}Xg&}X![&}X~Os/VO{$jO~Oo0cOv0qOz$ly!W$ly~P'vOo0cOv0qO{*gO!W&X!R~P'vOz/ZO!W&X!R~Og&RXs&RX!T&RX!U&RX!W&RX![&RXz&RX~P!/jOs-sO!T%dO!U%cOg&Qa!W&Qa![&Qaz&Qa~O{(kO![/^O~O!f.rOh$[as$[az$[a{$[a#[$[a%j$[a!W$[ag$[a![$[a~O!h/eO~O%o.vO%p.vOs$Uaz$Ua{$Ua#[$Ua%j$Ua!W$Uag$Ua![$Ua~O%j.yOs$Yaz$Ya{$Ya#[$Ya!W$Yag$Ya![$Ya~Os&|a{&|a!W&|ag&|a~P$)cOz/jOs&|a{&|a!W&|ag&|a~O!W/mO~Og/mO~O{/oO~O![/pO~Oo0cOv0qO{*gO!W&X!Z~P'vO{/sO~O%z/tO~P$-VOz/uO#[/OO%j.yOg'PX~Oz/uOg'PX~Og/wO~O!h/xO~O#[/OOs%Saz%Sa{%Sa%j%Sa!W%Sag%Sa![%Sa~O#[/OO%j.yOs%Waz%Wa{%Wa!W%Wag%Wa~Os&|i{&|i!W&|ig&|i~P$)cOz/zO#[/OO%j.yO!['Oa~O{$da~P%SOg'Pa~P$)cOz0SOg'Pa~Oa0UO!['Oi~P$)oOz0WO!['Oi~Oz0WO#[/OO%j.yO!['Oi~O#[/OO%j.yOg$biz$bi~O%z0ZO~P$-VO#[/OO%j.yOg%Vaz%Va~Og'Pi~P$)cO{0^O~Oa0UO!['Oq~P$)oOz0`O!['Oq~O#[/OO%j.yOz%Ui![%Ui~Oa0UO~P$)oOa0UO!['Oy~P$)oO#[/OO%j.yOg$ciz$ci~O#[/OO%j.yOz%Uq![%Uq~Oz+aO#g%ha%[%ha%_%ha%z%ha~P%SOV&OOo0cOv0qO~P'vOn0hO~Oo0hO~P'vO{0iO~Ot0jO~P!/jO&]&Z&j&h&i&g&f&d&e&c&b&`&a&_&^&[%u~", + goto: "!=l'QPPPPPP'RP'Z*s+]+v,b,}-kP.YP'Z.y.y'ZPPP'Z2cPPPPPP2c5VPP5VP7g7p=xPP={>m>pPP'Z'ZPP?PPP'Z'ZPP'Z'Z'Z'Z'Z?T?}'ZP@QP@WD_G{HPPHSH^Hb'ZPPPHeHn'RP'R'RP'RP'RP'RP'RP'R'R'RP'RPP'RPP'RP'RPHtIQIYPIaIgPIaPIaIaPPPIaPKuPLOLYL`KuPIaLiPIaPLpLvPLzM`M}NhLzLzNnN{LzLzLzLz! a! g! j! o! r! |!!S!!`!!r!!x!#S!#Y!#v!#|!$S!$^!$d!$j!$|!%W!%^!%d!%n!%t!%z!&Q!&W!&^!&h!&n!&x!'O!'X!'_!'n!'v!(Q!(XPPPPPPPPPPP!(_!(b!(h!(q!({!)WPPPPPPPPPPPP!-z!/`!3`!6pPP!6x!7X!7b!8Z!8Q!8d!8j!8m!8p!8s!8{!9lPPPPPPPPPPPPPPPPP!9o!9s!9yP!:_!:c!:o!:x!;U!;l!;o!;r!;x!_![!]Do!]!^Es!^!_FZ!_!`Gk!`!aHX!a!b%T!b!cIf!c!dJU!d!eK^!e!hJU!h!i!#f!i!tJU!t!u!,|!u!wJU!w!x!.t!x!}JU!}#O!0S#O#P&o#P#Q!0j#Q#R!1Q#R#SJU#S#T%T#T#UJU#U#VK^#V#YJU#Y#Z!#f#Z#fJU#f#g!,|#g#iJU#i#j!.t#j#oJU#o#p!1n#p#q!1s#q#r!2a#r#s!2f#s$g%T$g;'SJU;'S;=`KW<%lOJU`%YT&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%T`%lP;=`<%l%To%v]&n`%c_OX%TXY%oY[%T[]%o]p%Tpq%oq#O%T#O#P&o#P#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%To&tX&n`OY%TYZ%oZ]%T]^%o^#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tc'f[&n`O!_%T!_!`([!`#T%T#T#U(r#U#f%T#f#g(r#g#h(r#h#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tc(cTmR&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tc(yT!mR&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk)aV&n`&[ZOr%Trs)vs#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk){V&n`Or%Trs*bs#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk*iT&n`&^ZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%To+PZS_&n`OY*xYZ%TZ]*x]^%T^#o*x#o#p+r#p#q*x#q#r+r#r;'S*x;'S;=`,^<%lO*x_+wTS_OY+rZ]+r^;'S+r;'S;=`,W<%lO+r_,ZP;=`<%l+ro,aP;=`<%l*xj,kV%rQ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tj-XT!xY&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tj-oV%lQ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk.]V&n`&ZZOw%Twx.rx#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk.wV&n`Ow%Twx/^x#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk/eT&n`&]ZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk/{ThZ&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tc0cTgR&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk0yXVZ&n`Oz%Tz{1f{!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk1mVaR&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk2ZV%oZ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tc2wTzR&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%To3_W%pZ&n`O!_%T!_!`-Q!`!a3w!a#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Td4OT&{S&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk4fX!fQ&n`O!O%T!O!P5R!P!Q%T!Q![6T![#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk5WV&n`O!O%T!O!P5m!P#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk5tT!rZ&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti6[a!hX&n`O!Q%T!Q![6T![!g%T!g!h7a!h!l%T!l!m9s!m#R%T#R#S6T#S#X%T#X#Y7a#Y#^%T#^#_9s#_#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti7fZ&n`O{%T{|8X|}%T}!O8X!O!Q%T!Q![8s![#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti8^V&n`O!Q%T!Q![8s![#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti8z]!hX&n`O!Q%T!Q![8s![!l%T!l!m9s!m#R%T#R#S8s#S#^%T#^#_9s#_#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti9zT!hX&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk:bX%qR&n`O!P%T!P!Q:}!Q!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tj;UV%sQ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti;ro!hX&n`O!O%T!O!P=s!P!Q%T!Q![>_![!d%T!d!e?q!e!g%T!g!h7a!h!l%T!l!m9s!m!q%T!q!rA]!r!z%T!z!{Bq!{#R%T#R#S>_#S#U%T#U#V?q#V#X%T#X#Y7a#Y#^%T#^#_9s#_#c%T#c#dA]#d#l%T#l#mBq#m#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti=xV&n`O!Q%T!Q![6T![#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti>fc!hX&n`O!O%T!O!P=s!P!Q%T!Q![>_![!g%T!g!h7a!h!l%T!l!m9s!m#R%T#R#S>_#S#X%T#X#Y7a#Y#^%T#^#_9s#_#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti?vY&n`O!Q%T!Q!R@f!R!S@f!S#R%T#R#S@f#S#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Ti@mY!hX&n`O!Q%T!Q!R@f!R!S@f!S#R%T#R#S@f#S#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TiAbX&n`O!Q%T!Q!YA}!Y#R%T#R#SA}#S#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TiBUX!hX&n`O!Q%T!Q!YA}!Y#R%T#R#SA}#S#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TiBv]&n`O!Q%T!Q![Co![!c%T!c!iCo!i#R%T#R#SCo#S#T%T#T#ZCo#Z#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TiCv]!hX&n`O!Q%T!Q![Co![!c%T!c!iCo!i#R%T#R#SCo#S#T%T#T#ZCo#Z#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%ToDvV{_&n`O!_%T!_!`E]!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TcEdT%{R&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TkEzT#gZ&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TkFbXmR&n`O!^%T!^!_F}!_!`([!`!a([!a#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TjGUV%mQ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TkGrV%zZ&n`O!_%T!_!`([!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TkH`WmR&n`O!_%T!_!`([!`!aHx!a#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TjIPV%nQ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TkIoV_Q#}P&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%ToJ_]&n`&YS%uZO!Q%T!Q![JU![!c%T!c!}JU!}#R%T#R#SJU#S#T%T#T#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUoKZP;=`<%lJUoKge&n`&YS%uZOr%Trs)Ysw%Twx.Ux!Q%T!Q![JU![!c%T!c!tJU!t!uLx!u!}JU!}#R%T#R#SJU#S#T%T#T#fJU#f#gLx#g#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUoMRa&n`&YS%uZOr%TrsNWsw%Twx! vx!Q%T!Q![JU![!c%T!c!}JU!}#R%T#R#SJU#S#T%T#T#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUkN_V&n`&`ZOr%TrsNts#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%TkNyV&n`Or%Trs! `s#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk! gT&n`&bZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk! }V&n`&_ZOw%Twx!!dx#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!!iV&n`Ow%Twx!#Ox#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!#VT&n`&aZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%To!#oe&n`&YS%uZOr%Trs!%Qsw%Twx!&px!Q%T!Q![JU![!c%T!c!tJU!t!u!(`!u!}JU!}#R%T#R#SJU#S#T%T#T#fJU#f#g!(`#g#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUk!%XV&n`&dZOr%Trs!%ns#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!%sV&n`Or%Trs!&Ys#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!&aT&n`&fZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!&wV&n`&cZOw%Twx!'^x#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!'cV&n`Ow%Twx!'xx#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!(PT&n`&eZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%To!(ia&n`&YS%uZOr%Trs!)nsw%Twx!+^x!Q%T!Q![JU![!c%T!c!}JU!}#R%T#R#SJU#S#T%T#T#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUk!)uV&n`&hZOr%Trs!*[s#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!*aV&n`Or%Trs!*vs#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!*}T&n`&jZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!+eV&n`&gZOw%Twx!+zx#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!,PV&n`Ow%Twx!,fx#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tk!,mT&n`&iZO#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%To!-Vi&n`&YS%uZOr%TrsNWsw%Twx! vx!Q%T!Q![JU![!c%T!c!dJU!d!eLx!e!hJU!h!i!(`!i!}JU!}#R%T#R#SJU#S#T%T#T#UJU#U#VLx#V#YJU#Y#Z!(`#Z#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUo!.}a&n`&YS%uZOr%Trs)Ysw%Twx.Ux!Q%T!Q![JU![!c%T!c!}JU!}#R%T#R#SJU#S#T%T#T#oJU#p#q%T#r$g%T$g;'SJU;'S;=`KW<%lOJUk!0ZT!XZ&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tc!0qT!WR&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%Tj!1XV%kQ&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%T~!1sO!]~k!1zV%jR&n`O!_%T!_!`-Q!`#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%T~!2fO![~i!2mT%tX&n`O#o%T#p#q%T#r;'S%T;'S;=`%i<%lO%T", + tokenizers: [legacyPrint, indentation, newlines, strings, 0, 1, 2, 3, 4], + topRules: {"Script":[0,5]}, + specialized: [{term: 221, get: (value) => spec_identifier$1[value] || -1}], + tokenPrec: 7664 +}); + +const cache$1 = /*@__PURE__*/new NodeWeakMap(); +const ScopeNodes$1 = /*@__PURE__*/new Set([ + "Script", "Body", + "FunctionDefinition", "ClassDefinition", "LambdaExpression", + "ForStatement", "MatchClause" +]); +function defID$1(type) { + return (node, def, outer) => { + if (outer) + return false; + let id = node.node.getChild("VariableName"); + if (id) + def(id, type); + return true; + }; +} +const gatherCompletions$1 = { + FunctionDefinition: /*@__PURE__*/defID$1("function"), + ClassDefinition: /*@__PURE__*/defID$1("class"), + ForStatement(node, def, outer) { + if (outer) + for (let child = node.node.firstChild; child; child = child.nextSibling) { + if (child.name == "VariableName") + def(child, "variable"); + else if (child.name == "in") + break; + } + }, + ImportStatement(_node, def) { + var _a, _b; + let { node } = _node; + let isFrom = ((_a = node.firstChild) === null || _a === void 0 ? void 0 : _a.name) == "from"; + for (let ch = node.getChild("import"); ch; ch = ch.nextSibling) { + if (ch.name == "VariableName" && ((_b = ch.nextSibling) === null || _b === void 0 ? void 0 : _b.name) != "as") + def(ch, isFrom ? "variable" : "namespace"); + } + }, + AssignStatement(node, def) { + for (let child = node.node.firstChild; child; child = child.nextSibling) { + if (child.name == "VariableName") + def(child, "variable"); + else if (child.name == ":" || child.name == "AssignOp") + break; + } + }, + ParamList(node, def) { + for (let prev = null, child = node.node.firstChild; child; child = child.nextSibling) { + if (child.name == "VariableName" && (!prev || !/\*|AssignOp/.test(prev.name))) + def(child, "variable"); + prev = child; + } + }, + CapturePattern: /*@__PURE__*/defID$1("variable"), + AsPattern: /*@__PURE__*/defID$1("variable"), + __proto__: null +}; +function getScope$1(doc, node) { + let cached = cache$1.get(node); + if (cached) + return cached; + let completions = [], top = true; + function def(node, type) { + let name = doc.sliceString(node.from, node.to); + completions.push({ label: name, type }); + } + node.cursor(IterMode.IncludeAnonymous).iterate(node => { + if (node.name) { + let gather = gatherCompletions$1[node.name]; + if (gather && gather(node, def, top) || !top && ScopeNodes$1.has(node.name)) + return false; + top = false; + } + else if (node.to - node.from > 8192) { + // Allow caching for bigger internal nodes + for (let c of getScope$1(doc, node.node)) + completions.push(c); + return false; + } + }); + cache$1.set(node, completions); + return completions; +} +const Identifier$1 = /^[\w\xa1-\uffff][\w\d\xa1-\uffff]*$/; +const dontComplete$1 = ["String", "FormatString", "Comment", "PropertyName"]; +/** +Completion source that looks up locally defined names in +Python code. +*/ +function localCompletionSource$1(context) { + let inner = syntaxTree(context.state).resolveInner(context.pos, -1); + if (dontComplete$1.indexOf(inner.name) > -1) + return null; + let isWord = inner.name == "VariableName" || + inner.to - inner.from < 20 && Identifier$1.test(context.state.sliceDoc(inner.from, inner.to)); + if (!isWord && !context.explicit) + return null; + let options = []; + for (let pos = inner; pos; pos = pos.parent) { + if (ScopeNodes$1.has(pos.name)) + options = options.concat(getScope$1(context.state.doc, pos)); + } + return { + options, + from: isWord ? inner.from : context.pos, + validFor: Identifier$1 + }; +} +const globals = /*@__PURE__*/[ + "__annotations__", "__builtins__", "__debug__", "__doc__", "__import__", "__name__", + "__loader__", "__package__", "__spec__", + "False", "None", "True" +].map(n => ({ label: n, type: "constant" })).concat(/*@__PURE__*/[ + "ArithmeticError", "AssertionError", "AttributeError", "BaseException", "BlockingIOError", + "BrokenPipeError", "BufferError", "BytesWarning", "ChildProcessError", "ConnectionAbortedError", + "ConnectionError", "ConnectionRefusedError", "ConnectionResetError", "DeprecationWarning", + "EOFError", "Ellipsis", "EncodingWarning", "EnvironmentError", "Exception", "FileExistsError", + "FileNotFoundError", "FloatingPointError", "FutureWarning", "GeneratorExit", "IOError", + "ImportError", "ImportWarning", "IndentationError", "IndexError", "InterruptedError", + "IsADirectoryError", "KeyError", "KeyboardInterrupt", "LookupError", "MemoryError", + "ModuleNotFoundError", "NameError", "NotADirectoryError", "NotImplemented", "NotImplementedError", + "OSError", "OverflowError", "PendingDeprecationWarning", "PermissionError", "ProcessLookupError", + "RecursionError", "ReferenceError", "ResourceWarning", "RuntimeError", "RuntimeWarning", + "StopAsyncIteration", "StopIteration", "SyntaxError", "SyntaxWarning", "SystemError", + "SystemExit", "TabError", "TimeoutError", "TypeError", "UnboundLocalError", "UnicodeDecodeError", + "UnicodeEncodeError", "UnicodeError", "UnicodeTranslateError", "UnicodeWarning", "UserWarning", + "ValueError", "Warning", "ZeroDivisionError" +].map(n => ({ label: n, type: "type" }))).concat(/*@__PURE__*/[ + "bool", "bytearray", "bytes", "classmethod", "complex", "float", "frozenset", "int", "list", + "map", "memoryview", "object", "range", "set", "staticmethod", "str", "super", "tuple", "type" +].map(n => ({ label: n, type: "class" }))).concat(/*@__PURE__*/[ + "abs", "aiter", "all", "anext", "any", "ascii", "bin", "breakpoint", "callable", "chr", + "compile", "delattr", "dict", "dir", "divmod", "enumerate", "eval", "exec", "exit", "filter", + "format", "getattr", "globals", "hasattr", "hash", "help", "hex", "id", "input", "isinstance", + "issubclass", "iter", "len", "license", "locals", "max", "min", "next", "oct", "open", + "ord", "pow", "print", "property", "quit", "repr", "reversed", "round", "setattr", "slice", + "sorted", "sum", "vars", "zip" +].map(n => ({ label: n, type: "function" }))); +const snippets$1 = [ + /*@__PURE__*/snippetCompletion("def ${name}(${params}):\n\t${}", { + label: "def", + detail: "function", + type: "keyword" + }), + /*@__PURE__*/snippetCompletion("for ${name} in ${collection}:\n\t${}", { + label: "for", + detail: "loop", + type: "keyword" + }), + /*@__PURE__*/snippetCompletion("while ${}:\n\t${}", { + label: "while", + detail: "loop", + type: "keyword" + }), + /*@__PURE__*/snippetCompletion("try:\n\t${}\nexcept ${error}:\n\t${}", { + label: "try", + detail: "/ except block", + type: "keyword" + }), + /*@__PURE__*/snippetCompletion("if ${}:\n\t\n", { + label: "if", + detail: "block", + type: "keyword" + }), + /*@__PURE__*/snippetCompletion("if ${}:\n\t${}\nelse:\n\t${}", { + label: "if", + detail: "/ else block", + type: "keyword" + }), + /*@__PURE__*/snippetCompletion("class ${name}:\n\tdef __init__(self, ${params}):\n\t\t\t${}", { + label: "class", + detail: "definition", + type: "keyword" + }), + /*@__PURE__*/snippetCompletion("import ${module}", { + label: "import", + detail: "statement", + type: "keyword" + }), + /*@__PURE__*/snippetCompletion("from ${module} import ${names}", { + label: "from", + detail: "import", + type: "keyword" + }) +]; +/** +Autocompletion for built-in Python globals and keywords. +*/ +const globalCompletion = /*@__PURE__*/ifNotIn(dontComplete$1, /*@__PURE__*/completeFromList(/*@__PURE__*/globals.concat(snippets$1))); + +function innerBody(context) { + let { node, pos } = context; + let lineIndent = context.lineIndent(pos, -1); + let found = null; + for (;;) { + let before = node.childBefore(pos); + if (!before) { + break; + } + else if (before.name == "Comment") { + pos = before.from; + } + else if (before.name == "Body" || before.name == "MatchBody") { + if (context.baseIndentFor(before) + context.unit <= lineIndent) + found = before; + node = before; + } + else if (before.name == "MatchClause") { + node = before; + } + else if (before.type.is("Statement")) { + node = before; + } + else { + break; + } + } + return found; +} +function indentBody(context, node) { + let base = context.baseIndentFor(node); + let line = context.lineAt(context.pos, -1), to = line.from + line.text.length; + // Don't consider blank, deindented lines at the end of the + // block part of the block + if (/^\s*($|#)/.test(line.text) && + context.node.to < to + 100 && + !/\S/.test(context.state.sliceDoc(to, context.node.to)) && + context.lineIndent(context.pos, -1) <= base) + return null; + // A normally deindenting keyword that appears at a higher + // indentation than the block should probably be handled by the next + // level + if (/^\s*(else:|elif |except |finally:|case\s+[^=:]+:)/.test(context.textAfter) && context.lineIndent(context.pos, -1) > base) + return null; + return base + context.unit; +} +/** +A language provider based on the [Lezer Python +parser](https://github.com/lezer-parser/python), extended with +highlighting and indentation information. +*/ +const pythonLanguage = /*@__PURE__*/LRLanguage.define({ + name: "python", + parser: /*@__PURE__*/parser$2.configure({ + props: [ + /*@__PURE__*/indentNodeProp.add({ + Body: context => { + var _a; + let inner = innerBody(context); + return (_a = indentBody(context, inner || context.node)) !== null && _a !== void 0 ? _a : context.continue(); + }, + MatchBody: context => { + var _a; + let inner = innerBody(context); + return (_a = indentBody(context, inner || context.node)) !== null && _a !== void 0 ? _a : context.continue(); + }, + IfStatement: cx => /^\s*(else:|elif )/.test(cx.textAfter) ? cx.baseIndent : cx.continue(), + "ForStatement WhileStatement": cx => /^\s*else:/.test(cx.textAfter) ? cx.baseIndent : cx.continue(), + TryStatement: cx => /^\s*(except |finally:|else:)/.test(cx.textAfter) ? cx.baseIndent : cx.continue(), + MatchStatement: cx => { + if (/^\s*case /.test(cx.textAfter)) + return cx.baseIndent + cx.unit; + return cx.continue(); + }, + "TupleExpression ComprehensionExpression ParamList ArgList ParenthesizedExpression": /*@__PURE__*/delimitedIndent({ closing: ")" }), + "DictionaryExpression DictionaryComprehensionExpression SetExpression SetComprehensionExpression": /*@__PURE__*/delimitedIndent({ closing: "}" }), + "ArrayExpression ArrayComprehensionExpression": /*@__PURE__*/delimitedIndent({ closing: "]" }), + "String FormatString": () => null, + Script: context => { + var _a; + let inner = innerBody(context); + return (_a = (inner && indentBody(context, inner))) !== null && _a !== void 0 ? _a : context.continue(); + } + }), + /*@__PURE__*/foldNodeProp.add({ + "ArrayExpression DictionaryExpression SetExpression TupleExpression": foldInside, + Body: (node, state) => ({ from: node.from + 1, to: node.to - (node.to == state.doc.length ? 0 : 1) }) + }) + ], + }), + languageData: { + closeBrackets: { + brackets: ["(", "[", "{", "'", '"', "'''", '"""'], + stringPrefixes: ["f", "fr", "rf", "r", "u", "b", "br", "rb", + "F", "FR", "RF", "R", "U", "B", "BR", "RB"] + }, + commentTokens: { line: "#" }, + // Indent logic logic are triggered upon below input patterns + indentOnInput: /^\s*([\}\]\)]|else:|elif |except |finally:|case\s+[^:]*:?)$/, + } +}); +/** +Python language support. +*/ +function python() { + return new LanguageSupport(pythonLanguage, [ + pythonLanguage.data.of({ autocomplete: localCompletionSource$1 }), + pythonLanguage.data.of({ autocomplete: globalCompletion }), + ]); +} + +const rHighlight = styleTags({ + "repeat while for if else return break next in": tags.controlKeyword, + "Logical!": tags.bool, + function: tags.definitionKeyword, + "FunctionCall/Identifier FunctionCall/String": tags.function(tags.variableName), + "NamedArg!": tags.function(tags.attributeName), + Comment: tags.lineComment, + "Numeric Integer Complex Inf": tags.number, + "SpecialConstant!": tags.literal, + String: tags.string, + "ArithOp MatrixOp": tags.arithmeticOperator, + BitOp: tags.bitwiseOperator, + CompareOp: tags.compareOperator, + "ExtractionOp NamespaceOp": tags.operator, + AssignmentOperator: tags.definitionOperator, + "...": tags.punctuation, + "( )": tags.paren, + "[ ]": tags.squareBracket, + "{ }": tags.brace, + $: tags.derefOperator, + ", ;": tags.separator, +}); + +// This file was generated by lezer-generator. You probably shouldn't edit it. +const spec_Identifier = {__proto__:null,TRUE:12, T:14, FALSE:18, F:20, NULL:30, NA:34, Inf:38, NaN:42, function:46, "...":50, return:60, break:64, next:68, if:80, else:82, repeat:86, while:90, for:94, in:96}; +const parser$1 = LRParser.deserialize({ + version: 14, + states: "7dOYQPOOOOQO'#Dx'#DxOOQO'#Dw'#DwO$aQPO'#DwOOQO'#Dy'#DyO(]QPO'#DvOOQO'#Dv'#DvOOQO'#Cx'#CxO*UQPO'#CwO,YQPO'#DmO/rQPO'#DaO1kQPO'#D`OOQO'#Dz'#DzOOQO'#Du'#DuQYQPOOOOQO'#Ca'#CaOOQO'#Cd'#CdOOQO'#Cj'#CjOOQO'#Cl'#ClOOQO'#Cn'#CnOOQO'#Cp'#CpO1|QPO'#CrO2RQPO'#DTO!bQPO'#DWO2WQPO'#DYO2]QPO'#D[O3oQPO'#DROOQO,59l,59lO3yQPO'#DoOOQO'#Do'#DoO*UQPO,59cOOQO'#DP'#DPOOQO,59c,59cO5fQPO'#CyO5kQPO'#C{O7XQPO'#C}O8uQPO,59yO:ZQQO,59yOOQO'#Dd'#DdOOQO'#De'#DeOOQO'#Df'#DfOOQO'#Dg'#DgOOQO'#Dh'#DhOOQO'#Di'#DiOOQO'#Dj'#DjOOQO'#Dk'#DkOOQO'#Dl'#DlOYQPO,59}OYQPO,59}OYQPO,59}OYQPO,59}OYQPO,59}OYQPO,59}OYQPO,59}OYQPO,59}OYQPO,59}OOQO'#Db'#DbOYQPO,59zOOQO-E7k-E7kO:bQPO'#CtO!bQPO,59^OYQPO,59oOOQO,59r,59rOYQPO,59tOYQPO,59vO:mQPO'#DwO:tQPO'#DvO:{QPO'#ESO;VQPO'#ESO;aQPO,59mO;fQPO'#ESOOQO-E7m-E7mOOQO1G.}1G.}O;nQPO,59eO;uQPO,59gO;zQPO,59iOkQQO'#DvO@gQQO'#EUO@qQQO1G/eO@vQQO'#DaODcQPO1G/iODmQPO1G/iOH`QPO1G/iOHgQPO1G/iOLSQPO1G/iOL^QPO1G/iO! aQPO1G/iO!!WQPO1G/iO!$tQPO1G/iO!(dQPO1G/fO!*]QPO'#D}OYQPO'#D}O!*hQPO,59`O!*`QPO'#D}OOQO1G.x1G.xO!*mQPO1G/ZO!*tQPO1G/`O!*{QPO1G/bOOQO,59n,59nO!+SQPO'#DpO!+ZQPO,5:nO!+cQPO,5:nOOQO1G/X1G/XO!+mQPO1G/POOQO1G/P1G/POOQO1G/R1G/ROOQO1G/T1G/TOYQPO'#DqO!+tQPO,5:pOOQO7+%P7+%PO!+|QQO,5:pOOQO,59b,59bO!,UQPO'#DnO!,^QPO,5:iO!,fQPO,5:iOOQO1G.z1G.zO!bQPO7+$uO!bQPO7+$zOYQPO7+$|O!,pQPO,5:[O!,zQPO,5:[OOQO,5:[,5:[OOQO-E7n-E7nO!-UQPO1G0YOOQO7+$k7+$kO!-^QPO,5:]OOQO-E7o-E7oO!-hQQO,5:]O!/fQQO1G/iO!/pQQO1G/iO!1qQQO1G/iO!1xQQO1G/iO!3sQQO1G/iO!3}QQO1G/iO!5`QQO1G/iO!6VQQO1G/iO!6|QQO1G/iO!7TQQO1G/fO!7[QPO,5:YOYQPO,5:YOOQO,5:Y,5:YOOQO-E7l-E7lO!7gQPO1G0TO!9iQPO<SO!;}QQO<YO!s$lO!{!xX~P*fO!{#dO~O!{!nX~P-lO!pjOR!ViS!ViU!ViV!ViX!ViY!ViZ!Vi[!Vi]!Vi_!Via!Vic!Vie!Vig!Vix!Vi{!Vi}!Vi!P!Vi!f!Vi!u!Vi!w!Vi!z!Vi#S!Vi#T!Vi#U!Vi#V!Vi#W!Vi#X!Vi#Y!Vi#Z!Vi#[!Vi#]!Vi#^!Vi#_!Vi#`!Vi#a!Vi#b!Vi#c!Vi#d!Vi#e!Vi#f!Vi#g!Vi#h!Vin!Vip!Vir!Vi!t!Vi!o!Vi!s!Vi!y!Vi!Q!Vi~O#Q!Vi#R!Vi~P@}O#QvO#RvO~P@}O!pjO#QvO#RvO#SwO#TwOR!ViS!ViU!ViV!ViX!ViY!ViZ!Vi[!Vi]!Vi_!Via!Vic!Vie!Vig!Vix!Vi{!Vi}!Vi!P!Vi!f!Vi!u!Vi!w!Vi!z!Vi#V!Vi#W!Vi#X!Vi#Y!Vi#Z!Vi#[!Vi#]!Vi#^!Vi#_!Vi#`!Vi#a!Vi#b!Vi#c!Vi#d!Vi#e!Vi#f!Vi#g!Vi#h!Vin!Vip!Vir!Vi!t!Vi!o!Vi!s!Vi!y!Vi!Q!Vi~O#U!Vi~PDwO#UxO~PDwO!pjO#QvO#RvO#SwO#TwO#UxO#VyO#WyO#XyO#a|O#b|O#c|O#d|OR!ViS!ViU!ViV!ViX!ViY!ViZ!Vi[!Vi]!Vi_!Via!Vic!Vie!Vig!Vix!Vi{!Vi}!Vi!P!Vi!f!Vi!u!Vi!w!Vi!z!Vi#[!Vi#]!Vi#^!Vi#_!Vi#`!Vi#e!Vi#f!Vi#g!Vi#h!Vin!Vip!Vir!Vi!t!Vi!o!Vi!s!Vi!y!Vi!Q!Vi~O#Y!Vi#Z!Vi~PHnO#YzO#ZzO~PHnO!pjO#QvO#RvO#SwO#TwO#UxO#VyO#WyO#XyOR!ViS!ViU!ViV!ViX!ViY!ViZ!Vi[!Vi]!Vi_!Via!Vic!Vie!Vig!Vix!Vi{!Vi}!Vi!P!Vi!f!Vi!u!Vi!w!Vi!z!Vi#e!Vi#f!Vi#g!Vi#h!Vin!Vip!Vir!Vi!t!Vi!o!Vi!s!Vi!y!Vi!Q!Vi~O#Y!Vi#Z!Vi#[!Vi#]!Vi#^!Vi#_!Vi#`!Vi#a!Vi#b!Vi#c!Vi#d!Vi~PLhO#YzO#ZzO#[{O#]{O#^{O#_{O#`{O#a|O#b|O#c|O#d|O~PLhO!pjO#QvO#RvO#SwO#TwO#UxO#VyO#WyO#XyO#YzO#ZzO#[{O#]{O#^{O#_{O#`{O#a|O#b|O#c|O#d|O#e}O#f}O!w!Vi!z!Vi#g!Vi#h!Vi!s!Vi~OR!ViS!ViU!ViV!ViX!ViY!ViZ!Vi[!Vi]!Vi_!Via!Vic!Vie!Vig!Vix!Vi{!Vi}!Vi!P!Vi!f!Vi!u!Vin!Vip!Vir!Vi!t!Vi!o!Vi!y!Vi!Q!Vi~P!!}O!pjO!w!Si!z!Si#Q!Si#R!Si#S!Si#T!Si#U!Si#V!Si#W!Si#X!Si#Y!Si#Z!Si#[!Si#]!Si#^!Si#_!Si#`!Si#a!Si#b!Si#c!Si#d!Si#e!Si#f!Si#g!Si#h!Si!s!Si~OR!SiS!SiU!SiV!SiX!SiY!SiZ!Si[!Si]!Si_!Sia!Sic!Sie!Sig!Six!Si{!Si}!Si!P!Si!f!Si!u!Sin!Sip!Sir!Si!t!Si!o!Si!y!Si!Q!Si~P!&mO!r#fO!s#gO!o!qX~O!o#jO~O!o#kO~P*fO!o#lO~P*fO!Q#mO~P*fOi#pO~P2bO!s#YO!o!va~O!s#YO!o!va~P*fO!o#sO~P*fO!s#bO!y!xa~O!s$lO!{!xa~OR$ROi$TO~O!s#gO!o!qa~O!s#gO!o!qa~P*fO!o!da!s!da~P*fO!o!da!s!da~PYO!s#YO!o!vi~O!s!ea!y!ea~P*fO!s!ea!{!ea~P*fO!pjO!s!Vi!w!Vi!z!Vi!{!Vi#S!Vi#T!Vi#U!Vi#V!Vi#W!Vi#X!Vi#Y!Vi#Z!Vi#[!Vi#]!Vi#^!Vi#_!Vi#`!Vi#a!Vi#b!Vi#c!Vi#d!Vi#e!Vi#f!Vi#g!Vi#h!Vi~O#Q!Vi#R!Vi~P!-rO#QvO#RvO~P!-rO!pjO#QvO#RvO#SwO#TwO!s!Vi!w!Vi!z!Vi!{!Vi#V!Vi#W!Vi#X!Vi#Y!Vi#Z!Vi#[!Vi#]!Vi#^!Vi#_!Vi#`!Vi#a!Vi#b!Vi#c!Vi#d!Vi#e!Vi#f!Vi#g!Vi#h!Vi~O#U!Vi~P!/zO#UxO~P!/zO!pjO#QvO#RvO#SwO#TwO#UxO#VyO#WyO#XyO#a|O#b|O#c|O#d|O!s!Vi!w!Vi!z!Vi!{!Vi#[!Vi#]!Vi#^!Vi#_!Vi#`!Vi#e!Vi#f!Vi#g!Vi#h!Vi~O#Y!Vi#Z!Vi~P!2PO#YzO#ZzO~P!2PO!pjO#QvO#RvO#SwO#TwO#UxO#VyO#WyO#XyO!s!Vi!w!Vi!z!Vi!{!Vi#e!Vi#f!Vi#g!Vi#h!Vi~O#Y!Vi#Z!Vi#[!Vi#]!Vi#^!Vi#_!Vi#`!Vi#a!Vi#b!Vi#c!Vi#d!Vi~P!4XO#YzO#ZzO#[{O#]{O#^{O#_{O#`{O#a|O#b|O#c|O#d|O~P!4XO!{!Vi~P!!}O!{!Si~P!&mO!r#fO!o!ba!s!ba~O!s#gO!o!qi~Oy$]O!pwy!wwy!zwy#Qwy#Rwy#Swy#Twy#Uwy#Vwy#Wwy#Xwy#Ywy#Zwy#[wy#]wy#^wy#_wy#`wy#awy#bwy#cwy#dwy#ewy#fwy#gwy#hwy!swy~ORwySwyUwyVwyXwyYwyZwy[wy]wy_wyawycwyewygwyxwy{wy}wy!Pwy!fwy!uwynwypwyrwy!twy!owy!ywy!Qwy~P!7oO!o$^O~P*fO!o!di!s!di~P*fO!o!bi!s!bi~P*fO!{wy~P!7oO!o$mO~P*fO!p$pO~OS]ZRZ~", + goto: "7}!yPPPPP!zPP!zPPPPP#vP#vP#vP#vP$rP%nP%q%w'Y(]P(]P(]P(a(g)e*b$rPP$rP$rP$rPP(g$r*h+f$r+l,d-Y-|.n/[/v0f1O1f1l1w1}2ZPPP2e4}5y6u5y4}PP7qPPPP7tP7w!sPOW^jntu!P!Q!R!S!T!U!V!W!X!Z!_!a!b!f!k#Q#Y#b#m#o$S$b$c$d$e$f$g$h$i$j$k$l$p!sSOW^jntu!P!Q!R!S!T!U!V!W!X!Z!_!a!b!f!k#Q#Y#b#m#o$S$b$c$d$e$f$g$h$i$j$k$l$p!s[OW^jntu!P!Q!R!S!T!U!V!W!X!Z!_!a!b!f!k#Q#Y#b#m#o$S$b$c$d$e$f$g$h$i$j$k$l$pR!^eQ#Q!]R$S#g!r[OW^jntu!P!Q!R!S!T!U!V!W!X!Z!_!a!b!f!k#Q#Y#b#m#o$S$b$c$d$e$f$g$h$i$j$k$l$pQ!`gQ#T!^Q$W#kQ$X#lQ$_$mQ$`$]R$a$^#RWOW^gjntu!P!Q!R!S!T!U!V!W!X!Z!^!_!a!b!f!k#Q#Y#b#k#l#m#o$S$]$^$b$c$d$e$f$g$h$i$j$k$l$m$pTmWnQpWR!jn!YYOW^jnt!P!Q!R!S!T!U!V!W!X!Z!_!a!b!f!k#Q#Y#b#m#o$S$pi!tu$b$c$d$e$f$g$h$i$j$k$l!ukRXl!c!e!n!p!r!u!v!w!x!y!z!{!|!}#O#U#V#W#[#^#i#n#t#v#w#x#y#z#{#|#}$O$P$Q$Y$Z$[$oQ!fjR#o#Y!YZOW^jnt!P!Q!R!S!T!U!V!W!X!Z!_!a!b!f!k#Q#Y#b#m#o$S$pi$nu$b$c$d$e$f$g$h$i$j$k$lQ!ZZR$k$n!Q!PXl!e!n!v!w!x!y!z!{!|!}#U#V#W#[#^#i#n#t$Y$Z$[$oe$b!r#v#x#y#z#{#|#}$O$P!O!QXl!e!n!w!x!y!z!{!|!}#U#V#W#[#^#i#n#t$Y$Z$[$oc$c!r#v#y#z#{#|#}$O$P|!RXl!e!n!x!y!z!{!|!}#U#V#W#[#^#i#n#t$Y$Z$[$oa$d!r#v#z#{#|#}$O$Pz!SXl!e!n!y!z!{!|!}#U#V#W#[#^#i#n#t$Y$Z$[$o_$e!r#v#{#|#}$O$Pv!TXl!e!n!z!|!}#U#V#W#[#^#i#n#t$Y$Z$[$oZ$f!r#v#|$O$Pt!UXl!e!n!|!}#U#V#W#[#^#i#n#t$Y$Z$[$oX$g!r#v$O$Px!VXl!e!n!y!z!|!}#U#V#W#[#^#i#n#t$Y$Z$[$o]$h!r#v#{#|$O$Pr!WXl!e!n!}#U#V#W#[#^#i#n#t$Y$Z$[$oV$i!r#v$Pp!XXl!e!n#U#V#W#[#^#i#n#t$Y$Z$[$oT$j!r#vQ^OR![^S#h#P#SS$U#h$VR$V#iQnWR!inU#Z!e!f!hS#q#Z#rR#r#[Q#c!nQ#e!rT#u#c#eSXO^SlWnQ!ejQ!ntQ!ruQ!u!PQ!v!QQ!w!RQ!x!SQ!y!TQ!z!UQ!{!VQ!|!WQ!}!XQ#O!ZQ#U!_Q#V!aQ#W!bQ#[!fQ#^!kQ#i#QQ#n#YQ#t#bQ#v$lQ#w$bQ#x$cQ#y$dQ#z$eQ#{$fQ#|$gQ#}$hQ$O$iQ$P$jQ$Q$kQ$Y#mQ$Z#oQ$[$SR$o$p!s]OW^jntu!P!Q!R!S!T!U!V!W!X!Z!_!a!b!f!k#Q#Y#b#m#o$S$b$c$d$e$f$g$h$i$j$k$l$p!sUOW^jntu!P!Q!R!S!T!U!V!W!X!Z!_!a!b!f!k#Q#Y#b#m#o$S$b$c$d$e$f$g$h$i$j$k$l$p!sQOW^jntu!P!Q!R!S!T!U!V!W!X!Z!_!a!b!f!k#Q#Y#b#m#o$S$b$c$d$e$f$g$h$i$j$k$l$pR#R!]R!gjQ!otR!su", + nodeNames: "⚠ Comment Script Identifier Integer True TRUE T False FALSE F Numeric String Complex Null NULL NA NA Inf Inf NaN NaN FunctionDeclaration function ParamList ... NamedArg Block BlockOpenBrace ReturnStatement return BreakStatement break NextStatement next BlockCloseBrace FunctionCall ArgList NamedArg IfStatement if else RepeatStatement repeat WhileStatement while ForStatement for in IndexStatement VariableAssignment Assignable AssignmentOperator BinaryStatement NamespaceOp ExtractionOp ArithOp ArithOp ArithOp CompareOp MatrixOp LogicOp LogicOp", + maxTerm: 116, + nodeProps: [ + ["group", -11,3,22,27,36,39,42,44,46,49,50,53,"Expression",-4,4,11,12,13,"Constant Expression",-2,5,8,"Constant Expression Logical",-4,14,16,18,20,"Expression SpecialConstant"] + ], + propSources: [rHighlight], + skippedNodes: [0,1], + repeatNodeCount: 5, + tokenData: "9`~RzX^#upq#uqr$jrs$ust&mtu'Uuv'Zvw)Uwx)cxy+Uyz+Zz{+`{|+e|}+j}!O+o!O!P,U!P!Q1S!Q!R1X!R![3O![!]5j!^!_5}!_!`6p!`!a6}!b!c7Y!c!}-h!}#O7_#P#Q7l#Q#R7y#R#S-h#S#T8O#T#o-h#o#p8w#p#q8|#q#r9Z#y#z#u$f$g#u#BY#BZ#u$IS$I_#u$I|$JO#u$JT$JU#u$KV$KW#u&FU&FV#u~#zY!h~X^#upq#u#y#z#u$f$g#u#BY#BZ#u$IS$I_#u$I|$JO#u$JT$JU#u$KV$KW#u&FU&FV#u~$mP!_!`$p~$uO#]~~$zW[~OY$uZr$urs%ds#O$u#O#P%i#P;'S$u;'S;=`&g<%lO$u~%iO[~~%lRO;'S$u;'S;=`%u;=`O$u~%zX[~OY$uZr$urs%ds#O$u#O#P%i#P;'S$u;'S;=`&g;=`<%l$u<%lO$u~&jP;=`<%l$u~&rSP~OY&mZ;'S&m;'S;=`'O<%lO&m~'RP;=`<%l&m~'ZO#S~~'^Uuv'pz{'u!P!Q(Q#]#^(]#c#d(n#l#m(y~'uO#X~~'xPuv'{~(QO#b~~(TPuv(W~(]O#a~~(`P#b#c(c~(fPuv(i~(nO#`~~(qPuv(t~(yO#c~~(|Puv)P~)UO#d~~)ZP#g~vw)^~)cO#h~~)hW[~OY)cZw)cwx%dx#O)c#O#P*Q#P;'S)c;'S;=`+O<%lO)c~*TRO;'S)c;'S;=`*^;=`O)c~*cX[~OY)cZw)cwx%dx#O)c#O#P*Q#P;'S)c;'S;=`+O;=`<%l)c<%lO)c~+RP;=`<%l)c~+ZO!p~~+`O!o~~+eO#V~~+jO#Y~~+oO!s~~+tP#Z~!`!a+w~+|P!}~!`!a,P~,UO#P~~,ZTR~!O!P,j!Q![.S!c!}-h#R#S-h#T#o-h~,o]R~O!O-h!O!P-h!P!Q-h!Q![-h![!c-h!c!}-h!}#R-h#R#S-h#S#T-h#T#o-h#o;'S-h;'S;=`-|<%lO-h~-mTR~!O!P-h!Q![-h!c!}-h#R#S-h#T#o-h~.PP;=`<%l-h~.ZZZ~R~!O!P-h!Q![.S!c!g-h!g!h.|!h!}-h#R#S-h#T#X-h#X#Y.|#Y#]-h#]#^0l#^#o-h~/RVR~{|/h}!O/h!O!P-h!Q![0O!c!}-h#R#S-h#T#o-h~/kP!Q![/n~/sQZ~!Q![/n#]#^/y~0OO]~~0VVZ~R~!O!P-h!Q![0O!c!}-h#R#S-h#T#]-h#]#^0l#^#o-h~0sT]~R~!O!P-h!Q![-h!c!}-h#R#S-h#T#o-h~1XO#W~~1^WZ~!O!P1v!Q![3O!g!h3g!n!o2y!z!{4X#X#Y3g#]#^/y#l#m4X~1{TZ~!Q![2[!g!h2m!n!o2y#X#Y2m#]#^/y~2aSZ~!Q![2[!g!h2m#X#Y2m#]#^/y~2pR{|/h}!O/h!Q![/n~3OOS~~3TUZ~!O!P1v!Q![3O!g!h3g!n!o2y#X#Y3g#]#^/y~3jR{|3s}!O3s!Q![3y~3vP!Q![3y~4ORZ~!Q![3y!n!o2y#]#^/y~4[U!O!P4n!Q![5Q!c!i5Q!r!s2m#T#Z5Q#d#e2m~4qT!Q![4n!c!i4n!r!s2m#T#Z4n#d#e2m~5TV!O!P4n!Q![5Q!c!i5Q!n!o2y!r!s2m#T#Z5Q#d#e2m~5mP![!]5p~5uP#Q~![!]5x~5}O#R~~6QR}!O6Z!^!_6`!_!`6k~6`O!|~~6cP}!O6f~6kO#O~~6pO#_~~6uP!r~!_!`6x~6}O#[~~7QP!_!`7T~7YO#^~~7_O#T~~7dP!w~!}#O7g~7lO!z~R7qP!yP#P#Q7tQ7yO!{Q~8OO#U~~8RSO#S8_#T;'S8_;'S;=`8q<%lO8_~8bTO#S8_#S#T%d#T;'S8_;'S;=`8q<%lO8_~8tP;=`<%l8_~8|O!u~~9RP#e~#p#q9U~9ZO#f~~9`O!t~", + tokenizers: [0, 1], + topRules: {"Script":[0,2]}, + specialized: [{term: 3, get: (value) => spec_Identifier[value] || -1}], + tokenPrec: 3376 +}); + +const rLanguage = /*@__PURE__*/LRLanguage.define({ + parser: /*@__PURE__*/parser$1.configure({ + props: [ + /*@__PURE__*/indentNodeProp.add({ + Block: /*@__PURE__*/delimitedIndent({ closing: "}" }), + "ParamList ArgList": /*@__PURE__*/delimitedIndent({ closing: ")" }), + }), + /*@__PURE__*/foldNodeProp.add({ Block: foldInside }), + ], + }), + languageData: { + closeBrackets: { brackets: ["(", "[", "{", "'", '"'] }, + commentTokens: { line: "#" }, + }, +}); +function r() { + return new LanguageSupport(rLanguage); +} + +// This file was generated by lezer-generator. You probably shouldn't edit it. +const noSemi = 314, + noSemiType = 315, + incdec = 1, + incdecPrefix = 2, + questionDot = 3, + JSXStartTag = 4, + insertSemi = 316, + spaces = 318, + newline = 319, + LineComment = 5, + BlockComment = 6, + Dialect_jsx = 0; + +/* Hand-written tokenizers for JavaScript tokens that can't be + expressed by lezer's built-in tokenizer. */ + +const space = [9, 10, 11, 12, 13, 32, 133, 160, 5760, 8192, 8193, 8194, 8195, 8196, 8197, 8198, 8199, 8200, + 8201, 8202, 8232, 8233, 8239, 8287, 12288]; + +const braceR = 125, semicolon = 59, slash = 47, star = 42, plus = 43, minus = 45, lt = 60, comma = 44, + question = 63, dot = 46, bracketL = 91; + +const trackNewline = new ContextTracker({ + start: false, + shift(context, term) { + return term == LineComment || term == BlockComment || term == spaces ? context : term == newline + }, + strict: false +}); + +const insertSemicolon = new ExternalTokenizer((input, stack) => { + let {next} = input; + if (next == braceR || next == -1 || stack.context) + input.acceptToken(insertSemi); +}, {contextual: true, fallback: true}); + +const noSemicolon = new ExternalTokenizer((input, stack) => { + let {next} = input, after; + if (space.indexOf(next) > -1) return + if (next == slash && ((after = input.peek(1)) == slash || after == star)) return + if (next != braceR && next != semicolon && next != -1 && !stack.context) + input.acceptToken(noSemi); +}, {contextual: true}); + +const noSemicolonType = new ExternalTokenizer((input, stack) => { + if (input.next == bracketL && !stack.context) input.acceptToken(noSemiType); +}, {contextual: true}); + +const operatorToken = new ExternalTokenizer((input, stack) => { + let {next} = input; + if (next == plus || next == minus) { + input.advance(); + if (next == input.next) { + input.advance(); + let mayPostfix = !stack.context && stack.canShift(incdec); + input.acceptToken(mayPostfix ? incdec : incdecPrefix); + } + } else if (next == question && input.peek(1) == dot) { + input.advance(); input.advance(); + if (input.next < 48 || input.next > 57) // No digit after + input.acceptToken(questionDot); + } +}, {contextual: true}); + +function identifierChar(ch, start) { + return ch >= 65 && ch <= 90 || ch >= 97 && ch <= 122 || ch == 95 || ch >= 192 || + !start && ch >= 48 && ch <= 57 +} + +const jsx = new ExternalTokenizer((input, stack) => { + if (input.next != lt || !stack.dialectEnabled(Dialect_jsx)) return + input.advance(); + if (input.next == slash) return + // Scan for an identifier followed by a comma or 'extends', don't + // treat this as a start tag if present. + let back = 0; + while (space.indexOf(input.next) > -1) { input.advance(); back++; } + if (identifierChar(input.next, true)) { + input.advance(); + back++; + while (identifierChar(input.next, false)) { input.advance(); back++; } + while (space.indexOf(input.next) > -1) { input.advance(); back++; } + if (input.next == comma) return + for (let i = 0;; i++) { + if (i == 7) { + if (!identifierChar(input.next, true)) return + break + } + if (input.next != "extends".charCodeAt(i)) break + input.advance(); + back++; + } + } + input.acceptToken(JSXStartTag, -back); +}); + +const jsHighlight = styleTags({ + "get set async static": tags.modifier, + "for while do if else switch try catch finally return throw break continue default case": tags.controlKeyword, + "in of await yield void typeof delete instanceof": tags.operatorKeyword, + "let var const using function class extends": tags.definitionKeyword, + "import export from": tags.moduleKeyword, + "with debugger as new": tags.keyword, + TemplateString: tags.special(tags.string), + super: tags.atom, + BooleanLiteral: tags.bool, + this: tags.self, + null: tags.null, + Star: tags.modifier, + VariableName: tags.variableName, + "CallExpression/VariableName TaggedTemplateExpression/VariableName": tags.function(tags.variableName), + VariableDefinition: tags.definition(tags.variableName), + Label: tags.labelName, + PropertyName: tags.propertyName, + PrivatePropertyName: tags.special(tags.propertyName), + "CallExpression/MemberExpression/PropertyName": tags.function(tags.propertyName), + "FunctionDeclaration/VariableDefinition": tags.function(tags.definition(tags.variableName)), + "ClassDeclaration/VariableDefinition": tags.definition(tags.className), + "NewExpression/VariableName": tags.className, + PropertyDefinition: tags.definition(tags.propertyName), + PrivatePropertyDefinition: tags.definition(tags.special(tags.propertyName)), + UpdateOp: tags.updateOperator, + "LineComment Hashbang": tags.lineComment, + BlockComment: tags.blockComment, + Number: tags.number, + String: tags.string, + Escape: tags.escape, + ArithOp: tags.arithmeticOperator, + LogicOp: tags.logicOperator, + BitOp: tags.bitwiseOperator, + CompareOp: tags.compareOperator, + RegExp: tags.regexp, + Equals: tags.definitionOperator, + Arrow: tags.function(tags.punctuation), + ": Spread": tags.punctuation, + "( )": tags.paren, + "[ ]": tags.squareBracket, + "{ }": tags.brace, + "InterpolationStart InterpolationEnd": tags.special(tags.brace), + ".": tags.derefOperator, + ", ;": tags.separator, + "@": tags.meta, + + TypeName: tags.typeName, + TypeDefinition: tags.definition(tags.typeName), + "type enum interface implements namespace module declare": tags.definitionKeyword, + "abstract global Privacy readonly override": tags.modifier, + "is keyof unique infer asserts": tags.operatorKeyword, + + JSXAttributeValue: tags.attributeValue, + JSXText: tags.content, + "JSXStartTag JSXStartCloseTag JSXSelfCloseEndTag JSXEndTag": tags.angleBracket, + "JSXIdentifier JSXNameSpacedName": tags.tagName, + "JSXAttribute/JSXIdentifier JSXAttribute/JSXNameSpacedName": tags.attributeName, + "JSXBuiltin/JSXIdentifier": tags.standard(tags.tagName) +}); + +// This file was generated by lezer-generator. You probably shouldn't edit it. +const spec_identifier = {__proto__:null,export:20, as:25, from:33, default:36, async:41, function:42, const:52, extends:56, this:60, true:68, false:68, null:80, void:84, typeof:88, super:104, new:138, delete:150, yield:159, await:163, class:168, public:231, private:231, protected:231, readonly:233, instanceof:252, satisfies:255, in:256, import:290, keyof:347, unique:351, infer:357, asserts:393, is:395, abstract:415, implements:417, type:419, let:422, var:424, using:427, interface:433, enum:437, namespace:443, module:445, declare:449, global:453, for:472, of:481, while:484, with:488, do:492, if:496, else:498, switch:502, case:508, try:514, catch:518, finally:522, return:526, throw:530, break:534, continue:538, debugger:542}; +const spec_word = {__proto__:null,async:125, get:127, set:129, declare:191, public:193, private:193, protected:193, static:195, abstract:197, override:199, readonly:205, accessor:207, new:399}; +const spec_LessThan = {__proto__:null,"<":189}; +const parser = LRParser.deserialize({ + version: 14, + states: "$EOQ%TQlOOO%[QlOOO'_QpOOP(lO`OOO*zQ!0MxO'#CiO+RO#tO'#CjO+aO&jO'#CjO+oO#@ItO'#D_O.QQlO'#DeO.bQlO'#DpO%[QlO'#DxO0fQlO'#EQOOQ!0Lf'#EY'#EYO1PQ`O'#EVOOQO'#En'#EnOOQO'#Ij'#IjO1XQ`O'#GrO1dQ`O'#EmO1iQ`O'#EmO3hQ!0MxO'#JpO6[Q!0MxO'#JqO6uQ`O'#F[O6zQ,UO'#FsOOQ!0Lf'#Fe'#FeO7VO7dO'#FeO7eQMhO'#F{O9UQ`O'#FzOOQ!0Lf'#Jq'#JqOOQ!0Lb'#Jp'#JpO9ZQ`O'#GvOOQ['#K]'#K]O9fQ`O'#IWO9kQ!0LrO'#IXOOQ['#J^'#J^OOQ['#I]'#I]Q`QlOOQ`QlOOO9sQ!L^O'#DtO9zQlO'#D|O:RQlO'#EOO9aQ`O'#GrO:YQMhO'#CoO:hQ`O'#ElO:sQ`O'#EwO:xQMhO'#FdO;gQ`O'#GrOOQO'#K^'#K^O;lQ`O'#K^O;zQ`O'#GzO;zQ`O'#G{O;zQ`O'#G}O9aQ`O'#HQOYQ`O'#CeO>jQ`O'#HaO>rQ`O'#HgO>rQ`O'#HiO`QlO'#HkO>rQ`O'#HmO>rQ`O'#HpO>wQ`O'#HvO>|Q!0LsO'#H|O%[QlO'#IOO?XQ!0LsO'#IQO?dQ!0LsO'#ISO9kQ!0LrO'#IUO?oQ!0MxO'#CiO@qQpO'#DjQOQ`OOO%[QlO'#EOOAXQ`O'#ERO:YQMhO'#ElOAdQ`O'#ElOAoQ!bO'#FdOOQ['#Cg'#CgOOQ!0Lb'#Do'#DoOOQ!0Lb'#Jt'#JtO%[QlO'#JtOOQO'#Jw'#JwOOQO'#If'#IfOBoQpO'#EeOOQ!0Lb'#Ed'#EdOOQ!0Lb'#J{'#J{OCkQ!0MSO'#EeOCuQpO'#EUOOQO'#Jv'#JvODZQpO'#JwOEhQpO'#EUOCuQpO'#EePEuO&2DjO'#CbPOOO)CD{)CD{OOOO'#I^'#I^OFQO#tO,59UOOQ!0Lh,59U,59UOOOO'#I_'#I_OF`O&jO,59UOFnQ!L^O'#DaOOOO'#Ia'#IaOFuO#@ItO,59yOOQ!0Lf,59y,59yOGTQlO'#IbOGhQ`O'#JrOIgQ!fO'#JrO+}QlO'#JrOInQ`O,5:POJUQ`O'#EnOJcQ`O'#KROJnQ`O'#KQOJnQ`O'#KQOJvQ`O,5;[OJ{Q`O'#KPOOQ!0Ln,5:[,5:[OKSQlO,5:[OMQQ!0MxO,5:dOMqQ`O,5:lON[Q!0LrO'#KOONcQ`O'#J}O9ZQ`O'#J}ONwQ`O'#J}O! PQ`O,5;ZO! UQ`O'#J}O!#ZQ!fO'#JqOOQ!0Lh'#Ci'#CiO%[QlO'#EQO!#yQ!fO,5:qOOQS'#Jx'#JxOOQO-ErOOQ['#Jf'#JfOOQ[,5>s,5>sOOQ[-EbQ!0MxO,5:hO%[QlO,5:hO!@xQ!0MxO,5:jOOQO,5@x,5@xO!AiQMhO,5=^O!AwQ!0LrO'#JgO9UQ`O'#JgO!BYQ!0LrO,59ZO!BeQpO,59ZO!BmQMhO,59ZO:YQMhO,59ZO!BxQ`O,5;XO!CQQ`O'#H`O!CfQ`O'#KbO%[QlO,5;|O!9lQpO,5wQ`O'#HVO9aQ`O'#HXO!D}Q`O'#HXO:YQMhO'#HZO!ESQ`O'#HZOOQ[,5=o,5=oO!EXQ`O'#H[O!EjQ`O'#CoO!EoQ`O,59PO!EyQ`O,59PO!HOQlO,59POOQ[,59P,59PO!H`Q!0LrO,59PO%[QlO,59PO!JkQlO'#HcOOQ['#Hd'#HdOOQ['#He'#HeO`QlO,5={O!KRQ`O,5={O`QlO,5>RO`QlO,5>TO!KWQ`O,5>VO`QlO,5>XO!K]Q`O,5>[O!KbQlO,5>bOOQ[,5>h,5>hO%[QlO,5>hO9kQ!0LrO,5>jOOQ[,5>l,5>lO# lQ`O,5>lOOQ[,5>n,5>nO# lQ`O,5>nOOQ[,5>p,5>pO#!YQpO'#D]O%[QlO'#JtO#!{QpO'#JtO##VQpO'#DkO##hQpO'#DkO#%yQlO'#DkO#&QQ`O'#JsO#&YQ`O,5:UO#&_Q`O'#ErO#&mQ`O'#KSO#&uQ`O,5;]O#&zQpO'#DkO#'XQpO'#ETOOQ!0Lf,5:m,5:mO%[QlO,5:mO#'`Q`O,5:mO>wQ`O,5;WO!BeQpO,5;WO!BmQMhO,5;WO:YQMhO,5;WO#'hQ`O,5@`O#'mQ07dO,5:qOOQO-E|O+}QlO,5>|OOQO,5?S,5?SO#*uQlO'#IbOOQO-E<`-E<`O#+SQ`O,5@^O#+[Q!fO,5@^O#+cQ`O,5@lOOQ!0Lf1G/k1G/kO%[QlO,5@mO#+kQ`O'#IhOOQO-ErQ`O1G3qO$4rQlO1G3sO$8vQlO'#HrOOQ[1G3v1G3vO$9TQ`O'#HxO>wQ`O'#HzOOQ[1G3|1G3|O$9]QlO1G3|O9kQ!0LrO1G4SOOQ[1G4U1G4UOOQ!0Lb'#G^'#G^O9kQ!0LrO1G4WO9kQ!0LrO1G4YO$=dQ`O,5@`O!(yQlO,5;^O9ZQ`O,5;^O>wQ`O,5:VO!(yQlO,5:VO!BeQpO,5:VO$=iQ?MtO,5:VOOQO,5;^,5;^O$=sQpO'#IcO$>ZQ`O,5@_OOQ!0Lf1G/p1G/pO$>cQpO'#IiO$>mQ`O,5@nOOQ!0Lb1G0w1G0wO##hQpO,5:VOOQO'#Ie'#IeO$>uQpO,5:oOOQ!0Ln,5:o,5:oO#'cQ`O1G0XOOQ!0Lf1G0X1G0XO%[QlO1G0XOOQ!0Lf1G0r1G0rO>wQ`O1G0rO!BeQpO1G0rO!BmQMhO1G0rOOQ!0Lb1G5z1G5zO!BYQ!0LrO1G0[OOQO1G0k1G0kO%[QlO1G0kO$>|Q!0LrO1G0kO$?XQ!0LrO1G0kO!BeQpO1G0[OCuQpO1G0[O$?gQ!0LrO1G0kOOQO1G0[1G0[O$?{Q!0MxO1G0kPOOO-E|O$@iQ`O1G5xO$@qQ`O1G6WO$@yQ!fO1G6XO9ZQ`O,5?SO$ATQ!0MxO1G6UO%[QlO1G6UO$AeQ!0LrO1G6UO$AvQ`O1G6TO$AvQ`O1G6TO9ZQ`O1G6TO$BOQ`O,5?VO9ZQ`O,5?VOOQO,5?V,5?VO$BdQ`O,5?VO$)iQ`O,5?VOOQO-E^OOQ[,5>^,5>^O%[QlO'#HsO%=zQ`O'#HuOOQ[,5>d,5>dO9ZQ`O,5>dOOQ[,5>f,5>fOOQ[7+)h7+)hOOQ[7+)n7+)nOOQ[7+)r7+)rOOQ[7+)t7+)tO%>PQpO1G5zO%>kQ?MtO1G0xO%>uQ`O1G0xOOQO1G/q1G/qO%?QQ?MtO1G/qO>wQ`O1G/qO!(yQlO'#DkOOQO,5>},5>}OOQO-EwQ`O7+&^O!BeQpO7+&^OOQO7+%v7+%vO$?{Q!0MxO7+&VOOQO7+&V7+&VO%[QlO7+&VO%?[Q!0LrO7+&VO!BYQ!0LrO7+%vO!BeQpO7+%vO%?gQ!0LrO7+&VO%?uQ!0MxO7++pO%[QlO7++pO%@VQ`O7++oO%@VQ`O7++oOOQO1G4q1G4qO9ZQ`O1G4qO%@_Q`O1G4qOOQS7+%{7+%{O#'cQ`O<_OOQ[,5>a,5>aO&=aQ`O1G4OO9ZQ`O7+&dO!(yQlO7+&dOOQO7+%]7+%]O&=fQ?MtO1G6XO>wQ`O7+%]OOQ!0Lf<wQ`O<]Q`O<= ZOOQO7+*]7+*]O9ZQ`O7+*]OOQ[ANAjANAjO&>eQ!fOANAjO!&iQMhOANAjO#'cQ`OANAjO4UQ!fOANAjO&>lQ`OANAjO%[QlOANAjO&>tQ!0MzO7+'yO&AVQ!0MzO,5?_O&CbQ!0MzO,5?aO&EmQ!0MzO7+'{O&HOQ!fO1G4jO&HYQ?MtO7+&_O&J^Q?MvO,5=WO&LeQ?MvO,5=YO&LuQ?MvO,5=WO&MVQ?MvO,5=YO&MgQ?MvO,59sO' mQ?MvO,5wQ`O7+)jO'-]Q`O<|AN>|O%[QlOAN?]OOQO<PPPP!>XHwPPPPPPPPPP!AhP!BuPPHw!DWPHwPHwHwHwHwHwPHw!EjP!HtP!KzP!LO!LY!L^!L^P!HqP!Lb!LbP# hP# lHwPHw# r#$wCV@yP@yP@y@yP#&U@y@y#(h@y#+`@y#-l@y@y#.[#0p#0p#0u#1O#0p#1ZPP#0pP@y#1s@y#5r@y@y6aPPP#9wPPP#:b#:bP#:bP#:x#:bPP#;OP#:uP#:u#;c#:u#;}#R#>X#>c#>i#>s#>y#?Z#?a#@R#@e#@k#@q#AP#Af#CZ#Ci#Cp#E[#Ej#G[#Gj#Gp#Gv#G|#HW#H^#Hd#Hn#IQ#IWPPPPPPPPPPP#I^PPPPPPP#JR#MY#Nr#Ny$ RPPP$&mP$&v$)o$0Y$0]$0`$1_$1b$1i$1qP$1w$1zP$2h$2l$3d$4r$4w$5_PP$5d$5j$5n$5q$5u$5y$6u$7^$7u$7y$7|$8P$8V$8Y$8^$8bR!|RoqOXst!Z#d%l&p&r&s&u,n,s2S2VY!vQ'^-`1g5qQ%svQ%{yQ&S|Q&h!VS'U!e-WQ'd!iS'j!r!yU*h$|*X*lQ+l%|Q+y&UQ,_&bQ-^']Q-h'eQ-p'kQ0U*nQ1q,`R < TypeParamList const TypeDefinition extends ThisType this LiteralType ArithOp Number BooleanLiteral TemplateType InterpolationEnd Interpolation InterpolationStart NullType null VoidType void TypeofType typeof MemberExpression . PropertyName [ TemplateString Escape Interpolation super RegExp ] ArrayExpression Spread , } { ObjectExpression Property async get set PropertyDefinition Block : NewTarget new NewExpression ) ( ArgList UnaryExpression delete LogicOp BitOp YieldExpression yield AwaitExpression await ParenthesizedExpression ClassExpression class ClassBody MethodDeclaration Decorator @ MemberExpression PrivatePropertyName CallExpression TypeArgList CompareOp < declare Privacy static abstract override PrivatePropertyDefinition PropertyDeclaration readonly accessor Optional TypeAnnotation Equals StaticBlock FunctionExpression ArrowFunction ParamList ParamList ArrayPattern ObjectPattern PatternProperty Privacy readonly Arrow MemberExpression BinaryExpression ArithOp ArithOp ArithOp ArithOp BitOp CompareOp instanceof satisfies in CompareOp BitOp BitOp BitOp LogicOp LogicOp ConditionalExpression LogicOp LogicOp AssignmentExpression UpdateOp PostfixExpression CallExpression InstantiationExpression TaggedTemplateExpression DynamicImport import ImportMeta JSXElement JSXSelfCloseEndTag JSXSelfClosingTag JSXIdentifier JSXBuiltin JSXIdentifier JSXNamespacedName JSXMemberExpression JSXSpreadAttribute JSXAttribute JSXAttributeValue JSXEscape JSXEndTag JSXOpenTag JSXFragmentTag JSXText JSXEscape JSXStartCloseTag JSXCloseTag PrefixCast < ArrowFunction TypeParamList SequenceExpression InstantiationExpression KeyofType keyof UniqueType unique ImportType InferredType infer TypeName ParenthesizedType FunctionSignature ParamList NewSignature IndexedType TupleType Label ArrayType ReadonlyType ObjectType MethodType PropertyType IndexSignature PropertyDefinition CallSignature TypePredicate asserts is NewSignature new UnionType LogicOp IntersectionType LogicOp ConditionalType ParameterizedType ClassDeclaration abstract implements type VariableDeclaration let var using TypeAliasDeclaration InterfaceDeclaration interface EnumDeclaration enum EnumBody NamespaceDeclaration namespace module AmbientDeclaration declare GlobalDeclaration global ClassDeclaration ClassBody AmbientFunctionDeclaration ExportGroup VariableName VariableName ImportDeclaration ImportGroup ForStatement for ForSpec ForInSpec ForOfSpec of WhileStatement while WithStatement with DoStatement do IfStatement if else SwitchStatement switch SwitchBody CaseLabel case DefaultLabel TryStatement try CatchClause catch FinallyClause finally ReturnStatement return ThrowStatement throw BreakStatement break ContinueStatement continue DebuggerStatement debugger LabeledStatement ExpressionStatement SingleExpression SingleClassItem", + maxTerm: 378, + context: trackNewline, + nodeProps: [ + ["isolate", -8,5,6,14,35,37,49,51,53,""], + ["group", -26,9,17,19,66,206,210,214,215,217,220,223,233,235,241,243,245,247,250,256,262,264,266,268,270,272,273,"Statement",-34,13,14,30,33,34,40,49,52,53,55,60,68,70,74,78,80,82,83,108,109,118,119,135,138,140,141,142,143,144,146,147,166,168,170,"Expression",-23,29,31,35,39,41,43,172,174,176,177,179,180,181,183,184,185,187,188,189,200,202,204,205,"Type",-3,86,101,107,"ClassItem"], + ["openedBy", 23,"<",36,"InterpolationStart",54,"[",58,"{",71,"(",159,"JSXStartCloseTag"], + ["closedBy", -2,24,167,">",38,"InterpolationEnd",48,"]",59,"}",72,")",164,"JSXEndTag"] + ], + propSources: [jsHighlight], + skippedNodes: [0,5,6,276], + repeatNodeCount: 37, + tokenData: "$Fq07[R!bOX%ZXY+gYZ-yZ[+g[]%Z]^.c^p%Zpq+gqr/mrs3cst:_tuEruvJSvwLkwx! Yxy!'iyz!(sz{!)}{|!,q|}!.O}!O!,q!O!P!/Y!P!Q!9j!Q!R#:O!R![#<_![!]#I_!]!^#Jk!^!_#Ku!_!`$![!`!a$$v!a!b$*T!b!c$,r!c!}Er!}#O$-|#O#P$/W#P#Q$4o#Q#R$5y#R#SEr#S#T$7W#T#o$8b#o#p$x#r#s$@U#s$f%Z$f$g+g$g#BYEr#BY#BZ$A`#BZ$ISEr$IS$I_$A`$I_$I|Er$I|$I}$Dk$I}$JO$Dk$JO$JTEr$JT$JU$A`$JU$KVEr$KV$KW$A`$KW&FUEr&FU&FV$A`&FV;'SEr;'S;=`I|<%l?HTEr?HT?HU$A`?HUOEr(n%d_$h&j(Up(X!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z&j&hT$h&jO!^&c!_#o&c#p;'S&c;'S;=`&w<%lO&c&j&zP;=`<%l&c'|'U]$h&j(X!bOY&}YZ&cZw&}wx&cx!^&}!^!_'}!_#O&}#O#P&c#P#o&}#o#p'}#p;'S&};'S;=`(l<%lO&}!b(SU(X!bOY'}Zw'}x#O'}#P;'S'};'S;=`(f<%lO'}!b(iP;=`<%l'}'|(oP;=`<%l&}'[(y]$h&j(UpOY(rYZ&cZr(rrs&cs!^(r!^!_)r!_#O(r#O#P&c#P#o(r#o#p)r#p;'S(r;'S;=`*a<%lO(rp)wU(UpOY)rZr)rs#O)r#P;'S)r;'S;=`*Z<%lO)rp*^P;=`<%l)r'[*dP;=`<%l(r#S*nX(Up(X!bOY*gZr*grs'}sw*gwx)rx#O*g#P;'S*g;'S;=`+Z<%lO*g#S+^P;=`<%l*g(n+dP;=`<%l%Z07[+rq$h&j(Up(X!b'z0/lOX%ZXY+gYZ&cZ[+g[p%Zpq+gqr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p$f%Z$f$g+g$g#BY%Z#BY#BZ+g#BZ$IS%Z$IS$I_+g$I_$JT%Z$JT$JU+g$JU$KV%Z$KV$KW+g$KW&FU%Z&FU&FV+g&FV;'S%Z;'S;=`+a<%l?HT%Z?HT?HU+g?HUO%Z07[.ST(V#S$h&j'{0/lO!^&c!_#o&c#p;'S&c;'S;=`&w<%lO&c07[.n_$h&j(Up(X!b'{0/lOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z)3p/x`$h&j!n),Q(Up(X!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`0z!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(KW1V`#u(Ch$h&j(Up(X!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`2X!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(KW2d_#u(Ch$h&j(Up(X!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'At3l_(T':f$h&j(X!bOY4kYZ5qZr4krs7nsw4kwx5qx!^4k!^!_8p!_#O4k#O#P5q#P#o4k#o#p8p#p;'S4k;'S;=`:X<%lO4k(^4r_$h&j(X!bOY4kYZ5qZr4krs7nsw4kwx5qx!^4k!^!_8p!_#O4k#O#P5q#P#o4k#o#p8p#p;'S4k;'S;=`:X<%lO4k&z5vX$h&jOr5qrs6cs!^5q!^!_6y!_#o5q#o#p6y#p;'S5q;'S;=`7h<%lO5q&z6jT$c`$h&jO!^&c!_#o&c#p;'S&c;'S;=`&w<%lO&c`6|TOr6yrs7]s;'S6y;'S;=`7b<%lO6y`7bO$c``7eP;=`<%l6y&z7kP;=`<%l5q(^7w]$c`$h&j(X!bOY&}YZ&cZw&}wx&cx!^&}!^!_'}!_#O&}#O#P&c#P#o&}#o#p'}#p;'S&};'S;=`(l<%lO&}!r8uZ(X!bOY8pYZ6yZr8prs9hsw8pwx6yx#O8p#O#P6y#P;'S8p;'S;=`:R<%lO8p!r9oU$c`(X!bOY'}Zw'}x#O'}#P;'S'};'S;=`(f<%lO'}!r:UP;=`<%l8p(^:[P;=`<%l4k%9[:hh$h&j(Up(X!bOY%ZYZ&cZq%Zqr`#P#o`x!^=^!^!_?q!_#O=^#O#P>`#P#o=^#o#p?q#p;'S=^;'S;=`@h<%lO=^&n>gXWS$h&jOY>`YZ&cZ!^>`!^!_?S!_#o>`#o#p?S#p;'S>`;'S;=`?k<%lO>`S?XSWSOY?SZ;'S?S;'S;=`?e<%lO?SS?hP;=`<%l?S&n?nP;=`<%l>`!f?xWWS(X!bOY?qZw?qwx?Sx#O?q#O#P?S#P;'S?q;'S;=`@b<%lO?q!f@eP;=`<%l?q(Q@kP;=`<%l=^'`@w]WS$h&j(UpOY@nYZ&cZr@nrs>`s!^@n!^!_Ap!_#O@n#O#P>`#P#o@n#o#pAp#p;'S@n;'S;=`Bg<%lO@ntAwWWS(UpOYApZrAprs?Ss#OAp#O#P?S#P;'SAp;'S;=`Ba<%lOAptBdP;=`<%lAp'`BjP;=`<%l@n#WBvYWS(Up(X!bOYBmZrBmrs?qswBmwxApx#OBm#O#P?S#P;'SBm;'S;=`Cf<%lOBm#WCiP;=`<%lBm(rCoP;=`<%l^!Q^$h&j!V7`OY!=yYZ&cZ!P!=y!P!Q!>|!Q!^!=y!^!_!@c!_!}!=y!}#O!CW#O#P!Dy#P#o!=y#o#p!@c#p;'S!=y;'S;=`!Ek<%lO!=y|#X#Z&c#Z#[!>|#[#]&c#]#^!>|#^#a&c#a#b!>|#b#g&c#g#h!>|#h#i&c#i#j!>|#j#k!>|#k#m&c#m#n!>|#n#o&c#p;'S&c;'S;=`&w<%lO&c7`!@hX!V7`OY!@cZ!P!@c!P!Q!AT!Q!}!@c!}#O!Ar#O#P!Bq#P;'S!@c;'S;=`!CQ<%lO!@c7`!AYW!V7`#W#X!AT#Z#[!AT#]#^!AT#a#b!AT#g#h!AT#i#j!AT#j#k!AT#m#n!AT7`!AuVOY!ArZ#O!Ar#O#P!B[#P#Q!@c#Q;'S!Ar;'S;=`!Bk<%lO!Ar7`!B_SOY!ArZ;'S!Ar;'S;=`!Bk<%lO!Ar7`!BnP;=`<%l!Ar7`!BtSOY!@cZ;'S!@c;'S;=`!CQ<%lO!@c7`!CTP;=`<%l!@c^!Ezl$h&j(X!b!V7`OY&}YZ&cZw&}wx&cx!^&}!^!_'}!_#O&}#O#P&c#P#W&}#W#X!Eq#X#Z&}#Z#[!Eq#[#]&}#]#^!Eq#^#a&}#a#b!Eq#b#g&}#g#h!Eq#h#i&}#i#j!Eq#j#k!Eq#k#m&}#m#n!Eq#n#o&}#o#p'}#p;'S&};'S;=`(l<%lO&}8r!GyZ(X!b!V7`OY!GrZw!Grwx!@cx!P!Gr!P!Q!Hl!Q!}!Gr!}#O!JU#O#P!Bq#P;'S!Gr;'S;=`!J|<%lO!Gr8r!Hse(X!b!V7`OY'}Zw'}x#O'}#P#W'}#W#X!Hl#X#Z'}#Z#[!Hl#[#]'}#]#^!Hl#^#a'}#a#b!Hl#b#g'}#g#h!Hl#h#i'}#i#j!Hl#j#k!Hl#k#m'}#m#n!Hl#n;'S'};'S;=`(f<%lO'}8r!JZX(X!bOY!JUZw!JUwx!Arx#O!JU#O#P!B[#P#Q!Gr#Q;'S!JU;'S;=`!Jv<%lO!JU8r!JyP;=`<%l!JU8r!KPP;=`<%l!Gr>^!KZ^$h&j(X!bOY!KSYZ&cZw!KSwx!CWx!^!KS!^!_!JU!_#O!KS#O#P!DR#P#Q!^!LYP;=`<%l!KS>^!L`P;=`<%l!_#c#d#Bq#d#l%Z#l#m#Es#m#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#_#c#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#>j_$h&j(Up(X!bq'9tOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#?rd$h&j(Up(X!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q!R#AQ!R!S#AQ!S!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S#AQ#S#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#A]f$h&j(Up(X!bq'9tOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q!R#AQ!R!S#AQ!S!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S#AQ#S#b%Z#b#c#>_#c#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#Bzc$h&j(Up(X!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q!Y#DV!Y!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S#DV#S#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#Dbe$h&j(Up(X!bq'9tOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q!Y#DV!Y!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S#DV#S#b%Z#b#c#>_#c#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#E|g$h&j(Up(X!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q![#Ge![!^%Z!^!_*g!_!c%Z!c!i#Ge!i#O%Z#O#P&c#P#R%Z#R#S#Ge#S#T%Z#T#Z#Ge#Z#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#Gpi$h&j(Up(X!bq'9tOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q![#Ge![!^%Z!^!_*g!_!c%Z!c!i#Ge!i#O%Z#O#P&c#P#R%Z#R#S#Ge#S#T%Z#T#Z#Ge#Z#b%Z#b#c#>_#c#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z*)x#Il_!e$b$h&j#})Lv(Up(X!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z)[#Jv_al$h&j(Up(X!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z04f#LS^h#)`#P-v$?V_![(CdtBr$h&j(Up(X!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z?O$@a_!o7`$h&j(Up(X!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z07[$Aq|$h&j(Up(X!b'z0/l$[#t(R,2j(c$I[OX%ZXY+gYZ&cZ[+g[p%Zpq+gqr%Zrs&}st%ZtuEruw%Zwx(rx}%Z}!OGv!O!Q%Z!Q![Er![!^%Z!^!_*g!_!c%Z!c!}Er!}#O%Z#O#P&c#P#R%Z#R#SEr#S#T%Z#T#oEr#o#p*g#p$f%Z$f$g+g$g#BYEr#BY#BZ$A`#BZ$ISEr$IS$I_$A`$I_$JTEr$JT$JU$A`$JU$KVEr$KV$KW$A`$KW&FUEr&FU&FV$A`&FV;'SEr;'S;=`I|<%l?HTEr?HT?HU$A`?HUOEr07[$D|k$h&j(Up(X!b'{0/l$[#t(R,2j(c$I[OY%ZYZ&cZr%Zrs&}st%ZtuEruw%Zwx(rx}%Z}!OGv!O!Q%Z!Q![Er![!^%Z!^!_*g!_!c%Z!c!}Er!}#O%Z#O#P&c#P#R%Z#R#SEr#S#T%Z#T#oEr#o#p*g#p$g%Z$g;'SEr;'S;=`I|<%lOEr", + tokenizers: [noSemicolon, noSemicolonType, operatorToken, jsx, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, insertSemicolon, new LocalTokenGroup("$S~RRtu[#O#Pg#S#T#|~_P#o#pb~gOv~~jVO#i!P#i#j!U#j#l!P#l#m!q#m;'S!P;'S;=`#v<%lO!P~!UO!S~~!XS!Q![!e!c!i!e#T#Z!e#o#p#Z~!hR!Q![!q!c!i!q#T#Z!q~!tR!Q![!}!c!i!}#T#Z!}~#QR!Q![!P!c!i!P#T#Z!P~#^R!Q![#g!c!i#g#T#Z#g~#jS!Q![#g!c!i#g#T#Z#g#q#r!P~#yP;=`<%l!P~$RO(a~~", 141, 338), new LocalTokenGroup("j~RQYZXz{^~^O(O~~aP!P!Qd~iO(P~~", 25, 321)], + topRules: {"Script":[0,7],"SingleExpression":[1,274],"SingleClassItem":[2,275]}, + dialects: {jsx: 0, ts: 15091}, + dynamicPrecedences: {"78":1,"80":1,"92":1,"168":1,"198":1}, + specialized: [{term: 325, get: (value) => spec_identifier[value] || -1},{term: 341, get: (value) => spec_word[value] || -1},{term: 93, get: (value) => spec_LessThan[value] || -1}], + tokenPrec: 15116 +}); + +/** +A collection of JavaScript-related +[snippets](https://codemirror.net/6/docs/ref/#autocomplete.snippet). +*/ +const snippets = [ + /*@__PURE__*/snippetCompletion("function ${name}(${params}) {\n\t${}\n}", { + label: "function", + detail: "definition", + type: "keyword" + }), + /*@__PURE__*/snippetCompletion("for (let ${index} = 0; ${index} < ${bound}; ${index}++) {\n\t${}\n}", { + label: "for", + detail: "loop", + type: "keyword" + }), + /*@__PURE__*/snippetCompletion("for (let ${name} of ${collection}) {\n\t${}\n}", { + label: "for", + detail: "of loop", + type: "keyword" + }), + /*@__PURE__*/snippetCompletion("do {\n\t${}\n} while (${})", { + label: "do", + detail: "loop", + type: "keyword" + }), + /*@__PURE__*/snippetCompletion("while (${}) {\n\t${}\n}", { + label: "while", + detail: "loop", + type: "keyword" + }), + /*@__PURE__*/snippetCompletion("try {\n\t${}\n} catch (${error}) {\n\t${}\n}", { + label: "try", + detail: "/ catch block", + type: "keyword" + }), + /*@__PURE__*/snippetCompletion("if (${}) {\n\t${}\n}", { + label: "if", + detail: "block", + type: "keyword" + }), + /*@__PURE__*/snippetCompletion("if (${}) {\n\t${}\n} else {\n\t${}\n}", { + label: "if", + detail: "/ else block", + type: "keyword" + }), + /*@__PURE__*/snippetCompletion("class ${name} {\n\tconstructor(${params}) {\n\t\t${}\n\t}\n}", { + label: "class", + detail: "definition", + type: "keyword" + }), + /*@__PURE__*/snippetCompletion("import {${names}} from \"${module}\"\n${}", { + label: "import", + detail: "named", + type: "keyword" + }), + /*@__PURE__*/snippetCompletion("import ${name} from \"${module}\"\n${}", { + label: "import", + detail: "default", + type: "keyword" + }) +]; +/** +A collection of snippet completions for TypeScript. Includes the +JavaScript [snippets](https://codemirror.net/6/docs/ref/#lang-javascript.snippets). +*/ +const typescriptSnippets = /*@__PURE__*/snippets.concat([ + /*@__PURE__*/snippetCompletion("interface ${name} {\n\t${}\n}", { + label: "interface", + detail: "definition", + type: "keyword" + }), + /*@__PURE__*/snippetCompletion("type ${name} = ${type}", { + label: "type", + detail: "definition", + type: "keyword" + }), + /*@__PURE__*/snippetCompletion("enum ${name} {\n\t${}\n}", { + label: "enum", + detail: "definition", + type: "keyword" + }) +]); + +const cache = /*@__PURE__*/new NodeWeakMap(); +const ScopeNodes = /*@__PURE__*/new Set([ + "Script", "Block", + "FunctionExpression", "FunctionDeclaration", "ArrowFunction", "MethodDeclaration", + "ForStatement" +]); +function defID(type) { + return (node, def) => { + let id = node.node.getChild("VariableDefinition"); + if (id) + def(id, type); + return true; + }; +} +const functionContext = ["FunctionDeclaration"]; +const gatherCompletions = { + FunctionDeclaration: /*@__PURE__*/defID("function"), + ClassDeclaration: /*@__PURE__*/defID("class"), + ClassExpression: () => true, + EnumDeclaration: /*@__PURE__*/defID("constant"), + TypeAliasDeclaration: /*@__PURE__*/defID("type"), + NamespaceDeclaration: /*@__PURE__*/defID("namespace"), + VariableDefinition(node, def) { if (!node.matchContext(functionContext)) + def(node, "variable"); }, + TypeDefinition(node, def) { def(node, "type"); }, + __proto__: null +}; +function getScope(doc, node) { + let cached = cache.get(node); + if (cached) + return cached; + let completions = [], top = true; + function def(node, type) { + let name = doc.sliceString(node.from, node.to); + completions.push({ label: name, type }); + } + node.cursor(IterMode.IncludeAnonymous).iterate(node => { + if (top) { + top = false; + } + else if (node.name) { + let gather = gatherCompletions[node.name]; + if (gather && gather(node, def) || ScopeNodes.has(node.name)) + return false; + } + else if (node.to - node.from > 8192) { + // Allow caching for bigger internal nodes + for (let c of getScope(doc, node.node)) + completions.push(c); + return false; + } + }); + cache.set(node, completions); + return completions; +} +const Identifier = /^[\w$\xa1-\uffff][\w$\d\xa1-\uffff]*$/; +const dontComplete = [ + "TemplateString", "String", "RegExp", + "LineComment", "BlockComment", + "VariableDefinition", "TypeDefinition", "Label", + "PropertyDefinition", "PropertyName", + "PrivatePropertyDefinition", "PrivatePropertyName", + ".", "?." +]; +/** +Completion source that looks up locally defined names in +JavaScript code. +*/ +function localCompletionSource(context) { + let inner = syntaxTree(context.state).resolveInner(context.pos, -1); + if (dontComplete.indexOf(inner.name) > -1) + return null; + let isWord = inner.name == "VariableName" || + inner.to - inner.from < 20 && Identifier.test(context.state.sliceDoc(inner.from, inner.to)); + if (!isWord && !context.explicit) + return null; + let options = []; + for (let pos = inner; pos; pos = pos.parent) { + if (ScopeNodes.has(pos.name)) + options = options.concat(getScope(context.state.doc, pos)); + } + return { + options, + from: isWord ? inner.from : context.pos, + validFor: Identifier + }; +} + +/** +A language provider based on the [Lezer JavaScript +parser](https://github.com/lezer-parser/javascript), extended with +highlighting and indentation information. +*/ +const javascriptLanguage = /*@__PURE__*/LRLanguage.define({ + name: "javascript", + parser: /*@__PURE__*/parser.configure({ + props: [ + /*@__PURE__*/indentNodeProp.add({ + IfStatement: /*@__PURE__*/continuedIndent({ except: /^\s*({|else\b)/ }), + TryStatement: /*@__PURE__*/continuedIndent({ except: /^\s*({|catch\b|finally\b)/ }), + LabeledStatement: flatIndent, + SwitchBody: context => { + let after = context.textAfter, closed = /^\s*\}/.test(after), isCase = /^\s*(case|default)\b/.test(after); + return context.baseIndent + (closed ? 0 : isCase ? 1 : 2) * context.unit; + }, + Block: /*@__PURE__*/delimitedIndent({ closing: "}" }), + ArrowFunction: cx => cx.baseIndent + cx.unit, + "TemplateString BlockComment": () => null, + "Statement Property": /*@__PURE__*/continuedIndent({ except: /^{/ }), + JSXElement(context) { + let closed = /^\s*<\//.test(context.textAfter); + return context.lineIndent(context.node.from) + (closed ? 0 : context.unit); + }, + JSXEscape(context) { + let closed = /\s*\}/.test(context.textAfter); + return context.lineIndent(context.node.from) + (closed ? 0 : context.unit); + }, + "JSXOpenTag JSXSelfClosingTag"(context) { + return context.column(context.node.from) + context.unit; + } + }), + /*@__PURE__*/foldNodeProp.add({ + "Block ClassBody SwitchBody EnumBody ObjectExpression ArrayExpression ObjectType": foldInside, + BlockComment(tree) { return { from: tree.from + 2, to: tree.to - 2 }; } + }) + ] + }), + languageData: { + closeBrackets: { brackets: ["(", "[", "{", "'", '"', "`"] }, + commentTokens: { line: "//", block: { open: "/*", close: "*/" } }, + indentOnInput: /^\s*(?:case |default:|\{|\}|<\/)$/, + wordChars: "$" + } +}); +const jsxSublanguage = { + test: node => /^JSX/.test(node.name), + facet: /*@__PURE__*/defineLanguageFacet({ commentTokens: { block: { open: "{/*", close: "*/}" } } }) +}; +/** +A language provider for TypeScript. +*/ +const typescriptLanguage = /*@__PURE__*/javascriptLanguage.configure({ dialect: "ts" }, "typescript"); +/** +Language provider for JSX. +*/ +const jsxLanguage = /*@__PURE__*/javascriptLanguage.configure({ + dialect: "jsx", + props: [/*@__PURE__*/sublanguageProp.add(n => n.isTop ? [jsxSublanguage] : undefined)] +}); +/** +Language provider for JSX + TypeScript. +*/ +const tsxLanguage = /*@__PURE__*/javascriptLanguage.configure({ + dialect: "jsx ts", + props: [/*@__PURE__*/sublanguageProp.add(n => n.isTop ? [jsxSublanguage] : undefined)] +}, "typescript"); +let kwCompletion = (name) => ({ label: name, type: "keyword" }); +const keywords = /*@__PURE__*/"break case const continue default delete export extends false finally in instanceof let new return static super switch this throw true typeof var yield".split(" ").map(kwCompletion); +const typescriptKeywords = /*@__PURE__*/keywords.concat(/*@__PURE__*/["declare", "implements", "private", "protected", "public"].map(kwCompletion)); +/** +JavaScript support. Includes [snippet](https://codemirror.net/6/docs/ref/#lang-javascript.snippets) +and local variable completion. +*/ +function javascript(config = {}) { + let lang = config.jsx ? (config.typescript ? tsxLanguage : jsxLanguage) + : config.typescript ? typescriptLanguage : javascriptLanguage; + let completions = config.typescript ? typescriptSnippets.concat(typescriptKeywords) : snippets.concat(keywords); + return new LanguageSupport(lang, [ + javascriptLanguage.data.of({ + autocomplete: ifNotIn(dontComplete, completeFromList(completions)) + }), + javascriptLanguage.data.of({ + autocomplete: localCompletionSource + }), + config.jsx ? autoCloseTags : [], + ]); +} +function findOpenTag(node) { + for (;;) { + if (node.name == "JSXOpenTag" || node.name == "JSXSelfClosingTag" || node.name == "JSXFragmentTag") + return node; + if (node.name == "JSXEscape" || !node.parent) + return null; + node = node.parent; + } +} +function elementName(doc, tree, max = doc.length) { + for (let ch = tree === null || tree === void 0 ? void 0 : tree.firstChild; ch; ch = ch.nextSibling) { + if (ch.name == "JSXIdentifier" || ch.name == "JSXBuiltin" || ch.name == "JSXNamespacedName" || + ch.name == "JSXMemberExpression") + return doc.sliceString(ch.from, Math.min(ch.to, max)); + } + return ""; +} +const android = typeof navigator == "object" && /*@__PURE__*//Android\b/.test(navigator.userAgent); +/** +Extension that will automatically insert JSX close tags when a `>` or +`/` is typed. +*/ +const autoCloseTags = /*@__PURE__*/EditorView.inputHandler.of((view, from, to, text, defaultInsert) => { + if ((android ? view.composing : view.compositionStarted) || view.state.readOnly || + from != to || (text != ">" && text != "/") || + !javascriptLanguage.isActiveAt(view.state, from, -1)) + return false; + let base = defaultInsert(), { state } = base; + let closeTags = state.changeByRange(range => { + var _a; + let { head } = range, around = syntaxTree(state).resolveInner(head - 1, -1), name; + if (around.name == "JSXStartTag") + around = around.parent; + if (state.doc.sliceString(head - 1, head) != text || around.name == "JSXAttributeValue" && around.to > head) ; + else if (text == ">" && around.name == "JSXFragmentTag") { + return { range, changes: { from: head, insert: `` } }; + } + else if (text == "/" && around.name == "JSXStartCloseTag") { + let empty = around.parent, base = empty.parent; + if (base && empty.from == head - 2 && + ((name = elementName(state.doc, base.firstChild, head)) || ((_a = base.firstChild) === null || _a === void 0 ? void 0 : _a.name) == "JSXFragmentTag")) { + let insert = `${name}>`; + return { range: EditorSelection.cursor(head + insert.length, -1), changes: { from: head, insert } }; + } + } + else if (text == ">") { + let openTag = findOpenTag(around); + if (openTag && openTag.name == "JSXOpenTag" && + !/^\/?>|^<\//.test(state.doc.sliceString(head, head + 2)) && + (name = elementName(state.doc, openTag, head))) + return { range, changes: { from: head, insert: `` } }; + } + return { range }; + }); + if (closeTags.changes.empty) + return false; + view.dispatch([ + base, + state.update(closeTags, { userEvent: "input.complete", scrollIntoView: true }) + ]); + return true; +}); + +function switchState(source, setState, f) +{ + setState(f); + return f(source, setState); +} + +var lowerRE = /[a-z]/; +var upperRE = /[A-Z]/; +var innerRE = /[a-zA-Z0-9_]/; + +var digitRE = /[0-9]/; +var hexRE = /[0-9A-Fa-f]/; +var symbolRE = /[-&*+.\\/<>=?^|:]/; +var specialRE = /[(),[\]{}]/; +var spacesRE = /[ \v\f]/; // newlines are handled in tokenizer + +function normal() +{ + return function(source, setState) + { + if (source.eatWhile(spacesRE)) + { + return null; + } + + var char = source.next(); + + if (specialRE.test(char)) + { + return (char === '{' && source.eat('-')) + ? switchState(source, setState, chompMultiComment(1)) + : (char === '[' && source.match('glsl|')) + ? switchState(source, setState, chompGlsl) + : 'builtin'; + } + + if (char === '\'') + { + return switchState(source, setState, chompChar); + } + + if (char === '"') + { + return source.eat('"') + ? source.eat('"') + ? switchState(source, setState, chompMultiString) + : 'string' + : switchState(source, setState, chompSingleString); + } + + if (upperRE.test(char)) + { + source.eatWhile(innerRE); + return 'type'; + } + + if (lowerRE.test(char)) + { + var isDef = source.pos === 1; + source.eatWhile(innerRE); + return isDef ? "def" : "variable"; + } + + if (digitRE.test(char)) + { + if (char === '0') + { + if (source.eat(/[xX]/)) + { + source.eatWhile(hexRE); // should require at least 1 + return "number"; + } + } + else + { + source.eatWhile(digitRE); + } + if (source.eat('.')) + { + source.eatWhile(digitRE); // should require at least 1 + } + if (source.eat(/[eE]/)) + { + source.eat(/[-+]/); + source.eatWhile(digitRE); // should require at least 1 + } + return "number"; + } + + if (symbolRE.test(char)) + { + if (char === '-' && source.eat('-')) + { + source.skipToEnd(); + return "comment"; + } + source.eatWhile(symbolRE); + return "keyword"; + } + + if (char === '_') + { + return "keyword"; + } + + return "error"; + } +} + +function chompMultiComment(nest) +{ + if (nest == 0) + { + return normal(); + } + return function(source, setState) + { + while (!source.eol()) + { + var char = source.next(); + if (char == '{' && source.eat('-')) + { + ++nest; + } + else if (char == '-' && source.eat('}')) + { + --nest; + if (nest === 0) + { + setState(normal()); + return 'comment'; + } + } + } + setState(chompMultiComment(nest)); + return 'comment'; + } +} + +function chompMultiString(source, setState) +{ + while (!source.eol()) + { + var char = source.next(); + if (char === '"' && source.eat('"') && source.eat('"')) + { + setState(normal()); + return 'string'; + } + } + return 'string'; +} + +function chompSingleString(source, setState) +{ + while (source.skipTo('\\"')) { source.next(); source.next(); } + if (source.skipTo('"')) + { + source.next(); + setState(normal()); + return 'string'; + } + source.skipToEnd(); + setState(normal()); + return 'error'; +} + +function chompChar(source, setState) +{ + while (source.skipTo("\\'")) { source.next(); source.next(); } + if (source.skipTo("'")) + { + source.next(); + setState(normal()); + return 'string'; + } + source.skipToEnd(); + setState(normal()); + return 'error'; +} + +function chompGlsl(source, setState) +{ + while (!source.eol()) + { + var char = source.next(); + if (char === '|' && source.eat(']')) + { + setState(normal()); + return 'string'; + } + } + return 'string'; +} + +var wellKnownWords = { + case: 1, + of: 1, + as: 1, + if: 1, + then: 1, + else: 1, + let: 1, + in: 1, + type: 1, + alias: 1, + module: 1, + where: 1, + import: 1, + exposing: 1, + port: 1 +}; + +const elm = { + name: "elm", + startState: function () { return { f: normal() }; }, + copyState: function (s) { return { f: s.f }; }, + + token: function(stream, state) { + var type = state.f(stream, function(s) { state.f = s; }); + var word = stream.current(); + return (wellKnownWords.hasOwnProperty(word)) ? 'keyword' : type; + }, + + languageData: { + commentTokens: {line: "--"} + } +}; + //@ts-check // CodeMirror, copyright (c) by Marijn Haverbeke and others @@ -30667,15 +35588,6 @@ function runHistoryCommand(cm, revert) { } var keys = {}; class CodeMirror { - // -------------------------- - openDialog(template, callback, options) { - return openDialog(this, template, callback, options); - } - ; - openNotification(template, options) { - return openNotification(this, template, options); - } - ; constructor(cm6) { this.state = {}; this.marks = Object.create(null); @@ -30688,6 +35600,15 @@ class CodeMirror { this.onChange = this.onChange.bind(this); this.onSelectionChange = this.onSelectionChange.bind(this); } + // -------------------------- + openDialog(template, callback, options) { + return openDialog(this, template, callback, options); + } + ; + openNotification(template, options) { + return openNotification(this, template, options); + } + ; ; on(type, f) { on(this, type, f); } off(type, f) { off(this, type, f); } @@ -30756,7 +35677,12 @@ class CodeMirror { setSelections(p, primIndex) { var doc = this.cm6.state.doc; var ranges = p.map(x => { - return EditorSelection.range(indexFromPos(doc, x.anchor), indexFromPos(doc, x.head)); + var head = indexFromPos(doc, x.head); + var anchor = indexFromPos(doc, x.anchor); + // workaround for codemirror bug, see https://github.com/replit/codemirror-vim/issues/169 + if (head == anchor) + return EditorSelection.cursor(head, 1); + return EditorSelection.range(anchor, head); }); this.cm6.dispatch({ selection: EditorSelection.create(ranges, primIndex) @@ -30764,11 +35690,7 @@ class CodeMirror { } ; setSelection(anchor, head, options) { - var doc = this.cm6.state.doc; - var ranges = [EditorSelection.range(indexFromPos(doc, anchor), indexFromPos(doc, head))]; - this.cm6.dispatch({ - selection: EditorSelection.create(ranges, 0) - }); + this.setSelections([{ anchor, head }], 0); if (options && options.origin == '*mouse') { this.onBeforeEndOperation(); } @@ -31044,7 +35966,7 @@ class CodeMirror { const doc = cm6.state.doc; let pixels = unit == 'page' ? cm6.dom.clientHeight : 0; const startOffset = indexFromPos(doc, start); - let range = EditorSelection.range(startOffset, startOffset, goalColumn); + let range = EditorSelection.cursor(startOffset, 1, undefined, goalColumn); let count = Math.round(Math.abs(amount)); for (let i = 0; i < count; i++) { if (unit == 'page') { @@ -32212,7 +37134,15 @@ function getCM(view) { window.EditorView = EditorView; +const languages = { + 'python': python, + 'r': r, + 'javascript': javascript, + 'elm': () => StreamLanguage.define(elm), +}; + function codemirror_editor(language, options) { + const language_plugin = languages[language]; options = Object.assign({ extensions: [ @@ -32227,6 +37157,9 @@ function codemirror_editor(language, options) { }) ] }, options); + if(language_plugin) { + options.extensions.push(language_plugin()); + } let editor = new EditorView(options); @@ -32238,7 +37171,6 @@ class CodeEditorElement extends HTMLElement { constructor() { super(); - this.language = this.getAttribute('language') || ''; this.attachShadow({mode: 'open'}); } @@ -32249,9 +37181,10 @@ class CodeEditorElement extends HTMLElement { init_editor() { const code = this.textContent; const code_tag = this.shadowRoot; - + const language = this.getAttribute('language') || ''; + this.codeMirror = codemirror_editor( - this.language, + language, { doc: code, parent: code_tag, diff --git a/thinks/static/thinks/think-editor.css b/thinks/static/thinks/think-editor.css index 26e9476..05313da 100644 --- a/thinks/static/thinks/think-editor.css +++ b/thinks/static/thinks/think-editor.css @@ -9,6 +9,7 @@ --editor-size: 50%; --background: hsl(70,100%,95%); + --default-background: white; --color: black; --button-bg: #ddd; } @@ -16,6 +17,7 @@ @media (prefers-color-scheme: dark) { body { --background: hsl(70,100%,8%); + --default-background: black; --color: white; --button-bg: #333; } @@ -237,10 +239,9 @@ input { z-index: 1; } - & #code-editor { + & code-editor { + background: var(--default-background); display: block; - max-width: 50vw; - padding-bottom: 10em; } } diff --git a/thinks/static/thinks/think-editor.js b/thinks/static/thinks/think-editor.js index f30bb37..ed83263 100644 --- a/thinks/static/thinks/think-editor.js +++ b/thinks/static/thinks/think-editor.js @@ -6471,6 +6471,9 @@ var $author$project$App$ReloadPreview = {$: 'ReloadPreview'}; var $author$project$App$RunCommand = function (a) { return {$: 'RunCommand', a: a}; }; +var $author$project$App$RunningCommand = function (a) { + return {$: 'RunningCommand', a: a}; +}; var $author$project$App$SaveContent = function (a) { return {$: 'SaveContent', a: a}; }; @@ -6826,7 +6829,11 @@ var $author$project$App$update = F2( case 'RunCommand': var cmd = msg.a; return _Utils_Tuple2( - model, + _Utils_update( + model, + { + log: $author$project$App$RunningCommand(cmd) + }), $elm$http$Http$post( { body: $elm$http$Http$multipartBody( @@ -7185,6 +7192,27 @@ var $elm$html$Html$Attributes$action = function (uri) { }; var $elm$file$File$decoder = _File_decoder; var $elm$html$Html$details = _VirtualDom_node('details'); +var $elm$core$List$drop = F2( + function (n, list) { + drop: + while (true) { + if (n <= 0) { + return list; + } else { + if (!list.b) { + return list; + } else { + var x = list.a; + var xs = list.b; + var $temp$n = n - 1, + $temp$list = xs; + n = $temp$n; + list = $temp$list; + continue drop; + } + } + } + }); var $elm$html$Html$input = _VirtualDom_node('input'); var $elm$html$Html$Attributes$name = $elm$html$Html$Attributes$stringProperty('name'); var $author$project$App$form = F3( @@ -7207,6 +7235,18 @@ var $author$project$App$form = F3( _List_Nil) ]))); }); +var $elm$core$Dict$fromList = function (assocs) { + return A3( + $elm$core$List$foldl, + F2( + function (_v0, dict) { + var key = _v0.a; + var value = _v0.b; + return A3($elm$core$Dict$insert, key, value, dict); + }), + $elm$core$Dict$empty, + assocs); +}; var $elm$html$Html$nav = _VirtualDom_node('nav'); var $elm$virtual_dom$VirtualDom$MayPreventDefault = function (a) { return {$: 'MayPreventDefault', a: a}; @@ -7221,7 +7261,38 @@ var $elm$html$Html$Events$preventDefaultOn = F2( var $elm$html$Html$section = _VirtualDom_node('section'); var $elm$html$Html$span = _VirtualDom_node('span'); var $elm$html$Html$summary = _VirtualDom_node('summary'); +var $elm$core$Maybe$withDefault = F2( + function (_default, maybe) { + if (maybe.$ === 'Just') { + var value = maybe.a; + return value; + } else { + return _default; + } + }); var $author$project$App$editor_pane = function (model) { + var languages = $elm$core$Dict$fromList( + _List_fromArray( + [ + _Utils_Tuple2('js', 'javascript'), + _Utils_Tuple2('mjs', 'javascript'), + _Utils_Tuple2('py', 'python'), + _Utils_Tuple2('r', 'r'), + _Utils_Tuple2('R', 'r'), + _Utils_Tuple2('elm', 'elm') + ])); + var extension = A2( + $elm$core$Maybe$withDefault, + '', + $elm$core$List$head( + A2( + $elm$core$List$drop, + 1, + A2($elm$core$String$split, '.', model.file_path)))); + var language = A2( + $elm$core$Maybe$withDefault, + '', + A2($elm$core$Dict$get, extension, languages)); return A2( $elm$html$Html$section, _List_fromArray( @@ -7395,7 +7466,8 @@ var $author$project$App$editor_pane = function (model) { _List_fromArray( ['target', 'value']), $elm$json$Json$Decode$string))), - A2($elm$html$Html$Attributes$attribute, 'content', model.file_content) + A2($elm$html$Html$Attributes$attribute, 'content', model.file_content), + A2($elm$html$Html$Attributes$attribute, 'language', language) ]), _List_fromArray( [ @@ -7578,6 +7650,7 @@ var $author$project$App$header = function (model) { var $author$project$App$ToggleLog = function (a) { return {$: 'ToggleLog', a: a}; }; +var $elm$html$Html$code = _VirtualDom_node('code'); var $elm$html$Html$dd = _VirtualDom_node('dd'); var $elm$html$Html$div = _VirtualDom_node('div'); var $elm$html$Html$dl = _VirtualDom_node('dl'); @@ -7668,6 +7741,28 @@ var $author$project$App$log_pane = function (model) { ])) ])) ])); + case 'RunningCommand': + var cmd = _v0.a; + return A2( + $elm$html$Html$div, + _List_Nil, + _List_fromArray( + [ + A2( + $elm$html$Html$p, + _List_Nil, + _List_fromArray( + [ + $elm$html$Html$text('Running command '), + A2( + $elm$html$Html$code, + _List_Nil, + _List_fromArray( + [ + $elm$html$Html$text(cmd) + ])) + ])) + ])); case 'CommandResult': var stdout = _v0.a; var stderr = _v0.b; @@ -7792,27 +7887,6 @@ var $author$project$App$SetCommand = function (a) { }; var $author$project$App$ShowCommitModal = {$: 'ShowCommitModal'}; var $author$project$App$Upload = {$: 'Upload'}; -var $elm$core$List$drop = F2( - function (n, list) { - drop: - while (true) { - if (n <= 0) { - return list; - } else { - if (!list.b) { - return list; - } else { - var x = list.a; - var xs = list.b; - var $temp$n = n - 1, - $temp$list = xs; - n = $temp$n; - list = $temp$list; - continue drop; - } - } - } - }); var $elm$html$Html$li = _VirtualDom_node('li'); var $elm$core$Basics$not = _Basics_not; var $elm$html$Html$Events$onClick = function (msg) {