diff --git a/services/web/frontend/js/ide/log-parser/bib-log-parser.js b/services/web/frontend/js/ide/log-parser/bib-log-parser.js index a7aa241b45..a9669f7953 100644 --- a/services/web/frontend/js/ide/log-parser/bib-log-parser.js +++ b/services/web/frontend/js/ide/log-parser/bib-log-parser.js @@ -18,32 +18,21 @@ const MESSAGE_LEVELS = { ERROR: 'error', } -const parserReducer = function (maxErrors, buildMaxErrorsReachedMessage) { +const parserReducer = function (maxErrors) { return function (accumulator, parser) { const consume = function (logText, regex, process) { let match let text = logText const result = [] - const re = regex let iterationCount = 0 - while ((match = re.exec(text))) { - iterationCount += 1 + + while ((match = regex.exec(text))) { + iterationCount++ const newEntry = process(match) // Too many log entries can cause browser crashes // Construct a too many files error from the last match if (maxErrors != null && iterationCount >= maxErrors) { - if (buildMaxErrorsReachedMessage) { - const level = newEntry.level + 's' - newEntry.message = [ - 'Over', - maxErrors, - level, - 'returned. Download raw logs to see full list', - ].join(' ') - newEntry.line = undefined - result.unshift(newEntry) - } return [result, ''] } @@ -55,6 +44,7 @@ const parserReducer = function (maxErrors, buildMaxErrorsReachedMessage) { match.input.length ) } + return [result, text] } @@ -66,12 +56,12 @@ const parserReducer = function (maxErrors, buildMaxErrorsReachedMessage) { } export default class BibLogParser { - constructor(text, options) { + constructor(text, options = {}) { if (typeof text !== 'string') { throw new Error('BibLogParser Error: text parameter must be a string') } this.text = text.replace(/(\r\n)|\r/g, '\n') - this.options = options || {} + this.options = options this.lines = text.split('\n') // each parser is a pair of [regex, processFunction], where processFunction @@ -163,33 +153,23 @@ export default class BibLogParser { } parseBibtex() { - let allErrors - const result = { - all: [], - errors: [], - warnings: [], + // reduce over the parsers, starting with the log text, + const [allWarnings, remainingText] = this.warningParsers.reduce( + parserReducer(this.options.maxErrors), + [[], this.text] + ) + const [allErrors] = this.errorParsers.reduce( + parserReducer(this.options.maxErrors), + [[], remainingText] + ) + + return { + all: allWarnings.concat(allErrors), + errors: allErrors, + warnings: allWarnings, files: [], // not used typesetting: [], // not used } - // reduce over the parsers, starting with the log text, - let [allWarnings, remainingText] = this.warningParsers.reduce( - parserReducer( - this.options.maxErrors, - this.options.buildMaxErrorsReachedMessage - ), - [[], this.text] - ) - ;[allErrors, remainingText] = this.errorParsers.reduce( - parserReducer( - this.options.maxErrors, - this.options.buildMaxErrorsReachedMessage - ), - [[], remainingText] - ) - result.warnings = allWarnings - result.errors = allErrors - result.all = allWarnings.concat(allErrors) - return result } parseBiber() { @@ -203,9 +183,7 @@ export default class BibLogParser { this.lines.forEach(function (line) { const match = line.match(LINE_SPLITTER_REGEX) if (match) { - const fullLine = match[0] - const messageType = match[2] - const message = match[3] + const [fullLine, , messageType, message] = match const newEntry = { file: '', level: MESSAGE_LEVELS[messageType] || 'INFO', @@ -218,10 +196,8 @@ export default class BibLogParser { const lineMatch = newEntry.message.match( /^BibTeX subsystem: \/.+\/(\w+\.\w+)_.+, line (\d+), (.+)$/ ) - if (lineMatch && lineMatch.length === 4) { - const fileName = lineMatch[1] - const lineNumber = lineMatch[2] - const realMessage = lineMatch[3] + if (lineMatch) { + const [, fileName, lineNumber, realMessage] = lineMatch newEntry.file = fileName newEntry.line = lineNumber newEntry.message = realMessage 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 eec6ad4126..386bb8c2ec 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 @@ -15,9 +15,8 @@ const STATE = { } export default class LatexParser { - constructor(text, options) { + constructor(text, options = {}) { this.state = STATE.NORMAL - options = options || {} this.fileBaseNames = options.fileBaseNames || [/compiles/, /\/usr\/local/] this.ignoreDuplicates = options.ignoreDuplicates this.data = [] @@ -159,10 +158,10 @@ export default class LatexParser { parseMultipleWarningLine() { // Some package warnings are multiple lines, let's parse the first line let warningMatch = this.currentLine.match(PACKAGE_WARNING_REGEX) + // Something strange happened, return early if (!warningMatch) { return } - // Something strange happened, return early const warningLines = [warningMatch[1]] let lineMatch = this.currentLine.match(LINES_REGEX) let line = lineMatch ? parseInt(lineMatch[1], 10) : null @@ -285,62 +284,60 @@ export default class LatexParser { postProcess(data) { const all = [] - const errors = [] - const warnings = [] - const typesetting = [] - const hashes = [] + const errorsByLevel = { + error: [], + warning: [], + typesetting: [], + } + const hashes = new Set() const hashEntry = entry => entry.raw - let i = 0 - while (i < data.length) { - if (this.ignoreDuplicates && hashes.indexOf(hashEntry(data[i])) > -1) { - i++ - continue + data.forEach(item => { + const hash = hashEntry(item) + + if (this.ignoreDuplicates && hashes.has(hash)) { + return } - if (data[i].level === 'error') { - errors.push(data[i]) - } else if (data[i].level === 'typesetting') { - typesetting.push(data[i]) - } else if (data[i].level === 'warning') { - warnings.push(data[i]) - } - all.push(data[i]) - hashes.push(hashEntry(data[i])) - i++ - } + + errorsByLevel[item.level]?.push(item) + + all.push(item) + hashes.add(hash) + }) + return { - errors, - warnings, - typesetting, + errors: errorsByLevel.error, + warnings: errorsByLevel.warning, + typesetting: errorsByLevel.typesetting, all, files: this.rootFileList, } } } -const LogText = class LogText { +class LogText { constructor(text) { this.text = text.replace(/(\r\n)|\r/g, '\n') // Join any lines which look like they have wrapped. const wrappedLines = this.text.split('\n') this.lines = [wrappedLines[0]] - let i = 1 - while (i < wrappedLines.length) { + + for (let i = 1; i < wrappedLines.length; i++) { // If the previous line is as long as the wrap limit then // append this line to it. // Some lines end with ... when LaTeX knows it's hit the limit // These shouldn't be wrapped. - if ( - wrappedLines[i - 1].length === LOG_WRAP_LIMIT && - wrappedLines[i - 1].slice(-3) !== '...' - ) { - this.lines[this.lines.length - 1] += wrappedLines[i] + const prevLine = wrappedLines[i - 1] + const currentLine = wrappedLines[i] + + if (prevLine.length === LOG_WRAP_LIMIT && prevLine.slice(-3) !== '...') { + this.lines[this.lines.length - 1] += currentLine } else { - this.lines.push(wrappedLines[i]) + this.lines.push(currentLine) } - i++ } + this.row = 0 } @@ -363,16 +360,21 @@ const LogText = class LogText { linesUpToNextMatchingLine(match) { const lines = [] - let nextLine = this.nextLine() - if (nextLine !== false) { + + while (true) { + const nextLine = this.nextLine() + + if (nextLine === false) { + break + } + lines.push(nextLine) - } - while (nextLine !== false && !nextLine.match(match) && nextLine !== false) { - nextLine = this.nextLine() - if (nextLine !== false) { - lines.push(nextLine) + + if (nextLine.match(match)) { + break } } + return lines } }