roll out math mode linter for all users

from our ace commit 953ff92c3283f03da94559d50a933fe685b05631
This commit is contained in:
Brian Gough 2017-01-16 09:17:38 +00:00
parent 21411e83f3
commit 962a4d5039
2 changed files with 577 additions and 118 deletions

View file

@ -242,6 +242,10 @@ var createLatexWorker = function (session) {
var annotations = []; var annotations = [];
var newRange = {}; var newRange = {};
var cursor = selection.getCursor(); 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 = []; suppressions = [];
for (var i = 0, len = hints.length; i<len; i++) { for (var i = 0, len = hints.length; i<len; i++) {
@ -250,8 +254,8 @@ var createLatexWorker = function (session) {
var suppressedChanges = 0; var suppressedChanges = 0;
var hintRange = new Range(hint.start_row, hint.start_col, hint.end_row, hint.end_col); var hintRange = new Range(hint.start_row, hint.start_col, hint.end_row, hint.end_col);
var cursorInRange = hintRange.insideStart(cursor.row, cursor.column); var cursorInRange = hintRange.insideEnd(cursor.row, cursor.column);
var cursorAtStart = hintRange.isStart(cursor.row, cursor.column); var cursorAtStart = hintRange.isStart(cursor.row, cursor.column - 1); // cursor after start not before
var cursorAtEnd = hintRange.isEnd(cursor.row, cursor.column); var cursorAtEnd = hintRange.isEnd(cursor.row, cursor.column);
if (hint.suppressIfEditing && (cursorAtStart || cursorAtEnd)) { if (hint.suppressIfEditing && (cursorAtStart || cursorAtEnd)) {
suppressions.push(hintRange); suppressions.push(hintRange);
@ -289,8 +293,10 @@ var createLatexWorker = function (session) {
cursorInRange = newRange[key].cursorInRange; cursorInRange = newRange[key].cursorInRange;
hint = newRange[key].hint; hint = newRange[key].hint;
var errorAtStart = (hint.row === hint.start_row && hint.column === hint.start_col); var errorAtStart = (hint.row === hint.start_row && hint.column === hint.start_col);
var a = (cursorInRange && !errorAtStart) ? cursorAnchor : doc.createAnchor(new_range.start); var movableStart = (cursorInRange && !errorAtStart) && !cursorAtEndOfDocument;
var b = (cursorInRange && errorAtStart) ? cursorAnchor : doc.createAnchor(new_range.end); var movableEnd = (cursorInRange && errorAtStart) && !cursorAtEndOfDocument;
var a = movableStart ? cursorAnchor : doc.createAnchor(new_range.start);
var b = movableEnd ? cursorAnchor : doc.createAnchor(new_range.end);
var range = new Range(); var range = new Range();
range.start = a; range.start = a;
range.end = b; range.end = b;

View file

@ -1490,7 +1490,7 @@ var Tokenise = function (text) {
var controlSequence = NEXTCS.exec(text); var controlSequence = NEXTCS.exec(text);
var nextSpecialPos = controlSequence === null ? idx : controlSequence.index; var nextSpecialPos = controlSequence === null ? idx : controlSequence.index;
if (nextSpecialPos === idx) { if (nextSpecialPos === idx) {
Tokens.push([lineNumber, code, pos, idx + 1, text[idx]]); Tokens.push([lineNumber, code, pos, idx + 1, text[idx], "control-symbol"]);
idx = SPECIAL.lastIndex = idx + 1; idx = SPECIAL.lastIndex = idx + 1;
char = text[nextSpecialPos]; char = text[nextSpecialPos];
if (char === '\n') { lineNumber++; linePosition[lineNumber] = nextSpecialPos;}; if (char === '\n') { lineNumber++; linePosition[lineNumber] = nextSpecialPos;};
@ -1508,12 +1508,7 @@ var Tokenise = function (text) {
} else if (code === "}") { // close group } else if (code === "}") { // close group
Tokens.push([lineNumber, code, pos]); Tokens.push([lineNumber, code, pos]);
} else if (code === "$") { // math mode } else if (code === "$") { // math mode
if (text[idx] === "$") {
idx = SPECIAL.lastIndex = idx + 1;
Tokens.push([lineNumber, "$$", pos]);
} else {
Tokens.push([lineNumber, code, pos]); Tokens.push([lineNumber, code, pos]);
}
} else if (code === "&") { // tabalign } else if (code === "&") { // tabalign
Tokens.push([lineNumber, code, pos]); Tokens.push([lineNumber, code, pos]);
} else if (code === "#") { // macro parameter } else if (code === "#") { // macro parameter
@ -1559,6 +1554,25 @@ var read1arg = function (TokeniseResult, k, options) {
} }
}; };
var readLetDefinition = function (TokeniseResult, k) {
var Tokens = TokeniseResult.tokens;
var text = TokeniseResult.text;
var first = Tokens[k+1];
var second = Tokens[k+2];
var third = Tokens[k+3];
if(first && first[1] === "\\" && second && second[1] === "\\") {
return k + 2;
} else if(first && first[1] === "\\" &&
second && second[1] === "Text" && text.substring(second[2], second[3]) === "=" &&
third && third[1] === "\\") {
return k + 3;
} else {
return null;
}
};
var read1name = function (TokeniseResult, k) { var read1name = function (TokeniseResult, k) {
var Tokens = TokeniseResult.tokens; var Tokens = TokeniseResult.tokens;
@ -1594,21 +1608,90 @@ var read1name = function (TokeniseResult, k) {
} }
}; };
var read1filename = function (TokeniseResult, k) {
var Tokens = TokeniseResult.tokens;
var text = TokeniseResult.text;
var fileName = "";
for (var j = k + 1, tok; (tok = Tokens[j]); j++) {
if (tok[1] === "Text") {
var str = text.substring(tok[2], tok[3]);
if (!str.match(/^\S*$/)) { break; }
fileName = fileName + str;
} else if (tok[1] === "_") {
fileName = fileName + "_";
} else {
break;
}
}
if (fileName.length > 0) {
return j; // advance past these tokens
} else {
return null;
}
};
var readOptionalParams = function(TokeniseResult, k) { var readOptionalParams = function(TokeniseResult, k) {
var Tokens = TokeniseResult.tokens; var Tokens = TokeniseResult.tokens;
var text = TokeniseResult.text; 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]; var params = Tokens[k+1];
if(params && params[1] === "Text") { if(params && params[1] === "Text") {
var paramNum = text.substring(params[2], params[3]); var paramNum = text.substring(params[2], params[3]);
if (paramNum.match(/^\[\d+\](\[[^\]]*\])*\s*$/)) { if (paramNum.match(/^(\[[^\]]*\])+\s*$/)) {
return k + 1; // got it return k + 1; // got it
}; };
}; };
return null; 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 readDefinition = function(TokeniseResult, k) {
var Tokens = TokeniseResult.tokens; var Tokens = TokeniseResult.tokens;
var text = TokeniseResult.text; var text = TokeniseResult.text;
@ -1697,7 +1780,6 @@ var readUrl = function(TokeniseResult, k) {
return null; return null;
}; };
var InterpretTokens = function (TokeniseResult, ErrorReporter) { var InterpretTokens = function (TokeniseResult, ErrorReporter) {
var Tokens = TokeniseResult.tokens; var Tokens = TokeniseResult.tokens;
var linePosition = TokeniseResult.linePosition; var linePosition = TokeniseResult.linePosition;
@ -1706,11 +1788,30 @@ var InterpretTokens = function (TokeniseResult, ErrorReporter) {
var TokenErrorFromTo = ErrorReporter.TokenErrorFromTo; var TokenErrorFromTo = ErrorReporter.TokenErrorFromTo;
var TokenError = ErrorReporter.TokenError; 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++) { for (var i = 0, len = Tokens.length; i < len; i++) {
var token = Tokens[i]; var token = Tokens[i];
var line = token[0], type = token[1], start = token[2], end = token[3], seq = token[4]; 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 (type === "\\") {
if (seq === "begin" || seq === "end") { if (seq === "begin" || seq === "end") {
var open = Tokens[i+1]; var open = Tokens[i+1];
@ -1760,14 +1861,30 @@ var InterpretTokens = function (TokeniseResult, ErrorReporter) {
TokenError(token, "invalid environment command"); TokenError(token, "invalid environment command");
}; };
} }
} else if (seq === "newcommand" || seq === "renewcommand" || seq === "def" || seq === "DeclareRobustCommand") { } else if (typeof seq === "string" && seq.match(/^(be|beq|beqa|bea)$/i)) {
var newPos = read1arg(TokeniseResult, i, {allowStar: (seq != "def")}); 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;}; if (newPos === null) { continue; } else {i = newPos;};
newPos = readOptionalParams(TokeniseResult, i); newPos = readOptionalParams(TokeniseResult, i);
if (newPos === null) { /* do nothing */ } else {i = newPos;}; if (newPos === null) { /* do nothing */ } else {i = newPos;};
newPos = readDefinition(TokeniseResult, i); newPos = readDefinition(TokeniseResult, i);
if (newPos === null) { /* do nothing */ } else {i = newPos;}; 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") { } else if (seq === "newcolumntype") {
newPos = read1name(TokeniseResult, i); newPos = read1name(TokeniseResult, i);
if (newPos === null) { continue; } else {i = newPos;}; if (newPos === null) { continue; } else {i = newPos;};
@ -1791,120 +1908,420 @@ var InterpretTokens = function (TokeniseResult, ErrorReporter) {
} else if (seq === "url") { } else if (seq === "url") {
newPos = readUrl(TokeniseResult, i); newPos = readUrl(TokeniseResult, i);
if (newPos === null) { TokenError(token, "invalid url command"); } else {i = newPos;}; 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";
} }
} else if (type === "{") { if (char === "" || (char !== "unknown" && "(){}[]<>/|\\.".indexOf(char) === -1)) {
Environments.push({command:"{", token:token}); TokenError(token, "invalid bracket command");
} else if (type === "}") { } else {
Environments.push({command:"}", token:token}); 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 === "^" || 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; return Environments;
}; };
var EnvHandler = function (ErrorReporter) {
var CheckEnvironments = function (Environments, ErrorReporter) {
var ErrorTo = ErrorReporter.EnvErrorTo; var ErrorTo = ErrorReporter.EnvErrorTo;
var ErrorFromTo = ErrorReporter.EnvErrorFromTo; var ErrorFromTo = ErrorReporter.EnvErrorFromTo;
var ErrorFrom = ErrorReporter.EnvErrorFrom; var ErrorFrom = ErrorReporter.EnvErrorFrom;
var envs = [];
var state = []; var state = [];
var documentClosed = null; var documentClosed = null;
var inVerbatim = false; var inVerbatim = false;
var verbatimRanges = []; 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;
}
}
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 lastEnv = state.pop();
if (inVerbatim) { 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) { if (lastEnv && lastEnv.name === thisEnv.name) {
inVerbatim = false; inVerbatim = false;
verbatimRanges.push({start: lastEnv.token[2], end: thisEnv.token[2]}); verbatimRanges.push({start: lastEnv.token[2], end: thisEnv.token[2]});
continue;
} else { } else {
if(lastEnv) { state.push(lastEnv); } ; if(lastEnv) { state.push(lastEnv); } ;
continue; // ignore all other commands
} }
}; };
if (lastEnv && lastEnv.command === "{" && thisEnv.command === "}") { var invalidEnvs = [];
continue;
} else if (lastEnv && lastEnv.name === thisEnv.name) { this._end = function (thisEnv) {
if (thisEnv.name === "document" && !documentClosed) { do {
var lastEnv = state.pop();
var retry = false;
var i;
if (closedBy(lastEnv, thisEnv)) {
if (thisEnv.command === "end" && thisEnv.name === "document" && !documentClosed) {
documentClosed = thisEnv; documentClosed = thisEnv;
}; };
continue; return;
} else if (!lastEnv) { } 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) { if (documentClosed) {
ErrorFromTo(documentClosed, thisEnv, "\\end{" + documentClosed.name + "} is followed by unexpected content",{errorAtStart: true, type: "info"}); ErrorFromTo(documentClosed, thisEnv, "\\end{" + documentClosed.name + "} is followed by unexpected content",{errorAtStart: true, type: "info"});
} else { } else {
ErrorTo(thisEnv, "unexpected \\end{" + thisEnv.name + "}"); ErrorTo(thisEnv, "unexpected " + getName(thisEnv));
} }
} } else if (invalidEnvs.length > 0 && (i = indexOfClosingEnvInArray(invalidEnvs, thisEnv) > -1)) {
} else if (lastEnv.command === "begin" && thisEnv.command === "}") { invalidEnvs.splice(i, 1);
ErrorFromTo(lastEnv, thisEnv, "unexpected end group } after \\begin{" + lastEnv.name +"}"); if (lastEnv) { state.push(lastEnv); } ;
state.push(lastEnv); return;
} 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 { } else {
state.push(lastEnv); 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;
}
};
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 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) { while (state.length > 0) {
thisEnv = state.pop(); var thisEnv = state.pop();
if (thisEnv.command === "{") { if (thisEnv.command === "{") {
ErrorFrom(thisEnv, "unclosed group {", {type:"warning"}); ErrorFrom(thisEnv, "unclosed group {", {type:"warning"});
} else if (thisEnv.command === "begin") { } else {
ErrorFrom(thisEnv, "unclosed environment \\begin{" + thisEnv.name + "}"); ErrorFrom(thisEnv, "unclosed " + getName(thisEnv));
}; }
} }
var vlen = verbatimRanges.length; var vlen = verbatimRanges.length;
len = ErrorReporter.tokenErrors.length; var len = ErrorReporter.tokenErrors.length;
if (vlen >0 && len > 0) { if (vlen >0 && len > 0) {
for (i = 0; i < len; i++) { for (var i = 0; i < len; i++) {
var tokenError = ErrorReporter.tokenErrors[i]; var tokenError = ErrorReporter.tokenErrors[i];
var startPos = tokenError.startPos; var startPos = tokenError.startPos;
var endPos = tokenError.endPos; var endPos = tokenError.endPos;
for (j = 0; j < vlen; j++) { for (var j = 0; j < vlen; j++) {
if (startPos > verbatimRanges[j].start && startPos < verbatimRanges[j].end) { if (startPos > verbatimRanges[j].start && startPos < verbatimRanges[j].end) {
tokenError.ignore = true; tokenError.ignore = true;
break; break;
@ -1912,7 +2329,14 @@ var CheckEnvironments = function (Environments, ErrorReporter) {
} }
} }
} }
};
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 ErrorReporter = function (TokeniseResult) {
var text = TokeniseResult.text; var text = TokeniseResult.text;
@ -1922,18 +2346,41 @@ var ErrorReporter = function (TokeniseResult) {
var errors = [], tokenErrors = []; var errors = [], tokenErrors = [];
this.errors = errors; this.errors = errors;
this.tokenErrors = tokenErrors; this.tokenErrors = tokenErrors;
this.filterMath = false;
this.getErrors = function () { this.getErrors = function () {
var returnedErrors = []; var returnedErrors = [];
for (var i = 0, len = tokenErrors.length; i < len; i++) { for (var i = 0, len = tokenErrors.length; i < len; i++) {
if (!tokenErrors[i].ignore) { returnedErrors.push(tokenErrors[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 line = token[0], type = token[1], start = token[2], end = token[3];
var start_col = start - linePosition[line]; var start_col = start - linePosition[line];
if (!end) { end = start + 1; } ;
var end_col = end - linePosition[line]; var end_col = end - linePosition[line];
tokenErrors.push({row: line, tokenErrors.push({row: line,
column: start_col, column: start_col,
@ -1945,10 +2392,12 @@ var ErrorReporter = function (TokeniseResult) {
text:message, text:message,
startPos: start, startPos: start,
endPos: end, 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 fromLine = fromToken[0], fromStart = fromToken[2], fromEnd = fromToken[3];
var toLine = toToken[0], toStart = toToken[2], toEnd = toToken[3]; var toLine = toToken[0], toStart = toToken[2], toEnd = toToken[3];
if (!toEnd) { toEnd = toStart + 1;}; if (!toEnd) { toEnd = toStart + 1;};
@ -1965,7 +2414,8 @@ var ErrorReporter = function (TokeniseResult) {
text:message, text:message,
startPos: fromStart, startPos: fromStart,
endPos: toEnd, endPos: toEnd,
suppressIfEditing:true}); suppressIfEditing:options.suppressIfEditing,
mathMode: options.mathMode});
}; };
@ -1986,7 +2436,8 @@ var ErrorReporter = function (TokeniseResult) {
end_col: end_col, end_col: end_col,
type: options.type ? options.type : "error", type: options.type ? options.type : "error",
text:message, text:message,
suppressIfEditing:options.suppressIfEditing}); suppressIfEditing:options.suppressIfEditing,
mathMode: options.mathMode});
}; };
this.EnvErrorTo = function (toEnv, message, options) { this.EnvErrorTo = function (toEnv, message, options) {
@ -2002,7 +2453,8 @@ var ErrorReporter = function (TokeniseResult) {
end_row: line, end_row: line,
end_col: end_col, end_col: end_col,
type: options.type ? options.type : "error", type: options.type ? options.type : "error",
text:message}; text:message,
mathMode: options.mathMode};
errors.push(err); errors.push(err);
}; };
@ -2019,7 +2471,8 @@ var ErrorReporter = function (TokeniseResult) {
end_row: lineNumber, end_row: lineNumber,
end_col: end_col, end_col: end_col,
type: options.type ? options.type : "error", 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 TokeniseResult = Tokenise(text);
var Reporter = new ErrorReporter(TokeniseResult); var Reporter = new ErrorReporter(TokeniseResult);
var Environments = InterpretTokens(TokeniseResult, Reporter); var Environments = InterpretTokens(TokeniseResult, Reporter);
CheckEnvironments(Environments, Reporter); Environments.close();
return Reporter.getErrors(); return Reporter.getErrors();
}; };