File modules/editor/codemirror/src/model/mark_text.min.js
Last commit: Tue May 22 22:39:54 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 { eltP } from "../util/dom.js" 2 import { eventMixin, hasHandler, on } from "../util/event.js" 3 import { endOperation, operation, runInOp, startOperation } from "../display/operations.js" 4 import { clipPos, cmp, Pos } from "../line/pos.js" 5 import { lineNo, updateLineHeight } from "../line/utils_line.js" 6 import { clearLineMeasurementCacheFor, findViewForLine, textHeight } from "../measurement/position_measurement.js" 7 import { seeReadOnlySpans, seeCollapsedSpans } from "../line/saw_special_spans.js" 8 import { addMarkedSpan, conflictingCollapsedRange, getMarkedSpanFor, lineIsHidden, lineLength, MarkedSpan, removeMarkedSpan, visualLine } from "../line/spans.js" 9 import { copyObj, indexOf, lst } from "../util/misc.js" 10 import { signalLater } from "../util/operation_group.js" 11 import { widgetHeight } from "../measurement/widgets.js" 12 import { regChange, regLineChange } from "../display/view_tracking.js" 13 14 import { linkedDocs } from "./document_data.js" 15 import { addChangeToHistory } from "./history.js" 16 import { reCheckSelection } from "./selection_updates.js" 17 18 // TEXTMARKERS 19 20 // Created with markText and setBookmark methods. A TextMarker is a 21 // handle that can be used to clear or find a marked position in the 22 // document. Line objects hold arrays (markedSpans) containing 23 // {from, to, marker} object pointing to such marker objects, and 24 // indicating that such a marker is present on that line. Multiple 25 // lines may point to the same marker when it spans across lines. 26 // The spans will have null for their from/to properties when the 27 // marker continues beyond the start/end of the line. Markers have 28 // links back to the lines they currently touch. 29 30 // Collapsed markers have unique ids, in order to be able to order 31 // them, which is needed for uniquely determining an outer marker 32 // when they overlap (they may nest, but not partially overlap). 33 let nextMarkerId = 0 34 35 export class TextMarker { 36 constructor(doc, type) { 37 this.lines = [] 38 this.type = type 39 this.doc = doc 40 this.id = ++nextMarkerId 41 } 42 43 // Clear the marker. 44 clear() { 45 if (this.explicitlyCleared) return 46 let cm = this.doc.cm, withOp = cm && !cm.curOp 47 if (withOp) startOperation(cm) 48 if (hasHandler(this, "clear")) { 49 let found = this.find() 50 if (found) signalLater(this, "clear", found.from, found.to) 51 } 52 let min = null, max = null 53 for (let i = 0; i < this.lines.length; ++i) { 54 let line = this.lines[i] 55 let span = getMarkedSpanFor(line.markedSpans, this) 56 if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text") 57 else if (cm) { 58 if (span.to != null) max = lineNo(line) 59 if (span.from != null) min = lineNo(line) 60 } 61 line.markedSpans = removeMarkedSpan(line.markedSpans, span) 62 if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) 63 updateLineHeight(line, textHeight(cm.display)) 64 } 65 if (cm && this.collapsed && !cm.options.lineWrapping) for (let i = 0; i < this.lines.length; ++i) { 66 let visual = visualLine(this.lines[i]), len = lineLength(visual) 67 if (len > cm.display.maxLineLength) { 68 cm.display.maxLine = visual 69 cm.display.maxLineLength = len 70 cm.display.maxLineChanged = true 71 } 72 } 73 74 if (min != null && cm && this.collapsed) regChange(cm, min, max + 1) 75 this.lines.length = 0 76 this.explicitlyCleared = true 77 if (this.atomic && this.doc.cantEdit) { 78 this.doc.cantEdit = false 79 if (cm) reCheckSelection(cm.doc) 80 } 81 if (cm) signalLater(cm, "markerCleared", cm, this, min, max) 82 if (withOp) endOperation(cm) 83 if (this.parent) this.parent.clear() 84 } 85 86 // Find the position of the marker in the document. Returns a {from, 87 // to} object by default. Side can be passed to get a specific side 88 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the 89 // Pos objects returned contain a line object, rather than a line 90 // number (used to prevent looking up the same line twice). 91 find(side, lineObj) { 92 if (side == null && this.type == "bookmark") side = 1 93 let from, to 94 for (let i = 0; i < this.lines.length; ++i) { 95 let line = this.lines[i] 96 let span = getMarkedSpanFor(line.markedSpans, this) 97 if (span.from != null) { 98 from = Pos(lineObj ? line : lineNo(line), span.from) 99 if (side == -1) return from 100 } 101 if (span.to != null) { 102 to = Pos(lineObj ? line : lineNo(line), span.to) 103 if (side == 1) return to 104 } 105 } 106 return from && {from: from, to: to} 107 } 108 109 // Signals that the marker's widget changed, and surrounding layout 110 // should be recomputed. 111 changed() { 112 let pos = this.find(-1, true), widget = this, cm = this.doc.cm 113 if (!pos || !cm) return 114 runInOp(cm, () => { 115 let line = pos.line, lineN = lineNo(pos.line) 116 let view = findViewForLine(cm, lineN) 117 if (view) { 118 clearLineMeasurementCacheFor(view) 119 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true 120 } 121 cm.curOp.updateMaxLine = true 122 if (!lineIsHidden(widget.doc, line) && widget.height != null) { 123 let oldHeight = widget.height 124 widget.height = null 125 let dHeight = widgetHeight(widget) - oldHeight 126 if (dHeight) 127 updateLineHeight(line, line.height + dHeight) 128 } 129 signalLater(cm, "markerChanged", cm, this) 130 }) 131 } 132 133 attachLine(line) { 134 if (!this.lines.length && this.doc.cm) { 135 let op = this.doc.cm.curOp 136 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) 137 (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this) 138 } 139 this.lines.push(line) 140 } 141 142 detachLine(line) { 143 this.lines.splice(indexOf(this.lines, line), 1) 144 if (!this.lines.length && this.doc.cm) { 145 let op = this.doc.cm.curOp 146 ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this) 147 } 148 } 149 } 150 eventMixin(TextMarker) 151 152 // Create a marker, wire it up to the right lines, and 153 export function markText(doc, from, to, options, type) { 154 // Shared markers (across linked documents) are handled separately 155 // (markTextShared will call out to this again, once per 156 // document). 157 if (options && options.shared) return markTextShared(doc, from, to, options, type) 158 // Ensure we are in an operation. 159 if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type) 160 161 let marker = new TextMarker(doc, type), diff = cmp(from, to) 162 if (options) copyObj(options, marker, false) 163 // Don't connect empty markers unless clearWhenEmpty is false 164 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) 165 return marker 166 if (marker.replacedWith) { 167 // Showing up as a widget implies collapsed (widget replaces text) 168 marker.collapsed = true 169 marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget") 170 if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore-events", "true") 171 if (options.insertLeft) marker.widgetNode.insertLeft = true 172 } 173 if (marker.collapsed) { 174 if (conflictingCollapsedRange(doc, from.line, from, to, marker) || 175 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) 176 throw new Error("Inserting collapsed marker partially overlapping an existing one") 177 seeCollapsedSpans() 178 } 179 180 if (marker.addToHistory) 181 addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN) 182 183 let curLine = from.line, cm = doc.cm, updateMaxLine 184 doc.iter(curLine, to.line + 1, line => { 185 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) 186 updateMaxLine = true 187 if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0) 188 addMarkedSpan(line, new MarkedSpan(marker, 189 curLine == from.line ? from.ch : null, 190 curLine == to.line ? to.ch : null)) 191 ++curLine 192 }) 193 // lineIsHidden depends on the presence of the spans, so needs a second pass 194 if (marker.collapsed) doc.iter(from.line, to.line + 1, line => { 195 if (lineIsHidden(doc, line)) updateLineHeight(line, 0) 196 }) 197 198 if (marker.clearOnEnter) on(marker, "beforeCursorEnter", () => marker.clear()) 199 200 if (marker.readOnly) { 201 seeReadOnlySpans() 202 if (doc.history.done.length || doc.history.undone.length) 203 doc.clearHistory() 204 } 205 if (marker.collapsed) { 206 marker.id = ++nextMarkerId 207 marker.atomic = true 208 } 209 if (cm) { 210 // Sync editor state 211 if (updateMaxLine) cm.curOp.updateMaxLine = true 212 if (marker.collapsed) 213 regChange(cm, from.line, to.line + 1) 214 else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css) 215 for (let i = from.line; i <= to.line; i++) regLineChange(cm, i, "text") 216 if (marker.atomic) reCheckSelection(cm.doc) 217 signalLater(cm, "markerAdded", cm, marker) 218 } 219 return marker 220 } 221 222 // SHARED TEXTMARKERS 223 224 // A shared marker spans multiple linked documents. It is 225 // implemented as a meta-marker-object controlling multiple normal 226 // markers. 227 export class SharedTextMarker { 228 constructor(markers, primary) { 229 this.markers = markers 230 this.primary = primary 231 for (let i = 0; i < markers.length; ++i) 232 markers[i].parent = this 233 } 234 235 clear() { 236 if (this.explicitlyCleared) return 237 this.explicitlyCleared = true 238 for (let i = 0; i < this.markers.length; ++i) 239 this.markers[i].clear() 240 signalLater(this, "clear") 241 } 242 243 find(side, lineObj) { 244 return this.primary.find(side, lineObj) 245 } 246 } 247 eventMixin(SharedTextMarker) 248 249 function markTextShared(doc, from, to, options, type) { 250 options = copyObj(options) 251 options.shared = false 252 let markers = [markText(doc, from, to, options, type)], primary = markers[0] 253 let widget = options.widgetNode 254 linkedDocs(doc, doc => { 255 if (widget) options.widgetNode = widget.cloneNode(true) 256 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)) 257 for (let i = 0; i < doc.linked.length; ++i) 258 if (doc.linked[i].isParent) return 259 primary = lst(markers) 260 }) 261 return new SharedTextMarker(markers, primary) 262 } 263 264 export function findSharedMarkers(doc) { 265 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), m => m.parent) 266 } 267 268 export function copySharedMarkers(doc, markers) { 269 for (let i = 0; i < markers.length; i++) { 270 let marker = markers[i], pos = marker.find() 271 let mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to) 272 if (cmp(mFrom, mTo)) { 273 let subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type) 274 marker.markers.push(subMark) 275 subMark.parent = marker 276 } 277 } 278 } 279 280 export function detachSharedMarkers(markers) { 281 for (let i = 0; i < markers.length; i++) { 282 let marker = markers[i], linked = [marker.primary.doc] 283 linkedDocs(marker.primary.doc, d => linked.push(d)) 284 for (let j = 0; j < marker.markers.length; j++) { 285 let subMarker = marker.markers[j] 286 if (indexOf(linked, subMarker.doc) == -1) { 287 subMarker.parent = null 288 marker.markers.splice(j--, 1) 289 } 290 } 291 } 292 }
Downloadmodules/editor/codemirror/src/model/mark_text.min.js
History Tue, 22 May 2018 22:39:54 +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'.