From 962a4d50398dace36ea6473cdc030f3028974f99 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 16 Jan 2017 09:17:38 +0000 Subject: [PATCH] roll out math mode linter for all users from our ace commit 953ff92c3283f03da94559d50a933fe685b05631 --- .../web/public/js/ace-1.2.5/mode-latex.js | 14 +- .../web/public/js/ace-1.2.5/worker-latex.js | 681 +++++++++++++++--- 2 files changed, 577 insertions(+), 118 deletions(-) diff --git a/services/web/public/js/ace-1.2.5/mode-latex.js b/services/web/public/js/ace-1.2.5/mode-latex.js index f183d7c263..8e7bbe4802 100644 --- a/services/web/public/js/ace-1.2.5/mode-latex.js +++ b/services/web/public/js/ace-1.2.5/mode-latex.js @@ -242,6 +242,10 @@ var createLatexWorker = function (session) { var annotations = []; var newRange = {}; var cursor = selection.getCursor(); + var maxRow = session.getLength() - 1; + var maxCol = (maxRow > 0) ? session.getLine(maxRow).length : 0; + var cursorAtEndOfDocument = (cursor.row == maxRow) && (cursor.column === maxCol); + suppressions = []; for (var i = 0, len = hints.length; i 0) { + return j; // advance past these tokens + } else { + return null; + } +}; + var readOptionalParams = function(TokeniseResult, k) { var Tokens = TokeniseResult.tokens; var text = TokeniseResult.text; + var params = Tokens[k+1]; + if(params && params[1] === "Text") { + var paramNum = text.substring(params[2], params[3]); + if (paramNum.match(/^\[\d+\](\[[^\]]*\])*\s*$/)) { + return k + 1; // got it + }; + }; + var count = 0; + var nextToken = Tokens[k+1]; + var pos = nextToken[2]; + + for (var i = pos, end = text.length; i < end; i++) { + var char = text[i]; + if (nextToken && i >= nextToken[2]) { k++; nextToken = Tokens[k+1];}; + if (char === "[") { count++; } + if (char === "]") { count--; } + if (count === 0 && char === "{") { return k - 1; } + if (count > 0 && (char === '\r' || char === '\n')) { return null; } + }; + return null; +}; + +var readOptionalGeneric = function(TokeniseResult, k) { + var Tokens = TokeniseResult.tokens; + var text = TokeniseResult.text; + var params = Tokens[k+1]; if(params && params[1] === "Text") { var paramNum = text.substring(params[2], params[3]); - if (paramNum.match(/^\[\d+\](\[[^\]]*\])*\s*$/)) { + if (paramNum.match(/^(\[[^\]]*\])+\s*$/)) { return k + 1; // got it }; }; return null; }; +var readOptionalDef = function (TokeniseResult, k) { + var Tokens = TokeniseResult.tokens; + var text = TokeniseResult.text; + + var defToken = Tokens[k]; + var pos = defToken[3]; + + var openBrace = "{"; + var nextToken = Tokens[k+1]; + for (var i = pos, end = text.length; i < end; i++) { + var char = text[i]; + if (nextToken && i >= nextToken[2]) { k++; nextToken = Tokens[k+1];}; + if (char === openBrace) { return k - 1; }; // move back to the last token of the optional arguments + if (char === '\r' || char === '\n') { return null; } + }; + + return null; + +}; + var readDefinition = function(TokeniseResult, k) { var Tokens = TokeniseResult.tokens; var text = TokeniseResult.text; @@ -1697,7 +1780,6 @@ var readUrl = function(TokeniseResult, k) { return null; }; - var InterpretTokens = function (TokeniseResult, ErrorReporter) { var Tokens = TokeniseResult.tokens; var linePosition = TokeniseResult.linePosition; @@ -1706,11 +1788,30 @@ var InterpretTokens = function (TokeniseResult, ErrorReporter) { var TokenErrorFromTo = ErrorReporter.TokenErrorFromTo; var TokenError = ErrorReporter.TokenError; - var Environments = []; + var Environments = new EnvHandler(ErrorReporter); + + var nextGroupMathMode = null; // if the next group should have + var nextGroupMathModeStack = [] ; // tracking all nextGroupMathModes + var seenUserDefinedBeginEquation = false; // if we have seen macros like \beq + var seenUserDefinedEndEquation = false; // if we have seen macros like \eeq for (var i = 0, len = Tokens.length; i < len; i++) { var token = Tokens[i]; var line = token[0], type = token[1], start = token[2], end = token[3], seq = token[4]; + + if (type === "{") { + Environments.push({command:"{", token:token, mathMode: nextGroupMathMode}); + nextGroupMathModeStack.push(nextGroupMathMode); + nextGroupMathMode = null; + continue; + } else if (type === "}") { + Environments.push({command:"}", token:token}); + nextGroupMathMode = nextGroupMathModeStack.pop(); + continue; + } else { + nextGroupMathMode = null; + }; + if (type === "\\") { if (seq === "begin" || seq === "end") { var open = Tokens[i+1]; @@ -1759,15 +1860,31 @@ var InterpretTokens = function (TokeniseResult, ErrorReporter) { } else { TokenError(token, "invalid environment command"); }; - } - } else if (seq === "newcommand" || seq === "renewcommand" || seq === "def" || seq === "DeclareRobustCommand") { - var newPos = read1arg(TokeniseResult, i, {allowStar: (seq != "def")}); + } + } else if (typeof seq === "string" && seq.match(/^(be|beq|beqa|bea)$/i)) { + seenUserDefinedBeginEquation = true; + } else if (typeof seq === "string" && seq.match(/^(ee|eeq|eeqn|eeqa|eeqan|eea)$/i)) { + seenUserDefinedEndEquation = true; + } else if (seq === "newcommand" || seq === "renewcommand" || seq === "DeclareRobustCommand") { + var newPos = read1arg(TokeniseResult, i, {allowStar: true}); if (newPos === null) { continue; } else {i = newPos;}; newPos = readOptionalParams(TokeniseResult, i); if (newPos === null) { /* do nothing */ } else {i = newPos;}; newPos = readDefinition(TokeniseResult, i); if (newPos === null) { /* do nothing */ } else {i = newPos;}; + } else if (seq === "def") { + newPos = read1arg(TokeniseResult, i); + if (newPos === null) { continue; } else {i = newPos;}; + newPos = readOptionalDef(TokeniseResult, i); + if (newPos === null) { /* do nothing */ } else {i = newPos;}; + newPos = readDefinition(TokeniseResult, i); + if (newPos === null) { /* do nothing */ } else {i = newPos;}; + + } else if (seq === "let") { + newPos = readLetDefinition(TokeniseResult, i); + if (newPos === null) { continue; } else {i = newPos;}; + } else if (seq === "newcolumntype") { newPos = read1name(TokeniseResult, i); if (newPos === null) { continue; } else {i = newPos;}; @@ -1791,128 +1908,435 @@ var InterpretTokens = function (TokeniseResult, ErrorReporter) { } else if (seq === "url") { newPos = readUrl(TokeniseResult, i); if (newPos === null) { TokenError(token, "invalid url command"); } else {i = newPos;}; + } else if (seq === "left" || seq === "right") { + var nextToken = Tokens[i+1]; + char = ""; + if (nextToken && nextToken[1] === "Text") { + char = text.substring(nextToken[2], nextToken[2] + 1); + } else if (nextToken && nextToken[1] === "\\" && nextToken[5] == "control-symbol") { + char = nextToken[4]; + } else if (nextToken && nextToken[1] === "\\") { + char = "unknown"; + } + if (char === "" || (char !== "unknown" && "(){}[]<>/|\\.".indexOf(char) === -1)) { + TokenError(token, "invalid bracket command"); + } else { + i = i + 1; + Environments.push({command:seq, token:token}); + }; + } else if (seq === "(" || seq === ")" || seq === "[" || seq === "]") { + Environments.push({command:seq, token:token}); + } else if (seq === "input") { + newPos = read1filename(TokeniseResult, i); + if (newPos === null) { continue; } else {i = newPos;}; + } else if (seq === "hbox" || seq === "text" || seq === "mbox" || seq === "footnote" || seq === "intertext" || seq === "shortintertext" || seq === "textnormal" || seq === "tag" || seq === "reflectbox" || seq === "textrm") { + nextGroupMathMode = false; + } else if (seq === "rotatebox" || seq === "scalebox") { + newPos = readOptionalGeneric(TokeniseResult, i); + if (newPos === null) { /* do nothing */ } else {i = newPos;}; + newPos = readDefinition(TokeniseResult, i); + if (newPos === null) { /* do nothing */ } else {i = newPos;}; + nextGroupMathMode = false; + } else if (seq === "resizebox") { + newPos = readOptionalGeneric(TokeniseResult, i); + if (newPos === null) { /* do nothing */ } else {i = newPos;}; + newPos = readDefinition(TokeniseResult, i); + if (newPos === null) { /* do nothing */ } else {i = newPos;}; + newPos = readDefinition(TokeniseResult, i); + if (newPos === null) { /* do nothing */ } else {i = newPos;}; + + nextGroupMathMode = false; + } else if (seq === "DeclareMathOperator") { + newPos = readDefinition(TokeniseResult, i); + if (newPos === null) { /* do nothing */ } else {i = newPos;}; + newPos = readDefinition(TokeniseResult, i); + if (newPos === null) { /* do nothing */ } else {i = newPos;}; + } else if (seq === "DeclarePairedDelimiter") { + newPos = readDefinition(TokeniseResult, i); + if (newPos === null) { /* do nothing */ } else {i = newPos;}; + newPos = readDefinition(TokeniseResult, i); + if (newPos === null) { /* do nothing */ } else {i = newPos;}; + newPos = readDefinition(TokeniseResult, i); + if (newPos === null) { /* do nothing */ } else {i = newPos;}; + } else if (typeof seq === "string" && seq.match(/^(alpha|beta|gamma|delta|epsilon|varepsilon|zeta|eta|theta|vartheta|iota|kappa|lambda|mu|nu|xi|pi|varpi|rho|varrho|sigma|varsigma|tau|upsilon|phi|varphi|chi|psi|omega|Gamma|Delta|Theta|Lambda|Xi|Pi|Sigma|Upsilon|Phi|Psi|Omega)$/)) { + var currentMathMode = Environments.getMathMode() ; // returns null / $(inline) / $$(display) + if (currentMathMode === null) { + TokenError(token, type + seq + " must be inside math mode", {mathMode:true}); + }; + } else if (typeof seq === "string" && seq.match(/^(chapter|section|subsection|subsubsection)$/)) { + currentMathMode = Environments.getMathMode() ; // returns null / $(inline) / $$(display) + if (currentMathMode) { + TokenError(token, type + seq + " used inside math mode", {mathMode:true}); + Environments.resetMathMode(); + }; + } else if (typeof seq === "string" && seq.match(/^[a-z]+$/)) { + nextGroupMathMode = undefined; + }; + + } else if (type === "$") { + var lookAhead = Tokens[i+1]; + var nextIsDollar = lookAhead && lookAhead[1] === "$"; + currentMathMode = Environments.getMathMode() ; // returns null / $(inline) / $$(display) + if (nextIsDollar && (!currentMathMode || currentMathMode.command == "$$")) { + Environments.push({command:"$$", token:token}); + i = i + 1; + } else { + Environments.push({command:"$", token:token}); } - } else if (type === "{") { - Environments.push({command:"{", token:token}); - } else if (type === "}") { - Environments.push({command:"}", token:token}); - }; + } else if (type === "^" || type === "_") { + currentMathMode = Environments.getMathMode() ; // returns null / $(inline) / $$(display) + var insideGroup = Environments.insideGroup(); // true if inside {....} + if (currentMathMode === null && !insideGroup) { + TokenError(token, type + " must be inside math mode", {mathMode:true}); + }; + } }; + + if (seenUserDefinedBeginEquation && seenUserDefinedEndEquation) { + ErrorReporter.filterMath = true; + }; + return Environments; }; - -var CheckEnvironments = function (Environments, ErrorReporter) { +var EnvHandler = function (ErrorReporter) { var ErrorTo = ErrorReporter.EnvErrorTo; var ErrorFromTo = ErrorReporter.EnvErrorFromTo; var ErrorFrom = ErrorReporter.EnvErrorFrom; + var envs = []; + var state = []; var documentClosed = null; var inVerbatim = false; var verbatimRanges = []; - for (var i = 0, len = Environments.length; i < len; i++) { - var name = Environments[i].name ; - if (name && name.match(/^(verbatim|boxedverbatim|lstlisting|minted)$/)) { - Environments[i].verbatim = true; + + this.Environments = envs; + + this.push = function (newEnv) { + this.setEnvProps(newEnv); + this.checkAndUpdateState(newEnv); + envs.push(newEnv); + }; + + this._endVerbatim = function (thisEnv) { + var lastEnv = state.pop(); + if (lastEnv && lastEnv.name === thisEnv.name) { + inVerbatim = false; + verbatimRanges.push({start: lastEnv.token[2], end: thisEnv.token[2]}); + } else { + if(lastEnv) { state.push(lastEnv); } ; } - } - for (i = 0, len = Environments.length; i < len; i++) { - var thisEnv = Environments[i]; - if(thisEnv.command === "begin" || thisEnv.command === "{") { - if (inVerbatim) { continue; } // ignore anything in verbatim environments - if (thisEnv.verbatim) {inVerbatim = true;}; - state.push(thisEnv); - } else if (thisEnv.command === "end" || thisEnv.command === "}") { + }; + + var invalidEnvs = []; + + this._end = function (thisEnv) { + do { var lastEnv = state.pop(); + var retry = false; + var i; - if (inVerbatim) { - if (lastEnv && lastEnv.name === thisEnv.name) { - inVerbatim = false; - verbatimRanges.push({start: lastEnv.token[2], end: thisEnv.token[2]}); - continue; - } else { - if(lastEnv) { state.push(lastEnv); } ; - continue; // ignore all other commands - } - }; - - if (lastEnv && lastEnv.command === "{" && thisEnv.command === "}") { - continue; - } else if (lastEnv && lastEnv.name === thisEnv.name) { - if (thisEnv.name === "document" && !documentClosed) { + if (closedBy(lastEnv, thisEnv)) { + if (thisEnv.command === "end" && thisEnv.name === "document" && !documentClosed) { documentClosed = thisEnv; }; - continue; + return; } else if (!lastEnv) { - if (thisEnv.command === "}") { - if (documentClosed) { - ErrorFromTo(documentClosed, thisEnv, "\\end{" + documentClosed.name + "} is followed by unexpected end group }",{errorAtStart: true, type: "info"}); - } else { - ErrorTo(thisEnv, "unexpected end group }"); - }; - } else if (thisEnv.command === "end") { - if (documentClosed) { - ErrorFromTo(documentClosed, thisEnv, "\\end{" + documentClosed.name + "} is followed by unexpected content",{errorAtStart: true, type: "info"}); - } else { - ErrorTo(thisEnv, "unexpected \\end{" + thisEnv.name + "}"); - } + if (documentClosed) { + ErrorFromTo(documentClosed, thisEnv, "\\end{" + documentClosed.name + "} is followed by unexpected content",{errorAtStart: true, type: "info"}); + } else { + ErrorTo(thisEnv, "unexpected " + getName(thisEnv)); } - } else if (lastEnv.command === "begin" && thisEnv.command === "}") { - ErrorFromTo(lastEnv, thisEnv, "unexpected end group } after \\begin{" + lastEnv.name +"}"); - state.push(lastEnv); - } else if (lastEnv.command === "{" && thisEnv.command === "end") { - ErrorFromTo(lastEnv, thisEnv, - "unclosed group { found at \\end{" + thisEnv.name + "}", - {suppressIfEditing:true, errorAtStart: true, type:"warning"}); - i--; - } else if (lastEnv.command === "begin" && thisEnv.command === "end") { - ErrorFromTo(lastEnv, thisEnv, - "unclosed \\begin{" + lastEnv.name + "} found at \\end{" + thisEnv.name + "} " , - {errorAtStart: true}); - for (var j = i + 1; j < len; j++) { - var futureEnv = Environments[j]; - if (futureEnv.command === "end" && futureEnv.name === lastEnv.name) { - state.push(lastEnv); - continue; - } - } - lastEnv = state.pop(); - if(lastEnv) { - if (thisEnv.name === lastEnv.name) { - continue; - } else { - state.push(lastEnv); + } else if (invalidEnvs.length > 0 && (i = indexOfClosingEnvInArray(invalidEnvs, thisEnv) > -1)) { + invalidEnvs.splice(i, 1); + if (lastEnv) { state.push(lastEnv); } ; + return; + } else { + var status = reportError(lastEnv, thisEnv); + if (envPrecedence(lastEnv) < envPrecedence(thisEnv)) { + invalidEnvs.push(lastEnv); + retry = true; + } else { + var prevLastEnv = state.pop(); + if(prevLastEnv) { + if (thisEnv.name === prevLastEnv.name) { + return; + } else { + state.push(prevLastEnv); + } } + invalidEnvs.push(lastEnv); } } + } while (retry === true); + }; + + var CLOSING_DELIMITER = { + "{" : "}", + "left" : "right", + "[" : "]", + "(" : ")", + "$" : "$", + "$$": "$$" + }; + + var closedBy = function (lastEnv, thisEnv) { + if (!lastEnv) { + return false ; + } else if (thisEnv.command === "end") { + return lastEnv.command === "begin" && lastEnv.name === thisEnv.name; + } else if (thisEnv.command === CLOSING_DELIMITER[lastEnv.command]) { + return true; + } else { + return false; } - } - while (state.length > 0) { - thisEnv = state.pop(); - if (thisEnv.command === "{") { - ErrorFrom(thisEnv, "unclosed group {", {type:"warning"}); - } else if (thisEnv.command === "begin") { - ErrorFrom(thisEnv, "unclosed environment \\begin{" + thisEnv.name + "}"); + }; + + var indexOfClosingEnvInArray = function (envs, thisEnv) { + for (var i = 0, n = envs.length; i < n ; i++) { + if (closedBy(envs[i], thisEnv)) { + return i; + } + } + return -1; + }; + + var envPrecedence = function (env) { + var openScore = { + "{" : 1, + "left" : 2, + "$" : 3, + "$$" : 4, + "begin": 4 }; - } - var vlen = verbatimRanges.length; - len = ErrorReporter.tokenErrors.length; - if (vlen >0 && len > 0) { - for (i = 0; i < len; i++) { - var tokenError = ErrorReporter.tokenErrors[i]; - var startPos = tokenError.startPos; - var endPos = tokenError.endPos; - for (j = 0; j < vlen; j++) { - if (startPos > verbatimRanges[j].start && startPos < verbatimRanges[j].end) { - tokenError.ignore = true; - break; + var closeScore = { + "}" : 1, + "right" : 2, + "$" : 3, + "$$" : 5, + "end": 4 + }; + if (env.command) { + return openScore[env.command] || closeScore[env.command]; + } else { + return 0; + } + }; + + var getName = function(env) { + var description = { + "{" : "open group {", + "}" : "close group }", + "[" : "open display math \\[", + "]" : "close display math \\]", + "(" : "open inline math \\(", + ")" : "close inline math \\)", + "$" : "$", + "$$" : "$$", + "left" : "\\left", + "right" : "\\right" + }; + if (env.command === "begin" || env.command === "end") { + return "\\" + env.command + "{" + env.name + "}"; + } else if (env.command in description) { + return description[env.command]; + } else { + return env.command; + } + }; + + var EXTRA_CLOSE = 1; + var UNCLOSED_GROUP = 2; + var UNCLOSED_ENV = 3; + + var reportError = function(lastEnv, thisEnv) { + if (!lastEnv) { // unexpected close, nothing was open! + if (documentClosed) { + ErrorFromTo(documentClosed, thisEnv, "\\end{" + documentClosed.name + "} is followed by unexpected end group }",{errorAtStart: true, type: "info"}); + } else { + ErrorTo(thisEnv, "unexpected " + getName(thisEnv)); + }; + return EXTRA_CLOSE; + } else if (lastEnv.command === "{" && thisEnv.command === "end") { + ErrorFromTo(lastEnv, thisEnv, "unclosed " + getName(lastEnv) + " found at " + getName(thisEnv), + {suppressIfEditing:true, errorAtStart: true, type:"warning"}); + return UNCLOSED_GROUP; + } else { + var pLast = envPrecedence(lastEnv); + var pThis = envPrecedence(thisEnv); + if (pThis > pLast) { + ErrorFromTo(lastEnv, thisEnv, "unclosed " + getName(lastEnv) + " found at " + getName(thisEnv), + {suppressIfEditing:true, errorAtStart: true}); + } else { + ErrorFromTo(lastEnv, thisEnv, "unexpected " + getName(thisEnv) + " after " + getName(lastEnv)); + } + return UNCLOSED_ENV; + }; + }; + + this._beginMathMode = function (thisEnv) { + var currentMathMode = this.getMathMode(); // undefined, null, $, $$, name of mathmode env + if (currentMathMode) { + ErrorFrom(thisEnv, thisEnv.name + " used inside existing math mode " + getName(currentMathMode), + {suppressIfEditing:true, errorAtStart: true, mathMode:true}); + }; + thisEnv.mathMode = thisEnv; + state.push(thisEnv); + }; + + this._toggleMathMode = function (thisEnv) { + var lastEnv = state.pop(); + if (closedBy(lastEnv, thisEnv)) { + return; + } else { + if (lastEnv) {state.push(lastEnv);} + if (lastEnv && lastEnv.mathMode) { + this._end(thisEnv); + } else { + thisEnv.mathMode = thisEnv; + state.push(thisEnv); + } + }; + }; + + this.getMathMode = function () { + var n = state.length; + if (n > 0) { + return state[n-1].mathMode; + } else { + return null; + } + }; + + this.insideGroup = function () { + var n = state.length; + if (n > 0) { + return (state[n-1].command === "{"); + } else { + return null; + } + }; + + var resetMathMode = function () { + var n = state.length; + if (n > 0) { + var lastMathMode = state[n-1].mathMode; + do { + var lastEnv = state.pop(); + } while (lastEnv && lastEnv !== lastMathMode); + } else { + return; + } + }; + + this.resetMathMode = resetMathMode; + + var getNewMathMode = function (currentMathMode, thisEnv) { + var newMathMode = null; + + if (thisEnv.command === "{") { + if (thisEnv.mathMode !== null) { + newMathMode = thisEnv.mathMode; + } else { + newMathMode = currentMathMode; + } + } else if (thisEnv.command === "left") { + if (currentMathMode === null) { + ErrorFrom(thisEnv, "\\left can only be used in math mode", {mathMode: true}); + }; + newMathMode = currentMathMode; + } else if (thisEnv.command === "begin") { + var name = thisEnv.name; + if (name) { + if (name.match(/^(document|figure|center|enumerate|itemize|table|abstract|proof|lemma|theorem|definition|proposition|corollary|remark|notation|thebibliography)$/)) { + if (currentMathMode) { + ErrorFromTo(currentMathMode, thisEnv, thisEnv.name + " used inside " + getName(currentMathMode), + {suppressIfEditing:true, errorAtStart: true, mathMode: true}); + resetMathMode(); + }; + newMathMode = null; + } else if (name.match(/^(array|gathered|split|aligned|alignedat)\*?$/)) { + if (currentMathMode === null) { + ErrorFrom(thisEnv, thisEnv.name + " not inside math mode", {mathMode: true}); + }; + newMathMode = currentMathMode; + } else if (name.match(/^(math|displaymath|equation|eqnarray|multline|align|gather|flalign|alignat)\*?$/)) { + if (currentMathMode) { + ErrorFromTo(currentMathMode, thisEnv, thisEnv.name + " used inside " + getName(currentMathMode), + {suppressIfEditing:true, errorAtStart: true, mathMode: true}); + resetMathMode(); + }; + newMathMode = thisEnv; + } else { + newMathMode = undefined; // undefined means we don't know if we are in math mode or not + } + } + }; + return newMathMode; + }; + + this.checkAndUpdateState = function (thisEnv) { + if (inVerbatim) { + if (thisEnv.command === "end") { + this._endVerbatim(thisEnv); + } else { + return; // ignore anything in verbatim environments + } + } else if(thisEnv.command === "begin" || thisEnv.command === "{" || thisEnv.command === "left") { + if (thisEnv.verbatim) {inVerbatim = true;}; + var currentMathMode = this.getMathMode(); // undefined, null, $, $$, name of mathmode env + var newMathMode = getNewMathMode(currentMathMode, thisEnv); + thisEnv.mathMode = newMathMode; + state.push(thisEnv); + } else if (thisEnv.command === "end") { + this._end(thisEnv); + } else if (thisEnv.command === "(" || thisEnv.command === "[") { + this._beginMathMode(thisEnv); + } else if (thisEnv.command === ")" || thisEnv.command === "]") { + this._end(thisEnv); + } else if (thisEnv.command === "}") { + this._end(thisEnv); + } else if (thisEnv.command === "right") { + this._end(thisEnv); + } else if (thisEnv.command === "$" || thisEnv.command === "$$") { + this._toggleMathMode(thisEnv); + } + }; + + this.close = function () { + while (state.length > 0) { + var thisEnv = state.pop(); + if (thisEnv.command === "{") { + ErrorFrom(thisEnv, "unclosed group {", {type:"warning"}); + } else { + ErrorFrom(thisEnv, "unclosed " + getName(thisEnv)); + } + } + var vlen = verbatimRanges.length; + var len = ErrorReporter.tokenErrors.length; + if (vlen >0 && len > 0) { + for (var i = 0; i < len; i++) { + var tokenError = ErrorReporter.tokenErrors[i]; + var startPos = tokenError.startPos; + var endPos = tokenError.endPos; + for (var j = 0; j < vlen; j++) { + if (startPos > verbatimRanges[j].start && startPos < verbatimRanges[j].end) { + tokenError.ignore = true; + break; + } } } } - } + }; + this.setEnvProps = function (env) { + var name = env.name ; + if (name && name.match(/^(verbatim|boxedverbatim|lstlisting|minted|Verbatim)$/)) { + env.verbatim = true; + } + }; }; var ErrorReporter = function (TokeniseResult) { var text = TokeniseResult.text; @@ -1922,18 +2346,41 @@ var ErrorReporter = function (TokeniseResult) { var errors = [], tokenErrors = []; this.errors = errors; this.tokenErrors = tokenErrors; + this.filterMath = false; this.getErrors = function () { var returnedErrors = []; for (var i = 0, len = tokenErrors.length; i < len; i++) { if (!tokenErrors[i].ignore) { returnedErrors.push(tokenErrors[i]); } } - return returnedErrors.concat(errors); + var allErrors = returnedErrors.concat(errors); + var result = []; + var mathErrorCount = 0; + for (i = 0, len = allErrors.length; i < len; i++) { + if (allErrors[i].mathMode) { + mathErrorCount++; + } + if (mathErrorCount > 10) { + return []; + } + } + if (this.filterMath && mathErrorCount > 0) { + for (i = 0, len = allErrors.length; i < len; i++) { + if (!allErrors[i].mathMode) { + result.push(allErrors[i]); + } + } + return result; + } else { + return allErrors; + } }; - this.TokenError = function (token, message) { + this.TokenError = function (token, message, options) { + if(!options) { options = { suppressIfEditing:true } ; }; var line = token[0], type = token[1], start = token[2], end = token[3]; var start_col = start - linePosition[line]; + if (!end) { end = start + 1; } ; var end_col = end - linePosition[line]; tokenErrors.push({row: line, column: start_col, @@ -1945,10 +2392,12 @@ var ErrorReporter = function (TokeniseResult) { text:message, startPos: start, endPos: end, - suppressIfEditing:true}); + suppressIfEditing:options.suppressIfEditing, + mathMode: options.mathMode}); }; - this.TokenErrorFromTo = function (fromToken, toToken, message) { + this.TokenErrorFromTo = function (fromToken, toToken, message, options) { + if(!options) { options = {suppressIfEditing:true } ; }; var fromLine = fromToken[0], fromStart = fromToken[2], fromEnd = fromToken[3]; var toLine = toToken[0], toStart = toToken[2], toEnd = toToken[3]; if (!toEnd) { toEnd = toStart + 1;}; @@ -1965,7 +2414,8 @@ var ErrorReporter = function (TokeniseResult) { text:message, startPos: fromStart, endPos: toEnd, - suppressIfEditing:true}); + suppressIfEditing:options.suppressIfEditing, + mathMode: options.mathMode}); }; @@ -1986,7 +2436,8 @@ var ErrorReporter = function (TokeniseResult) { end_col: end_col, type: options.type ? options.type : "error", text:message, - suppressIfEditing:options.suppressIfEditing}); + suppressIfEditing:options.suppressIfEditing, + mathMode: options.mathMode}); }; this.EnvErrorTo = function (toEnv, message, options) { @@ -2002,7 +2453,8 @@ var ErrorReporter = function (TokeniseResult) { end_row: line, end_col: end_col, type: options.type ? options.type : "error", - text:message}; + text:message, + mathMode: options.mathMode}; errors.push(err); }; @@ -2019,7 +2471,8 @@ var ErrorReporter = function (TokeniseResult) { end_row: lineNumber, end_col: end_col, type: options.type ? options.type : "error", - text:message}); + text:message, + mathMode: options.mathMode}); }; }; @@ -2027,7 +2480,7 @@ var Parse = function (text) { var TokeniseResult = Tokenise(text); var Reporter = new ErrorReporter(TokeniseResult); var Environments = InterpretTokens(TokeniseResult, Reporter); - CheckEnvironments(Environments, Reporter); + Environments.close(); return Reporter.getErrors(); };