2023-04-13 04:21:25 -04:00
|
|
|
import {
|
|
|
|
Decoration,
|
|
|
|
DecorationSet,
|
|
|
|
EditorView,
|
|
|
|
layer,
|
|
|
|
LayerMarker,
|
|
|
|
RectangleMarker,
|
|
|
|
ViewPlugin,
|
|
|
|
ViewUpdate,
|
|
|
|
} from '@codemirror/view'
|
|
|
|
import { sourceOnly } from './visual/visual'
|
|
|
|
import { fullHeightCoordsAtPos } from '../utils/layer'
|
|
|
|
|
2023-06-08 04:35:51 -04:00
|
|
|
/**
|
|
|
|
* An alternative version of the built-in highlightActiveLine extension,
|
|
|
|
* using a custom approach for highlighting the full height of the active “visual line” of a wrapped line.
|
|
|
|
*/
|
2023-04-13 04:21:25 -04:00
|
|
|
export const highlightActiveLine = (visual: boolean) => {
|
|
|
|
// this extension should only be active in the source editor
|
|
|
|
return sourceOnly(visual, [
|
|
|
|
activeLineLayer,
|
|
|
|
singleLineHighlighter,
|
|
|
|
EditorView.baseTheme({
|
|
|
|
'.ol-cm-activeLineLayer': {
|
|
|
|
pointerEvents: 'none',
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
])
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Line decoration approach used for non-wrapped lines, adapted from built-in
|
|
|
|
* CodeMirror 6 highlightActiveLine, licensed under the MIT license:
|
|
|
|
* https://github.com/codemirror/view/blob/main/src/active-line.ts
|
|
|
|
*/
|
|
|
|
const lineDeco = Decoration.line({ class: 'cm-activeLine' })
|
|
|
|
|
|
|
|
const singleLineHighlighter = ViewPlugin.fromClass(
|
|
|
|
class {
|
|
|
|
decorations: DecorationSet
|
|
|
|
|
|
|
|
constructor(view: EditorView) {
|
|
|
|
this.decorations = this.getDeco(view)
|
|
|
|
}
|
|
|
|
|
|
|
|
update(update: ViewUpdate) {
|
2023-04-18 04:14:39 -04:00
|
|
|
if (update.geometryChanged || update.selectionSet) {
|
2023-04-13 04:21:25 -04:00
|
|
|
this.decorations = this.getDeco(update.view)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getDeco(view: EditorView) {
|
|
|
|
const deco = []
|
|
|
|
|
|
|
|
// NOTE: only highlighting the active line for the main selection
|
|
|
|
const { main } = view.state.selection
|
|
|
|
|
|
|
|
// No active line highlight when text is selected
|
|
|
|
if (main.empty) {
|
|
|
|
const line = view.lineBlockAt(main.head)
|
|
|
|
if (line.height <= view.defaultLineHeight) {
|
|
|
|
deco.push(lineDeco.range(line.from))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Decoration.set(deco)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
decorations: v => v.decorations,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
// Custom layer approach, used only for wrapped lines
|
|
|
|
const activeLineLayer = layer({
|
|
|
|
above: false,
|
|
|
|
class: 'ol-cm-activeLineLayer',
|
|
|
|
markers(view: EditorView): readonly LayerMarker[] {
|
|
|
|
const markers: LayerMarker[] = []
|
|
|
|
|
|
|
|
// NOTE: only highlighting the active line for the main selection
|
|
|
|
const { main } = view.state.selection
|
|
|
|
|
|
|
|
// no active line highlight when text is selected
|
|
|
|
if (!main.empty) {
|
|
|
|
return markers
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use line decoration when line doesn't wrap
|
|
|
|
if (view.lineBlockAt(main.head).height <= view.defaultLineHeight) {
|
|
|
|
return markers
|
|
|
|
}
|
|
|
|
|
|
|
|
const coords = fullHeightCoordsAtPos(
|
|
|
|
view,
|
|
|
|
main.head,
|
|
|
|
main.assoc || undefined
|
|
|
|
)
|
|
|
|
|
|
|
|
if (coords) {
|
|
|
|
const scrollRect = view.scrollDOM.getBoundingClientRect()
|
|
|
|
const contentRect = view.contentDOM.getBoundingClientRect()
|
|
|
|
const scrollTop = view.scrollDOM.scrollTop
|
|
|
|
|
|
|
|
const top = coords.top - scrollRect.top + scrollTop
|
|
|
|
const left = contentRect.left - scrollRect.left
|
|
|
|
const width = contentRect.right - contentRect.left
|
|
|
|
const height = coords.bottom - coords.top
|
|
|
|
|
|
|
|
markers.push(
|
|
|
|
new RectangleMarker('cm-activeLine', left, top, width, height)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return markers
|
|
|
|
},
|
|
|
|
update(update: ViewUpdate): boolean {
|
|
|
|
return update.geometryChanged || update.selectionSet
|
|
|
|
},
|
|
|
|
})
|