Fix bin/publish: copy docs.dist from project root

Fix bin/publish: use correct .env path for rspade_system
Fix bin/publish script: prevent grep exit code 1 from terminating script

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-10-21 02:08:33 +00:00
commit f6fac6c4bc
79758 changed files with 10547827 additions and 0 deletions

436
node_modules/codemirror/src/model/Doc.js generated vendored Executable file
View File

@@ -0,0 +1,436 @@
import CodeMirror from "../edit/CodeMirror.js"
import { docMethodOp } from "../display/operations.js"
import { Line } from "../line/line_data.js"
import { clipPos, clipPosArray, Pos } from "../line/pos.js"
import { visualLine } from "../line/spans.js"
import { getBetween, getLine, getLines, isLine, lineNo } from "../line/utils_line.js"
import { classTest } from "../util/dom.js"
import { splitLinesAuto } from "../util/feature_detection.js"
import { createObj, map, isEmpty, sel_dontScroll } from "../util/misc.js"
import { ensureCursorVisible, scrollToCoords } from "../display/scrolling.js"
import { changeLine, makeChange, makeChangeFromHistory, replaceRange } from "./changes.js"
import { computeReplacedSel } from "./change_measurement.js"
import { BranchChunk, LeafChunk } from "./chunk.js"
import { directionChanged, linkedDocs, updateDoc } from "./document_data.js"
import { copyHistoryArray, History } from "./history.js"
import { addLineWidget } from "./line_widget.js"
import { copySharedMarkers, detachSharedMarkers, findSharedMarkers, markText } from "./mark_text.js"
import { normalizeSelection, Range, simpleSelection } from "./selection.js"
import { extendSelection, extendSelections, setSelection, setSelectionReplaceHistory, setSimpleSelection } from "./selection_updates.js"
let nextDocId = 0
let Doc = function(text, mode, firstLine, lineSep, direction) {
if (!(this instanceof Doc)) return new Doc(text, mode, firstLine, lineSep, direction)
if (firstLine == null) firstLine = 0
BranchChunk.call(this, [new LeafChunk([new Line("", null)])])
this.first = firstLine
this.scrollTop = this.scrollLeft = 0
this.cantEdit = false
this.cleanGeneration = 1
this.modeFrontier = this.highlightFrontier = firstLine
let start = Pos(firstLine, 0)
this.sel = simpleSelection(start)
this.history = new History(null)
this.id = ++nextDocId
this.modeOption = mode
this.lineSep = lineSep
this.direction = (direction == "rtl") ? "rtl" : "ltr"
this.extend = false
if (typeof text == "string") text = this.splitLines(text)
updateDoc(this, {from: start, to: start, text: text})
setSelection(this, simpleSelection(start), sel_dontScroll)
}
Doc.prototype = createObj(BranchChunk.prototype, {
constructor: Doc,
// Iterate over the document. Supports two forms -- with only one
// argument, it calls that for each line in the document. With
// three, it iterates over the range given by the first two (with
// the second being non-inclusive).
iter: function(from, to, op) {
if (op) this.iterN(from - this.first, to - from, op)
else this.iterN(this.first, this.first + this.size, from)
},
// Non-public interface for adding and removing lines.
insert: function(at, lines) {
let height = 0
for (let i = 0; i < lines.length; ++i) height += lines[i].height
this.insertInner(at - this.first, lines, height)
},
remove: function(at, n) { this.removeInner(at - this.first, n) },
// From here, the methods are part of the public interface. Most
// are also available from CodeMirror (editor) instances.
getValue: function(lineSep) {
let lines = getLines(this, this.first, this.first + this.size)
if (lineSep === false) return lines
return lines.join(lineSep || this.lineSeparator())
},
setValue: docMethodOp(function(code) {
let top = Pos(this.first, 0), last = this.first + this.size - 1
makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
text: this.splitLines(code), origin: "setValue", full: true}, true)
if (this.cm) scrollToCoords(this.cm, 0, 0)
setSelection(this, simpleSelection(top), sel_dontScroll)
}),
replaceRange: function(code, from, to, origin) {
from = clipPos(this, from)
to = to ? clipPos(this, to) : from
replaceRange(this, code, from, to, origin)
},
getRange: function(from, to, lineSep) {
let lines = getBetween(this, clipPos(this, from), clipPos(this, to))
if (lineSep === false) return lines
if (lineSep === '') return lines.join('')
return lines.join(lineSep || this.lineSeparator())
},
getLine: function(line) {let l = this.getLineHandle(line); return l && l.text},
getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line)},
getLineNumber: function(line) {return lineNo(line)},
getLineHandleVisualStart: function(line) {
if (typeof line == "number") line = getLine(this, line)
return visualLine(line)
},
lineCount: function() {return this.size},
firstLine: function() {return this.first},
lastLine: function() {return this.first + this.size - 1},
clipPos: function(pos) {return clipPos(this, pos)},
getCursor: function(start) {
let range = this.sel.primary(), pos
if (start == null || start == "head") pos = range.head
else if (start == "anchor") pos = range.anchor
else if (start == "end" || start == "to" || start === false) pos = range.to()
else pos = range.from()
return pos
},
listSelections: function() { return this.sel.ranges },
somethingSelected: function() {return this.sel.somethingSelected()},
setCursor: docMethodOp(function(line, ch, options) {
setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options)
}),
setSelection: docMethodOp(function(anchor, head, options) {
setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options)
}),
extendSelection: docMethodOp(function(head, other, options) {
extendSelection(this, clipPos(this, head), other && clipPos(this, other), options)
}),
extendSelections: docMethodOp(function(heads, options) {
extendSelections(this, clipPosArray(this, heads), options)
}),
extendSelectionsBy: docMethodOp(function(f, options) {
let heads = map(this.sel.ranges, f)
extendSelections(this, clipPosArray(this, heads), options)
}),
setSelections: docMethodOp(function(ranges, primary, options) {
if (!ranges.length) return
let out = []
for (let i = 0; i < ranges.length; i++)
out[i] = new Range(clipPos(this, ranges[i].anchor),
clipPos(this, ranges[i].head || ranges[i].anchor))
if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex)
setSelection(this, normalizeSelection(this.cm, out, primary), options)
}),
addSelection: docMethodOp(function(anchor, head, options) {
let ranges = this.sel.ranges.slice(0)
ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)))
setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options)
}),
getSelection: function(lineSep) {
let ranges = this.sel.ranges, lines
for (let i = 0; i < ranges.length; i++) {
let sel = getBetween(this, ranges[i].from(), ranges[i].to())
lines = lines ? lines.concat(sel) : sel
}
if (lineSep === false) return lines
else return lines.join(lineSep || this.lineSeparator())
},
getSelections: function(lineSep) {
let parts = [], ranges = this.sel.ranges
for (let i = 0; i < ranges.length; i++) {
let sel = getBetween(this, ranges[i].from(), ranges[i].to())
if (lineSep !== false) sel = sel.join(lineSep || this.lineSeparator())
parts[i] = sel
}
return parts
},
replaceSelection: function(code, collapse, origin) {
let dup = []
for (let i = 0; i < this.sel.ranges.length; i++)
dup[i] = code
this.replaceSelections(dup, collapse, origin || "+input")
},
replaceSelections: docMethodOp(function(code, collapse, origin) {
let changes = [], sel = this.sel
for (let i = 0; i < sel.ranges.length; i++) {
let range = sel.ranges[i]
changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin}
}
let newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse)
for (let i = changes.length - 1; i >= 0; i--)
makeChange(this, changes[i])
if (newSel) setSelectionReplaceHistory(this, newSel)
else if (this.cm) ensureCursorVisible(this.cm)
}),
undo: docMethodOp(function() {makeChangeFromHistory(this, "undo")}),
redo: docMethodOp(function() {makeChangeFromHistory(this, "redo")}),
undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true)}),
redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true)}),
setExtending: function(val) {this.extend = val},
getExtending: function() {return this.extend},
historySize: function() {
let hist = this.history, done = 0, undone = 0
for (let i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done
for (let i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone
return {undo: done, redo: undone}
},
clearHistory: function() {
this.history = new History(this.history)
linkedDocs(this, doc => doc.history = this.history, true)
},
markClean: function() {
this.cleanGeneration = this.changeGeneration(true)
},
changeGeneration: function(forceSplit) {
if (forceSplit)
this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null
return this.history.generation
},
isClean: function (gen) {
return this.history.generation == (gen || this.cleanGeneration)
},
getHistory: function() {
return {done: copyHistoryArray(this.history.done),
undone: copyHistoryArray(this.history.undone)}
},
setHistory: function(histData) {
let hist = this.history = new History(this.history)
hist.done = copyHistoryArray(histData.done.slice(0), null, true)
hist.undone = copyHistoryArray(histData.undone.slice(0), null, true)
},
setGutterMarker: docMethodOp(function(line, gutterID, value) {
return changeLine(this, line, "gutter", line => {
let markers = line.gutterMarkers || (line.gutterMarkers = {})
markers[gutterID] = value
if (!value && isEmpty(markers)) line.gutterMarkers = null
return true
})
}),
clearGutter: docMethodOp(function(gutterID) {
this.iter(line => {
if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
changeLine(this, line, "gutter", () => {
line.gutterMarkers[gutterID] = null
if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null
return true
})
}
})
}),
lineInfo: function(line) {
let n
if (typeof line == "number") {
if (!isLine(this, line)) return null
n = line
line = getLine(this, line)
if (!line) return null
} else {
n = lineNo(line)
if (n == null) return null
}
return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
widgets: line.widgets}
},
addLineClass: docMethodOp(function(handle, where, cls) {
return changeLine(this, handle, where == "gutter" ? "gutter" : "class", line => {
let prop = where == "text" ? "textClass"
: where == "background" ? "bgClass"
: where == "gutter" ? "gutterClass" : "wrapClass"
if (!line[prop]) line[prop] = cls
else if (classTest(cls).test(line[prop])) return false
else line[prop] += " " + cls
return true
})
}),
removeLineClass: docMethodOp(function(handle, where, cls) {
return changeLine(this, handle, where == "gutter" ? "gutter" : "class", line => {
let prop = where == "text" ? "textClass"
: where == "background" ? "bgClass"
: where == "gutter" ? "gutterClass" : "wrapClass"
let cur = line[prop]
if (!cur) return false
else if (cls == null) line[prop] = null
else {
let found = cur.match(classTest(cls))
if (!found) return false
let end = found.index + found[0].length
line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null
}
return true
})
}),
addLineWidget: docMethodOp(function(handle, node, options) {
return addLineWidget(this, handle, node, options)
}),
removeLineWidget: function(widget) { widget.clear() },
markText: function(from, to, options) {
return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
},
setBookmark: function(pos, options) {
let realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
insertLeft: options && options.insertLeft,
clearWhenEmpty: false, shared: options && options.shared,
handleMouseEvents: options && options.handleMouseEvents}
pos = clipPos(this, pos)
return markText(this, pos, pos, realOpts, "bookmark")
},
findMarksAt: function(pos) {
pos = clipPos(this, pos)
let markers = [], spans = getLine(this, pos.line).markedSpans
if (spans) for (let i = 0; i < spans.length; ++i) {
let span = spans[i]
if ((span.from == null || span.from <= pos.ch) &&
(span.to == null || span.to >= pos.ch))
markers.push(span.marker.parent || span.marker)
}
return markers
},
findMarks: function(from, to, filter) {
from = clipPos(this, from); to = clipPos(this, to)
let found = [], lineNo = from.line
this.iter(from.line, to.line + 1, line => {
let spans = line.markedSpans
if (spans) for (let i = 0; i < spans.length; i++) {
let span = spans[i]
if (!(span.to != null && lineNo == from.line && from.ch >= span.to ||
span.from == null && lineNo != from.line ||
span.from != null && lineNo == to.line && span.from >= to.ch) &&
(!filter || filter(span.marker)))
found.push(span.marker.parent || span.marker)
}
++lineNo
})
return found
},
getAllMarks: function() {
let markers = []
this.iter(line => {
let sps = line.markedSpans
if (sps) for (let i = 0; i < sps.length; ++i)
if (sps[i].from != null) markers.push(sps[i].marker)
})
return markers
},
posFromIndex: function(off) {
let ch, lineNo = this.first, sepSize = this.lineSeparator().length
this.iter(line => {
let sz = line.text.length + sepSize
if (sz > off) { ch = off; return true }
off -= sz
++lineNo
})
return clipPos(this, Pos(lineNo, ch))
},
indexFromPos: function (coords) {
coords = clipPos(this, coords)
let index = coords.ch
if (coords.line < this.first || coords.ch < 0) return 0
let sepSize = this.lineSeparator().length
this.iter(this.first, coords.line, line => { // iter aborts when callback returns a truthy value
index += line.text.length + sepSize
})
return index
},
copy: function(copyHistory) {
let doc = new Doc(getLines(this, this.first, this.first + this.size),
this.modeOption, this.first, this.lineSep, this.direction)
doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft
doc.sel = this.sel
doc.extend = false
if (copyHistory) {
doc.history.undoDepth = this.history.undoDepth
doc.setHistory(this.getHistory())
}
return doc
},
linkedDoc: function(options) {
if (!options) options = {}
let from = this.first, to = this.first + this.size
if (options.from != null && options.from > from) from = options.from
if (options.to != null && options.to < to) to = options.to
let copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction)
if (options.sharedHist) copy.history = this.history
;(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist})
copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]
copySharedMarkers(copy, findSharedMarkers(this))
return copy
},
unlinkDoc: function(other) {
if (other instanceof CodeMirror) other = other.doc
if (this.linked) for (let i = 0; i < this.linked.length; ++i) {
let link = this.linked[i]
if (link.doc != other) continue
this.linked.splice(i, 1)
other.unlinkDoc(this)
detachSharedMarkers(findSharedMarkers(this))
break
}
// If the histories were shared, split them again
if (other.history == this.history) {
let splitIds = [other.id]
linkedDocs(other, doc => splitIds.push(doc.id), true)
other.history = new History(null)
other.history.done = copyHistoryArray(this.history.done, splitIds)
other.history.undone = copyHistoryArray(this.history.undone, splitIds)
}
},
iterLinkedDocs: function(f) {linkedDocs(this, f)},
getMode: function() {return this.mode},
getEditor: function() {return this.cm},
splitLines: function(str) {
if (this.lineSep) return str.split(this.lineSep)
return splitLinesAuto(str)
},
lineSeparator: function() { return this.lineSep || "\n" },
setDirection: docMethodOp(function (dir) {
if (dir != "rtl") dir = "ltr"
if (dir == this.direction) return
this.direction = dir
this.iter(line => line.order = null)
if (this.cm) directionChanged(this.cm)
})
})
// Public alias.
Doc.prototype.eachLine = Doc.prototype.iter
export default Doc

