diff .cms/lib/codemirror/src/display/update_line.js @ 0:78edf6b517a0 draft

24.10
author Coffee CMS <info@coffee-cms.ru>
date Fri, 11 Oct 2024 22:40:23 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.cms/lib/codemirror/src/display/update_line.js	Fri Oct 11 22:40:23 2024 +0000
@@ -0,0 +1,189 @@
+import { buildLineContent } from "../line/line_data.js"
+import { lineNumberFor } from "../line/utils_line.js"
+import { ie, ie_version } from "../util/browser.js"
+import { elt, classTest } from "../util/dom.js"
+import { signalLater } from "../util/operation_group.js"
+
+// When an aspect of a line changes, a string is added to
+// lineView.changes. This updates the relevant part of the line's
+// DOM structure.
+export function updateLineForChanges(cm, lineView, lineN, dims) {
+  for (let j = 0; j < lineView.changes.length; j++) {
+    let type = lineView.changes[j]
+    if (type == "text") updateLineText(cm, lineView)
+    else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims)
+    else if (type == "class") updateLineClasses(cm, lineView)
+    else if (type == "widget") updateLineWidgets(cm, lineView, dims)
+  }
+  lineView.changes = null
+}
+
+// Lines with gutter elements, widgets or a background class need to
+// be wrapped, and have the extra elements added to the wrapper div
+function ensureLineWrapped(lineView) {
+  if (lineView.node == lineView.text) {
+    lineView.node = elt("div", null, null, "position: relative")
+    if (lineView.text.parentNode)
+      lineView.text.parentNode.replaceChild(lineView.node, lineView.text)
+    lineView.node.appendChild(lineView.text)
+    if (ie && ie_version < 8) lineView.node.style.zIndex = 2
+  }
+  return lineView.node
+}
+
+function updateLineBackground(cm, lineView) {
+  let cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass
+  if (cls) cls += " CodeMirror-linebackground"
+  if (lineView.background) {
+    if (cls) lineView.background.className = cls
+    else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null }
+  } else if (cls) {
+    let wrap = ensureLineWrapped(lineView)
+    lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild)
+    cm.display.input.setUneditable(lineView.background)
+  }
+}
+
+// Wrapper around buildLineContent which will reuse the structure
+// in display.externalMeasured when possible.
+function getLineContent(cm, lineView) {
+  let ext = cm.display.externalMeasured
+  if (ext && ext.line == lineView.line) {
+    cm.display.externalMeasured = null
+    lineView.measure = ext.measure
+    return ext.built
+  }
+  return buildLineContent(cm, lineView)
+}
+
+// Redraw the line's text. Interacts with the background and text
+// classes because the mode may output tokens that influence these
+// classes.
+function updateLineText(cm, lineView) {
+  let cls = lineView.text.className
+  let built = getLineContent(cm, lineView)
+  if (lineView.text == lineView.node) lineView.node = built.pre
+  lineView.text.parentNode.replaceChild(built.pre, lineView.text)
+  lineView.text = built.pre
+  if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
+    lineView.bgClass = built.bgClass
+    lineView.textClass = built.textClass
+    updateLineClasses(cm, lineView)
+  } else if (cls) {
+    lineView.text.className = cls
+  }
+}
+
+function updateLineClasses(cm, lineView) {
+  updateLineBackground(cm, lineView)
+  if (lineView.line.wrapClass)
+    ensureLineWrapped(lineView).className = lineView.line.wrapClass
+  else if (lineView.node != lineView.text)
+    lineView.node.className = ""
+  let textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass
+  lineView.text.className = textClass || ""
+}
+
+function updateLineGutter(cm, lineView, lineN, dims) {
+  if (lineView.gutter) {
+    lineView.node.removeChild(lineView.gutter)
+    lineView.gutter = null
+  }
+  if (lineView.gutterBackground) {
+    lineView.node.removeChild(lineView.gutterBackground)
+    lineView.gutterBackground = null
+  }
+  if (lineView.line.gutterClass) {
+    let wrap = ensureLineWrapped(lineView)
+    lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
+                                    `left: ${cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth}px; width: ${dims.gutterTotalWidth}px`)
+    cm.display.input.setUneditable(lineView.gutterBackground)
+    wrap.insertBefore(lineView.gutterBackground, lineView.text)
+  }
+  let markers = lineView.line.gutterMarkers
+  if (cm.options.lineNumbers || markers) {
+    let wrap = ensureLineWrapped(lineView)
+    let gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", `left: ${cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth}px`)
+    gutterWrap.setAttribute("aria-hidden", "true")
+    cm.display.input.setUneditable(gutterWrap)
+    wrap.insertBefore(gutterWrap, lineView.text)
+    if (lineView.line.gutterClass)
+      gutterWrap.className += " " + lineView.line.gutterClass
+    if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
+      lineView.lineNumber = gutterWrap.appendChild(
+        elt("div", lineNumberFor(cm.options, lineN),
+            "CodeMirror-linenumber CodeMirror-gutter-elt",
+            `left: ${dims.gutterLeft["CodeMirror-linenumbers"]}px; width: ${cm.display.lineNumInnerWidth}px`))
+    if (markers) for (let k = 0; k < cm.display.gutterSpecs.length; ++k) {
+      let id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id]
+      if (found)
+        gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
+                                   `left: ${dims.gutterLeft[id]}px; width: ${dims.gutterWidth[id]}px`))
+    }
+  }
+}
+
+function updateLineWidgets(cm, lineView, dims) {
+  if (lineView.alignable) lineView.alignable = null
+  let isWidget = classTest("CodeMirror-linewidget")
+  for (let node = lineView.node.firstChild, next; node; node = next) {
+    next = node.nextSibling
+    if (isWidget.test(node.className)) lineView.node.removeChild(node)
+  }
+  insertLineWidgets(cm, lineView, dims)
+}
+
+// Build a line's DOM representation from scratch
+export function buildLineElement(cm, lineView, lineN, dims) {
+  let built = getLineContent(cm, lineView)
+  lineView.text = lineView.node = built.pre
+  if (built.bgClass) lineView.bgClass = built.bgClass
+  if (built.textClass) lineView.textClass = built.textClass
+
+  updateLineClasses(cm, lineView)
+  updateLineGutter(cm, lineView, lineN, dims)
+  insertLineWidgets(cm, lineView, dims)
+  return lineView.node
+}
+
+// A lineView may contain multiple logical lines (when merged by
+// collapsed spans). The widgets for all of them need to be drawn.
+function insertLineWidgets(cm, lineView, dims) {
+  insertLineWidgetsFor(cm, lineView.line, lineView, dims, true)
+  if (lineView.rest) for (let i = 0; i < lineView.rest.length; i++)
+    insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false)
+}
+
+function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
+  if (!line.widgets) return
+  let wrap = ensureLineWrapped(lineView)
+  for (let i = 0, ws = line.widgets; i < ws.length; ++i) {
+    let widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : ""))
+    if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true")
+    positionLineWidget(widget, node, lineView, dims)
+    cm.display.input.setUneditable(node)
+    if (allowAbove && widget.above)
+      wrap.insertBefore(node, lineView.gutter || lineView.text)
+    else
+      wrap.appendChild(node)
+    signalLater(widget, "redraw")
+  }
+}
+
+function positionLineWidget(widget, node, lineView, dims) {
+  if (widget.noHScroll) {
+    ;(lineView.alignable || (lineView.alignable = [])).push(node)
+    let width = dims.wrapperWidth
+    node.style.left = dims.fixedPos + "px"
+    if (!widget.coverGutter) {
+      width -= dims.gutterTotalWidth
+      node.style.paddingLeft = dims.gutterTotalWidth + "px"
+    }
+    node.style.width = width + "px"
+  }
+  if (widget.coverGutter) {
+    node.style.zIndex = 5
+    node.style.position = "relative"
+    if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px"
+  }
+}