browser/devtools/sourceeditor/test/cm_mode_test.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /**
     2  * Helper to test CodeMirror highlighting modes. It pretty prints output of the
     3  * highlighter and can check against expected styles.
     4  *
     5  * Mode tests are registered by calling test.mode(testName, mode,
     6  * tokens), where mode is a mode object as returned by
     7  * CodeMirror.getMode, and tokens is an array of lines that make up
     8  * the test.
     9  *
    10  * These lines are strings, in which styled stretches of code are
    11  * enclosed in brackets `[]`, and prefixed by their style. For
    12  * example, `[keyword if]`. Brackets in the code itself must be
    13  * duplicated to prevent them from being interpreted as token
    14  * boundaries. For example `a[[i]]` for `a[i]`. If a token has
    15  * multiple styles, the styles must be separated by ampersands, for
    16  * example `[tag&error </hmtl>]`.
    17  *
    18  * See the test.js files in the css, markdown, gfm, and stex mode
    19  * directories for examples.
    20  */
    21 (function() {
    22   function findSingle(str, pos, ch) {
    23     for (;;) {
    24       var found = str.indexOf(ch, pos);
    25       if (found == -1) return null;
    26       if (str.charAt(found + 1) != ch) return found;
    27       pos = found + 2;
    28     }
    29   }
    31   var styleName = /[\w&-_]+/g;
    32   function parseTokens(strs) {
    33     var tokens = [], plain = "";
    34     for (var i = 0; i < strs.length; ++i) {
    35       if (i) plain += "\n";
    36       var str = strs[i], pos = 0;
    37       while (pos < str.length) {
    38         var style = null, text;
    39         if (str.charAt(pos) == "[" && str.charAt(pos+1) != "[") {
    40           styleName.lastIndex = pos + 1;
    41           var m = styleName.exec(str);
    42           style = m[0].replace(/&/g, " ");
    43           var textStart = pos + style.length + 2;
    44           var end = findSingle(str, textStart, "]");
    45           if (end == null) throw new Error("Unterminated token at " + pos + " in '" + str + "'" + style);
    46           text = str.slice(textStart, end);
    47           pos = end + 1;
    48         } else {
    49           var end = findSingle(str, pos, "[");
    50           if (end == null) end = str.length;
    51           text = str.slice(pos, end);
    52           pos = end;
    53         }
    54         text = text.replace(/\[\[|\]\]/g, function(s) {return s.charAt(0);});
    55         tokens.push(style, text);
    56         plain += text;
    57       }
    58     }
    59     return {tokens: tokens, plain: plain};
    60   }
    62   test.mode = function(name, mode, tokens, modeName) {
    63     var data = parseTokens(tokens);
    64     return test((modeName || mode.name) + "_" + name, function() {
    65       return compare(data.plain, data.tokens, mode);
    66     });
    67   };
    69   function esc(str) {
    70     return str.replace('&', '&amp;').replace('<', '&lt;');
    71   }
    73   function compare(text, expected, mode) {
    75     var expectedOutput = [];
    76     for (var i = 0; i < expected.length; i += 2) {
    77       var sty = expected[i];
    78       if (sty && sty.indexOf(" ")) sty = sty.split(' ').sort().join(' ');
    79       expectedOutput.push(sty, expected[i + 1]);
    80     }
    82     var observedOutput = highlight(text, mode);
    84     var s = "";
    85     var diff = highlightOutputsDifferent(expectedOutput, observedOutput);
    86     if (diff != null) {
    87       s += '<div class="mt-test mt-fail">';
    88       s +=   '<pre>' + esc(text) + '</pre>';
    89       s +=   '<div class="cm-s-default">';
    90       s += 'expected:';
    91       s +=   prettyPrintOutputTable(expectedOutput, diff);
    92       s += 'observed:';
    93       s +=   prettyPrintOutputTable(observedOutput, diff);
    94       s +=   '</div>';
    95       s += '</div>';
    96     }
    97     if (observedOutput.indentFailures) {
    98       for (var i = 0; i < observedOutput.indentFailures.length; i++)
    99         s += "<div class='mt-test mt-fail'>" + esc(observedOutput.indentFailures[i]) + "</div>";
   100     }
   101     if (s) throw new Failure(s);
   102   }
   104   function highlight(string, mode) {
   105     var state = mode.startState()
   107     var lines = string.replace(/\r\n/g,'\n').split('\n');
   108     var st = [], pos = 0;
   109     for (var i = 0; i < lines.length; ++i) {
   110       var line = lines[i], newLine = true;
   111       if (mode.indent) {
   112         var ws = line.match(/^\s*/)[0];
   113         var indent = mode.indent(state, line.slice(ws.length));
   114         if (indent != CodeMirror.Pass && indent != ws.length)
   115           (st.indentFailures || (st.indentFailures = [])).push(
   116             "Indentation of line " + (i + 1) + " is " + indent + " (expected " + ws.length + ")");
   117       }
   118       var stream = new CodeMirror.StringStream(line);
   119       if (line == "" && mode.blankLine) mode.blankLine(state);
   120       /* Start copied code from CodeMirror.highlight */
   121       while (!stream.eol()) {
   122         var compare = mode.token(stream, state), substr = stream.current();
   123         if (compare && compare.indexOf(" ") > -1) compare = compare.split(' ').sort().join(' ');
   124         stream.start = stream.pos;
   125         if (pos && st[pos-2] == compare && !newLine) {
   126           st[pos-1] += substr;
   127         } else if (substr) {
   128           st[pos++] = compare; st[pos++] = substr;
   129         }
   130         // Give up when line is ridiculously long
   131         if (stream.pos > 5000) {
   132           st[pos++] = null; st[pos++] = this.text.slice(stream.pos);
   133           break;
   134         }
   135         newLine = false;
   136       }
   137     }
   139     return st;
   140   }
   142   function highlightOutputsDifferent(o1, o2) {
   143     var minLen = Math.min(o1.length, o2.length);
   144     for (var i = 0; i < minLen; ++i)
   145       if (o1[i] != o2[i]) return i >> 1;
   146     if (o1.length > minLen || o2.length > minLen) return minLen;
   147   }
   149   function prettyPrintOutputTable(output, diffAt) {
   150     var s = '<table class="mt-output">';
   151     s += '<tr>';
   152     for (var i = 0; i < output.length; i += 2) {
   153       var style = output[i], val = output[i+1];
   154       s +=
   155       '<td class="mt-token"' + (i == diffAt * 2 ? " style='background: pink'" : "") + '>' +
   156         '<span class="cm-' + esc(String(style)) + '">' +
   157         esc(val.replace(/ /g,'\xb7')) +
   158         '</span>' +
   159         '</td>';
   160     }
   161     s += '</tr><tr>';
   162     for (var i = 0; i < output.length; i += 2) {
   163       s += '<td class="mt-style"><span>' + (output[i] || null) + '</span></td>';
   164     }
   165     s += '</table>';
   166     return s;
   167   }
   168 })();

mercurial