From 4994ecf1dd07194f0aae5f0ce704746cdd95af30 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Fri, 15 Sep 2023 13:17:58 +0530 Subject: [PATCH] feat: Add error highlight --- .eslintrc.cjs | 2 ++ src/lib/util/errorHandling.ts | 47 +++++++++++++++++++++++++++++++++++ src/lib/util/state.ts | 37 +++++++++++++++++++++------ 3 files changed, 78 insertions(+), 8 deletions(-) create mode 100644 src/lib/util/errorHandling.ts diff --git a/.eslintrc.cjs b/.eslintrc.cjs index af91af2d..aecc89b6 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -86,6 +86,8 @@ module.exports = { env: true, fn: true, i: true, + j: true, + k: true, param: true, req: true, res: true, diff --git a/src/lib/util/errorHandling.ts b/src/lib/util/errorHandling.ts new file mode 100644 index 00000000..9e3b1bf4 --- /dev/null +++ b/src/lib/util/errorHandling.ts @@ -0,0 +1,47 @@ +// Function to find the line number with the most characters in common with the error +export function findMostRelevantLineNumber(errorLineText: string, code: string): number { + const codeLines = code.split('\n'); + let mostRelevantLineNumber = -1; + let maxCommonLength = 0; + + for (const [i, line] of codeLines.entries()) { + let commonLength = 0; + for (let j = 0; j <= errorLineText.length; j++) { + for (let k = j + 1; k <= errorLineText.length; k++) { + const sub = errorLineText.slice(j, k); + if (line.includes(sub)) { + commonLength = Math.max(commonLength, sub.length); + } + } + } + if (commonLength > maxCommonLength) { + maxCommonLength = commonLength; + mostRelevantLineNumber = i + 1; // Line numbers start from 1 + } + } + return mostRelevantLineNumber; +} + +// Function to replace the incorrect line number in the error message +export function replaceLineNumberInErrorMessage( + errorMessage: string, + realLineNumber: number +): string { + const regexParseError = /Parse error on line (\d+):/; + const regexLexError = /Lexical error on line (\d+)/; + return errorMessage + .replace(regexParseError, `Parse error on line ${realLineNumber}:`) + .replace(regexLexError, `Lexical error on line ${realLineNumber}:`); +} + +export function extractErrorLineText(errorMessage: string): string { + const regex = /Error: Parse error on line \d+:\n(.+)\n+/; + const match = errorMessage.match(regex); + if (match) { + return match[1].slice(3); + } + + const regexLex = /Error: Lexical error on line \d+. Unrecognized text.\n(.+)\n-+/; + const matchLex = errorMessage.match(regexLex); + return matchLex ? matchLex[1].slice(3) : ''; +} diff --git a/src/lib/util/state.ts b/src/lib/util/state.ts index 2cff194e..89311005 100644 --- a/src/lib/util/state.ts +++ b/src/lib/util/state.ts @@ -7,6 +7,11 @@ import { parse } from './mermaid'; import type { ErrorHash, MarkerData, State, ValidatedState } from '$lib/types'; import type { MermaidConfig } from 'mermaid'; +import { + findMostRelevantLineNumber, + extractErrorLineText, + replaceLineNumberInErrorMessage +} from './errorHandling'; export const defaultState: State = { code: `flowchart TD @@ -67,18 +72,34 @@ const processState = async (state: State) => { console.error(error); if ('hash' in error) { try { - const { - loc: { first_line, last_line, first_column, last_column } + let errorString = processed.error.toString(); + const errorLineText = extractErrorLineText(errorString); + const realLineNumber = findMostRelevantLineNumber(errorLineText, state.code); + + let first_line: number, last_line: number, first_column: number, last_column: number; + try { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - } = error.hash as ErrorHash; + ({ first_line, last_line, first_column, last_column } = (error.hash as ErrorHash).loc); + } catch { + const lineNo = findMostRelevantLineNumber(errorString, state.code); + first_line = lineNo; + last_line = lineNo + 1; + first_column = 0; + last_column = 0; + } + + if (realLineNumber !== -1) { + errorString = replaceLineNumberInErrorMessage(errorString, realLineNumber); + } + + processed.error = new Error(errorString); const marker: MarkerData = { severity: 8, // Error - startLineNumber: first_line, + startLineNumber: realLineNumber, startColumn: first_column, - endLineNumber: last_line, - endColumn: last_column + 1, - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment - message: error.str + endLineNumber: last_line + (realLineNumber - first_line), + endColumn: last_column + (first_column === last_column ? 0 : 5), + message: errorString || 'Syntax error' }; processed.errorMarkers = [marker]; } catch (error) {