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.

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

mercurial