61
node_modules/codemirror/src/model/change_measurement.js generated vendored Executable file
View File

@@ -0,0 +1,61 @@
import { cmp, Pos } from "../line/pos.js"
import { lst } from "../util/misc.js"
import { normalizeSelection, Range, Selection } from "./selection.js"
// Compute the position of the end of a change (its 'to' property
// refers to the pre-change end).
export function changeEnd(change) {
if (!change.text) return change.to
return Pos(change.from.line + change.text.length - 1,
lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
}
// Adjust a position to refer to the post-change position of the
// same text, or the end of the change if the change covers it.
function adjustForChange(pos, change) {
if (cmp(pos, change.from) < 0) return pos
if (cmp(pos, change.to) <= 0) return changeEnd(change)
let line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch
if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch
return Pos(line, ch)
}
export function computeSelAfterChange(doc, change) {
let out = []
for (let i = 0; i < doc.sel.ranges.length; i++) {
let range = doc.sel.ranges[i]
out.push(new Range(adjustForChange(range.anchor, change),
adjustForChange(range.head, change)))
}
return normalizeSelection(doc.cm, out, doc.sel.primIndex)
}
function offsetPos(pos, old, nw) {
if (pos.line == old.line)
return Pos(nw.line, pos.ch - old.ch + nw.ch)
else
return Pos(nw.line + (pos.line - old.line), pos.ch)
}
// Used by replaceSelections to allow moving the selection to the
// start or around the replaced test. Hint may be "start" or "around".
export function computeReplacedSel(doc, changes, hint) {
let out = []
let oldPrev = Pos(doc.first, 0), newPrev = oldPrev
for (let i = 0; i < changes.length; i++) {
let change = changes[i]
let from = offsetPos(change.from, oldPrev, newPrev)
let to = offsetPos(changeEnd(change), oldPrev, newPrev)
oldPrev = change.to
newPrev = to
if (hint == "around") {
let range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0
out[i] = new Range(inv ? to : from, inv ? from : to)
} else {
out[i] = new Range(from, from)
}
}
return new Selection(out, doc.sel.primIndex)
}

339
node_modules/codemirror/src/model/changes.js generated vendored Executable file
View File

@@ -0,0 +1,339 @@
import { retreatFrontier } from "../line/highlight.js"
import { startWorker } from "../display/highlight_worker.js"
import { operation } from "../display/operations.js"
import { regChange, regLineChange } from "../display/view_tracking.js"
import { clipLine, clipPos, cmp, Pos } from "../line/pos.js"
import { sawReadOnlySpans } from "../line/saw_special_spans.js"
import { lineLength, removeReadOnlyRanges, stretchSpansOverChange, visualLine } from "../line/spans.js"
import { getBetween, getLine, lineNo } from "../line/utils_line.js"
import { estimateHeight } from "../measurement/position_measurement.js"
import { hasHandler, signal, signalCursorActivity } from "../util/event.js"
import { indexOf, lst, map, sel_dontScroll } from "../util/misc.js"
import { signalLater } from "../util/operation_group.js"
import { changeEnd, computeSelAfterChange } from "./change_measurement.js"
import { isWholeLineUpdate, linkedDocs, updateDoc } from "./document_data.js"
import { addChangeToHistory, historyChangeFromChange, mergeOldSpans, pushSelectionToHistory } from "./history.js"
import { Range, Selection } from "./selection.js"
import { setSelection, setSelectionNoUndo, skipAtomic } from "./selection_updates.js"
// UPDATING
// Allow "beforeChange" event handlers to influence a change
function filterChange(doc, change, update) {
let obj = {
canceled: false,
from: change.from,
to: change.to,
text: change.text,
origin: change.origin,
cancel: () => obj.canceled = true
}
if (update) obj.update = (from, to, text, origin) => {
if (from) obj.from = clipPos(doc, from)
if (to) obj.to = clipPos(doc, to)
if (text) obj.text = text
if (origin !== undefined) obj.origin = origin
}
signal(doc, "beforeChange", doc, obj)
if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj)
if (obj.canceled) {
if (doc.cm) doc.cm.curOp.updateInput = 2
return null
}
return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
}
// Apply a change to a document, and add it to the document's
// history, and propagating it to all linked documents.
export function makeChange(doc, change, ignoreReadOnly) {
if (doc.cm) {
if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly)
if (doc.cm.state.suppressEdits) return
}
if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
change = filterChange(doc, change, true)
if (!change) return
}
// Possibly split or suppress the update based on the presence
// of read-only spans in its range.
let split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to)
if (split) {
for (let i = split.length - 1; i >= 0; --i)
makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin})
} else {
makeChangeInner(doc, change)
}
}
function makeChangeInner(doc, change) {
if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return
let selAfter = computeSelAfterChange(doc, change)
addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN)
makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change))
let rebased = []
linkedDocs(doc, (doc, sharedHist) => {
if (!sharedHist && indexOf(rebased, doc.history) == -1) {
rebaseHist(doc.history, change)
rebased.push(doc.history)
}
makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change))
})
}
// Revert a change stored in a document's history.
export function makeChangeFromHistory(doc, type, allowSelectionOnly) {
let suppress = doc.cm && doc.cm.state.suppressEdits
if (suppress && !allowSelectionOnly) return
let hist = doc.history, event, selAfter = doc.sel
let source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done
// Verify that there is a useable event (so that ctrl-z won't
// needlessly clear selection events)
let i = 0
for (; i < source.length; i++) {
event = source[i]
if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
break
}
if (i == source.length) return
hist.lastOrigin = hist.lastSelOrigin = null
for (;;) {
event = source.pop()
if (event.ranges) {
pushSelectionToHistory(event, dest)
if (allowSelectionOnly && !event.equals(doc.sel)) {
setSelection(doc, event, {clearRedo: false})
return
}
selAfter = event
} else if (suppress) {
source.push(event)
return
} else break
}
// Build up a reverse change object to add to the opposite history
// stack (redo when undoing, and vice versa).
let antiChanges = []
pushSelectionToHistory(selAfter, dest)
dest.push({changes: antiChanges, generation: hist.generation})
hist.generation = event.generation || ++hist.maxGeneration
let filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")
for (let i = event.changes.length - 1; i >= 0; --i) {
let change = event.changes[i]
change.origin = type
if (filter && !filterChange(doc, change, false)) {
source.length = 0
return
}
antiChanges.push(historyChangeFromChange(doc, change))
let after = i ? computeSelAfterChange(doc, change) : lst(source)
makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change))
if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)})
let rebased = []
// Propagate to the linked documents
linkedDocs(doc, (doc, sharedHist) => {
if (!sharedHist && indexOf(rebased, doc.history) == -1) {
rebaseHist(doc.history, change)
rebased.push(doc.history)
}
makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change))
})
}
}
// Sub-views need their line numbers shifted when text is added
// above or below them in the parent document.
function shiftDoc(doc, distance) {
if (distance == 0) return
doc.first += distance
doc.sel = new Selection(map(doc.sel.ranges, range => new Range(
Pos(range.anchor.line + distance, range.anchor.ch),
Pos(range.head.line + distance, range.head.ch)
)), doc.sel.primIndex)
if (doc.cm) {
regChange(doc.cm, doc.first, doc.first - distance, distance)
for (let d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
regLineChange(doc.cm, l, "gutter")
}
}
// More lower-level change function, handling only a single document
// (not linked ones).
function makeChangeSingleDoc(doc, change, selAfter, spans) {
if (doc.cm && !doc.cm.curOp)
return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans)
if (change.to.line < doc.first) {
shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line))
return
}
if (change.from.line > doc.lastLine()) return
// Clip the change to the size of this doc
if (change.from.line < doc.first) {
let shift = change.text.length - 1 - (doc.first - change.from.line)
shiftDoc(doc, shift)
change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
text: [lst(change.text)], origin: change.origin}
}
let last = doc.lastLine()
if (change.to.line > last) {
change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
text: [change.text[0]], origin: change.origin}
}
change.removed = getBetween(doc, change.from, change.to)
if (!selAfter) selAfter = computeSelAfterChange(doc, change)
if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans)
else updateDoc(doc, change, spans)
setSelectionNoUndo(doc, selAfter, sel_dontScroll)
if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0)))
doc.cantEdit = false
}
// Handle the interaction of a change to a document with the editor
// that this document is part of.
function makeChangeSingleDocInEditor(cm, change, spans) {
let doc = cm.doc, display = cm.display, from = change.from, to = change.to
let recomputeMaxLength = false, checkWidthStart = from.line
if (!cm.options.lineWrapping) {
checkWidthStart = lineNo(visualLine(getLine(doc, from.line)))
doc.iter(checkWidthStart, to.line + 1, line => {
if (line == display.maxLine) {
recomputeMaxLength = true
return true
}
})
}
if (doc.sel.contains(change.from, change.to) > -1)
signalCursorActivity(cm)
updateDoc(doc, change, spans, estimateHeight(cm))
if (!cm.options.lineWrapping) {
doc.iter(checkWidthStart, from.line + change.text.length, line => {
let len = lineLength(line)
if (len > display.maxLineLength) {
display.maxLine = line
display.maxLineLength = len
display.maxLineChanged = true
recomputeMaxLength = false
}
})
if (recomputeMaxLength) cm.curOp.updateMaxLine = true
}
retreatFrontier(doc, from.line)
startWorker(cm, 400)
let lendiff = change.text.length - (to.line - from.line) - 1
// Remember that these lines changed, for updating the display
if (change.full)
regChange(cm)
else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
regLineChange(cm, from.line, "text")
else
regChange(cm, from.line, to.line + 1, lendiff)
let changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change")
if (changeHandler || changesHandler) {
let obj = {
from: from, to: to,
text: change.text,
removed: change.removed,
origin: change.origin
}
if (changeHandler) signalLater(cm, "change", cm, obj)
if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj)
}
cm.display.selForContextMenu = null
}
export function replaceRange(doc, code, from, to, origin) {
if (!to) to = from
if (cmp(to, from) < 0) [from, to] = [to, from]
if (typeof code == "string") code = doc.splitLines(code)
makeChange(doc, {from, to, text: code, origin})
}
// Rebasing/resetting history to deal with externally-sourced changes
function rebaseHistSelSingle(pos, from, to, diff) {
if (to < pos.line) {
pos.line += diff
} else if (from < pos.line) {
pos.line = from
pos.ch = 0
}
}
// Tries to rebase an array of history events given a change in the
// document. If the change touches the same lines as the event, the
// event, and everything 'behind' it, is discarded. If the change is
// before the event, the event's positions are updated. Uses a
// copy-on-write scheme for the positions, to avoid having to
// reallocate them all on every rebase, but also avoid problems with
// shared position objects being unsafely updated.
function rebaseHistArray(array, from, to, diff) {
for (let i = 0; i < array.length; ++i) {
let sub = array[i], ok = true
if (sub.ranges) {
if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true }
for (let j = 0; j < sub.ranges.length; j++) {
rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff)
rebaseHistSelSingle(sub.ranges[j].head, from, to, diff)
}
continue
}
for (let j = 0; j < sub.changes.length; ++j) {
let cur = sub.changes[j]
if (to < cur.from.line) {
cur.from = Pos(cur.from.line + diff, cur.from.ch)
cur.to = Pos(cur.to.line + diff, cur.to.ch)
} else if (from <= cur.to.line) {
ok = false
break
}
}
if (!ok) {
array.splice(0, i + 1)
i = 0
}
}
}
function rebaseHist(hist, change) {
let from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1
rebaseHistArray(hist.done, from, to, diff)
rebaseHistArray(hist.undone, from, to, diff)
}
// Utility for applying a change to a line by handle or number,
// returning the number and optionally registering the line as
// changed.
export function changeLine(doc, handle, changeType, op) {
let no = handle, line = handle
if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle))
else no = lineNo(handle)
if (no == null) return null
if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType)
return line
}

