empty-line-filler.ts 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import {
  2. Decoration,
  3. DecorationSet,
  4. EditorView,
  5. ViewPlugin,
  6. ViewUpdate,
  7. WidgetType,
  8. } from '@codemirror/view'
  9. import browser from './browser'
  10. class EmptyLineWidget extends WidgetType {
  11. toDOM(): HTMLElement {
  12. const element = document.createElement('span')
  13. element.className = 'ol-cm-filler'
  14. return element
  15. }
  16. eq() {
  17. return true
  18. }
  19. }
  20. /**
  21. * A custom extension which adds a widget decoration at the start of each empty line in the viewport,
  22. * so that the line is highlighted when part of tracked changes.
  23. */
  24. export const emptyLineFiller = () => {
  25. if (browser.ios) {
  26. // disable on iOS as it breaks Backspace across empty lines
  27. // https://github.com/overleaf/internal/issues/12192
  28. return []
  29. }
  30. return [
  31. ViewPlugin.fromClass(
  32. class {
  33. decorations: DecorationSet
  34. constructor(view: EditorView) {
  35. this.decorations = this.buildDecorations(view)
  36. }
  37. update(update: ViewUpdate) {
  38. if (update.docChanged || update.viewportChanged)
  39. this.decorations = this.buildDecorations(update.view)
  40. }
  41. buildDecorations(view: EditorView) {
  42. const decorations = []
  43. const { from, to } = view.viewport
  44. const { doc } = view.state
  45. let pos = from
  46. while (pos <= to) {
  47. const line = doc.lineAt(pos)
  48. if (line.length === 0) {
  49. const decoration = Decoration.widget({
  50. widget: new EmptyLineWidget(),
  51. side: 1,
  52. })
  53. decorations.push(decoration.range(pos))
  54. }
  55. pos = line.to + 1
  56. }
  57. return Decoration.set(decorations)
  58. }
  59. },
  60. {
  61. decorations(value) {
  62. return value.decorations
  63. },
  64. }
  65. ),
  66. emptyLineFillerTheme,
  67. ]
  68. }
  69. const emptyLineFillerTheme = EditorView.baseTheme({
  70. '.ol-cm-filler': {
  71. padding: '0 2px',
  72. },
  73. })