Merge pull request #7060 from overleaf/ii-5273-latex-log-parsers

Clean up log parsers code

GitOrigin-RevId: 7dc4dbbb07bba72dda63d75502602570620fe07d
This commit is contained in:
ilkin-overleaf 2022-03-21 16:46:39 +02:00 committed by Copybot
parent 21e753ca21
commit be0774be8f
2 changed files with 69 additions and 91 deletions

View file

@ -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

View file

@ -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
}
}