From 1a6f3fc2565779ac0c5308ca6b6c45a8a1618535 Mon Sep 17 00:00:00 2001 From: Mathias Jakobsen Date: Mon, 18 Dec 2023 10:55:11 +0000 Subject: [PATCH] Merge pull request #16253 from overleaf/mj-human-readable-logs-fixes [web] Fix incorrect error log parsing and imprecise package recommendations GitOrigin-RevId: a0b9c6c51ebf680bb77be88167ab6d35eaa8fa70 --- .../HumanReadableLogsPackageSuggestions.js | 1 + .../HumanReadableLogsRules.tsx | 4 +- .../js/ide/log-parser/latex-log-parser.js | 15 +- .../logs/undefined-control-sequence.log | 179 ++++++++++++++++++ .../human-readable-logs.test.js | 81 ++++++++ .../frontend/ide/log-parser/logParserTests.js | 17 ++ 6 files changed, 290 insertions(+), 7 deletions(-) create mode 100644 services/web/test/frontend/helpers/fixtures/logs/undefined-control-sequence.log create mode 100644 services/web/test/frontend/ide/human-readable-logs/human-readable-logs.test.js diff --git a/services/web/frontend/js/ide/human-readable-logs/HumanReadableLogsPackageSuggestions.js b/services/web/frontend/js/ide/human-readable-logs/HumanReadableLogsPackageSuggestions.js index 8f4dd0000b..fe1e9c8aa1 100644 --- a/services/web/frontend/js/ide/human-readable-logs/HumanReadableLogsPackageSuggestions.js +++ b/services/web/frontend/js/ide/human-readable-logs/HumanReadableLogsPackageSuggestions.js @@ -25,6 +25,7 @@ const commandSuggestions = [ ['\\href', { name: 'hyperref', command: '\\usepackage{hyperref}' }], ['\\texorpdfstring', { name: 'hyperref', command: '\\usepackage{hyperref}' }], ['\\phantomsection', { name: 'hyperref', command: '\\usepackage{hyperref}' }], + ['\\arraybackslash', { name: 'array', command: '\\usepackage{array}' }], ] const environmentSuggestions = [ diff --git a/services/web/frontend/js/ide/human-readable-logs/HumanReadableLogsRules.tsx b/services/web/frontend/js/ide/human-readable-logs/HumanReadableLogsRules.tsx index 1808545ba9..7d64d6463f 100644 --- a/services/web/frontend/js/ide/human-readable-logs/HumanReadableLogsRules.tsx +++ b/services/web/frontend/js/ide/human-readable-logs/HumanReadableLogsRules.tsx @@ -61,8 +61,8 @@ const rules: Rule[] = [ { ruleId: 'hint_undefined_control_sequence', regexToMatch: /Undefined control sequence/, - contentRegex: - /^(?:l\.[0-9]+|<(?:recently read|inserted text|to be read again)>)\s*(\\\S+)/, + // Match the last control sequence in the line + contentRegex: /^[^\n]*(\\\S+)\s*[\n]/, improvedTitle: (currentTitle: string, details?: [string]) => { if (details?.length && packageSuggestionsForCommands.has(details[0])) { const command = details[0] diff --git a/services/web/frontend/js/ide/log-parser/latex-log-parser.js b/services/web/frontend/js/ide/log-parser/latex-log-parser.js index f38f65d8fb..6463f6e93f 100644 --- a/services/web/frontend/js/ide/log-parser/latex-log-parser.js +++ b/services/web/frontend/js/ide/log-parser/latex-log-parser.js @@ -74,11 +74,11 @@ export default class LatexParser { .join('\n') this.currentError.content += '\n' this.currentError.content += this.log - .linesUpToNextWhitespaceLine() + .linesUpToNextWhitespaceLine(true) .join('\n') this.currentError.content += '\n' this.currentError.content += this.log - .linesUpToNextWhitespaceLine() + .linesUpToNextWhitespaceLine(true) .join('\n') this.currentError.raw += this.currentError.content const lineNo = this.currentError.raw.match(/l\.([0-9]+)/) @@ -372,11 +372,11 @@ class LogText { this.row-- } - linesUpToNextWhitespaceLine() { - return this.linesUpToNextMatchingLine(/^ *$/) + linesUpToNextWhitespaceLine(stopAtError) { + return this.linesUpToNextMatchingLine(/^ *$/, stopAtError) } - linesUpToNextMatchingLine(match) { + linesUpToNextMatchingLine(match, stopAtError) { const lines = [] while (true) { @@ -386,6 +386,11 @@ class LogText { break } + if (stopAtError && nextLine.match(/^! /)) { + this.rewindLine() + break + } + lines.push(nextLine) if (nextLine.match(match)) { diff --git a/services/web/test/frontend/helpers/fixtures/logs/undefined-control-sequence.log b/services/web/test/frontend/helpers/fixtures/logs/undefined-control-sequence.log new file mode 100644 index 0000000000..206317f25d --- /dev/null +++ b/services/web/test/frontend/helpers/fixtures/logs/undefined-control-sequence.log @@ -0,0 +1,179 @@ +This is pdfTeX, Version 3.141592653-2.6-1.40.25 (TeX Live 2023) (preloaded format=pdflatex 2023.8.28) 14 DEC 2023 11:46 +entering extended mode + \write18 enabled. + %&-line parsing enabled. +**main.tex +(./main.tex +LaTeX2e <2023-06-01> patch level 1 +L3 programming layer <2023-06-30> +(/usr/local/texlive/2023/texmf-dist/tex/latex/base/article.cls +Document Class: article 2023/05/17 v1.4n Standard LaTeX document class +(/usr/local/texlive/2023/texmf-dist/tex/latex/base/size10.clo +File: size10.clo 2023/05/17 v1.4n Standard LaTeX file (size option) +) +\c@part=\count185 +\c@section=\count186 +\c@subsection=\count187 +\c@subsubsection=\count188 +\c@paragraph=\count189 +\c@subparagraph=\count190 +\c@figure=\count191 +\c@table=\count192 +\abovecaptionskip=\skip48 +\belowcaptionskip=\skip49 +\bibindent=\dimen140 +) (/usr/local/texlive/2023/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def +File: l3backend-pdftex.def 2023-04-19 L3 backend support: PDF output (pdfTeX) +\l__color_backend_stack_int=\count193 +\l__pdf_internal_box=\box51 +) (./output.aux) +\openout1 = `output.aux'. + +LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 2. +LaTeX Font Info: ... okay on input line 2. +LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 2. +LaTeX Font Info: ... okay on input line 2. +LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 2. +LaTeX Font Info: ... okay on input line 2. +LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 2. +LaTeX Font Info: ... okay on input line 2. +LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 2. +LaTeX Font Info: ... okay on input line 2. +LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 2. +LaTeX Font Info: ... okay on input line 2. +LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 2. +LaTeX Font Info: ... okay on input line 2. +! Undefined control sequence. +l.3 \foo + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.4 bar \baz + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +LaTeX Font Info: External font `cmex10' loaded for size +(Font) <7> on input line 5. +LaTeX Font Info: External font `cmex10' loaded for size +(Font) <5> on input line 5. +! Undefined control sequence. + >{\qux + }p{2cm}|c +l.5 \begin{tabular}{>{\qux}p{2cm}|c} + \end{tabular} +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + + +! LaTeX Error: Illegal character in array arg. + +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... + +l.5 \begin{tabular}{>{\qux}p{2cm}|c} + \end{tabular} +You're in trouble here. Try typing to proceed. +If that doesn't work, type X to quit. + + +! LaTeX Error: Illegal character in array arg. + +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... + +l.5 \begin{tabular}{>{\qux}p{2cm}|c} + \end{tabular} +You're in trouble here. Try typing to proceed. +If that doesn't work, type X to quit. + +! Undefined control sequence. +l.6 \url + {http://example.com} +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.7 my \text + {foo} +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. + >{\arraybackslash + }p{2cm}|c +l.8 \begin{tabular}{>{\arraybackslash}p{2cm}|c} + \end{tabular} +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + + +! LaTeX Error: Illegal character in array arg. + +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... + +l.8 \begin{tabular}{>{\arraybackslash}p{2cm}|c} + \end{tabular} +You're in trouble here. Try typing to proceed. +If that doesn't work, type X to quit. + + +! LaTeX Error: Illegal character in array arg. + +See the LaTeX manual or LaTeX Companion for explanation. +Type H for immediate help. + ... + +l.8 \begin{tabular}{>{\arraybackslash}p{2cm}|c} + \end{tabular} +You're in trouble here. Try typing to proceed. +If that doesn't work, type X to quit. + +[1 + +{/usr/local/texlive/2023/texmf-var/fonts/map/pdftex/updmap/pdftex.map}] (./output.aux) + *********** +LaTeX2e <2023-06-01> patch level 1 +L3 programming layer <2023-06-30> + *********** + ) +Here is how much of TeX's memory you used: + 433 strings out of 475237 + 8319 string characters out of 5764570 + 1916244 words of memory out of 5000000 + 21757 multiletter control sequences out of 15000+600000 + 558069 words of font info for 36 fonts, out of 8000000 for 9000 + 1141 hyphenation exceptions out of 8191 + 35i,5n,50p,139b,107s stack positions out of 10000i,1000n,20000p,200000b,200000s + +Output written on output.pdf (1 page, 14417 bytes). +PDF statistics: + 13 PDF objects out of 1000 (max. 8388607) + 7 compressed objects within 1 object stream + 0 named destinations out of 1000 (max. 500000) + 1 words of extra memory for PDF output out of 10000 (max. 10000000) + diff --git a/services/web/test/frontend/ide/human-readable-logs/human-readable-logs.test.js b/services/web/test/frontend/ide/human-readable-logs/human-readable-logs.test.js new file mode 100644 index 0000000000..d2f7b8daaf --- /dev/null +++ b/services/web/test/frontend/ide/human-readable-logs/human-readable-logs.test.js @@ -0,0 +1,81 @@ +import { expect } from 'chai' +import HumanReadableLogs from '../../../../frontend/js/ide/human-readable-logs/HumanReadableLogs' +import { readFile } from 'fs/promises' +import { join } from 'path' +import { some } from 'lodash' + +const fixturePath = '../../helpers/fixtures/logs/' + +async function parse(fixtureName) { + const filePath = join(__dirname, fixturePath, fixtureName) + const data = await readFile(filePath, 'utf-8', 'r') + return HumanReadableLogs.parse(data) +} + +describe('HumanReadableLogs', function () { + describe('Undefined commands', function () { + before(async function () { + this.errors = (await parse('undefined-control-sequence.log')).errors + }) + + describe('For unknown commands', function () { + it('Identifies command at beginning of line', function () { + expect( + some(this.errors, { + line: 3, + level: 'error', + message: 'Undefined control sequence.', + }) + ).to.be.true + }) + it('Identifies command at end of line', function () { + expect( + some(this.errors, { + line: 4, + level: 'error', + message: 'Undefined control sequence.', + }) + ).to.be.true + }) + it('Identifies command inside argument', function () { + expect( + some(this.errors, { + line: 5, + level: 'error', + message: 'Undefined control sequence.', + }) + ).to.be.true + }) + }) + + describe('For known commands', function () { + it('Identifies command at beginning of line', function () { + expect( + some(this.errors, { + line: 6, + level: 'error', + message: 'Is \\usepackage{url} missing?', + }) + ).to.be.true + }) + it('Identifies command at end of line', function () { + expect( + some(this.errors, { + line: 7, + level: 'error', + message: 'Is \\usepackage{amsmath} missing?', + }) + ).to.be.true + }) + it('Identifies command inside argument', function () { + expect( + some(this.errors, { + line: 8, + level: 'error', + message: 'Is \\usepackage{array} missing?', + }) + ).to.be.true + }) + }) + }) +}) diff --git a/services/web/test/frontend/ide/log-parser/logParserTests.js b/services/web/test/frontend/ide/log-parser/logParserTests.js index 3e0752dc6a..2d2ec43c28 100644 --- a/services/web/test/frontend/ide/log-parser/logParserTests.js +++ b/services/web/test/frontend/ide/log-parser/logParserTests.js @@ -400,6 +400,23 @@ describe('logParser', function (done) { ], ]) }) + + it('should parse errors without blank lines between them', function () { + const { errors, warnings } = parseLatexLog('undefined-control-sequence.log') + expect(warnings).to.be.empty + expect(errors.map(x => [x.line, x.message, x.file])).to.deep.equal([ + [3, 'Undefined control sequence.', './main.tex'], + [4, 'Undefined control sequence.', './main.tex'], + [5, 'Undefined control sequence.', './main.tex'], + [5, 'LaTeX Error: Illegal character in array arg.', './main.tex'], + [5, 'LaTeX Error: Illegal character in array arg.', './main.tex'], + [6, 'Undefined control sequence.', './main.tex'], + [7, 'Undefined control sequence.', './main.tex'], + [8, 'Undefined control sequence.', './main.tex'], + [8, 'LaTeX Error: Illegal character in array arg.', './main.tex'], + [8, 'LaTeX Error: Illegal character in array arg.', './main.tex'], + ]) + }) }) function readLog(filename) {