diff .cms/lib/codemirror/src/display/view_tracking.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/view_tracking.js	Fri Oct 11 22:40:23 2024 +0000
@@ -0,0 +1,153 @@
+import { buildViewArray } from "../line/line_data.js"
+import { sawCollapsedSpans } from "../line/saw_special_spans.js"
+import { visualLineEndNo, visualLineNo } from "../line/spans.js"
+import { findViewIndex } from "../measurement/position_measurement.js"
+import { indexOf } from "../util/misc.js"
+
+// Updates the display.view data structure for a given change to the
+// document. From and to are in pre-change coordinates. Lendiff is
+// the amount of lines added or subtracted by the change. This is
+// used for changes that span multiple lines, or change the way
+// lines are divided into visual lines. regLineChange (below)
+// registers single-line changes.
+export function regChange(cm, from, to, lendiff) {
+  if (from == null) from = cm.doc.first
+  if (to == null) to = cm.doc.first + cm.doc.size
+  if (!lendiff) lendiff = 0
+
+  let display = cm.display
+  if (lendiff && to < display.viewTo &&
+      (display.updateLineNumbers == null || display.updateLineNumbers > from))
+    display.updateLineNumbers = from
+
+  cm.curOp.viewChanged = true
+
+  if (from >= display.viewTo) { // Change after
+    if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
+      resetView(cm)
+  } else if (to <= display.viewFrom) { // Change before
+    if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
+      resetView(cm)
+    } else {
+      display.viewFrom += lendiff
+      display.viewTo += lendiff
+    }
+  } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
+    resetView(cm)
+  } else if (from <= display.viewFrom) { // Top overlap
+    let cut = viewCuttingPoint(cm, to, to + lendiff, 1)
+    if (cut) {
+      display.view = display.view.slice(cut.index)
+      display.viewFrom = cut.lineN
+      display.viewTo += lendiff
+    } else {
+      resetView(cm)
+    }
+  } else if (to >= display.viewTo) { // Bottom overlap
+    let cut = viewCuttingPoint(cm, from, from, -1)
+    if (cut) {
+      display.view = display.view.slice(0, cut.index)
+      display.viewTo = cut.lineN
+    } else {
+      resetView(cm)
+    }
+  } else { // Gap in the middle
+    let cutTop = viewCuttingPoint(cm, from, from, -1)
+    let cutBot = viewCuttingPoint(cm, to, to + lendiff, 1)
+    if (cutTop && cutBot) {
+      display.view = display.view.slice(0, cutTop.index)
+        .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
+        .concat(display.view.slice(cutBot.index))
+      display.viewTo += lendiff
+    } else {
+      resetView(cm)
+    }
+  }
+
+  let ext = display.externalMeasured
+  if (ext) {
+    if (to < ext.lineN)
+      ext.lineN += lendiff
+    else if (from < ext.lineN + ext.size)
+      display.externalMeasured = null
+  }
+}
+
+// Register a change to a single line. Type must be one of "text",
+// "gutter", "class", "widget"
+export function regLineChange(cm, line, type) {
+  cm.curOp.viewChanged = true
+  let display = cm.display, ext = cm.display.externalMeasured
+  if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
+    display.externalMeasured = null
+
+  if (line < display.viewFrom || line >= display.viewTo) return
+  let lineView = display.view[findViewIndex(cm, line)]
+  if (lineView.node == null) return
+  let arr = lineView.changes || (lineView.changes = [])
+  if (indexOf(arr, type) == -1) arr.push(type)
+}
+
+// Clear the view.
+export function resetView(cm) {
+  cm.display.viewFrom = cm.display.viewTo = cm.doc.first
+  cm.display.view = []
+  cm.display.viewOffset = 0
+}
+
+function viewCuttingPoint(cm, oldN, newN, dir) {
+  let index = findViewIndex(cm, oldN), diff, view = cm.display.view
+  if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
+    return {index: index, lineN: newN}
+  let n = cm.display.viewFrom
+  for (let i = 0; i < index; i++)
+    n += view[i].size
+  if (n != oldN) {
+    if (dir > 0) {
+      if (index == view.length - 1) return null
+      diff = (n + view[index].size) - oldN
+      index++
+    } else {
+      diff = n - oldN
+    }
+    oldN += diff; newN += diff
+  }
+  while (visualLineNo(cm.doc, newN) != newN) {
+    if (index == (dir < 0 ? 0 : view.length - 1)) return null
+    newN += dir * view[index - (dir < 0 ? 1 : 0)].size
+    index += dir
+  }
+  return {index: index, lineN: newN}
+}
+
+// Force the view to cover a given range, adding empty view element
+// or clipping off existing ones as needed.
+export function adjustView(cm, from, to) {
+  let display = cm.display, view = display.view
+  if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
+    display.view = buildViewArray(cm, from, to)
+    display.viewFrom = from
+  } else {
+    if (display.viewFrom > from)
+      display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view)
+    else if (display.viewFrom < from)
+      display.view = display.view.slice(findViewIndex(cm, from))
+    display.viewFrom = from
+    if (display.viewTo < to)
+      display.view = display.view.concat(buildViewArray(cm, display.viewTo, to))
+    else if (display.viewTo > to)
+      display.view = display.view.slice(0, findViewIndex(cm, to))
+  }
+  display.viewTo = to
+}
+
+// Count the number of lines in the view whose DOM representation is
+// out of date (or nonexistent).
+export function countDirtyView(cm) {
+  let view = cm.display.view, dirty = 0
+  for (let i = 0; i < view.length; i++) {
+    let lineView = view[i]
+    if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty
+  }
+  return dirty
+}