From 5871978272d3eac6d3c7c93758e378094d28e97a Mon Sep 17 00:00:00 2001
From: Alf Eaton <alf.eaton@overleaf.com>
Date: Tue, 23 Jan 2024 13:00:32 +0000
Subject: [PATCH] Use renderMessage (#16628)

GitOrigin-RevId: 9f627a31d0f8ffce841f04f5a9bfa09f6abcd0a3
---
 .../features/pdf-preview/util/output-files.js |  1 +
 .../source-editor/extensions/annotations.ts   | 45 +++++++++++++++----
 .../latex/linter/errors-to-diagnostics.ts     | 10 ++++-
 services/web/types/annotation.ts              |  1 +
 4 files changed, 47 insertions(+), 10 deletions(-)

diff --git a/services/web/frontend/js/features/pdf-preview/util/output-files.js b/services/web/frontend/js/features/pdf-preview/util/output-files.js
index ce4a037444..64fe101b3f 100644
--- a/services/web/frontend/js/features/pdf-preview/util/output-files.js
+++ b/services/web/frontend/js/features/pdf-preview/util/output-files.js
@@ -155,6 +155,7 @@ export function buildLogEntryAnnotations(entries, fileTreeData, rootDocId) {
           type: entry.level === 'error' ? 'error' : 'warning',
           text: entry.message,
           source: 'compile', // NOTE: this is used in Ace for filtering the annotations
+          ruleId: entry.ruleId,
         })
       }
     }
diff --git a/services/web/frontend/js/features/source-editor/extensions/annotations.ts b/services/web/frontend/js/features/source-editor/extensions/annotations.ts
index d99ef399d3..b0a13c3ddb 100644
--- a/services/web/frontend/js/features/source-editor/extensions/annotations.ts
+++ b/services/web/frontend/js/features/source-editor/extensions/annotations.ts
@@ -11,6 +11,7 @@ import {
 } from '@codemirror/state'
 import { Annotation } from '../../../../../types/annotation'
 import { debugConsole } from '@/utils/debugging'
+import { sendMB } from '@/infrastructure/event-tracking'
 
 const compileLintSourceConf = new Compartment()
 
@@ -56,29 +57,36 @@ const compileLogLintSource = (): Extension =>
     const items: Diagnostic[] = []
     const cursor = view.state.field(compileDiagnosticsState).iter()
     while (cursor.value !== null) {
+      const { diagnostic } = cursor.value
       items.push({
-        ...cursor.value.diagnostic,
+        ...diagnostic,
         from: cursor.from,
         to: cursor.to,
+        renderMessage: () => renderMessage(diagnostic),
       })
       cursor.next()
     }
     return items
   }, lintSourceConfig)
 
-class DiagnosticRangeValue extends RangeValue {
-  constructor(public diagnostic: Diagnostic) {
+interface CompileLogDiagnostic extends Diagnostic {
+  compile?: true
+  ruleId?: string
+}
+
+class CompileLogDiagnosticRangeValue extends RangeValue {
+  constructor(public diagnostic: CompileLogDiagnostic) {
     super()
   }
 }
 
-const setCompileDiagnosticsEffect = StateEffect.define<Diagnostic[]>()
+const setCompileDiagnosticsEffect = StateEffect.define<CompileLogDiagnostic[]>()
 
 /**
  * A state field for the compile log diagnostics
  */
 export const compileDiagnosticsState = StateField.define<
-  RangeSet<DiagnosticRangeValue>
+  RangeSet<CompileLogDiagnosticRangeValue>
 >({
   create() {
     return RangeSet.empty
@@ -88,7 +96,7 @@ export const compileDiagnosticsState = StateField.define<
       if (effect.is(setCompileDiagnosticsEffect)) {
         return RangeSet.of(
           effect.value.map(diagnostic =>
-            new DiagnosticRangeValue(diagnostic).range(
+            new CompileLogDiagnosticRangeValue(diagnostic).range(
               diagnostic.from,
               diagnostic.to
             )
@@ -138,7 +146,7 @@ export const showCompileLogDiagnostics = (show: boolean) => {
 const convertAnnotationToDiagnostic = (
   doc: Text,
   annotation: Annotation
-): Diagnostic => {
+): CompileLogDiagnostic => {
   if (annotation.row < 0) {
     throw new Error(`Invalid annotation row ${annotation.row}`)
   }
@@ -150,6 +158,27 @@ const convertAnnotationToDiagnostic = (
     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
+    ruleId: annotation.ruleId,
+    compile: true,
   }
 }
+
+export const renderMessage = (
+  diagnostic: Pick<
+    CompileLogDiagnostic,
+    'message' | 'severity' | 'ruleId' | 'compile'
+  >
+) => {
+  const { message, severity, ruleId, compile = false } = diagnostic
+
+  const div = document.createElement('div')
+  div.textContent = message
+
+  window.setTimeout(() => {
+    if (div.isConnected) {
+      sendMB('lint-gutter-marker-view', { severity, ruleId, compile })
+    }
+  }, 500) // 500ms delay to indicate intention, rather than accidental hover
+
+  return div
+}
diff --git a/services/web/frontend/js/features/source-editor/languages/latex/linter/errors-to-diagnostics.ts b/services/web/frontend/js/features/source-editor/languages/latex/linter/errors-to-diagnostics.ts
index f3e095c52c..c2e9646bf5 100644
--- a/services/web/frontend/js/features/source-editor/languages/latex/linter/errors-to-diagnostics.ts
+++ b/services/web/frontend/js/features/source-editor/languages/latex/linter/errors-to-diagnostics.ts
@@ -1,5 +1,6 @@
 import { Diagnostic } from '@codemirror/lint'
 import { Range } from '../../../utils/range'
+import { renderMessage } from '@/features/source-editor/extensions/annotations'
 
 export type LintError = {
   startPos: number
@@ -78,12 +79,17 @@ export const errorsToDiagnostics = (
     const newStart = movableStart ? cursorPosition : errorRange.from
     const newEnd = movableEnd ? cursorPosition : errorRange.to
 
-    // Create the diagnostic
-    diagnostics.push({
+    const diagnostic: Diagnostic = {
       from: newStart,
       to: newEnd,
       severity: error.type,
       message: error.text,
+    }
+
+    // Create the diagnostic
+    diagnostics.push({
+      ...diagnostic,
+      renderMessage: () => renderMessage(diagnostic),
     })
   }
 
diff --git a/services/web/types/annotation.ts b/services/web/types/annotation.ts
index bae368b7c5..9d5f974b6f 100644
--- a/services/web/types/annotation.ts
+++ b/services/web/types/annotation.ts
@@ -3,4 +3,5 @@ export type Annotation = {
   type: 'info' | 'warning' | 'error'
   text: string
   source?: string
+  ruleId?: string
 }