167
node_modules/codemirror/src/model/chunk.js generated vendored Executable file
View File

@@ -0,0 +1,167 @@
import { cleanUpLine } from "../line/line_data.js"
import { indexOf } from "../util/misc.js"
import { signalLater } from "../util/operation_group.js"
// The document is represented as a BTree consisting of leaves, with
// chunk of lines in them, and branches, with up to ten leaves or
// other branch nodes below them. The top node is always a branch
// node, and is the document object itself (meaning it has
// additional methods and properties).
//
// All nodes have parent links. The tree is used both to go from
// line numbers to line objects, and to go from objects to numbers.
// It also indexes by height, and is used to convert between height
// and line object, and to find the total height of the document.
//
// See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
export function LeafChunk(lines) {
this.lines = lines
this.parent = null
let height = 0
for (let i = 0; i < lines.length; ++i) {
lines[i].parent = this
height += lines[i].height
}
this.height = height
}
LeafChunk.prototype = {
chunkSize() { return this.lines.length },
// Remove the n lines at offset 'at'.
removeInner(at, n) {
for (let i = at, e = at + n; i < e; ++i) {
let line = this.lines[i]
this.height -= line.height
cleanUpLine(line)
signalLater(line, "delete")
}
this.lines.splice(at, n)
},
// Helper used to collapse a small branch into a single leaf.
collapse(lines) {
lines.push.apply(lines, this.lines)
},
// Insert the given array of lines at offset 'at', count them as
// having the given height.
insertInner(at, lines, height) {
this.height += height
this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at))
for (let i = 0; i < lines.length; ++i) lines[i].parent = this
},
// Used to iterate over a part of the tree.
iterN(at, n, op) {
for (let e = at + n; at < e; ++at)
if (op(this.lines[at])) return true
}
}
export function BranchChunk(children) {
this.children = children
let size = 0, height = 0
for (let i = 0; i < children.length; ++i) {
let ch = children[i]
size += ch.chunkSize(); height += ch.height
ch.parent = this
}
this.size = size
this.height = height
this.parent = null
}
BranchChunk.prototype = {
chunkSize() { return this.size },
removeInner(at, n) {
this.size -= n
for (let i = 0; i < this.children.length; ++i) {
let child = this.children[i], sz = child.chunkSize()
if (at < sz) {
let rm = Math.min(n, sz - at), oldHeight = child.height
child.removeInner(at, rm)
this.height -= oldHeight - child.height
if (sz == rm) { this.children.splice(i--, 1); child.parent = null }
if ((n -= rm) == 0) break
at = 0
} else at -= sz
}
// If the result is smaller than 25 lines, ensure that it is a
// single leaf node.
if (this.size - n < 25 &&
(this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
let lines = []
this.collapse(lines)
this.children = [new LeafChunk(lines)]
this.children[0].parent = this
}
},
collapse(lines) {
for (let i = 0; i < this.children.length; ++i) this.children[i].collapse(lines)
},
insertInner(at, lines, height) {
this.size += lines.length
this.height += height
for (let i = 0; i < this.children.length; ++i) {
let child = this.children[i], sz = child.chunkSize()
if (at <= sz) {
child.insertInner(at, lines, height)
if (child.lines && child.lines.length > 50) {
// To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
// Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
let remaining = child.lines.length % 25 + 25
for (let pos = remaining; pos < child.lines.length;) {
let leaf = new LeafChunk(child.lines.slice(pos, pos += 25))
child.height -= leaf.height
this.children.splice(++i, 0, leaf)
leaf.parent = this
}
child.lines = child.lines.slice(0, remaining)
this.maybeSpill()
}
break
}
at -= sz
}
},
// When a node has grown, check whether it should be split.
maybeSpill() {
if (this.children.length <= 10) return
let me = this
do {
let spilled = me.children.splice(me.children.length - 5, 5)
let sibling = new BranchChunk(spilled)
if (!me.parent) { // Become the parent node
let copy = new BranchChunk(me.children)
copy.parent = me
me.children = [copy, sibling]
me = copy
} else {
me.size -= sibling.size
me.height -= sibling.height
let myIndex = indexOf(me.parent.children, me)
me.parent.children.splice(myIndex + 1, 0, sibling)
}
sibling.parent = me.parent
} while (me.children.length > 10)
me.parent.maybeSpill()
},
iterN(at, n, op) {
for (let i = 0; i < this.children.length; ++i) {
let child = this.children[i], sz = child.chunkSize()
if (at < sz) {
let used = Math.min(n, sz - at)
if (child.iterN(at, used, op)) return true
if ((n -= used) == 0) break
at = 0
} else at -= sz
}
}
}

112
node_modules/codemirror/src/model/document_data.js generated vendored Executable file
View File

@@ -0,0 +1,112 @@
import { loadMode } from "../display/mode_state.js"
import { runInOp } from "../display/operations.js"
import { regChange } from "../display/view_tracking.js"
import { Line, updateLine } from "../line/line_data.js"
import { findMaxLine } from "../line/spans.js"
import { getLine } from "../line/utils_line.js"
import { estimateLineHeights } from "../measurement/position_measurement.js"
import { addClass, rmClass } from "../util/dom.js"
import { lst } from "../util/misc.js"
import { signalLater } from "../util/operation_group.js"
// DOCUMENT DATA STRUCTURE
// By default, updates that start and end at the beginning of a line
// are treated specially, in order to make the association of line
// widgets and marker elements with the text behave more intuitive.
export function isWholeLineUpdate(doc, change) {
return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
(!doc.cm || doc.cm.options.wholeLineUpdateBefore)
}
// Perform a change on the document data structure.
export function updateDoc(doc, change, markedSpans, estimateHeight) {
function spansFor(n) {return markedSpans ? markedSpans[n] : null}
function update(line, text, spans) {
updateLine(line, text, spans, estimateHeight)
signalLater(line, "change", line, change)
}
function linesFor(start, end) {
let result = []
for (let i = start; i < end; ++i)
result.push(new Line(text[i], spansFor(i), estimateHeight))
return result
}
let from = change.from, to = change.to, text = change.text
let firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line)
let lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line
// Adjust the line structure
if (change.full) {
doc.insert(0, linesFor(0, text.length))
doc.remove(text.length, doc.size - text.length)
} else if (isWholeLineUpdate(doc, change)) {
// This is a whole-line replace. Treated specially to make
// sure line objects move the way they are supposed to.
let added = linesFor(0, text.length - 1)
update(lastLine, lastLine.text, lastSpans)
if (nlines) doc.remove(from.line, nlines)
if (added.length) doc.insert(from.line, added)
} else if (firstLine == lastLine) {
if (text.length == 1) {
update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans)
} else {
let added = linesFor(1, text.length - 1)
added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight))
update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
doc.insert(from.line + 1, added)
}
} else if (text.length == 1) {
update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0))
doc.remove(from.line + 1, nlines)
} else {
update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans)
let added = linesFor(1, text.length - 1)
if (nlines > 1) doc.remove(from.line + 1, nlines - 1)
doc.insert(from.line + 1, added)
}
signalLater(doc, "change", doc, change)
}
// Call f for all linked documents.
export function linkedDocs(doc, f, sharedHistOnly) {
function propagate(doc, skip, sharedHist) {
if (doc.linked) for (let i = 0; i < doc.linked.length; ++i) {
let rel = doc.linked[i]
if (rel.doc == skip) continue
let shared = sharedHist && rel.sharedHist
if (sharedHistOnly && !shared) continue
f(rel.doc, shared)
propagate(rel.doc, doc, shared)
}
}
propagate(doc, null, true)
}
// Attach a document to an editor.
export function attachDoc(cm, doc) {
if (doc.cm) throw new Error("This document is already in use.")
cm.doc = doc
doc.cm = cm
estimateLineHeights(cm)
loadMode(cm)
setDirectionClass(cm)
cm.options.direction = doc.direction
if (!cm.options.lineWrapping) findMaxLine(cm)
cm.options.mode = doc.modeOption
regChange(cm)
}
function setDirectionClass(cm) {
;(cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl")
}
export function directionChanged(cm) {
runInOp(cm, () => {
setDirectionClass(cm)
regChange(cm)
})
}

228
node_modules/codemirror/src/model/history.js generated vendored Executable file
View File

@@ -0,0 +1,228 @@
import { cmp, copyPos } from "../line/pos.js"
import { stretchSpansOverChange } from "../line/spans.js"
import { getBetween } from "../line/utils_line.js"
import { signal } from "../util/event.js"
import { indexOf, lst } from "../util/misc.js"
import { changeEnd } from "./change_measurement.js"
import { linkedDocs } from "./document_data.js"
import { Selection } from "./selection.js"
export function History(prev) {
// Arrays of change events and selections. Doing something adds an
// event to done and clears undo. Undoing moves events from done
// to undone, redoing moves them in the other direction.
this.done = []; this.undone = []
this.undoDepth = prev ? prev.undoDepth : Infinity
// Used to track when changes can be merged into a single undo
// event
this.lastModTime = this.lastSelTime = 0
this.lastOp = this.lastSelOp = null
this.lastOrigin = this.lastSelOrigin = null
// Used by the isClean() method
this.generation = this.maxGeneration = prev ? prev.maxGeneration : 1
}
// Create a history change event from an updateDoc-style change
// object.
export function historyChangeFromChange(doc, change) {
let histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}
attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1)
linkedDocs(doc, doc => attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1), true)
return histChange
}
// Pop all selection events off the end of a history array. Stop at
// a change event.
function clearSelectionEvents(array) {
while (array.length) {
let last = lst(array)
if (last.ranges) array.pop()
else break
}
}
// Find the top change event in the history. Pop off selection
// events that are in the way.
function lastChangeEvent(hist, force) {
if (force) {
clearSelectionEvents(hist.done)
return lst(hist.done)
} else if (hist.done.length && !lst(hist.done).ranges) {
return lst(hist.done)
} else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
hist.done.pop()
return lst(hist.done)
}
}
// Register a change in the history. Merges changes that are within
// a single operation, or are close together with an origin that
// allows merging (starting with "+") into a single event.
export function addChangeToHistory(doc, change, selAfter, opId) {
let hist = doc.history
hist.undone.length = 0
let time = +new Date, cur
let last
if ((hist.lastOp == opId ||
hist.lastOrigin == change.origin && change.origin &&
((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||
change.origin.charAt(0) == "*")) &&
(cur = lastChangeEvent(hist, hist.lastOp == opId))) {
// Merge this change into the last event
last = lst(cur.changes)
if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
// Optimized case for simple insertion -- don't want to add
// new changesets for every character typed
last.to = changeEnd(change)
} else {
// Add new sub-event
cur.changes.push(historyChangeFromChange(doc, change))
}
} else {
// Can not be merged, start a new event.
let before = lst(hist.done)
if (!before || !before.ranges)
pushSelectionToHistory(doc.sel, hist.done)
cur = {changes: [historyChangeFromChange(doc, change)],
generation: hist.generation}
hist.done.push(cur)
while (hist.done.length > hist.undoDepth) {
hist.done.shift()
if (!hist.done[0].ranges) hist.done.shift()
}
}
hist.done.push(selAfter)
hist.generation = ++hist.maxGeneration
hist.lastModTime = hist.lastSelTime = time
hist.lastOp = hist.lastSelOp = opId
hist.lastOrigin = hist.lastSelOrigin = change.origin
if (!last) signal(doc, "historyAdded")
}
function selectionEventCanBeMerged(doc, origin, prev, sel) {
let ch = origin.charAt(0)
return ch == "*" ||
ch == "+" &&
prev.ranges.length == sel.ranges.length &&
prev.somethingSelected() == sel.somethingSelected() &&
new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
}
// Called whenever the selection changes, sets the new selection as
// the pending selection in the history, and pushes the old pending
// selection into the 'done' array when it was significantly
// different (in number of selected ranges, emptiness, or time).
export function addSelectionToHistory(doc, sel, opId, options) {
let hist = doc.history, origin = options && options.origin
// A new event is started when the previous origin does not match
// the current, or the origins don't allow matching. Origins
// starting with * are always merged, those starting with + are
// merged when similar and close together in time.
if (opId == hist.lastSelOp ||
(origin && hist.lastSelOrigin == origin &&
(hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
hist.done[hist.done.length - 1] = sel
else
pushSelectionToHistory(sel, hist.done)
hist.lastSelTime = +new Date
hist.lastSelOrigin = origin
hist.lastSelOp = opId
if (options && options.clearRedo !== false)
clearSelectionEvents(hist.undone)
}
export function pushSelectionToHistory(sel, dest) {
let top = lst(dest)
if (!(top && top.ranges && top.equals(sel)))
dest.push(sel)
}
// Used to store marked span information in the history.
function attachLocalSpans(doc, change, from, to) {
let existing = change["spans_" + doc.id], n = 0
doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), line => {
if (line.markedSpans)
(existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans
++n
})
}
// When un/re-doing restores text containing marked spans, those
// that have been explicitly cleared should not be restored.
function removeClearedSpans(spans) {
if (!spans) return null
let out
for (let i = 0; i < spans.length; ++i) {
if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i) }
else if (out) out.push(spans[i])
}
return !out ? spans : out.length ? out : null
}
// Retrieve and filter the old marked spans stored in a change event.
function getOldSpans(doc, change) {
let found = change["spans_" + doc.id]
if (!found) return null
let nw = []
for (let i = 0; i < change.text.length; ++i)
nw.push(removeClearedSpans(found[i]))
return nw
}
// Used for un/re-doing changes from the history. Combines the
// result of computing the existing spans with the set of spans that
// existed in the history (so that deleting around a span and then
// undoing brings back the span).
export function mergeOldSpans(doc, change) {
let old = getOldSpans(doc, change)
let stretched = stretchSpansOverChange(doc, change)
if (!old) return stretched
if (!stretched) return old
for (let i = 0; i < old.length; ++i) {
let oldCur = old[i], stretchCur = stretched[i]
if (oldCur && stretchCur) {
spans: for (let j = 0; j < stretchCur.length; ++j) {
let span = stretchCur[j]
for (let k = 0; k < oldCur.length; ++k)
if (oldCur[k].marker == span.marker) continue spans
oldCur.push(span)
}
} else if (stretchCur) {
old[i] = stretchCur
}
}
return old
}
// Used both to provide a JSON-safe object in .getHistory, and, when
// detaching a document, to split the history in two
export function copyHistoryArray(events, newGroup, instantiateSel) {
let copy = []
for (let i = 0; i < events.length; ++i) {
let event = events[i]
if (event.ranges) {
copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event)
continue
}
let changes = event.changes, newChanges = []
copy.push({changes: newChanges})
for (let j = 0; j < changes.length; ++j) {
let change = changes[j], m
newChanges.push({from: change.from, to: change.to, text: change.text})
if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
if (indexOf(newGroup, Number(m[1])) > -1) {
lst(newChanges)[prop] = change[prop]
delete change[prop]
}
}
}
}
return copy
}

