Преглед изворни кода

Merge pull request #327 from sharelatex/ja-log-parser-improvements

Update log parser to better handle Runaway argument errors
James Allen пре 10 година
родитељ
комит
5188a138de
1 измењених фајлова са 288 додато и 311 уклоњено
  1. 288 311
      services/web/public/js/libs/latex-log-parser.js

+ 288 - 311
services/web/public/js/libs/latex-log-parser.js

@@ -1,321 +1,298 @@
+// Generated by CoffeeScript 1.10.0
 define(function() {
-    // Define some constants
-    var LOG_WRAP_LIMIT = 79;
-    var LATEX_WARNING_REGEX = /^LaTeX Warning: (.*)$/;
-    var HBOX_WARNING_REGEX = /^(Over|Under)full \\(v|h)box/;
-    var PACKAGE_WARNING_REGEX = /^(Package \b.+\b Warning:.*)$/; 
-    // This is used to parse the line number from common latex warnings
-    var LINES_REGEX = /lines? ([0-9]+)/;
-    // This is used to parse the package name from the package warnings
-    var PACKAGE_REGEX = /^Package (\b.+\b) Warning/;
-
-    var LogText = function(text) {
-        this.text = text.replace(/(\r\n)|\r/g, "\n");
-
-        // Join any lines which look like they have wrapped.
-        var wrappedLines = this.text.split("\n");
-        this.lines = [wrappedLines[0]];
-        for (var 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];
-            } else {
-                this.lines.push(wrappedLines[i]);
-            }
+  var HBOX_WARNING_REGEX, LATEX_WARNING_REGEX, LINES_REGEX, LOG_WRAP_LIMIT, LatexParser, LogText, PACKAGE_REGEX, PACKAGE_WARNING_REGEX, state;
+  LOG_WRAP_LIMIT = 79;
+  LATEX_WARNING_REGEX = /^LaTeX Warning: (.*)$/;
+  HBOX_WARNING_REGEX = /^(Over|Under)full \\(v|h)box/;
+  PACKAGE_WARNING_REGEX = /^(Package \b.+\b Warning:.*)$/;
+  LINES_REGEX = /lines? ([0-9]+)/;
+  PACKAGE_REGEX = /^Package (\b.+\b) Warning/;
+  LogText = function(text) {
+    var i, wrappedLines;
+    this.text = text.replace(/(\r\n)|\r/g, '\n');
+    wrappedLines = this.text.split('\n');
+    this.lines = [wrappedLines[0]];
+    i = 1;
+    while (i < wrappedLines.length) {
+      if (wrappedLines[i - 1].length === LOG_WRAP_LIMIT && wrappedLines[i - 1].slice(-3) !== '...') {
+        this.lines[this.lines.length - 1] += wrappedLines[i];
+      } else {
+        this.lines.push(wrappedLines[i]);
+      }
+      i++;
+    }
+    this.row = 0;
+  };
+  (function() {
+    this.nextLine = function() {
+      this.row++;
+      if (this.row >= this.lines.length) {
+        return false;
+      } else {
+        return this.lines[this.row];
+      }
+    };
+    this.rewindLine = function() {
+      this.row--;
+    };
+    this.linesUpToNextWhitespaceLine = function() {
+      return this.linesUpToNextMatchingLine(/^ *$/);
+    };
+    this.linesUpToNextMatchingLine = function(match) {
+      var lines, nextLine;
+      lines = [];
+      nextLine = this.nextLine();
+      if (nextLine !== false) {
+        lines.push(nextLine);
+      }
+      while (nextLine !== false && !nextLine.match(match) && nextLine !== false) {
+        nextLine = this.nextLine();
+        if (nextLine !== false) {
+          lines.push(nextLine);
         }
-
-        this.row = 0;
+      }
+      return lines;
     };
-
-    (function() {
-        this.nextLine = function() {
-            this.row++;
-            if (this.row >= this.lines.length) {
-                return false;
-            } else {
-                return this.lines[this.row];
-            }
-        };
-
-        this.rewindLine = function() {
-            this.row--;
-        };
-
-        this.linesUpToNextWhitespaceLine = function() {
-            return this.linesUpToNextMatchingLine(/^ *$/);
-        };
-
-        this.linesUpToNextMatchingLine = function(match) {
-            var lines = [];
-            var nextLine = this.nextLine();
-            if (nextLine !== false) {
-                lines.push(nextLine);
-            }
-            while (nextLine !== false && !nextLine.match(match) && nextLine !== false) {
-                nextLine = this.nextLine();
-                if (nextLine !== false) {
-                    lines.push(nextLine);
-                }
-            }
-            return lines;
+  }).call(LogText.prototype);
+  state = {
+    NORMAL: 0,
+    ERROR: 1
+  };
+  LatexParser = function(text, options) {
+    this.log = new LogText(text);
+    this.state = state.NORMAL;
+    options = options || {};
+    this.fileBaseNames = options.fileBaseNames || [/compiles/, /\/usr\/local/];
+    this.ignoreDuplicates = options.ignoreDuplicates;
+    this.data = [];
+    this.fileStack = [];
+    this.currentFileList = this.rootFileList = [];
+    this.openParens = 0;
+  };
+  (function() {
+    this.parse = function() {
+      var lineNo;
+      while ((this.currentLine = this.log.nextLine()) !== false) {
+        if (this.state === state.NORMAL) {
+          if (this.currentLineIsError()) {
+            this.state = state.ERROR;
+            this.currentError = {
+              line: null,
+              file: this.currentFilePath,
+              level: 'error',
+              message: this.currentLine.slice(2),
+              content: '',
+              raw: this.currentLine + '\n'
+            };
+          } else if (this.currentLineIsRunawayArgument()) {
+            this.parseRunawayArgumentError();
+          } else if (this.currentLineIsWarning()) {
+            this.parseSingleWarningLine(LATEX_WARNING_REGEX);
+          } else if (this.currentLineIsHboxWarning()) {
+            this.parseHboxLine();
+          } else if (this.currentLineIsPackageWarning()) {
+            this.parseMultipleWarningLine();
+          } else {
+            this.parseParensForFilenames();
+          }
         }
-    }).call(LogText.prototype);
-
-    var state = {
-        NORMAL : 0,
-        ERROR  : 1
+        if (this.state === state.ERROR) {
+          this.currentError.content += this.log.linesUpToNextMatchingLine(/^l\.[0-9]+/).join('\n');
+          this.currentError.content += '\n';
+          this.currentError.content += this.log.linesUpToNextWhitespaceLine().join('\n');
+          this.currentError.content += '\n';
+          this.currentError.content += this.log.linesUpToNextWhitespaceLine().join('\n');
+          this.currentError.raw += this.currentError.content;
+          lineNo = this.currentError.raw.match(/l\.([0-9]+)/);
+          if (lineNo) {
+            this.currentError.line = parseInt(lineNo[1], 10);
+          }
+          this.data.push(this.currentError);
+          this.state = state.NORMAL;
+        }
+      }
+      return this.postProcess(this.data);
     };
-
-    var LatexParser = function(text, options) {
-        this.log = new LogText(text);
-        this.state = state.NORMAL;
-
-        options = options || {};
-        this.fileBaseNames = options.fileBaseNames || [/compiles/, /\/usr\/local/];
-        this.ignoreDuplicates = options.ignoreDuplicates;
-
-        this.data  = [];
-        this.fileStack = [];
-        this.currentFileList = this.rootFileList = [];
-
-        this.openParens = 0;
+    this.currentLineIsError = function() {
+      return this.currentLine[0] === '!';
     };
-
-    (function() {
-        this.parse = function() {
-            while ((this.currentLine = this.log.nextLine()) !== false) {
-                if (this.state == state.NORMAL) {
-                    if (this.currentLineIsError()) {
-                        this.state = state.ERROR;
-                        this.currentError = {
-                            line        : null,
-                            file        : this.currentFilePath,
-                            level       : "error",
-                            message     : this.currentLine.slice(2),
-                            content     : "",
-                            raw         : this.currentLine + "\n"
-                        }
-                    } else if (this.currentLineIsWarning()) {
-                        this.parseSingleWarningLine(LATEX_WARNING_REGEX);
-                    } else if (this.currentLineIsHboxWarning()) {
-                        this.parseHboxLine();
-                    } else if (this.currentLineIsPackageWarning()) {
-                        this.parseMultipleWarningLine();
-                    } else {
-                        this.parseParensForFilenames();
-                    }
-                }
-
-                if (this.state == state.ERROR) {
-                    this.currentError.content += this.log.linesUpToNextMatchingLine(/^l\.[0-9]+/).join("\n");
-                    this.currentError.content += "\n";
-                    this.currentError.content += this.log.linesUpToNextWhitespaceLine().join("\n");
-                    this.currentError.content += "\n";
-                    this.currentError.content += this.log.linesUpToNextWhitespaceLine().join("\n");
-
-                    this.currentError.raw += this.currentError.content;
-
-                    var lineNo = this.currentError.raw.match(/l\.([0-9]+)/);
-                    if (lineNo) {
-                        this.currentError.line = parseInt(lineNo[1], 10);
-                    }
-
-                    this.data.push(this.currentError);
-                    this.state = state.NORMAL;
-                }
-            }
-
-            return this.postProcess(this.data);
-        };
-
-        this.currentLineIsError = function() {
-            return this.currentLine[0] == "!";
-        };
-
-        this.currentLineIsWarning = function() {
-            return !!(this.currentLine.match(LATEX_WARNING_REGEX));
-        };
-
-        this.currentLineIsPackageWarning = function () {
-            return !!(this.currentLine.match(PACKAGE_WARNING_REGEX));
-        };
-
-        this.currentLineIsHboxWarning = function() {
-            return !!(this.currentLine.match(HBOX_WARNING_REGEX));
-        };
-
-        this.parseSingleWarningLine = function(prefix_regex) {
-            var warningMatch = this.currentLine.match(prefix_regex);
-            if (!warningMatch) return;
-            var warning = warningMatch[1];
-
-            var lineMatch = warning.match(LINES_REGEX);
-            var line = lineMatch ? parseInt(lineMatch[1], 10) : null;
-
-            this.data.push({
-                line    : line,
-                file    : this.currentFilePath,
-                level   : "warning",
-                message : warning,
-                raw     : warning
-            });
-        };
-
-        this.parseMultipleWarningLine = function() {
-            // Some package warnings are multiple lines, let's parse the first line
-            var warningMatch = this.currentLine.match(PACKAGE_WARNING_REGEX);
-            if (!warningMatch) return;  // Something strange happened, return early
-
-            var warning_lines = [warningMatch[1]];
-            var lineMatch = this.currentLine.match(LINES_REGEX);
-            var line = lineMatch ? parseInt(lineMatch[1], 10) : null;
-            var packageMatch = this.currentLine.match(PACKAGE_REGEX);
-            var packageName = packageMatch[1];
-
-            // Regex to get rid of the unnecesary (packagename) prefix in most multi-line warnings
-            var prefixRegex = new RegExp("(?:\\(" + packageName + "\\))*[\\s]*(.*)", "i");
-
-            // After every warning message there's a blank line, let's use it
-            while (!!(this.currentLine = this.log.nextLine())) {
-                lineMatch = this.currentLine.match(LINES_REGEX);
-                line = lineMatch ? parseInt(lineMatch[1], 10) : line;
-                warningMatch = this.currentLine.match(prefixRegex)
-                warning_lines.push(warningMatch[1]);
-            }
-
-            var raw_message = warning_lines.join(' ');
-            this.data.push({
-                line    : line,
-                file    : this.currentFilePath,
-                level   : "warning",
-                message : raw_message,
-                raw     : raw_message
-            });
-        };
-
-        this.parseHboxLine = function() {
-            var lineMatch = this.currentLine.match(LINES_REGEX);
-            var line = lineMatch ? parseInt(lineMatch[1], 10) : null;
-
-            this.data.push({
-                line    : line,
-                file    : this.currentFilePath,
-                level   : "typesetting",
-                message : this.currentLine,
-                raw     : this.currentLine
-            });
-        };
-
-        // Check if we're entering or leaving a new file in this line
-        this.parseParensForFilenames = function() {
-            var pos = this.currentLine.search(/\(|\)/);
-
-            if (pos != -1) {
-                var token = this.currentLine[pos];
-                this.currentLine = this.currentLine.slice(pos + 1);
-
-                if (token == "(") {
-                    var filePath = this.consumeFilePath();
-                    if (filePath) {
-                        this.currentFilePath = filePath;
-
-                        var newFile = {
-                            path : filePath,
-                            files : []
-                        };
-                        this.fileStack.push(newFile);
-                        this.currentFileList.push(newFile);
-                        this.currentFileList = newFile.files;
-                    } else {
-                        this.openParens++;
-                    }
-                } else if (token == ")") {
-                    if (this.openParens > 0) {
-                        this.openParens--;
-                    } else {
-                        if (this.fileStack.length > 1) {
-                            this.fileStack.pop();
-                            var previousFile = this.fileStack[this.fileStack.length - 1];
-                            this.currentFilePath = previousFile.path;
-                            this.currentFileList = previousFile.files;
-                        }
-                        // else {
-                        //     Something has gone wrong but all we can do now is ignore it :(
-                        // }
-                    }
-                }
-
-                // Process the rest of the line
-                this.parseParensForFilenames();
-            }
-        };
-
-        this.consumeFilePath = function() {
-            // Our heuristic for detecting file names are rather crude
-            // A file may not contain a space, or ) in it
-            // To be a file path it must have at least one /
-            if (!this.currentLine.match(/^\/?([^ \)]+\/)+/)) {
-                return false;
-            }
-
-            var endOfFilePath = this.currentLine.search(/ |\)/);
-            var path;
-            if (endOfFilePath == -1) {
-                path = this.currentLine;
-                this.currentLine = "";
-            } else {
-                path = this.currentLine.slice(0, endOfFilePath);
-                this.currentLine = this.currentLine.slice(endOfFilePath);
-            }
-
-            return path;
-        };
-
-        this.postProcess = function(data) {
-            var all         = [];
-            var errors      = [];
-            var warnings    = [];
-            var typesetting = [];
-
-            var hashes = [];
-
-            function hashEntry(entry) {
-                return entry.raw;
-            }
-
-            for (var i = 0; i < data.length; i++) {
-                if (this.ignoreDuplicates && hashes.indexOf(hashEntry(data[i])) > -1) {
-                    continue;
-                }
-
-                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]));
-            }
-
-            return {
-              errors      : errors,
-              warnings    : warnings,
-              typesetting : typesetting,
-              all         : all,
-              files       : this.rootFileList
+    this.currentLineIsRunawayArgument = function() {
+      return this.currentLine.match(/^Runaway argument/);
+    };
+    this.currentLineIsWarning = function() {
+      return !!this.currentLine.match(LATEX_WARNING_REGEX);
+    };
+    this.currentLineIsPackageWarning = function() {
+      return !!this.currentLine.match(PACKAGE_WARNING_REGEX);
+    };
+    this.currentLineIsHboxWarning = function() {
+      return !!this.currentLine.match(HBOX_WARNING_REGEX);
+    };
+    this.parseRunawayArgumentError = function() {
+      var lineNo;
+      this.currentError = {
+        line: null,
+        file: this.currentFilePath,
+        level: 'error',
+        message: this.currentLine,
+        content: '',
+        raw: this.currentLine + '\n'
+      };
+      this.currentError.content += this.log.linesUpToNextWhitespaceLine().join('\n');
+      this.currentError.content += '\n';
+      this.currentError.content += this.log.linesUpToNextWhitespaceLine().join('\n');
+      this.currentError.raw += this.currentError.content;
+      lineNo = this.currentError.raw.match(/l\.([0-9]+)/);
+      if (lineNo) {
+        this.currentError.line = parseInt(lineNo[1], 10);
+      }
+      return this.data.push(this.currentError);
+    };
+    this.parseSingleWarningLine = function(prefix_regex) {
+      var line, lineMatch, warning, warningMatch;
+      warningMatch = this.currentLine.match(prefix_regex);
+      if (!warningMatch) {
+        return;
+      }
+      warning = warningMatch[1];
+      lineMatch = warning.match(LINES_REGEX);
+      line = lineMatch ? parseInt(lineMatch[1], 10) : null;
+      this.data.push({
+        line: line,
+        file: this.currentFilePath,
+        level: 'warning',
+        message: warning,
+        raw: warning
+      });
+    };
+    this.parseMultipleWarningLine = function() {
+      var line, lineMatch, packageMatch, packageName, prefixRegex, raw_message, warningMatch, warning_lines;
+      warningMatch = this.currentLine.match(PACKAGE_WARNING_REGEX);
+      if (!warningMatch) {
+        return;
+      }
+      warning_lines = [warningMatch[1]];
+      lineMatch = this.currentLine.match(LINES_REGEX);
+      line = lineMatch ? parseInt(lineMatch[1], 10) : null;
+      packageMatch = this.currentLine.match(PACKAGE_REGEX);
+      packageName = packageMatch[1];
+      prefixRegex = new RegExp('(?:\\(' + packageName + '\\))*[\\s]*(.*)', 'i');
+      while (!!(this.currentLine = this.log.nextLine())) {
+        lineMatch = this.currentLine.match(LINES_REGEX);
+        line = lineMatch ? parseInt(lineMatch[1], 10) : line;
+        warningMatch = this.currentLine.match(prefixRegex);
+        warning_lines.push(warningMatch[1]);
+      }
+      raw_message = warning_lines.join(' ');
+      this.data.push({
+        line: line,
+        file: this.currentFilePath,
+        level: 'warning',
+        message: raw_message,
+        raw: raw_message
+      });
+    };
+    this.parseHboxLine = function() {
+      var line, lineMatch;
+      lineMatch = this.currentLine.match(LINES_REGEX);
+      line = lineMatch ? parseInt(lineMatch[1], 10) : null;
+      this.data.push({
+        line: line,
+        file: this.currentFilePath,
+        level: 'typesetting',
+        message: this.currentLine,
+        raw: this.currentLine
+      });
+    };
+    this.parseParensForFilenames = function() {
+      var filePath, newFile, pos, previousFile, token;
+      pos = this.currentLine.search(/\(|\)/);
+      if (pos !== -1) {
+        token = this.currentLine[pos];
+        this.currentLine = this.currentLine.slice(pos + 1);
+        if (token === '(') {
+          filePath = this.consumeFilePath();
+          if (filePath) {
+            this.currentFilePath = filePath;
+            newFile = {
+              path: filePath,
+              files: []
+            };
+            this.fileStack.push(newFile);
+            this.currentFileList.push(newFile);
+            this.currentFileList = newFile.files;
+          } else {
+            this.openParens++;
+          }
+        } else if (token === ')') {
+          if (this.openParens > 0) {
+            this.openParens--;
+          } else {
+            if (this.fileStack.length > 1) {
+              this.fileStack.pop();
+              previousFile = this.fileStack[this.fileStack.length - 1];
+              this.currentFilePath = previousFile.path;
+              this.currentFileList = previousFile.files;
             }
+          }
+        }
+        this.parseParensForFilenames();
+      }
+    };
+    this.consumeFilePath = function() {
+      var endOfFilePath, path;
+      if (!this.currentLine.match(/^\/?([^ \)]+\/)+/)) {
+        return false;
+      }
+      endOfFilePath = this.currentLine.search(RegExp(' |\\)'));
+      path = void 0;
+      if (endOfFilePath === -1) {
+        path = this.currentLine;
+        this.currentLine = '';
+      } else {
+        path = this.currentLine.slice(0, endOfFilePath);
+        this.currentLine = this.currentLine.slice(endOfFilePath);
+      }
+      return path;
+    };
+    return this.postProcess = function(data) {
+      var all, errors, hashEntry, hashes, i, typesetting, warnings;
+      all = [];
+      errors = [];
+      warnings = [];
+      typesetting = [];
+      hashes = [];
+      hashEntry = function(entry) {
+        return entry.raw;
+      };
+      i = 0;
+      while (i < data.length) {
+        if (this.ignoreDuplicates && hashes.indexOf(hashEntry(data[i])) > -1) {
+          i++;
+          continue;
+        }
+        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]);
         }
-    }).call(LatexParser.prototype);
-
-    LatexParser.parse = function(text, options) {
-        return (new LatexParser(text, options)).parse()
+        all.push(data[i]);
+        hashes.push(hashEntry(data[i]));
+        i++;
+      }
+      return {
+        errors: errors,
+        warnings: warnings,
+        typesetting: typesetting,
+        all: all,
+        files: this.rootFileList
+      };
     };
-
-    return LatexParser;
+  }).call(LatexParser.prototype);
+  LatexParser.parse = function(text, options) {
+    return new LatexParser(text, options).parse();
+  };
+  return LatexParser;
 });