| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- 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
- }
|