78
node_modules/codemirror/src/model/line_widget.js generated vendored Executable file
View File

@@ -0,0 +1,78 @@
import { runInOp } from "../display/operations.js"
import { addToScrollTop } from "../display/scrolling.js"
import { regLineChange } from "../display/view_tracking.js"
import { heightAtLine, lineIsHidden } from "../line/spans.js"
import { lineNo, updateLineHeight } from "../line/utils_line.js"
import { widgetHeight } from "../measurement/widgets.js"
import { changeLine } from "./changes.js"
import { eventMixin } from "../util/event.js"
import { signalLater } from "../util/operation_group.js"
// Line widgets are block elements displayed above or below a line.
export class LineWidget {
constructor(doc, node, options) {
if (options) for (let opt in options) if (options.hasOwnProperty(opt))
this[opt] = options[opt]
this.doc = doc
this.node = node
}
clear() {
let cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line)
if (no == null || !ws) return
for (let i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1)
if (!ws.length) line.widgets = null
let height = widgetHeight(this)
updateLineHeight(line, Math.max(0, line.height - height))
if (cm) {
runInOp(cm, () => {
adjustScrollWhenAboveVisible(cm, line, -height)
regLineChange(cm, no, "widget")
})
signalLater(cm, "lineWidgetCleared", cm, this, no)
}
}
changed() {
let oldH = this.height, cm = this.doc.cm, line = this.line
this.height = null
let diff = widgetHeight(this) - oldH
if (!diff) return
if (!lineIsHidden(this.doc, line)) updateLineHeight(line, line.height + diff)
if (cm) {
runInOp(cm, () => {
cm.curOp.forceUpdate = true
adjustScrollWhenAboveVisible(cm, line, diff)
signalLater(cm, "lineWidgetChanged", cm, this, lineNo(line))
})
}
}
}
eventMixin(LineWidget)
function adjustScrollWhenAboveVisible(cm, line, diff) {
if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
addToScrollTop(cm, diff)
}
export function addLineWidget(doc, handle, node, options) {
let widget = new LineWidget(doc, node, options)
let cm = doc.cm
if (cm && widget.noHScroll) cm.display.alignWidgets = true
changeLine(doc, handle, "widget", line => {
let widgets = line.widgets || (line.widgets = [])
if (widget.insertAt == null) widgets.push(widget)
else widgets.splice(Math.min(widgets.length, Math.max(0, widget.insertAt)), 0, widget)
widget.line = line
if (cm && !lineIsHidden(doc, line)) {
let aboveVisible = heightAtLine(line) < doc.scrollTop
updateLineHeight(line, line.height + widgetHeight(widget))
if (aboveVisible) addToScrollTop(cm, widget.height)
cm.curOp.forceUpdate = true
}
return true
})
if (cm) signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle))
return widget
}

