Decorating newline characters

highlightNewLine extension

import type { DecorationSet, EditorView, ViewUpdate } from '@codemirror/view'
import { Decoration, ViewPlugin, WidgetType } from '@codemirror/view'

class NewlineWidget extends WidgetType {
  toDOM() {
    const span = document.createElement('span')
    span.className = 'cm-newline'
    span.textContent = '↵'
    return span
  }
}

function highlightNewLine() {
  return ViewPlugin.fromClass(
    class {
      decorations: DecorationSet
      constructor(view: EditorView) {
        this.decorations = this.getDecorations(view)
      }

      getDecorations(view: EditorView) {
        const widgets = []
        for (const { from, to } of view.visibleRanges) {
          for (let pos = from; pos <= to;) {
            const line = view.state.doc.lineAt(pos)
            if (line.length === 0) {
              widgets.push(Decoration.widget({ widget: new NewlineWidget(), side: 1 }).range(pos))
            }
            else {
              widgets.push(Decoration.widget({ widget: new NewlineWidget(), side: 1 }).range(line.to))
            }
            pos = line.to + 1
          }
        }
        return Decoration.set(widgets, true)
      }

      update(update: ViewUpdate) {
        if (update.docChanged || update.viewportChanged) {
          this.decorations = this.getDecorations(update.view)
        }
      }
    },
    {
      decorations: v => v.decorations,
    },
  )
}

export { highlightNewLine }

style

.cm-newline {
  color: currentColor;
  pointer-events: none;
  opacity: 0.5;
}
2 Likes