File modules/editor/codemirror/src/model/Doc.min.js
Last commit: Tue May 22 22:39:53 2018 +0200 Jan Dankert Fix für PHP 7.2: 'Object' darf nun nicht mehr als Klassennamen verwendet werden. AUCH NICHT IN EINEM NAMESPACE! WTF, wozu habe ich das in einen verfickten Namespace gepackt? Wozu soll der sonst da sein??? Amateure. Daher nun notgedrungen unbenannt in 'BaseObject'.
1 import CodeMirror from "../edit/CodeMirror.js" 2 import { docMethodOp } from "../display/operations.js" 3 import { Line } from "../line/line_data.js" 4 import { clipPos, clipPosArray, Pos } from "../line/pos.js" 5 import { visualLine } from "../line/spans.js" 6 import { getBetween, getLine, getLines, isLine, lineNo } from "../line/utils_line.js" 7 import { classTest } from "../util/dom.js" 8 import { splitLinesAuto } from "../util/feature_detection.js" 9 import { createObj, map, isEmpty, sel_dontScroll } from "../util/misc.js" 10 import { ensureCursorVisible, scrollToCoords } from "../display/scrolling.js" 11 12 import { changeLine, makeChange, makeChangeFromHistory, replaceRange } from "./changes.js" 13 import { computeReplacedSel } from "./change_measurement.js" 14 import { BranchChunk, LeafChunk } from "./chunk.js" 15 import { directionChanged, linkedDocs, updateDoc } from "./document_data.js" 16 import { copyHistoryArray, History } from "./history.js" 17 import { addLineWidget } from "./line_widget.js" 18 import { copySharedMarkers, detachSharedMarkers, findSharedMarkers, markText } from "./mark_text.js" 19 import { normalizeSelection, Range, simpleSelection } from "./selection.js" 20 import { extendSelection, extendSelections, setSelection, setSelectionReplaceHistory, setSimpleSelection } from "./selection_updates.js" 21 22 let nextDocId = 0 23 let Doc = function(text, mode, firstLine, lineSep, direction) { 24 if (!(this instanceof Doc)) return new Doc(text, mode, firstLine, lineSep, direction) 25 if (firstLine == null) firstLine = 0 26 27 BranchChunk.call(this, [new LeafChunk([new Line("", null)])]) 28 this.first = firstLine 29 this.scrollTop = this.scrollLeft = 0 30 this.cantEdit = false 31 this.cleanGeneration = 1 32 this.modeFrontier = this.highlightFrontier = firstLine 33 let start = Pos(firstLine, 0) 34 this.sel = simpleSelection(start) 35 this.history = new History(null) 36 this.id = ++nextDocId 37 this.modeOption = mode 38 this.lineSep = lineSep 39 this.direction = (direction == "rtl") ? "rtl" : "ltr" 40 this.extend = false 41 42 if (typeof text == "string") text = this.splitLines(text) 43 updateDoc(this, {from: start, to: start, text: text}) 44 setSelection(this, simpleSelection(start), sel_dontScroll) 45 } 46 47 Doc.prototype = createObj(BranchChunk.prototype, { 48 constructor: Doc, 49 // Iterate over the document. Supports two forms -- with only one 50 // argument, it calls that for each line in the document. With 51 // three, it iterates over the range given by the first two (with 52 // the second being non-inclusive). 53 iter: function(from, to, op) { 54 if (op) this.iterN(from - this.first, to - from, op) 55 else this.iterN(this.first, this.first + this.size, from) 56 }, 57 58 // Non-public interface for adding and removing lines. 59 insert: function(at, lines) { 60 let height = 0 61 for (let i = 0; i < lines.length; ++i) height += lines[i].height 62 this.insertInner(at - this.first, lines, height) 63 }, 64 remove: function(at, n) { this.removeInner(at - this.first, n) }, 65 66 // From here, the methods are part of the public interface. Most 67 // are also available from CodeMirror (editor) instances. 68 69 getValue: function(lineSep) { 70 let lines = getLines(this, this.first, this.first + this.size) 71 if (lineSep === false) return lines 72 return lines.join(lineSep || this.lineSeparator()) 73 }, 74 setValue: docMethodOp(function(code) { 75 let top = Pos(this.first, 0), last = this.first + this.size - 1 76 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), 77 text: this.splitLines(code), origin: "setValue", full: true}, true) 78 if (this.cm) scrollToCoords(this.cm, 0, 0) 79 setSelection(this, simpleSelection(top), sel_dontScroll) 80 }), 81 replaceRange: function(code, from, to, origin) { 82 from = clipPos(this, from) 83 to = to ? clipPos(this, to) : from 84 replaceRange(this, code, from, to, origin) 85 }, 86 getRange: function(from, to, lineSep) { 87 let lines = getBetween(this, clipPos(this, from), clipPos(this, to)) 88 if (lineSep === false) return lines 89 return lines.join(lineSep || this.lineSeparator()) 90 }, 91 92 getLine: function(line) {let l = this.getLineHandle(line); return l && l.text}, 93 94 getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line)}, 95 getLineNumber: function(line) {return lineNo(line)}, 96 97 getLineHandleVisualStart: function(line) { 98 if (typeof line == "number") line = getLine(this, line) 99 return visualLine(line) 100 }, 101 102 lineCount: function() {return this.size}, 103 firstLine: function() {return this.first}, 104 lastLine: function() {return this.first + this.size - 1}, 105 106 clipPos: function(pos) {return clipPos(this, pos)}, 107 108 getCursor: function(start) { 109 let range = this.sel.primary(), pos 110 if (start == null || start == "head") pos = range.head 111 else if (start == "anchor") pos = range.anchor 112 else if (start == "end" || start == "to" || start === false) pos = range.to() 113 else pos = range.from() 114 return pos 115 }, 116 listSelections: function() { return this.sel.ranges }, 117 somethingSelected: function() {return this.sel.somethingSelected()}, 118 119 setCursor: docMethodOp(function(line, ch, options) { 120 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options) 121 }), 122 setSelection: docMethodOp(function(anchor, head, options) { 123 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options) 124 }), 125 extendSelection: docMethodOp(function(head, other, options) { 126 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options) 127 }), 128 extendSelections: docMethodOp(function(heads, options) { 129 extendSelections(this, clipPosArray(this, heads), options) 130 }), 131 extendSelectionsBy: docMethodOp(function(f, options) { 132 let heads = map(this.sel.ranges, f) 133 extendSelections(this, clipPosArray(this, heads), options) 134 }), 135 setSelections: docMethodOp(function(ranges, primary, options) { 136 if (!ranges.length) return 137 let out = [] 138 for (let i = 0; i < ranges.length; i++) 139 out[i] = new Range(clipPos(this, ranges[i].anchor), 140 clipPos(this, ranges[i].head)) 141 if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex) 142 setSelection(this, normalizeSelection(out, primary), options) 143 }), 144 addSelection: docMethodOp(function(anchor, head, options) { 145 let ranges = this.sel.ranges.slice(0) 146 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))) 147 setSelection(this, normalizeSelection(ranges, ranges.length - 1), options) 148 }), 149 150 getSelection: function(lineSep) { 151 let ranges = this.sel.ranges, lines 152 for (let i = 0; i < ranges.length; i++) { 153 let sel = getBetween(this, ranges[i].from(), ranges[i].to()) 154 lines = lines ? lines.concat(sel) : sel 155 } 156 if (lineSep === false) return lines 157 else return lines.join(lineSep || this.lineSeparator()) 158 }, 159 getSelections: function(lineSep) { 160 let parts = [], ranges = this.sel.ranges 161 for (let i = 0; i < ranges.length; i++) { 162 let sel = getBetween(this, ranges[i].from(), ranges[i].to()) 163 if (lineSep !== false) sel = sel.join(lineSep || this.lineSeparator()) 164 parts[i] = sel 165 } 166 return parts 167 }, 168 replaceSelection: function(code, collapse, origin) { 169 let dup = [] 170 for (let i = 0; i < this.sel.ranges.length; i++) 171 dup[i] = code 172 this.replaceSelections(dup, collapse, origin || "+input") 173 }, 174 replaceSelections: docMethodOp(function(code, collapse, origin) { 175 let changes = [], sel = this.sel 176 for (let i = 0; i < sel.ranges.length; i++) { 177 let range = sel.ranges[i] 178 changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin} 179 } 180 let newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse) 181 for (let i = changes.length - 1; i >= 0; i--) 182 makeChange(this, changes[i]) 183 if (newSel) setSelectionReplaceHistory(this, newSel) 184 else if (this.cm) ensureCursorVisible(this.cm) 185 }), 186 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo")}), 187 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo")}), 188 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true)}), 189 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true)}), 190 191 setExtending: function(val) {this.extend = val}, 192 getExtending: function() {return this.extend}, 193 194 historySize: function() { 195 let hist = this.history, done = 0, undone = 0 196 for (let i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done 197 for (let i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone 198 return {undo: done, redo: undone} 199 }, 200 clearHistory: function() {this.history = new History(this.history.maxGeneration)}, 201 202 markClean: function() { 203 this.cleanGeneration = this.changeGeneration(true) 204 }, 205 changeGeneration: function(forceSplit) { 206 if (forceSplit) 207 this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null 208 return this.history.generation 209 }, 210 isClean: function (gen) { 211 return this.history.generation == (gen || this.cleanGeneration) 212 }, 213 214 getHistory: function() { 215 return {done: copyHistoryArray(this.history.done), 216 undone: copyHistoryArray(this.history.undone)} 217 }, 218 setHistory: function(histData) { 219 let hist = this.history = new History(this.history.maxGeneration) 220 hist.done = copyHistoryArray(histData.done.slice(0), null, true) 221 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true) 222 }, 223 224 setGutterMarker: docMethodOp(function(line, gutterID, value) { 225 return changeLine(this, line, "gutter", line => { 226 let markers = line.gutterMarkers || (line.gutterMarkers = {}) 227 markers[gutterID] = value 228 if (!value && isEmpty(markers)) line.gutterMarkers = null 229 return true 230 }) 231 }), 232 233 clearGutter: docMethodOp(function(gutterID) { 234 this.iter(line => { 235 if (line.gutterMarkers && line.gutterMarkers[gutterID]) { 236 changeLine(this, line, "gutter", () => { 237 line.gutterMarkers[gutterID] = null 238 if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null 239 return true 240 }) 241 } 242 }) 243 }), 244 245 lineInfo: function(line) { 246 let n 247 if (typeof line == "number") { 248 if (!isLine(this, line)) return null 249 n = line 250 line = getLine(this, line) 251 if (!line) return null 252 } else { 253 n = lineNo(line) 254 if (n == null) return null 255 } 256 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, 257 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, 258 widgets: line.widgets} 259 }, 260 261 addLineClass: docMethodOp(function(handle, where, cls) { 262 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", line => { 263 let prop = where == "text" ? "textClass" 264 : where == "background" ? "bgClass" 265 : where == "gutter" ? "gutterClass" : "wrapClass" 266 if (!line[prop]) line[prop] = cls 267 else if (classTest(cls).test(line[prop])) return false 268 else line[prop] += " " + cls 269 return true 270 }) 271 }), 272 removeLineClass: docMethodOp(function(handle, where, cls) { 273 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", line => { 274 let prop = where == "text" ? "textClass" 275 : where == "background" ? "bgClass" 276 : where == "gutter" ? "gutterClass" : "wrapClass" 277 let cur = line[prop] 278 if (!cur) return false 279 else if (cls == null) line[prop] = null 280 else { 281 let found = cur.match(classTest(cls)) 282 if (!found) return false 283 let end = found.index + found[0].length 284 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null 285 } 286 return true 287 }) 288 }), 289 290 addLineWidget: docMethodOp(function(handle, node, options) { 291 return addLineWidget(this, handle, node, options) 292 }), 293 removeLineWidget: function(widget) { widget.clear() }, 294 295 markText: function(from, to, options) { 296 return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range") 297 }, 298 setBookmark: function(pos, options) { 299 let realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), 300 insertLeft: options && options.insertLeft, 301 clearWhenEmpty: false, shared: options && options.shared, 302 handleMouseEvents: options && options.handleMouseEvents} 303 pos = clipPos(this, pos) 304 return markText(this, pos, pos, realOpts, "bookmark") 305 }, 306 findMarksAt: function(pos) { 307 pos = clipPos(this, pos) 308 let markers = [], spans = getLine(this, pos.line).markedSpans 309 if (spans) for (let i = 0; i < spans.length; ++i) { 310 let span = spans[i] 311 if ((span.from == null || span.from <= pos.ch) && 312 (span.to == null || span.to >= pos.ch)) 313 markers.push(span.marker.parent || span.marker) 314 } 315 return markers 316 }, 317 findMarks: function(from, to, filter) { 318 from = clipPos(this, from); to = clipPos(this, to) 319 let found = [], lineNo = from.line 320 this.iter(from.line, to.line + 1, line => { 321 let spans = line.markedSpans 322 if (spans) for (let i = 0; i < spans.length; i++) { 323 let span = spans[i] 324 if (!(span.to != null && lineNo == from.line && from.ch >= span.to || 325 span.from == null && lineNo != from.line || 326 span.from != null && lineNo == to.line && span.from >= to.ch) && 327 (!filter || filter(span.marker))) 328 found.push(span.marker.parent || span.marker) 329 } 330 ++lineNo 331 }) 332 return found 333 }, 334 getAllMarks: function() { 335 let markers = [] 336 this.iter(line => { 337 let sps = line.markedSpans 338 if (sps) for (let i = 0; i < sps.length; ++i) 339 if (sps[i].from != null) markers.push(sps[i].marker) 340 }) 341 return markers 342 }, 343 344 posFromIndex: function(off) { 345 let ch, lineNo = this.first, sepSize = this.lineSeparator().length 346 this.iter(line => { 347 let sz = line.text.length + sepSize 348 if (sz > off) { ch = off; return true } 349 off -= sz 350 ++lineNo 351 }) 352 return clipPos(this, Pos(lineNo, ch)) 353 }, 354 indexFromPos: function (coords) { 355 coords = clipPos(this, coords) 356 let index = coords.ch 357 if (coords.line < this.first || coords.ch < 0) return 0 358 let sepSize = this.lineSeparator().length 359 this.iter(this.first, coords.line, line => { // iter aborts when callback returns a truthy value 360 index += line.text.length + sepSize 361 }) 362 return index 363 }, 364 365 copy: function(copyHistory) { 366 let doc = new Doc(getLines(this, this.first, this.first + this.size), 367 this.modeOption, this.first, this.lineSep, this.direction) 368 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft 369 doc.sel = this.sel 370 doc.extend = false 371 if (copyHistory) { 372 doc.history.undoDepth = this.history.undoDepth 373 doc.setHistory(this.getHistory()) 374 } 375 return doc 376 }, 377 378 linkedDoc: function(options) { 379 if (!options) options = {} 380 let from = this.first, to = this.first + this.size 381 if (options.from != null && options.from > from) from = options.from 382 if (options.to != null && options.to < to) to = options.to 383 let copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction) 384 if (options.sharedHist) copy.history = this.history 385 ;(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}) 386 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}] 387 copySharedMarkers(copy, findSharedMarkers(this)) 388 return copy 389 }, 390 unlinkDoc: function(other) { 391 if (other instanceof CodeMirror) other = other.doc 392 if (this.linked) for (let i = 0; i < this.linked.length; ++i) { 393 let link = this.linked[i] 394 if (link.doc != other) continue 395 this.linked.splice(i, 1) 396 other.unlinkDoc(this) 397 detachSharedMarkers(findSharedMarkers(this)) 398 break 399 } 400 // If the histories were shared, split them again 401 if (other.history == this.history) { 402 let splitIds = [other.id] 403 linkedDocs(other, doc => splitIds.push(doc.id), true) 404 other.history = new History(null) 405 other.history.done = copyHistoryArray(this.history.done, splitIds) 406 other.history.undone = copyHistoryArray(this.history.undone, splitIds) 407 } 408 }, 409 iterLinkedDocs: function(f) {linkedDocs(this, f)}, 410 411 getMode: function() {return this.mode}, 412 getEditor: function() {return this.cm}, 413 414 splitLines: function(str) { 415 if (this.lineSep) return str.split(this.lineSep) 416 return splitLinesAuto(str) 417 }, 418 lineSeparator: function() { return this.lineSep || "\n" }, 419 420 setDirection: docMethodOp(function (dir) { 421 if (dir != "rtl") dir = "ltr" 422 if (dir == this.direction) return 423 this.direction = dir 424 this.iter(line => line.order = null) 425 if (this.cm) directionChanged(this.cm) 426 }) 427 }) 428 429 // Public alias. 430 Doc.prototype.eachLine = Doc.prototype.iter 431 432 export default Doc
Downloadmodules/editor/codemirror/src/model/Doc.min.js
History Tue, 22 May 2018 22:39:53 +0200 Jan Dankert Fix für PHP 7.2: 'Object' darf nun nicht mehr als Klassennamen verwendet werden. AUCH NICHT IN EINEM NAMESPACE! WTF, wozu habe ich das in einen verfickten Namespace gepackt? Wozu soll der sonst da sein??? Amateure. Daher nun notgedrungen unbenannt in 'BaseObject'.