293
node_modules/codemirror/src/model/mark_text.js generated vendored Executable file
View File

@@ -0,0 +1,293 @@
import { eltP } from "../util/dom.js"
import { eventMixin, hasHandler, on } from "../util/event.js"
import { endOperation, operation, runInOp, startOperation } from "../display/operations.js"
import { clipPos, cmp, Pos } from "../line/pos.js"
import { lineNo, updateLineHeight } from "../line/utils_line.js"
import { clearLineMeasurementCacheFor, findViewForLine, textHeight } from "../measurement/position_measurement.js"
import { seeReadOnlySpans, seeCollapsedSpans } from "../line/saw_special_spans.js"
import { addMarkedSpan, conflictingCollapsedRange, getMarkedSpanFor, lineIsHidden, lineLength, MarkedSpan, removeMarkedSpan, visualLine } from "../line/spans.js"
import { copyObj, indexOf, lst } from "../util/misc.js"
import { signalLater } from "../util/operation_group.js"
import { widgetHeight } from "../measurement/widgets.js"
import { regChange, regLineChange } from "../display/view_tracking.js"
import { linkedDocs } from "./document_data.js"
import { addChangeToHistory } from "./history.js"
import { reCheckSelection } from "./selection_updates.js"
// TEXTMARKERS
// Created with markText and setBookmark methods. A TextMarker is a
// handle that can be used to clear or find a marked position in the
// document. Line objects hold arrays (markedSpans) containing
// {from, to, marker} object pointing to such marker objects, and
// indicating that such a marker is present on that line. Multiple
// lines may point to the same marker when it spans across lines.
// The spans will have null for their from/to properties when the
// marker continues beyond the start/end of the line. Markers have
// links back to the lines they currently touch.
// Collapsed markers have unique ids, in order to be able to order
// them, which is needed for uniquely determining an outer marker
// when they overlap (they may nest, but not partially overlap).
let nextMarkerId = 0
export class TextMarker {
constructor(doc, type) {
this.lines = []
this.type = type
this.doc = doc
this.id = ++nextMarkerId
}
// Clear the marker.
clear() {
if (this.explicitlyCleared) return
let cm = this.doc.cm, withOp = cm && !cm.curOp
if (withOp) startOperation(cm)
if (hasHandler(this, "clear")) {
let found = this.find()
if (found) signalLater(this, "clear", found.from, found.to)
}
let min = null, max = null
for (let i = 0; i < this.lines.length; ++i) {
let line = this.lines[i]
let span = getMarkedSpanFor(line.markedSpans, this)
if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text")
else if (cm) {
if (span.to != null) max = lineNo(line)
if (span.from != null) min = lineNo(line)
}
line.markedSpans = removeMarkedSpan(line.markedSpans, span)
if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)
updateLineHeight(line, textHeight(cm.display))
}
if (cm && this.collapsed && !cm.options.lineWrapping) for (let i = 0; i < this.lines.length; ++i) {
let visual = visualLine(this.lines[i]), len = lineLength(visual)
if (len > cm.display.maxLineLength) {
cm.display.maxLine = visual
cm.display.maxLineLength = len
cm.display.maxLineChanged = true
}
}
if (min != null && cm && this.collapsed) regChange(cm, min, max + 1)
this.lines.length = 0
this.explicitlyCleared = true
if (this.atomic && this.doc.cantEdit) {
this.doc.cantEdit = false
if (cm) reCheckSelection(cm.doc)
}
if (cm) signalLater(cm, "markerCleared", cm, this, min, max)
if (withOp) endOperation(cm)
if (this.parent) this.parent.clear()
}
// Find the position of the marker in the document. Returns a {from,
// to} object by default. Side can be passed to get a specific side
// -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
// Pos objects returned contain a line object, rather than a line
// number (used to prevent looking up the same line twice).
find(side, lineObj) {
if (side == null && this.type == "bookmark") side = 1
let from, to
for (let i = 0; i < this.lines.length; ++i) {
let line = this.lines[i]
let span = getMarkedSpanFor(line.markedSpans, this)
if (span.from != null) {
from = Pos(lineObj ? line : lineNo(line), span.from)
if (side == -1) return from
}
if (span.to != null) {
to = Pos(lineObj ? line : lineNo(line), span.to)
if (side == 1) return to
}
}
return from && {from: from, to: to}
}
// Signals that the marker's widget changed, and surrounding layout
// should be recomputed.
changed() {
let pos = this.find(-1, true), widget = this, cm = this.doc.cm
if (!pos || !cm) return
runInOp(cm, () => {
let line = pos.line, lineN = lineNo(pos.line)
let view = findViewForLine(cm, lineN)
if (view) {
clearLineMeasurementCacheFor(view)
cm.curOp.selectionChanged = cm.curOp.forceUpdate = true
}
cm.curOp.updateMaxLine = true
if (!lineIsHidden(widget.doc, line) && widget.height != null) {
let oldHeight = widget.height
widget.height = null
let dHeight = widgetHeight(widget) - oldHeight
if (dHeight)
updateLineHeight(line, line.height + dHeight)
}
signalLater(cm, "markerChanged", cm, this)
})
}
attachLine(line) {
if (!this.lines.length && this.doc.cm) {
let op = this.doc.cm.curOp
if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
(op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this)
}
this.lines.push(line)
}
detachLine(line) {
this.lines.splice(indexOf(this.lines, line), 1)
if (!this.lines.length && this.doc.cm) {
let op = this.doc.cm.curOp
;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this)
}
}
}
eventMixin(TextMarker)
// Create a marker, wire it up to the right lines, and
export function markText(doc, from, to, options, type) {
// Shared markers (across linked documents) are handled separately
// (markTextShared will call out to this again, once per
// document).
if (options && options.shared) return markTextShared(doc, from, to, options, type)
// Ensure we are in an operation.
if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type)
let marker = new TextMarker(doc, type), diff = cmp(from, to)
if (options) copyObj(options, marker, false)
// Don't connect empty markers unless clearWhenEmpty is false
if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
return marker
if (marker.replacedWith) {
// Showing up as a widget implies collapsed (widget replaces text)
marker.collapsed = true
marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget")
if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore-events", "true")
if (options.insertLeft) marker.widgetNode.insertLeft = true
}
if (marker.collapsed) {
if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
throw new Error("Inserting collapsed marker partially overlapping an existing one")
seeCollapsedSpans()
}
if (marker.addToHistory)
addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN)
let curLine = from.line, cm = doc.cm, updateMaxLine
doc.iter(curLine, to.line + 1, line => {
if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
updateMaxLine = true
if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0)
addMarkedSpan(line, new MarkedSpan(marker,
curLine == from.line ? from.ch : null,
curLine == to.line ? to.ch : null), doc.cm && doc.cm.curOp)
++curLine
})
// lineIsHidden depends on the presence of the spans, so needs a second pass
if (marker.collapsed) doc.iter(from.line, to.line + 1, line => {
if (lineIsHidden(doc, line)) updateLineHeight(line, 0)
})
if (marker.clearOnEnter) on(marker, "beforeCursorEnter", () => marker.clear())
if (marker.readOnly) {
seeReadOnlySpans()
if (doc.history.done.length || doc.history.undone.length)
doc.clearHistory()
}
if (marker.collapsed) {
marker.id = ++nextMarkerId
marker.atomic = true
}
if (cm) {
// Sync editor state
if (updateMaxLine) cm.curOp.updateMaxLine = true
if (marker.collapsed)
regChange(cm, from.line, to.line + 1)
else if (marker.className || marker.startStyle || marker.endStyle || marker.css ||
marker.attributes || marker.title)
for (let i = from.line; i <= to.line; i++) regLineChange(cm, i, "text")
if (marker.atomic) reCheckSelection(cm.doc)
signalLater(cm, "markerAdded", cm, marker)
}
return marker
}
// SHARED TEXTMARKERS
// A shared marker spans multiple linked documents. It is
// implemented as a meta-marker-object controlling multiple normal
// markers.
export class SharedTextMarker {
constructor(markers, primary) {
this.markers = markers
this.primary = primary
for (let i = 0; i < markers.length; ++i)
markers[i].parent = this
}
clear() {
if (this.explicitlyCleared) return
this.explicitlyCleared = true
for (let i = 0; i < this.markers.length; ++i)
this.markers[i].clear()
signalLater(this, "clear")
}
find(side, lineObj) {
return this.primary.find(side, lineObj)
}
}
eventMixin(SharedTextMarker)
function markTextShared(doc, from, to, options, type) {
options = copyObj(options)
options.shared = false
let markers = [markText(doc, from, to, options, type)], primary = markers[0]
let widget = options.widgetNode
linkedDocs(doc, doc => {
if (widget) options.widgetNode = widget.cloneNode(true)
markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type))
for (let i = 0; i < doc.linked.length; ++i)
if (doc.linked[i].isParent) return
primary = lst(markers)
})
return new SharedTextMarker(markers, primary)
}
export function findSharedMarkers(doc) {
return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), m => m.parent)
}
export function copySharedMarkers(doc, markers) {
for (let i = 0; i < markers.length; i++) {
let marker = markers[i], pos = marker.find()
let mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to)
if (cmp(mFrom, mTo)) {
let subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type)
marker.markers.push(subMark)
subMark.parent = marker
}
}
}
export function detachSharedMarkers(markers) {
for (let i = 0; i < markers.length; i++) {
let marker = markers[i], linked = [marker.primary.doc]
linkedDocs(marker.primary.doc, d => linked.push(d))
for (let j = 0; j < marker.markers.length; j++) {
let subMarker = marker.markers[j]
if (indexOf(linked, subMarker.doc) == -1) {
subMarker.parent = null
marker.markers.splice(j--, 1)
}
}
}
}

