ec45fe92a67d90f7f3bb5b4d3fb40ed165fd6206f4bf6c6040de65380a175f191a4acfe29a22e2bd8a413cd310014da7ef57455cc4421d5ad23aff48a009c1 5.3 KB

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