openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

view_tracking.min.js (5385B)


      1 import { buildViewArray } from "../line/line_data.js"
      2 import { sawCollapsedSpans } from "../line/saw_special_spans.js"
      3 import { visualLineEndNo, visualLineNo } from "../line/spans.js"
      4 import { findViewIndex } from "../measurement/position_measurement.js"
      5 import { indexOf } from "../util/misc.js"
      6 
      7 // Updates the display.view data structure for a given change to the
      8 // document. From and to are in pre-change coordinates. Lendiff is
      9 // the amount of lines added or subtracted by the change. This is
     10 // used for changes that span multiple lines, or change the way
     11 // lines are divided into visual lines. regLineChange (below)
     12 // registers single-line changes.
     13 export function regChange(cm, from, to, lendiff) {
     14   if (from == null) from = cm.doc.first
     15   if (to == null) to = cm.doc.first + cm.doc.size
     16   if (!lendiff) lendiff = 0
     17 
     18   let display = cm.display
     19   if (lendiff && to < display.viewTo &&
     20       (display.updateLineNumbers == null || display.updateLineNumbers > from))
     21     display.updateLineNumbers = from
     22 
     23   cm.curOp.viewChanged = true
     24 
     25   if (from >= display.viewTo) { // Change after
     26     if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
     27       resetView(cm)
     28   } else if (to <= display.viewFrom) { // Change before
     29     if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
     30       resetView(cm)
     31     } else {
     32       display.viewFrom += lendiff
     33       display.viewTo += lendiff
     34     }
     35   } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
     36     resetView(cm)
     37   } else if (from <= display.viewFrom) { // Top overlap
     38     let cut = viewCuttingPoint(cm, to, to + lendiff, 1)
     39     if (cut) {
     40       display.view = display.view.slice(cut.index)
     41       display.viewFrom = cut.lineN
     42       display.viewTo += lendiff
     43     } else {
     44       resetView(cm)
     45     }
     46   } else if (to >= display.viewTo) { // Bottom overlap
     47     let cut = viewCuttingPoint(cm, from, from, -1)
     48     if (cut) {
     49       display.view = display.view.slice(0, cut.index)
     50       display.viewTo = cut.lineN
     51     } else {
     52       resetView(cm)
     53     }
     54   } else { // Gap in the middle
     55     let cutTop = viewCuttingPoint(cm, from, from, -1)
     56     let cutBot = viewCuttingPoint(cm, to, to + lendiff, 1)
     57     if (cutTop && cutBot) {
     58       display.view = display.view.slice(0, cutTop.index)
     59         .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
     60         .concat(display.view.slice(cutBot.index))
     61       display.viewTo += lendiff
     62     } else {
     63       resetView(cm)
     64     }
     65   }
     66 
     67   let ext = display.externalMeasured
     68   if (ext) {
     69     if (to < ext.lineN)
     70       ext.lineN += lendiff
     71     else if (from < ext.lineN + ext.size)
     72       display.externalMeasured = null
     73   }
     74 }
     75 
     76 // Register a change to a single line. Type must be one of "text",
     77 // "gutter", "class", "widget"
     78 export function regLineChange(cm, line, type) {
     79   cm.curOp.viewChanged = true
     80   let display = cm.display, ext = cm.display.externalMeasured
     81   if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
     82     display.externalMeasured = null
     83 
     84   if (line < display.viewFrom || line >= display.viewTo) return
     85   let lineView = display.view[findViewIndex(cm, line)]
     86   if (lineView.node == null) return
     87   let arr = lineView.changes || (lineView.changes = [])
     88   if (indexOf(arr, type) == -1) arr.push(type)
     89 }
     90 
     91 // Clear the view.
     92 export function resetView(cm) {
     93   cm.display.viewFrom = cm.display.viewTo = cm.doc.first
     94   cm.display.view = []
     95   cm.display.viewOffset = 0
     96 }
     97 
     98 function viewCuttingPoint(cm, oldN, newN, dir) {
     99   let index = findViewIndex(cm, oldN), diff, view = cm.display.view
    100   if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
    101     return {index: index, lineN: newN}
    102   let n = cm.display.viewFrom
    103   for (let i = 0; i < index; i++)
    104     n += view[i].size
    105   if (n != oldN) {
    106     if (dir > 0) {
    107       if (index == view.length - 1) return null
    108       diff = (n + view[index].size) - oldN
    109       index++
    110     } else {
    111       diff = n - oldN
    112     }
    113     oldN += diff; newN += diff
    114   }
    115   while (visualLineNo(cm.doc, newN) != newN) {
    116     if (index == (dir < 0 ? 0 : view.length - 1)) return null
    117     newN += dir * view[index - (dir < 0 ? 1 : 0)].size
    118     index += dir
    119   }
    120   return {index: index, lineN: newN}
    121 }
    122 
    123 // Force the view to cover a given range, adding empty view element
    124 // or clipping off existing ones as needed.
    125 export function adjustView(cm, from, to) {
    126   let display = cm.display, view = display.view
    127   if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
    128     display.view = buildViewArray(cm, from, to)
    129     display.viewFrom = from
    130   } else {
    131     if (display.viewFrom > from)
    132       display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view)
    133     else if (display.viewFrom < from)
    134       display.view = display.view.slice(findViewIndex(cm, from))
    135     display.viewFrom = from
    136     if (display.viewTo < to)
    137       display.view = display.view.concat(buildViewArray(cm, display.viewTo, to))
    138     else if (display.viewTo > to)
    139       display.view = display.view.slice(0, findViewIndex(cm, to))
    140   }
    141   display.viewTo = to
    142 }
    143 
    144 // Count the number of lines in the view whose DOM representation is
    145 // out of date (or nonexistent).
    146 export function countDirtyView(cm) {
    147   let view = cm.display.view, dirty = 0
    148   for (let i = 0; i < view.length; i++) {
    149     let lineView = view[i]
    150     if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty
    151   }
    152   return dirty
    153 }