84
node_modules/codemirror/src/model/selection.js generated vendored Executable file
View File

@@ -0,0 +1,84 @@
import { cmp, copyPos, equalCursorPos, maxPos, minPos } from "../line/pos.js"
import { indexOf } from "../util/misc.js"
// Selection objects are immutable. A new one is created every time
// the selection changes. A selection is one or more non-overlapping
// (and non-touching) ranges, sorted, and an integer that indicates
// which one is the primary selection (the one that's scrolled into
// view, that getCursor returns, etc).
export class Selection {
constructor(ranges, primIndex) {
this.ranges = ranges
this.primIndex = primIndex
}
primary() { return this.ranges[this.primIndex] }
equals(other) {
if (other == this) return true
if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false
for (let i = 0; i < this.ranges.length; i++) {
let here = this.ranges[i], there = other.ranges[i]
if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) return false
}
return true
}
deepCopy() {
let out = []
for (let i = 0; i < this.ranges.length; i++)
out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head))
return new Selection(out, this.primIndex)
}
somethingSelected() {
for (let i = 0; i < this.ranges.length; i++)
if (!this.ranges[i].empty()) return true
return false
}
contains(pos, end) {
if (!end) end = pos
for (let i = 0; i < this.ranges.length; i++) {
let range = this.ranges[i]
if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
return i
}
return -1
}
}
export class Range {
constructor(anchor, head) {
this.anchor = anchor; this.head = head
}
from() { return minPos(this.anchor, this.head) }
to() { return maxPos(this.anchor, this.head) }
empty() { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch }
}
// Take an unsorted, potentially overlapping set of ranges, and
// build a selection out of it. 'Consumes' ranges array (modifying
// it).
export function normalizeSelection(cm, ranges, primIndex) {
let mayTouch = cm && cm.options.selectionsMayTouch
let prim = ranges[primIndex]
ranges.sort((a, b) => cmp(a.from(), b.from()))
primIndex = indexOf(ranges, prim)
for (let i = 1; i < ranges.length; i++) {
let cur = ranges[i], prev = ranges[i - 1]
let diff = cmp(prev.to(), cur.from())
if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {
let from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to())
let inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head
if (i <= primIndex) --primIndex
ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to))
}
}
return new Selection(ranges, primIndex)
}
export function simpleSelection(anchor, head) {
return new Selection([new Range(anchor, head || anchor)], 0)
}

