update_line.js (7819B)
1 import { buildLineContent } from "../line/line_data.js" 2 import { lineNumberFor } from "../line/utils_line.js" 3 import { ie, ie_version } from "../util/browser.js" 4 import { elt } from "../util/dom.js" 5 import { signalLater } from "../util/operation_group.js" 6 7 // When an aspect of a line changes, a string is added to 8 // lineView.changes. This updates the relevant part of the line's 9 // DOM structure. 10 export function updateLineForChanges(cm, lineView, lineN, dims) { 11 for (let j = 0; j < lineView.changes.length; j++) { 12 let type = lineView.changes[j] 13 if (type == "text") updateLineText(cm, lineView) 14 else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims) 15 else if (type == "class") updateLineClasses(cm, lineView) 16 else if (type == "widget") updateLineWidgets(cm, lineView, dims) 17 } 18 lineView.changes = null 19 } 20 21 // Lines with gutter elements, widgets or a background class need to 22 // be wrapped, and have the extra elements added to the wrapper div 23 function ensureLineWrapped(lineView) { 24 if (lineView.node == lineView.text) { 25 lineView.node = elt("div", null, null, "position: relative") 26 if (lineView.text.parentNode) 27 lineView.text.parentNode.replaceChild(lineView.node, lineView.text) 28 lineView.node.appendChild(lineView.text) 29 if (ie && ie_version < 8) lineView.node.style.zIndex = 2 30 } 31 return lineView.node 32 } 33 34 function updateLineBackground(cm, lineView) { 35 let cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass 36 if (cls) cls += " CodeMirror-linebackground" 37 if (lineView.background) { 38 if (cls) lineView.background.className = cls 39 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null } 40 } else if (cls) { 41 let wrap = ensureLineWrapped(lineView) 42 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild) 43 cm.display.input.setUneditable(lineView.background) 44 } 45 } 46 47 // Wrapper around buildLineContent which will reuse the structure 48 // in display.externalMeasured when possible. 49 function getLineContent(cm, lineView) { 50 let ext = cm.display.externalMeasured 51 if (ext && ext.line == lineView.line) { 52 cm.display.externalMeasured = null 53 lineView.measure = ext.measure 54 return ext.built 55 } 56 return buildLineContent(cm, lineView) 57 } 58 59 // Redraw the line's text. Interacts with the background and text 60 // classes because the mode may output tokens that influence these 61 // classes. 62 function updateLineText(cm, lineView) { 63 let cls = lineView.text.className 64 let built = getLineContent(cm, lineView) 65 if (lineView.text == lineView.node) lineView.node = built.pre 66 lineView.text.parentNode.replaceChild(built.pre, lineView.text) 67 lineView.text = built.pre 68 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { 69 lineView.bgClass = built.bgClass 70 lineView.textClass = built.textClass 71 updateLineClasses(cm, lineView) 72 } else if (cls) { 73 lineView.text.className = cls 74 } 75 } 76 77 function updateLineClasses(cm, lineView) { 78 updateLineBackground(cm, lineView) 79 if (lineView.line.wrapClass) 80 ensureLineWrapped(lineView).className = lineView.line.wrapClass 81 else if (lineView.node != lineView.text) 82 lineView.node.className = "" 83 let textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass 84 lineView.text.className = textClass || "" 85 } 86 87 function updateLineGutter(cm, lineView, lineN, dims) { 88 if (lineView.gutter) { 89 lineView.node.removeChild(lineView.gutter) 90 lineView.gutter = null 91 } 92 if (lineView.gutterBackground) { 93 lineView.node.removeChild(lineView.gutterBackground) 94 lineView.gutterBackground = null 95 } 96 if (lineView.line.gutterClass) { 97 let wrap = ensureLineWrapped(lineView) 98 lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, 99 `left: ${cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth}px; width: ${dims.gutterTotalWidth}px`) 100 cm.display.input.setUneditable(lineView.gutterBackground) 101 wrap.insertBefore(lineView.gutterBackground, lineView.text) 102 } 103 let markers = lineView.line.gutterMarkers 104 if (cm.options.lineNumbers || markers) { 105 let wrap = ensureLineWrapped(lineView) 106 let gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", `left: ${cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth}px`) 107 cm.display.input.setUneditable(gutterWrap) 108 wrap.insertBefore(gutterWrap, lineView.text) 109 if (lineView.line.gutterClass) 110 gutterWrap.className += " " + lineView.line.gutterClass 111 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) 112 lineView.lineNumber = gutterWrap.appendChild( 113 elt("div", lineNumberFor(cm.options, lineN), 114 "CodeMirror-linenumber CodeMirror-gutter-elt", 115 `left: ${dims.gutterLeft["CodeMirror-linenumbers"]}px; width: ${cm.display.lineNumInnerWidth}px`)) 116 if (markers) for (let k = 0; k < cm.options.gutters.length; ++k) { 117 let id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id] 118 if (found) 119 gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", 120 `left: ${dims.gutterLeft[id]}px; width: ${dims.gutterWidth[id]}px`)) 121 } 122 } 123 } 124 125 function updateLineWidgets(cm, lineView, dims) { 126 if (lineView.alignable) lineView.alignable = null 127 for (let node = lineView.node.firstChild, next; node; node = next) { 128 next = node.nextSibling 129 if (node.className == "CodeMirror-linewidget") 130 lineView.node.removeChild(node) 131 } 132 insertLineWidgets(cm, lineView, dims) 133 } 134 135 // Build a line's DOM representation from scratch 136 export function buildLineElement(cm, lineView, lineN, dims) { 137 let built = getLineContent(cm, lineView) 138 lineView.text = lineView.node = built.pre 139 if (built.bgClass) lineView.bgClass = built.bgClass 140 if (built.textClass) lineView.textClass = built.textClass 141 142 updateLineClasses(cm, lineView) 143 updateLineGutter(cm, lineView, lineN, dims) 144 insertLineWidgets(cm, lineView, dims) 145 return lineView.node 146 } 147 148 // A lineView may contain multiple logical lines (when merged by 149 // collapsed spans). The widgets for all of them need to be drawn. 150 function insertLineWidgets(cm, lineView, dims) { 151 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true) 152 if (lineView.rest) for (let i = 0; i < lineView.rest.length; i++) 153 insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false) 154 } 155 156 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { 157 if (!line.widgets) return 158 let wrap = ensureLineWrapped(lineView) 159 for (let i = 0, ws = line.widgets; i < ws.length; ++i) { 160 let widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget") 161 if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true") 162 positionLineWidget(widget, node, lineView, dims) 163 cm.display.input.setUneditable(node) 164 if (allowAbove && widget.above) 165 wrap.insertBefore(node, lineView.gutter || lineView.text) 166 else 167 wrap.appendChild(node) 168 signalLater(widget, "redraw") 169 } 170 } 171 172 function positionLineWidget(widget, node, lineView, dims) { 173 if (widget.noHScroll) { 174 ;(lineView.alignable || (lineView.alignable = [])).push(node) 175 let width = dims.wrapperWidth 176 node.style.left = dims.fixedPos + "px" 177 if (!widget.coverGutter) { 178 width -= dims.gutterTotalWidth 179 node.style.paddingLeft = dims.gutterTotalWidth + "px" 180 } 181 node.style.width = width + "px" 182 } 183 if (widget.coverGutter) { 184 node.style.zIndex = 5 185 node.style.position = "relative" 186 if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px" 187 } 188 }