Parcourir la source

Update ace latex worker to include context reporting

James Allen il y a 9 ans
Parent
commit
4276f1544d

+ 1 - 1
services/web/public/js/ace-1.2.5/mode-latex.js

@@ -330,7 +330,7 @@ var createLatexWorker = function (session) {
     };
     worker.on("lint", function(results) {
         if(docChangePending) { docChangePending = false; };
-        hints = results.data;
+        hints = results.data.errors;
         if (hints.length > 100) {
             hints = hints.slice(0, 100); // limit to 100 errors
         };

+ 289 - 189
services/web/public/js/ace-1.2.5/worker-latex.js

@@ -1419,6 +1419,33 @@ var LatexWorker = exports.LatexWorker = function(sender) {
 
 oop.inherits(LatexWorker, Mirror);
 
+(function() {
+    var disabled = false;
+    this.onUpdate = function() {
+        if (disabled) { return ; };
+
+        var value = this.doc.getValue();
+        var errors = [];
+        var contexts = [];
+        try {
+            if (value) {
+                var result = Parse(value);
+                errors = result.errors;
+                contexts = result.contexts;
+            }
+        } catch (e) {
+            console.log(e);
+            disabled = true;
+            errors = [];
+        }
+        this.sender.emit("lint", {
+          errors: errors,
+          contexts: contexts
+        });
+    };
+
+}).call(LatexWorker.prototype);
+
 var Tokenise = function (text) {
     var Tokens = [];
     var Comments = [];
@@ -1503,22 +1530,8 @@ var Tokenise = function (text) {
                 }
                 idx = SPECIAL.lastIndex = nextSpecialPos;
             }
-        } else if (code === "{") {  // open group
-            Tokens.push([lineNumber, code, pos]);
-        } else if (code === "}") {  // close group
-            Tokens.push([lineNumber, code, pos]);
-        } else if (code === "$") {  // math mode
-            Tokens.push([lineNumber, code, pos]);
-        } else if (code === "&") {  // tabalign
-            Tokens.push([lineNumber, code, pos]);
-        } else if (code === "#") {  // macro parameter
-            Tokens.push([lineNumber, code, pos]);
-        } else if (code === "^") {  // superscript
-            Tokens.push([lineNumber, code, pos]);
-        } else if (code === "_") {  // subscript
-            Tokens.push([lineNumber, code, pos]);
-        } else if (code === "~") {  // active character (space)
-            Tokens.push([lineNumber, code, pos]);
+        } else if (["{", "}", "$", "&", "#", "^", "_", "~"].indexOf(code) > -1) {  // special characters
+            Tokens.push([lineNumber, code, pos, pos+1]);
         } else {
             throw "unrecognised character " + code;
         }
@@ -1539,15 +1552,15 @@ var read1arg = function (TokeniseResult, k, options) {
     };
 
     var open = Tokens[k+1];
-    var env = Tokens[k+2];
+    var delimiter = Tokens[k+2];
     var close = Tokens[k+3];
-    var envName;
+    var delimiterName;
 
     if(open && open[1] === "\\") {
-        envName = open[4]; // array element 4 is command sequence
+        delimiterName = open[4]; // array element 4 is command sequence
         return k + 1;
-    } else if(open && open[1] === "{" && env && env[1] === "\\" && close && close[1] === "}") {
-        envName = env[4]; // NOTE: if we were actually using this, keep track of * above
+    } else if(open && open[1] === "{" && delimiter && delimiter[1] === "\\" && close && close[1] === "}") {
+        delimiterName = delimiter[4]; // NOTE: if we were actually using this, keep track of * above
         return k + 3; // array element 4 is command sequence
     } else {
         return null;
@@ -1579,21 +1592,21 @@ var read1name = function (TokeniseResult, k) {
     var text = TokeniseResult.text;
 
     var open = Tokens[k+1];
-    var env = Tokens[k+2];
+    var delimiter = Tokens[k+2];
     var close = Tokens[k+3];
 
-    if(open && open[1] === "{" && env && env[1] === "Text" && close && close[1] === "}") {
-        var envName = text.substring(env[2], env[3]);
+    if(open && open[1] === "{" && delimiter && delimiter[1] === "Text" && close && close[1] === "}") {
+        var delimiterName = text.substring(delimiter[2], delimiter[3]);
         return k + 3;
-    } else if (open && open[1] === "{" && env && env[1] === "Text") {
-        envName = "";
+    } else if (open && open[1] === "{" && delimiter && delimiter[1] === "Text") {
+        delimiterName = "";
         for (var j = k + 2, tok; (tok = Tokens[j]); j++) {
             if (tok[1] === "Text") {
                 var str = text.substring(tok[2], tok[3]);
                 if (!str.match(/^\S*$/)) { break; }
-                envName = envName + str;
+                delimiterName = delimiterName + str;
             } else if (tok[1] === "_") {
-                envName = envName + "_";
+                delimiterName = delimiterName + "_";
             } else {
                 break;
             }
@@ -1788,7 +1801,7 @@ var InterpretTokens = function (TokeniseResult, ErrorReporter) {
 
     var TokenErrorFromTo = ErrorReporter.TokenErrorFromTo;
     var TokenError = ErrorReporter.TokenError;
-    var Environments = new EnvHandler(ErrorReporter);
+    var Environments = new EnvHandler(TokeniseResult, ErrorReporter);
 
     var nextGroupMathMode = null; // if the next group should have
     var nextGroupMathModeStack = [] ; // tracking all nextGroupMathModes
@@ -1815,28 +1828,28 @@ var InterpretTokens = function (TokeniseResult, ErrorReporter) {
         if (type === "\\") {
             if (seq === "begin" || seq === "end") {
                 var open = Tokens[i+1];
-                var env = Tokens[i+2];
+                var delimiter = Tokens[i+2];
                 var close = Tokens[i+3];
-                if(open && open[1] === "{" && env && env[1] === "Text" && close && close[1] === "}") {
-                    var envName = text.substring(env[2], env[3]);
-                    Environments.push({command: seq, name: envName, token: token, closeToken: close});
+                if(open && open[1] === "{" && delimiter && delimiter[1] === "Text" && close && close[1] === "}") {
+                    var delimiterName = text.substring(delimiter[2], delimiter[3]);
+                    Environments.push({command: seq, name: delimiterName, token: token, closeToken: close});
                     i = i + 3; // advance past these tokens
                 } else {
-                    if (open && open[1] === "{" && env && env[1] === "Text") {
-                        envName = "";
+                    if (open && open[1] === "{" && delimiter && delimiter[1] === "Text") {
+                        delimiterName = "";
                         for (var j = i + 2, tok; (tok = Tokens[j]); j++) {
                             if (tok[1] === "Text") {
                                 var str = text.substring(tok[2], tok[3]);
                                 if (!str.match(/^\S*$/)) { break; }
-                                envName = envName + str;
+                                delimiterName = delimiterName + str;
                             } else if (tok[1] === "_") {
-                                envName = envName + "_";
+                                delimiterName = delimiterName + "_";
                             } else {
                                 break;
                             }
                         }
                         if (tok && tok[1] === "}") {
-                            Environments.push({command: seq, name: envName, token: token, closeToken: close});
+                            Environments.push({command: seq, name: delimiterName, token: token, closeToken: close});
                             i = j; // advance past these tokens
                             continue;
                         }
@@ -1844,8 +1857,8 @@ var InterpretTokens = function (TokeniseResult, ErrorReporter) {
                     var endToken = null;
                     if (open && open[1] === "{") {
                         endToken = open; // we've got a {
-                        if (env && env[1] === "Text") {
-                            endToken = env.slice(); // we've got some text following the {
+                        if (delimiter && delimiter[1] === "Text") {
+                            endToken = delimiter.slice(); // we've got some text following the {
                             start = endToken[2]; end = endToken[3];
                             for (j = start; j < end; j++) {
                                 var char = text[j];
@@ -1978,7 +1991,12 @@ var InterpretTokens = function (TokeniseResult, ErrorReporter) {
             var nextIsDollar = lookAhead && lookAhead[1] === "$";
             currentMathMode = Environments.getMathMode() ; // returns null / $(inline) / $$(display)
             if (nextIsDollar && (!currentMathMode || currentMathMode.command == "$$")) {
-                Environments.push({command:"$$", token:token});
+                if (currentMathMode && currentMathMode.command == "$$") {
+                    var delimiterToken = lookAhead;
+                } else {
+                    var delimiterToken = token;
+                }
+                Environments.push({command:"$$", token:delimiterToken});
                 i = i + 1;
             } else {
                 Environments.push({command:"$", token:token});
@@ -1999,74 +2017,174 @@ var InterpretTokens = function (TokeniseResult, ErrorReporter) {
     return Environments;
 };
 
-var EnvHandler = function (ErrorReporter) {
+var DocumentTree = function(TokeniseResult) {
+    var tree = {
+      children: []
+    };
+    var stack = [tree];
+    
+    this.openEnv = function(startDelimiter) {
+        var currentNode = this.getCurrentNode();
+        var newNode = {
+            startDelimiter: startDelimiter,
+            children: []
+        };
+        currentNode.children.push(newNode);
+        stack.push(newNode);
+    };
+    
+    this.closeEnv = function(endDelimiter) {
+        if (stack.length == 1) {
+            return null
+        }
+        var currentNode = stack.pop();
+        currentNode.endDelimiter = endDelimiter;
+        return currentNode.startDelimiter;
+    };
+    
+    this.getNthPreviousNode = function(n) {
+        var offset = stack.length - n - 1;
+        if (offset < 0)
+            return null;
+        return stack[offset];
+    }
+    
+    this.getCurrentNode = function() {
+        return this.getNthPreviousNode(0);
+    }
+    
+    this.getCurrentDelimiter = function() {
+        return this.getCurrentNode().startDelimiter;
+    };
+
+    this.getPreviousDelimiter = function() {
+        var node = this.getNthPreviousNode(1);
+        if (!node)
+            return null
+        return node.startDelimiter;
+    }
+    
+    this.getDepth = function() {
+        return (stack.length - 1) // Root node doesn't count
+    }
+    
+    this.getContexts = function() {
+        var linePosition = TokeniseResult.linePosition;
+
+        function tokenToRange(token) {
+            var line = token[0], start = token[2], end = token[3];
+            var start_col = start - linePosition[line];
+            if (!end) { end = start + 1; } ;
+            var end_col = end - linePosition[line];
+            return {
+                start: {
+                    row: line,
+                    column: start_col
+                },
+                end: {
+                    row: line,
+                    column: end_col
+                }
+            }
+        };
+        
+        function getContextsFromNode(node) {
+            if (node.startDelimiter && node.startDelimiter.mathMode) {
+                var context = {
+                    type: "math",
+                    range: {
+                        start: tokenToRange(node.startDelimiter.token).start
+                    }
+                };
+                if (node.endDelimiter) {
+                    var closeToken = node.endDelimiter.closeToken || node.endDelimiter.token;
+                    context.range.end = tokenToRange(closeToken).end;
+                };
+                return [context];
+            } else {
+                var contexts = [];
+                for (var i = 0; i < node.children.length; i++) {
+                    var child = node.children[i];
+                    contexts = contexts.concat(getContextsFromNode(child));
+                }
+                return contexts;
+            }
+        };
+        
+        return getContextsFromNode(tree);
+    }
+}
+
+var EnvHandler = function (TokeniseResult, ErrorReporter) {
     var ErrorTo = ErrorReporter.EnvErrorTo;
     var ErrorFromTo = ErrorReporter.EnvErrorFromTo;
     var ErrorFrom = ErrorReporter.EnvErrorFrom;
 
-    var envs = [];
+    var delimiters = [];
 
-    var state = [];
+    var document = new DocumentTree(TokeniseResult);
     var documentClosed = null;
     var inVerbatim = false;
     var verbatimRanges = [];
+    
+    this.getDocument = function() {
+        return document;
+    };
 
-    this.Environments = envs;
-
-    this.push = function (newEnv) {
-        this.setEnvProps(newEnv);
-        this.checkAndUpdateState(newEnv);
-        envs.push(newEnv);
+    this.push = function (newDelimiter) {
+        this.setDelimiterProps(newDelimiter);
+        this.checkAndUpdateState(newDelimiter);
+        delimiters.push(newDelimiter);
     };
 
-    this._endVerbatim = function (thisEnv) {
-        var lastEnv = state.pop();
-        if (lastEnv && lastEnv.name === thisEnv.name) {
+    this._endVerbatim = function (thisDelimiter) {
+        var lastDelimiter = document.getCurrentDelimiter();
+        if (lastDelimiter && lastDelimiter.name === thisDelimiter.name) {
             inVerbatim = false;
-            verbatimRanges.push({start: lastEnv.token[2], end: thisEnv.token[2]});
-        } else {
-            if(lastEnv) { state.push(lastEnv); } ;
+            document.closeEnv(thisDelimiter);
+            verbatimRanges.push({start: lastDelimiter.token[2], end: thisDelimiter.token[2]});
         }
     };
 
     var invalidEnvs = [];
 
-    this._end = function (thisEnv) {
+    this._end = function (thisDelimiter) {
         do {
-            var lastEnv = state.pop();
+            var lastDelimiter = document.getCurrentDelimiter();
             var retry = false;
             var i;
 
-            if (closedBy(lastEnv, thisEnv)) {
-                if (thisEnv.command === "end" && thisEnv.name === "document" && !documentClosed) {
-                    documentClosed = thisEnv;
+            if (closedBy(lastDelimiter, thisDelimiter)) {
+                document.closeEnv(thisDelimiter);
+                if (thisDelimiter.command === "end" && thisDelimiter.name === "document" && !documentClosed) {
+                    documentClosed = thisDelimiter;
                 };
                 return;
-            } else if (!lastEnv) {
+            } else if (!lastDelimiter) {
                 if (documentClosed) {
-                    ErrorFromTo(documentClosed, thisEnv, "\\end{" + documentClosed.name + "} is followed by unexpected content",{errorAtStart: true, type: "info"});
+                    ErrorFromTo(documentClosed, thisDelimiter, "\\end{" + documentClosed.name + "} is followed by unexpected content",{errorAtStart: true, type: "info"});
                 } else {
-                    ErrorTo(thisEnv, "unexpected " + getName(thisEnv));
+                    ErrorTo(thisDelimiter, "unexpected " + getName(thisDelimiter));
                 }
-            } else if (invalidEnvs.length > 0 && (i = indexOfClosingEnvInArray(invalidEnvs, thisEnv) > -1)) {
+            } else if (invalidEnvs.length > 0 && (i = indexOfClosingEnvInArray(invalidEnvs, thisDelimiter) > -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);
+                var status = reportError(lastDelimiter, thisDelimiter);
+                if (delimiterPrecedence(lastDelimiter) < delimiterPrecedence(thisDelimiter)) {
+                    document.closeEnv();
+                    invalidEnvs.push(lastDelimiter);
                     retry = true;
                 } else {
-                    var prevLastEnv = state.pop();
-                    if(prevLastEnv) {
-                        if (thisEnv.name === prevLastEnv.name) {
+                    var prevDelimiter = document.getPreviousDelimiter();
+                    if(prevDelimiter) {
+                        if (thisDelimiter.name === prevDelimiter.name) {
+                            document.closeEnv() // Close current env
+                            document.closeEnv(thisDelimiter) // Close previous env
                             return;
-                        } else {
-                            state.push(prevLastEnv);
                         }
                     }
-                    invalidEnvs.push(lastEnv);
+                    invalidEnvs.push(lastDelimiter);
                 }
 
             }
@@ -2082,28 +2200,28 @@ var EnvHandler = function (ErrorReporter) {
         "$$": "$$"
     };
 
-    var closedBy = function (lastEnv, thisEnv) {
-        if (!lastEnv) {
+    var closedBy = function (lastDelimiter, thisDelimiter) {
+        if (!lastDelimiter) {
             return false ;
-        } else if (thisEnv.command === "end") {
-            return lastEnv.command === "begin" && lastEnv.name === thisEnv.name;
-        } else if (thisEnv.command === CLOSING_DELIMITER[lastEnv.command]) {
+        } else if (thisDelimiter.command === "end") {
+            return lastDelimiter.command === "begin" && lastDelimiter.name === thisDelimiter.name;
+        } else if (thisDelimiter.command === CLOSING_DELIMITER[lastDelimiter.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)) {
+    var indexOfClosingEnvInArray = function (delimiters, thisDelimiter) {
+        for (var i = 0, n = delimiters.length; i < n ; i++) {
+            if (closedBy(delimiters[i], thisDelimiter)) {
                 return i;
             }
         }
         return -1;
     };
 
-    var envPrecedence = function (env) {
+    var delimiterPrecedence = function (delimiter) {
         var openScore = {
             "{" : 1,
             "left" : 2,
@@ -2118,14 +2236,14 @@ var EnvHandler = function (ErrorReporter) {
             "$$" : 5,
             "end": 4
         };
-        if (env.command) {
-            return openScore[env.command] || closeScore[env.command];
+        if (delimiter.command) {
+            return openScore[delimiter.command] || closeScore[delimiter.command];
         } else {
             return 0;
         }
     };
 
-    var getName = function(env) {
+    var getName = function(delimiter) {
         var description = {
             "{" : "open group {",
             "}" : "close group }",
@@ -2138,12 +2256,12 @@ var EnvHandler = function (ErrorReporter) {
             "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];
+        if (delimiter.command === "begin" || delimiter.command === "end") {
+            return "\\" + delimiter.command + "{" + delimiter.name + "}";
+        } else if (delimiter.command in description) {
+            return description[delimiter.command];
         } else {
-            return env.command;
+            return delimiter.command;
         }
     };
 
@@ -2151,81 +2269,81 @@ var EnvHandler = function (ErrorReporter) {
     var UNCLOSED_GROUP = 2;
     var UNCLOSED_ENV = 3;
 
-    var reportError = function(lastEnv, thisEnv) {
-        if (!lastEnv) { // unexpected close, nothing was open!
+    var reportError = function(lastDelimiter, thisDelimiter) {
+        if (!lastDelimiter) { // unexpected close, nothing was open!
             if (documentClosed) {
-                ErrorFromTo(documentClosed, thisEnv, "\\end{" + documentClosed.name + "} is followed by unexpected end group }",{errorAtStart: true, type: "info"});
+                ErrorFromTo(documentClosed, thisDelimiter, "\\end{" + documentClosed.name + "} is followed by unexpected end group }",{errorAtStart: true, type: "info"});
             } else {
-                ErrorTo(thisEnv, "unexpected " + getName(thisEnv));
+                ErrorTo(thisDelimiter, "unexpected " + getName(thisDelimiter));
             };
             return EXTRA_CLOSE;
-        } else if (lastEnv.command === "{" && thisEnv.command === "end") {
-            ErrorFromTo(lastEnv, thisEnv, "unclosed " + getName(lastEnv) + " found at " + getName(thisEnv),
+        } else if (lastDelimiter.command === "{" && thisDelimiter.command === "end") {
+            ErrorFromTo(lastDelimiter, thisDelimiter, "unclosed " + getName(lastDelimiter) + " found at " + getName(thisDelimiter),
                         {suppressIfEditing:true, errorAtStart: true, type:"warning"});
             return UNCLOSED_GROUP;
         } else {
-            var pLast = envPrecedence(lastEnv);
-            var pThis = envPrecedence(thisEnv);
+            var pLast = delimiterPrecedence(lastDelimiter);
+            var pThis = delimiterPrecedence(thisDelimiter);
             if (pThis > pLast) {
-                ErrorFromTo(lastEnv, thisEnv, "unclosed " + getName(lastEnv) + " found at " + getName(thisEnv),
+                ErrorFromTo(lastDelimiter, thisDelimiter, "unclosed " + getName(lastDelimiter) + " found at " + getName(thisDelimiter),
                            {suppressIfEditing:true, errorAtStart: true});
             } else {
-                ErrorFromTo(lastEnv, thisEnv, "unexpected " + getName(thisEnv) + " after " + getName(lastEnv));
+                ErrorFromTo(lastDelimiter, thisDelimiter, "unexpected " + getName(thisDelimiter) + " after " + getName(lastDelimiter));
             }
             return UNCLOSED_ENV;
         };
     };
 
-    this._beginMathMode = function (thisEnv) {
+    this._beginMathMode = function (thisDelimiter) {
         var currentMathMode = this.getMathMode(); // undefined, null, $, $$, name of mathmode env
         if (currentMathMode) {
-            ErrorFrom(thisEnv, getName(thisEnv) + " used inside existing math mode " + getName(currentMathMode),
+            ErrorFrom(thisDelimiter, getName(thisDelimiter) + " used inside existing math mode " + getName(currentMathMode),
                       {suppressIfEditing:true, errorAtStart: true, mathMode:true});
         };
-        thisEnv.mathMode = thisEnv;
-        state.push(thisEnv);
+        thisDelimiter.mathMode = thisDelimiter;
+        document.openEnv(thisDelimiter);
     };
 
-    this._toggleMathMode = function (thisEnv) {
-        var lastEnv = state.pop();
-        if (closedBy(lastEnv, thisEnv)) {
+    this._toggleMathMode = function (thisDelimiter) {
+        var lastDelimiter = document.getCurrentDelimiter();
+        if (closedBy(lastDelimiter, thisDelimiter)) {
+            document.closeEnv(thisDelimiter)
             return;
         } else {
-            if (lastEnv) {state.push(lastEnv);}
-            if (lastEnv && lastEnv.mathMode) {
-                this._end(thisEnv);
+            if (lastDelimiter && lastDelimiter.mathMode) {
+                this._end(thisDelimiter);
             } else {
-                thisEnv.mathMode = thisEnv;
-                state.push(thisEnv);
+                thisDelimiter.mathMode = thisDelimiter;
+                document.openEnv(thisDelimiter);
             }
         };
     };
 
     this.getMathMode = function () {
-        var n = state.length;
-        if (n > 0) {
-            return state[n-1].mathMode;
+        var currentDelimiter = document.getCurrentDelimiter();
+        if (currentDelimiter) {
+            return currentDelimiter.mathMode;
         } else {
             return null;
         }
     };
 
     this.insideGroup = function () {
-        var n = state.length;
-        if (n > 0) {
-            return (state[n-1].command === "{");
+        var currentDelimiter = document.getCurrentDelimiter();
+        if (currentDelimiter) {
+            return (currentDelimiter.command === "{");
         } else {
             return null;
         }
     };
 
     var resetMathMode = function () {
-        var n = state.length;
-        if (n > 0) {
-            var lastMathMode = state[n-1].mathMode;
+        var currentDelimiter = document.getCurrentDelimiter();
+        if (currentDelimiter) {
+            var lastMathMode = currentDelimiter.mathMode;
             do {
-                var lastEnv = state.pop();
-            } while (lastEnv && lastEnv !== lastMathMode);
+                var lastDelimiter = document.closeEnv();
+            } while (lastDelimiter && lastDelimiter !== lastMathMode);
         } else {
             return;
         }
@@ -2233,42 +2351,42 @@ var EnvHandler = function (ErrorReporter) {
 
     this.resetMathMode = resetMathMode;
 
-    var getNewMathMode = function (currentMathMode, thisEnv) {
+    var getNewMathMode = function (currentMathMode, thisDelimiter) {
         var newMathMode = null;
 
-        if (thisEnv.command === "{") {
-            if (thisEnv.mathMode !== null) {
-                newMathMode = thisEnv.mathMode;
+        if (thisDelimiter.command === "{") {
+            if (thisDelimiter.mathMode !== null) {
+                newMathMode = thisDelimiter.mathMode;
             } else {
                 newMathMode = currentMathMode;
             }
-        } else if (thisEnv.command === "left") {
+        } else if (thisDelimiter.command === "left") {
             if (currentMathMode === null) {
-                ErrorFrom(thisEnv, "\\left can only be used in math mode", {mathMode: true});
+                ErrorFrom(thisDelimiter, "\\left can only be used in math mode", {mathMode: true});
             };
             newMathMode = currentMathMode;
-        } else if (thisEnv.command === "begin") {
-            var name = thisEnv.name;
+        } else if (thisDelimiter.command === "begin") {
+            var name = thisDelimiter.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),
+                        ErrorFromTo(currentMathMode, thisDelimiter, thisDelimiter.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});
+                        ErrorFrom(thisDelimiter, thisDelimiter.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),
+                        ErrorFromTo(currentMathMode, thisDelimiter, thisDelimiter.name + " used inside " + getName(currentMathMode),
                                     {suppressIfEditing:true, errorAtStart: true, mathMode: true});
                         resetMathMode();
                     };
-                    newMathMode = thisEnv;
+                    newMathMode = thisDelimiter;
                 } else {
                     newMathMode = undefined;  // undefined means we don't know if we are in math mode or not
                 }
@@ -2277,41 +2395,41 @@ var EnvHandler = function (ErrorReporter) {
         return newMathMode;
     };
 
-    this.checkAndUpdateState = function (thisEnv) {
+    this.checkAndUpdateState = function (thisDelimiter) {
         if (inVerbatim) {
-            if (thisEnv.command === "end") {
-                this._endVerbatim(thisEnv);
+            if (thisDelimiter.command === "end") {
+                this._endVerbatim(thisDelimiter);
             } else {
                 return; // ignore anything in verbatim environments
             }
-        } else if(thisEnv.command === "begin" || thisEnv.command === "{" || thisEnv.command === "left") {
-            if (thisEnv.verbatim) {inVerbatim = true;};
+        } else if(thisDelimiter.command === "begin" || thisDelimiter.command === "{" || thisDelimiter.command === "left") {
+            if (thisDelimiter.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);
+            var newMathMode = getNewMathMode(currentMathMode, thisDelimiter);
+            thisDelimiter.mathMode = newMathMode;
+            document.openEnv(thisDelimiter);
+        } else if (thisDelimiter.command === "end") {
+            this._end(thisDelimiter);
+        } else if (thisDelimiter.command === "(" || thisDelimiter.command === "[") {
+            this._beginMathMode(thisDelimiter);
+        } else if (thisDelimiter.command === ")" || thisDelimiter.command === "]") {
+            this._end(thisDelimiter);
+        } else if (thisDelimiter.command === "}") {
+            this._end(thisDelimiter);
+        } else if (thisDelimiter.command === "right") {
+            this._end(thisDelimiter);
+        } else if (thisDelimiter.command === "$" || thisDelimiter.command === "$$") {
+            this._toggleMathMode(thisDelimiter);
         }
     };
 
     this.close = function () {
-        while (state.length > 0) {
-            var thisEnv = state.pop();
-            if (thisEnv.command === "{") {
-                ErrorFrom(thisEnv, "unclosed group {", {type:"warning"});
+        while (document.getDepth() > 0) {
+            var thisDelimiter = document.closeEnv();
+            if (thisDelimiter.command === "{") {
+                ErrorFrom(thisDelimiter, "unclosed group {", {type:"warning"});
             } else {
-                ErrorFrom(thisEnv, "unclosed " + getName(thisEnv));
+                ErrorFrom(thisDelimiter, "unclosed " + getName(thisDelimiter));
             }
         }
         var vlen = verbatimRanges.length;
@@ -2331,10 +2449,10 @@ var EnvHandler = function (ErrorReporter) {
         }
     };
 
-    this.setEnvProps = function (env) {
-        var name = env.name ;
+    this.setDelimiterProps = function (delimiter) {
+        var name = delimiter.name ;
         if (name && name.match(/^(verbatim|boxedverbatim|lstlisting|minted|Verbatim)$/)) {
-            env.verbatim = true;
+            delimiter.verbatim = true;
         }
     };
 };
@@ -2458,9 +2576,9 @@ var ErrorReporter = function (TokeniseResult) {
         errors.push(err);
     };
 
-    this.EnvErrorFrom = function (env, message, options) {
+    this.EnvErrorFrom = function (delimiter, message, options) {
         if(!options) { options = {} ; };
-        var token = env.token;
+        var token = delimiter.token;
         var line = token[0], type = token[1], start = token[2], end = token[3];
         var start_col = start - linePosition[line];
         var end_col = Infinity;
@@ -2481,29 +2599,11 @@ var Parse = function (text) {
     var Reporter = new ErrorReporter(TokeniseResult);
     var Environments = InterpretTokens(TokeniseResult, Reporter);
     Environments.close();
-    return Reporter.getErrors();
+    return {
+      errors: Reporter.getErrors(),
+      contexts: Environments.getDocument().getContexts()
+    }
 };
-
-(function() {
-    var disabled = false;
-
-    this.onUpdate = function() {
-        if (disabled) { return ; };
-
-        var value = this.doc.getValue();
-        var errors = [];
-        try {
-            if (value)
-                errors = Parse(value);
-        } catch (e) {
-            disabled = true;
-            errors = [];
-        }
-        this.sender.emit("lint", errors);
-    };
-
-}).call(LatexWorker.prototype);
-
 });
 
 ace.define("ace/lib/es5-shim",["require","exports","module"], function(require, exports, module) {