annotations.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import { EditorView, ViewUpdate } from '@codemirror/view'
  2. import { Diagnostic, linter, lintGutter } from '@codemirror/lint'
  3. import {
  4. Compartment,
  5. RangeSet,
  6. RangeValue,
  7. StateEffect,
  8. StateField,
  9. Text,
  10. } from '@codemirror/state'
  11. import { Annotation } from '../../../../../types/annotation'
  12. const compileLintSourceConf = new Compartment()
  13. export const annotations = () => [
  14. compileDiagnosticsState,
  15. compileLintSourceConf.of(compileLogLintSource()),
  16. lintGutter({
  17. hoverTime: 0,
  18. }),
  19. // move the lint gutter outside the line numbers
  20. EditorView.baseTheme({
  21. '.cm-gutter-lint': {
  22. order: -1,
  23. },
  24. }),
  25. ]
  26. export const lintSourceConfig = {
  27. delay: 100,
  28. // Show highlights only for errors
  29. markerFilter(diagnostics: readonly Diagnostic[]) {
  30. return diagnostics.filter(d => d.severity === 'error')
  31. },
  32. // Do not show any tooltips for highlights within the editor content
  33. tooltipFilter() {
  34. return []
  35. },
  36. needsRefresh(update: ViewUpdate) {
  37. return update.selectionSet
  38. },
  39. }
  40. const compileLogLintSource = () =>
  41. linter(view => {
  42. const items: Diagnostic[] = []
  43. const cursor = view.state.field(compileDiagnosticsState).iter()
  44. while (cursor.value !== null) {
  45. items.push({
  46. ...cursor.value.diagnostic,
  47. from: cursor.from,
  48. to: cursor.to,
  49. })
  50. cursor.next()
  51. }
  52. return items
  53. }, lintSourceConfig)
  54. class DiagnosticRangeValue extends RangeValue {
  55. constructor(public diagnostic: Diagnostic) {
  56. super()
  57. }
  58. }
  59. const setCompileDiagnosticsEffect = StateEffect.define<Diagnostic[]>()
  60. export const compileDiagnosticsState = StateField.define<
  61. RangeSet<DiagnosticRangeValue>
  62. >({
  63. create() {
  64. return RangeSet.empty
  65. },
  66. update(value, transaction) {
  67. for (const effect of transaction.effects) {
  68. if (effect.is(setCompileDiagnosticsEffect)) {
  69. return RangeSet.of(
  70. effect.value.map(diagnostic =>
  71. new DiagnosticRangeValue(diagnostic).range(
  72. diagnostic.from,
  73. diagnostic.to
  74. )
  75. ),
  76. true
  77. )
  78. }
  79. }
  80. if (transaction.docChanged) {
  81. value = value.map(transaction.changes)
  82. }
  83. return value
  84. },
  85. })
  86. export const setAnnotations = (doc: Text, annotations: Annotation[]) => {
  87. const diagnostics: Diagnostic[] = []
  88. for (const annotation of annotations) {
  89. // ignore "whole document" (row: -1) annotations
  90. if (annotation.row !== -1) {
  91. try {
  92. diagnostics.push(convertAnnotationToDiagnostic(doc, annotation))
  93. } catch (error) {
  94. // ignore invalid annotations
  95. console.debug('invalid annotation position', error)
  96. }
  97. }
  98. }
  99. return {
  100. effects: setCompileDiagnosticsEffect.of(diagnostics),
  101. }
  102. }
  103. export const showCompileLogDiagnostics = (show: boolean) => {
  104. return {
  105. effects: [
  106. // reconfigure the compile log lint source
  107. compileLintSourceConf.reconfigure(show ? compileLogLintSource() : []),
  108. ],
  109. }
  110. }
  111. const convertAnnotationToDiagnostic = (
  112. doc: Text,
  113. annotation: Annotation
  114. ): Diagnostic => {
  115. if (annotation.row < 0) {
  116. throw new Error(`Invalid annotation row ${annotation.row}`)
  117. }
  118. const line = doc.line(annotation.row + 1)
  119. return {
  120. from: line.from,
  121. to: line.to, // NOTE: highlight whole line as synctex doesn't output column number
  122. severity: annotation.type,
  123. message: annotation.text,
  124. // source: annotation.source, // NOTE: the source is displayed in the tooltip
  125. }
  126. }