Просмотр исходного кода

Merge pull request #16253 from overleaf/mj-human-readable-logs-fixes

[web] Fix incorrect error log parsing and imprecise package recommendations

GitOrigin-RevId: a0b9c6c51ebf680bb77be88167ab6d35eaa8fa70
Mathias Jakobsen 2 лет назад
Родитель
Сommit
1a6f3fc256

+ 1 - 0
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 = [

+ 2 - 2
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]

+ 10 - 5
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)) {

+ 179 - 0
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.
+<argument> >{\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 <return>  for immediate help.
+ ...                                              
+                                                  
+l.5 \begin{tabular}{>{\qux}p{2cm}|c}
+                                    \end{tabular}
+You're in trouble here.  Try typing  <return>  to proceed.
+If that doesn't work, type  X <return>  to quit.
+
+
+! LaTeX Error: Illegal character in array arg.
+
+See the LaTeX manual or LaTeX Companion for explanation.
+Type  H <return>  for immediate help.
+ ...                                              
+                                                  
+l.5 \begin{tabular}{>{\qux}p{2cm}|c}
+                                    \end{tabular}
+You're in trouble here.  Try typing  <return>  to proceed.
+If that doesn't work, type  X <return>  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.
+<argument> >{\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 <return>  for immediate help.
+ ...                                              
+                                                  
+l.8 \begin{tabular}{>{\arraybackslash}p{2cm}|c}
+                                               \end{tabular}
+You're in trouble here.  Try typing  <return>  to proceed.
+If that doesn't work, type  X <return>  to quit.
+
+
+! LaTeX Error: Illegal character in array arg.
+
+See the LaTeX manual or LaTeX Companion for explanation.
+Type  H <return>  for immediate help.
+ ...                                              
+                                                  
+l.8 \begin{tabular}{>{\arraybackslash}p{2cm}|c}
+                                               \end{tabular}
+You're in trouble here.  Try typing  <return>  to proceed.
+If that doesn't work, type  X <return>  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
+</usr/local/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb>
+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)
+

+ 81 - 0
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
+      })
+    })
+  })
+})

+ 17 - 0
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) {