1Formatter.prototype.debug = function(from, to)
2{
3    var debug = "";
4    var outerMode = this._codeMirror.getMode();
5    var content = this._codeMirror.getRange(from, to);
6    var state = CodeMirror.copyState(outerMode, this._codeMirror.getTokenAt(from).state);
7
8    function pad(str, x, doNotQuote)
9    {
10        var result = doNotQuote ? str : "'" + str + "'";
11        for (var toPad = x - result.length; toPad > 0; --toPad)
12            result += " ";
13        return result;
14    }
15
16    function debugToken(mode, token, state, stream, originalPosition, startOfNewLine)
17    {
18        // Token Type
19        debug += "Token: " + pad(String(token), 14, !token);
20
21        // Original Position
22        debug += "Position: " + pad(String(originalPosition), 10);
23
24        // Language Specific Info
25        if (state.lexical) {
26            debug += "Lexical: " + pad(String(state.lexical.type), 10); // JavaScript
27            debug += "Prev: " + pad(String(state.lexical.prev ? state.lexical.prev.type : state.lexical.prev), 10, !state.lexical.prev);
28        }
29        else if (state.stack)
30            debug += "Stack: " + pad(String(state.stack[state.stack.length-1]), 16); // CSS
31
32        // String
33        debug += "Current: '" + stream.current() + "'\n";
34    }
35
36    var lineOffset = 0;
37    var lines = content.split("\n");
38    for (var i = 0; i < lines.length; ++i) {
39        var line = lines[i];
40        var startOfNewLine = true;
41        var stream = new CodeMirror.StringStream(line);
42        while (!stream.eol()) {
43            var innerMode = CodeMirror.innerMode(outerMode, state);
44            var token = outerMode.token(stream, state);
45            debugToken(innerMode.mode, token, state, stream, lineOffset + stream.start, startOfNewLine);
46            stream.start = stream.pos;
47            startOfNewLine = false;
48        }
49    }
50
51    return debug;
52}
53