overleaf/services/web/frontend/js/features/source-editor/extensions/empty-line-filler.ts
Alf Eaton 3f29aa2195 Ensure that each editor theme is only created once (#17095)
GitOrigin-RevId: 3551e02fab44fae7fcab5cb12886d45969e3990f
2024-02-16 09:05:03 +00:00

81 lines
1.9 KiB
TypeScript

import {
Decoration,
DecorationSet,
EditorView,
ViewPlugin,
ViewUpdate,
WidgetType,
} from '@codemirror/view'
import browser from './browser'
class EmptyLineWidget extends WidgetType {
toDOM(view: EditorView): HTMLElement {
const element = document.createElement('span')
element.className = 'ol-cm-filler'
return element
}
eq(widget: EmptyLineWidget) {
return true
}
}
/**
* A custom extension which adds a widget decoration at the start of each empty line in the viewport,
* so that the line is highlighted when part of tracked changes.
*/
export const emptyLineFiller = () => {
if (browser.ios) {
// disable on iOS as it breaks Backspace across empty lines
// https://github.com/overleaf/internal/issues/12192
return []
}
return [
ViewPlugin.fromClass(
class {
decorations: DecorationSet
constructor(view: EditorView) {
this.decorations = this.buildDecorations(view)
}
update(update: ViewUpdate) {
if (update.docChanged || update.viewportChanged)
this.decorations = this.buildDecorations(update.view)
}
buildDecorations(view: EditorView) {
const decorations = []
const { from, to } = view.viewport
const { doc } = view.state
let pos = from
while (pos <= to) {
const line = doc.lineAt(pos)
if (line.length === 0) {
const decoration = Decoration.widget({
widget: new EmptyLineWidget(),
side: 1,
})
decorations.push(decoration.range(pos))
}
pos = line.to + 1
}
return Decoration.set(decorations)
}
},
{
decorations(value) {
return value.decorations
},
}
),
emptyLineFillerTheme,
]
}
const emptyLineFillerTheme = EditorView.baseTheme({
'.ol-cm-filler': {
padding: '0 2px',
},
})