88388bffed2463344f5e17b9e29119d48b6f999a45e16314e4f800ec430a3dca45f442c897751ad0b95d1486ffd257e8e25ac9caa339615062056d7970a6a5 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import { buildLineContent } from "../line/line_data.js"
  2. import { lineNumberFor } from "../line/utils_line.js"
  3. import { ie, ie_version } from "../util/browser.js"
  4. import { elt, classTest } from "../util/dom.js"
  5. import { signalLater } from "../util/operation_group.js"
  6. // When an aspect of a line changes, a string is added to
  7. // lineView.changes. This updates the relevant part of the line's
  8. // DOM structure.
  9. export function updateLineForChanges(cm, lineView, lineN, dims) {
  10. for (let j = 0; j < lineView.changes.length; j++) {
  11. let type = lineView.changes[j]
  12. if (type == "text") updateLineText(cm, lineView)
  13. else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims)
  14. else if (type == "class") updateLineClasses(cm, lineView)
  15. else if (type == "widget") updateLineWidgets(cm, lineView, dims)
  16. }
  17. lineView.changes = null
  18. }
  19. // Lines with gutter elements, widgets or a background class need to
  20. // be wrapped, and have the extra elements added to the wrapper div
  21. function ensureLineWrapped(lineView) {
  22. if (lineView.node == lineView.text) {
  23. lineView.node = elt("div", null, null, "position: relative")
  24. if (lineView.text.parentNode)
  25. lineView.text.parentNode.replaceChild(lineView.node, lineView.text)
  26. lineView.node.appendChild(lineView.text)
  27. if (ie && ie_version < 8) lineView.node.style.zIndex = 2
  28. }
  29. return lineView.node
  30. }
  31. function updateLineBackground(cm, lineView) {
  32. let cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass
  33. if (cls) cls += " CodeMirror-linebackground"
  34. if (lineView.background) {
  35. if (cls) lineView.background.className = cls
  36. else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null }
  37. } else if (cls) {
  38. let wrap = ensureLineWrapped(lineView)
  39. lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild)
  40. cm.display.input.setUneditable(lineView.background)
  41. }
  42. }
  43. // Wrapper around buildLineContent which will reuse the structure
  44. // in display.externalMeasured when possible.
  45. function getLineContent(cm, lineView) {
  46. let ext = cm.display.externalMeasured
  47. if (ext && ext.line == lineView.line) {
  48. cm.display.externalMeasured = null
  49. lineView.measure = ext.measure
  50. return ext.built
  51. }
  52. return buildLineContent(cm, lineView)
  53. }
  54. // Redraw the line's text. Interacts with the background and text
  55. // classes because the mode may output tokens that influence these
  56. // classes.
  57. function updateLineText(cm, lineView) {
  58. let cls = lineView.text.className
  59. let built = getLineContent(cm, lineView)
  60. if (lineView.text == lineView.node) lineView.node = built.pre
  61. lineView.text.parentNode.replaceChild(built.pre, lineView.text)
  62. lineView.text = built.pre
  63. if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
  64. lineView.bgClass = built.bgClass
  65. lineView.textClass = built.textClass
  66. updateLineClasses(cm, lineView)
  67. } else if (cls) {
  68. lineView.text.className = cls
  69. }
  70. }
  71. function updateLineClasses(cm, lineView) {
  72. updateLineBackground(cm, lineView)
  73. if (lineView.line.wrapClass)
  74. ensureLineWrapped(lineView).className = lineView.line.wrapClass
  75. else if (lineView.node != lineView.text)
  76. lineView.node.className = ""
  77. let textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass
  78. lineView.text.className = textClass || ""
  79. }
  80. function updateLineGutter(cm, lineView, lineN, dims) {
  81. if (lineView.gutter) {
  82. lineView.node.removeChild(lineView.gutter)
  83. lineView.gutter = null
  84. }
  85. if (lineView.gutterBackground) {
  86. lineView.node.removeChild(lineView.gutterBackground)
  87. lineView.gutterBackground = null
  88. }
  89. if (lineView.line.gutterClass) {
  90. let wrap = ensureLineWrapped(lineView)
  91. lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
  92. `left: ${cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth}px; width: ${dims.gutterTotalWidth}px`)
  93. cm.display.input.setUneditable(lineView.gutterBackground)
  94. wrap.insertBefore(lineView.gutterBackground, lineView.text)
  95. }
  96. let markers = lineView.line.gutterMarkers
  97. if (cm.options.lineNumbers || markers) {
  98. let wrap = ensureLineWrapped(lineView)
  99. let gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", `left: ${cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth}px`)
  100. gutterWrap.setAttribute("aria-hidden", "true")
  101. cm.display.input.setUneditable(gutterWrap)
  102. wrap.insertBefore(gutterWrap, lineView.text)
  103. if (lineView.line.gutterClass)
  104. gutterWrap.className += " " + lineView.line.gutterClass
  105. if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
  106. lineView.lineNumber = gutterWrap.appendChild(
  107. elt("div", lineNumberFor(cm.options, lineN),
  108. "CodeMirror-linenumber CodeMirror-gutter-elt",
  109. `left: ${dims.gutterLeft["CodeMirror-linenumbers"]}px; width: ${cm.display.lineNumInnerWidth}px`))
  110. if (markers) for (let k = 0; k < cm.display.gutterSpecs.length; ++k) {
  111. let id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id]
  112. if (found)
  113. gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
  114. `left: ${dims.gutterLeft[id]}px; width: ${dims.gutterWidth[id]}px`))
  115. }
  116. }
  117. }
  118. function updateLineWidgets(cm, lineView, dims) {
  119. if (lineView.alignable) lineView.alignable = null
  120. let isWidget = classTest("CodeMirror-linewidget")
  121. for (let node = lineView.node.firstChild, next; node; node = next) {
  122. next = node.nextSibling
  123. if (isWidget.test(node.className)) lineView.node.removeChild(node)
  124. }
  125. insertLineWidgets(cm, lineView, dims)
  126. }
  127. // Build a line's DOM representation from scratch
  128. export function buildLineElement(cm, lineView, lineN, dims) {
  129. let built = getLineContent(cm, lineView)
  130. lineView.text = lineView.node = built.pre
  131. if (built.bgClass) lineView.bgClass = built.bgClass
  132. if (built.textClass) lineView.textClass = built.textClass
  133. updateLineClasses(cm, lineView)
  134. updateLineGutter(cm, lineView, lineN, dims)
  135. insertLineWidgets(cm, lineView, dims)
  136. return lineView.node
  137. }
  138. // A lineView may contain multiple logical lines (when merged by
  139. // collapsed spans). The widgets for all of them need to be drawn.
  140. function insertLineWidgets(cm, lineView, dims) {
  141. insertLineWidgetsFor(cm, lineView.line, lineView, dims, true)
  142. if (lineView.rest) for (let i = 0; i < lineView.rest.length; i++)
  143. insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false)
  144. }
  145. function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
  146. if (!line.widgets) return
  147. let wrap = ensureLineWrapped(lineView)
  148. for (let i = 0, ws = line.widgets; i < ws.length; ++i) {
  149. let widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : ""))
  150. if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true")
  151. positionLineWidget(widget, node, lineView, dims)
  152. cm.display.input.setUneditable(node)
  153. if (allowAbove && widget.above)
  154. wrap.insertBefore(node, lineView.gutter || lineView.text)
  155. else
  156. wrap.appendChild(node)
  157. signalLater(widget, "redraw")
  158. }
  159. }
  160. function positionLineWidget(widget, node, lineView, dims) {
  161. if (widget.noHScroll) {
  162. ;(lineView.alignable || (lineView.alignable = [])).push(node)
  163. let width = dims.wrapperWidth
  164. node.style.left = dims.fixedPos + "px"
  165. if (!widget.coverGutter) {
  166. width -= dims.gutterTotalWidth
  167. node.style.paddingLeft = dims.gutterTotalWidth + "px"
  168. }
  169. node.style.width = width + "px"
  170. }
  171. if (widget.coverGutter) {
  172. node.style.zIndex = 5
  173. node.style.position = "relative"
  174. if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px"
  175. }
  176. }