| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798 |
- import { Annotation, Compartment } from '@codemirror/state'
- import { EditorView, ViewPlugin } from '@codemirror/view'
- import { themeOptionsChange } from './theme'
- import { sourceOnly } from './visual/visual'
- import { round } from 'lodash'
- import { hasLanguageLoadedEffect } from './language'
- import { fontLoad, hasFontLoadedEffect } from './font-load'
- const themeConf = new Compartment()
- const changeHalfLeadingAnnotation = Annotation.define<boolean>()
- function firstVisibleNonSpacePos(view: EditorView) {
- for (const range of view.visibleRanges) {
- const match = /\S/.exec(view.state.sliceDoc(range.from, range.to))
- if (match) {
- return range.from + match.index
- }
- }
- return null
- }
- function measureHalfLeading(view: EditorView) {
- const pos = firstVisibleNonSpacePos(view)
- if (pos === null) {
- return 0
- }
- const coords = view.coordsAtPos(pos)
- if (!coords) {
- return 0
- }
- const inlineBoxHeight = coords.bottom - coords.top
- // Rounding prevents gaps appearing in some situations
- return round((view.defaultLineHeight - inlineBoxHeight) / 2, 2)
- }
- function createTheme(halfLeading: number) {
- return EditorView.contentAttributes.of({
- style: `--half-leading: ${halfLeading}px`,
- })
- }
- /**
- * A custom extension which measures the height of the first non-space position and provides a CSS variable via an editor theme,
- * used for extending elements over the whole line height using padding.
- */
- const plugin = ViewPlugin.define(
- view => {
- let halfLeading = 0
- const measureRequest = {
- read: () => {
- return measureHalfLeading(view)
- },
- write: (newHalfLeading: number) => {
- if (newHalfLeading !== halfLeading) {
- halfLeading = newHalfLeading
- window.setTimeout(() =>
- view.dispatch({
- effects: themeConf.reconfigure(createTheme(newHalfLeading)),
- annotations: changeHalfLeadingAnnotation.of(true),
- })
- )
- }
- },
- }
- return {
- update(update) {
- // Ignore any update triggered by this plugin
- if (
- update.transactions.some(tr =>
- tr.annotation(changeHalfLeadingAnnotation)
- )
- ) {
- return
- }
- if (
- hasFontLoadedEffect(update) ||
- (update.geometryChanged && !update.docChanged) ||
- update.transactions.some(tr => tr.annotation(themeOptionsChange)) ||
- hasLanguageLoadedEffect(update)
- ) {
- view.requestMeasure(measureRequest)
- }
- },
- }
- },
- {
- provide: () => [themeConf.of(createTheme(0))],
- }
- )
- export const inlineBackground = (visual: boolean) => {
- return sourceOnly(visual, [fontLoad, plugin])
- }
|