216
node_modules/codemirror/src/model/selection_updates.js generated vendored Executable file
View File

@@ -0,0 +1,216 @@
import { signalLater } from "../util/operation_group.js"
import { ensureCursorVisible } from "../display/scrolling.js"
import { clipPos, cmp, Pos } from "../line/pos.js"
import { getLine } from "../line/utils_line.js"
import { hasHandler, signal, signalCursorActivity } from "../util/event.js"
import { lst, sel_dontScroll } from "../util/misc.js"
import { addSelectionToHistory } from "./history.js"
import { normalizeSelection, Range, Selection, simpleSelection } from "./selection.js"
// The 'scroll' parameter given to many of these indicated whether
// the new cursor position should be scrolled into view after
// modifying the selection.
// If shift is held or the extend flag is set, extends a range to
// include a given position (and optionally a second position).
// Otherwise, simply returns the range between the given positions.
// Used for cursor motion and such.
export function extendRange(range, head, other, extend) {
if (extend) {
let anchor = range.anchor
if (other) {
let posBefore = cmp(head, anchor) < 0
if (posBefore != (cmp(other, anchor) < 0)) {
anchor = head
head = other
} else if (posBefore != (cmp(head, other) < 0)) {
head = other
}
}
return new Range(anchor, head)
} else {
return new Range(other || head, head)
}
}
// Extend the primary selection range, discard the rest.
export function extendSelection(doc, head, other, options, extend) {
if (extend == null) extend = doc.cm && (doc.cm.display.shift || doc.extend)
setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options)
}
// Extend all selections (pos is an array of selections with length
// equal the number of selections)
export function extendSelections(doc, heads, options) {
let out = []
let extend = doc.cm && (doc.cm.display.shift || doc.extend)
for (let i = 0; i < doc.sel.ranges.length; i++)
out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend)
let newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex)
setSelection(doc, newSel, options)
}
// Updates a single range in the selection.
export function replaceOneSelection(doc, i, range, options) {
let ranges = doc.sel.ranges.slice(0)
ranges[i] = range
setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options)
}
// Reset the selection to a single range.
export function setSimpleSelection(doc, anchor, head, options) {
setSelection(doc, simpleSelection(anchor, head), options)
}
// Give beforeSelectionChange handlers a change to influence a
// selection update.
function filterSelectionChange(doc, sel, options) {
let obj = {
ranges: sel.ranges,
update: function(ranges) {
this.ranges = []
for (let i = 0; i < ranges.length; i++)
this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
clipPos(doc, ranges[i].head))
},
origin: options && options.origin
}
signal(doc, "beforeSelectionChange", doc, obj)
if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj)
if (obj.ranges != sel.ranges) return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1)
else return sel
}
export function setSelectionReplaceHistory(doc, sel, options) {
let done = doc.history.done, last = lst(done)
if (last && last.ranges) {
done[done.length - 1] = sel
setSelectionNoUndo(doc, sel, options)
} else {
setSelection(doc, sel, options)
}
}
// Set a new selection.
export function setSelection(doc, sel, options) {
setSelectionNoUndo(doc, sel, options)
addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options)
}
export function setSelectionNoUndo(doc, sel, options) {
if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
sel = filterSelectionChange(doc, sel, options)
let bias = options && options.bias ||
(cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1)
setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true))
if (!(options && options.scroll === false) && doc.cm && doc.cm.getOption("readOnly") != "nocursor")
ensureCursorVisible(doc.cm)
}
function setSelectionInner(doc, sel) {
if (sel.equals(doc.sel)) return
doc.sel = sel
if (doc.cm) {
doc.cm.curOp.updateInput = 1
doc.cm.curOp.selectionChanged = true
signalCursorActivity(doc.cm)
}
signalLater(doc, "cursorActivity", doc)
}
// Verify that the selection does not partially select any atomic
// marked ranges.
export function reCheckSelection(doc) {
setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false))
}
// Return a selection that does not partially select any atomic
// ranges.
function skipAtomicInSelection(doc, sel, bias, mayClear) {
let out
for (let i = 0; i < sel.ranges.length; i++) {
let range = sel.ranges[i]
let old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]
let newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear)
let newHead = range.head == range.anchor ? newAnchor : skipAtomic(doc, range.head, old && old.head, bias, mayClear)
if (out || newAnchor != range.anchor || newHead != range.head) {
if (!out) out = sel.ranges.slice(0, i)
out[i] = new Range(newAnchor, newHead)
}
}
return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel
}
function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
let line = getLine(doc, pos.line)
if (line.markedSpans) for (let i = 0; i < line.markedSpans.length; ++i) {
let sp = line.markedSpans[i], m = sp.marker
// Determine if we should prevent the cursor being placed to the left/right of an atomic marker
// Historically this was determined using the inclusiveLeft/Right option, but the new way to control it
// is with selectLeft/Right
let preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft
let preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight
if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
(sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
if (mayClear) {
signal(m, "beforeCursorEnter")
if (m.explicitlyCleared) {
if (!line.markedSpans) break
else {--i; continue}
}
}
if (!m.atomic) continue
if (oldPos) {
let near = m.find(dir < 0 ? 1 : -1), diff
if (dir < 0 ? preventCursorRight : preventCursorLeft)
near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null)
if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
return skipAtomicInner(doc, near, pos, dir, mayClear)
}
let far = m.find(dir < 0 ? -1 : 1)
if (dir < 0 ? preventCursorLeft : preventCursorRight)
far = movePos(doc, far, dir, far.line == pos.line ? line : null)
return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
}
}
return pos
}
// Ensure a given position is not inside an atomic range.
export function skipAtomic(doc, pos, oldPos, bias, mayClear) {
let dir = bias || 1
let found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
(!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
(!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true))
if (!found) {
doc.cantEdit = true
return Pos(doc.first, 0)
}
return found
}
function movePos(doc, pos, dir, line) {
if (dir < 0 && pos.ch == 0) {
if (pos.line > doc.first) return clipPos(doc, Pos(pos.line - 1))
else return null
} else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
if (pos.line < doc.first + doc.size - 1) return Pos(pos.line + 1, 0)
else return null
} else {
return new Pos(pos.line, pos.ch + dir)
}
}
export function selectAll(cm) {
cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll)
}