mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-07 20:31:06 -05:00
140 lines
3.3 KiB
TypeScript
140 lines
3.3 KiB
TypeScript
|
import { EditorView } from '@codemirror/view'
|
||
|
import { Diagnostic, linter, lintGutter } from '@codemirror/lint'
|
||
|
import {
|
||
|
Compartment,
|
||
|
RangeSet,
|
||
|
RangeValue,
|
||
|
StateEffect,
|
||
|
StateField,
|
||
|
Text,
|
||
|
} from '@codemirror/state'
|
||
|
import { Annotation } from '../../../../../types/annotation'
|
||
|
|
||
|
const compileLintSourceConf = new Compartment()
|
||
|
|
||
|
export const annotations = () => [
|
||
|
compileDiagnosticsState,
|
||
|
compileLintSourceConf.of(compileLogLintSource()),
|
||
|
lintGutter({
|
||
|
hoverTime: 0,
|
||
|
}),
|
||
|
// move the lint gutter outside the line numbers
|
||
|
EditorView.baseTheme({
|
||
|
'.cm-gutter-lint': {
|
||
|
order: -1,
|
||
|
},
|
||
|
}),
|
||
|
]
|
||
|
|
||
|
export const lintSourceConfig = {
|
||
|
delay: 100,
|
||
|
// Show highlights only for errors
|
||
|
markerFilter(diagnostics: readonly Diagnostic[]) {
|
||
|
return diagnostics.filter(d => d.severity === 'error')
|
||
|
},
|
||
|
// Do not show any tooltips for highlights within the editor content
|
||
|
tooltipFilter() {
|
||
|
return []
|
||
|
},
|
||
|
}
|
||
|
|
||
|
const compileLogLintSource = () =>
|
||
|
linter(view => {
|
||
|
const items: Diagnostic[] = []
|
||
|
const cursor = view.state.field(compileDiagnosticsState).iter()
|
||
|
while (cursor.value !== null) {
|
||
|
items.push({
|
||
|
...cursor.value.diagnostic,
|
||
|
from: cursor.from,
|
||
|
to: cursor.to,
|
||
|
})
|
||
|
cursor.next()
|
||
|
}
|
||
|
return items
|
||
|
}, lintSourceConfig)
|
||
|
|
||
|
class DiagnosticRangeValue extends RangeValue {
|
||
|
constructor(public diagnostic: Diagnostic) {
|
||
|
super()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const setCompileDiagnosticsEffect = StateEffect.define<Diagnostic[]>()
|
||
|
|
||
|
export const compileDiagnosticsState = StateField.define<
|
||
|
RangeSet<DiagnosticRangeValue>
|
||
|
>({
|
||
|
create() {
|
||
|
return RangeSet.empty
|
||
|
},
|
||
|
update(value, transaction) {
|
||
|
for (const effect of transaction.effects) {
|
||
|
if (effect.is(setCompileDiagnosticsEffect)) {
|
||
|
return RangeSet.of(
|
||
|
effect.value.map(diagnostic =>
|
||
|
new DiagnosticRangeValue(diagnostic).range(
|
||
|
diagnostic.from,
|
||
|
diagnostic.to
|
||
|
)
|
||
|
),
|
||
|
true
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (transaction.docChanged) {
|
||
|
value = value.map(transaction.changes)
|
||
|
}
|
||
|
|
||
|
return value
|
||
|
},
|
||
|
})
|
||
|
|
||
|
export const setAnnotations = (doc: Text, annotations: Annotation[]) => {
|
||
|
const diagnostics: Diagnostic[] = []
|
||
|
|
||
|
for (const annotation of annotations) {
|
||
|
// ignore "whole document" (row: -1) annotations
|
||
|
if (annotation.row !== -1) {
|
||
|
try {
|
||
|
diagnostics.push(convertAnnotationToDiagnostic(doc, annotation))
|
||
|
} catch (error) {
|
||
|
// ignore invalid annotations
|
||
|
console.debug('invalid annotation position', error)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
effects: setCompileDiagnosticsEffect.of(diagnostics),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export const showCompileLogDiagnostics = (show: boolean) => {
|
||
|
return {
|
||
|
effects: [
|
||
|
// reconfigure the compile log lint source
|
||
|
compileLintSourceConf.reconfigure(show ? compileLogLintSource() : []),
|
||
|
],
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const convertAnnotationToDiagnostic = (
|
||
|
doc: Text,
|
||
|
annotation: Annotation
|
||
|
): Diagnostic => {
|
||
|
if (annotation.row < 0) {
|
||
|
throw new Error(`Invalid annotation row ${annotation.row}`)
|
||
|
}
|
||
|
|
||
|
const line = doc.line(annotation.row + 1)
|
||
|
|
||
|
return {
|
||
|
from: line.from,
|
||
|
to: line.to, // NOTE: highlight whole line as synctex doesn't output column number
|
||
|
severity: annotation.type,
|
||
|
message: annotation.text,
|
||
|
// source: annotation.source, // NOTE: the source is displayed in the tooltip
|
||
|
}
|
||
|
}
|