Skip to content

Commit 8bca1f2

Browse files
committed
Add warnings & fix tag fill for error tooltips
1 parent 9f8e4b0 commit 8bca1f2

File tree

15 files changed

+106
-65
lines changed

15 files changed

+106
-65
lines changed

assets/workers/RegExWorker.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ onmessage = function (evt) {
77
// shared between BrowserSolver & RegExWorker
88
var matches = [], match, index, error;
99
while (match = regex.exec(text)) {
10-
if (index === regex.lastIndex) { error = {id:"infinite"}; break; }
10+
if (index === regex.lastIndex) { error = {id:"infinite", warning:true}; ++regex.lastIndex; }
1111
index = regex.lastIndex;
1212
var groups = match.reduce(function (arr, s, i) { return (i===0 || arr.push({s:s})) && arr },[]);
1313
matches.push({i:match.index, l:match[0].length, groups:groups});

dev/sass/colors.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ $strong-shadow: rgba(0,0,0, 0.45);
3939

4040
// syntax coloring:
4141
$error-color: #C22;
42+
$warning-color: $error-color;
4243

4344
$group-color: #090;
4445
$groupbg-color: #0E0;

dev/sass/styles.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ span.match {
120120
span.error {
121121
color: $error-color;
122122
font-weight: bold;
123+
124+
&.warning {
125+
color: $warning-color;
126+
}
123127
}
124128

125129
.anim-spin {

dev/sass/views/expression.scss

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,11 @@
9999
}
100100

101101
.exp-error {
102-
border-bottom: solid 3px $error-color;
102+
border-bottom: solid 2px $error-color;
103+
}
104+
105+
.exp-warning {
106+
border-bottom: dotted 2px $warning-color;
103107
}
104108

105109
.exp-char {

dev/sass/views/text.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
&.error {
1010
background: $error-color;
1111
color: $doc-lightest;
12+
13+
&.warning {
14+
background: $warning-color;
15+
}
1216
}
1317

1418
em {

dev/sass/views/tools/explain.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@
3939
background: mix($doc-white, $error-color, 85);
4040
border-color: $error-color;
4141

42+
&.warning {
43+
/* nothing yet */
44+
}
45+
4246
.error-title {
4347
font-weight: bold;
4448
}

dev/src/ExpressionLexer.js

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export default class ExpressionLexer {
7474
brgroups.pop();
7575
}
7676
} else {
77-
token.err = "groupclose";
77+
token.error = {id: "groupclose"};
7878
}
7979
} else if (c === "[") {
8080
charset = this.parseSquareBracket(str, token, charset);
@@ -120,18 +120,12 @@ export default class ExpressionLexer {
120120
// quantifier:
121121
if (token.clss === "quant") {
122122
if (!prv || prv.close !== undefined || unquantifiable[prv.type] || (prv.open && unquantifiable[prv.open.type])) {
123-
token.err = "quanttarg";
123+
token.error = {id: "quanttarg"};
124124
} else {
125125
token.related = [prv.open || prv];
126126
}
127127
}
128128

129-
// js warnings:
130-
// TODO: this isn't ideal, but I'm hesitant to write a more robust solution for a couple of edge cases.
131-
if (profile.id === "js" && (token.type === "neglookbehind" || token.type === "poslookbehind")) {
132-
token.err = "jsfuture";
133-
}
134-
135129
// reference:
136130
if (token.group === true) {
137131
refs.push(token);
@@ -141,7 +135,7 @@ export default class ExpressionLexer {
141135
let curGroup = groups.length ? groups[groups.length-1] : null;
142136
if (curGroup && (curGroup.type === "conditional" || curGroup.type === "conditionalgroup") && token.type === "alt") {
143137
if (!curGroup.alt) { curGroup.alt = token; }
144-
else { token.err = "extraelse"; }
138+
else { token.error = {id: "extraelse"}; }
145139
token.related = [curGroup];
146140
token.type = "conditionalelse";
147141
token.clss = "special";
@@ -154,13 +148,17 @@ export default class ExpressionLexer {
154148
if (prv && prv.type === "range" && prv.l === 1) {
155149
this.validateRange(str, token);
156150
}
151+
152+
// js warnings:
153+
// TODO: this isn't ideal, but I'm hesitant to write a more robust solution for a couple of edge cases.
154+
if (profile.id === "js") { this.addJSWarnings(token); }
157155

158156
// general:
159157
if (token.open && !token.clss) {
160158
token.clss = token.open.clss;
161159
}
162-
if (token.err) {
163-
this.errors.push(token.err);
160+
if (token.error) {
161+
this.addError(token);
164162
}
165163
i += token.l;
166164
prev = token;
@@ -169,16 +167,28 @@ export default class ExpressionLexer {
169167

170168
// post processing:
171169
while (groups.length) {
172-
this.errors.push(groups.pop().err = "groupopen");
170+
this.addError(groups.pop(), {id: "groupopen"});
173171
}
174172
this.matchRefs(refs, capgroups, namedgroups);
175173
if (charset) {
176-
this.errors.push(charset.err = "setopen");
174+
this.addError(charset, {id: "setopen"});
177175
}
178176

179177
return this.token;
180178
}
181179

180+
addError(token, error=token.error) {
181+
token.error = error;
182+
this.errors.push(token);
183+
}
184+
185+
addJSWarnings(token) {
186+
if ((token.type === "neglookbehind" || token.type === "poslookbehind") ||
187+
(token.type === "sticky" || token.type === "unicode")) {
188+
token.error = {id: "jsfuture", warning:true};
189+
}
190+
}
191+
182192
addCaptureGroup(token, groups) {
183193
// it would be nice to make branch reset groups actually highlight all of the groups that share the same number
184194
// that would require switching to arrays of groups for each group num - requires rearchitecture throughout the app.
@@ -191,10 +201,10 @@ export default class ExpressionLexer {
191201
token.num = capgroups.length+1;
192202
}
193203
if (!capgroups[token.num-1]) { capgroups.push(token); }
194-
if (token.name && !token.err) {
195-
if (/\d/.test(token.name[0])) { token.err = "badname"; }
204+
if (token.name && !token.error) {
205+
if (/\d/.test(token.name[0])) { token.error = {id: "badname"}; }
196206
else if (namedgroups[token.name]) {
197-
token.err = "dupname";
207+
token.error = {id: "dupname"};
198208
token.related = [namedgroups[token.name]];
199209
} else { namedgroups[token.name] = token; }
200210
}
@@ -224,7 +234,7 @@ export default class ExpressionLexer {
224234
delete token.group;
225235
delete token.relIndex;
226236
this.refToOctal(token);
227-
if (token.err) { this.errors.push(token.err); }
237+
if (token.error) { this.errors.push(token.error); }
228238
}
229239
}
230240
};
@@ -241,7 +251,7 @@ export default class ExpressionLexer {
241251
let name = token.name, profile = this._profile;
242252
if (token.type !== "numref") {
243253
// not a simple \4 style reference, so can't decompose into an octal.
244-
token.err = "unmatchedref";
254+
token.error = {id: "unmatchedref"};
245255
} else if (/^[0-7]{2}$/.test(name) || (profile.config.reftooctalalways && /^[0-7]$/.test(name))) { // octal
246256
let next = token.next, char = String.fromCharCode(next.code);
247257
if (next.type === "char" && char >= "0" && char <= "7" && parseInt(name+char, 8) <= 255) {
@@ -256,7 +266,7 @@ export default class ExpressionLexer {
256266
this.parseEscChar(token, name);
257267
delete token.name;
258268
} else {
259-
token.err = "unmatchedref";
269+
token.error = {id: "unmatchedref"};
260270
}
261271
};
262272

@@ -279,14 +289,14 @@ export default class ExpressionLexer {
279289
} else {
280290
token.type = this._profile.flags[c];
281291
}
282-
token.clear = true;
292+
//token.clear = true;
283293
};
284294

285295
parseChar(str, token, charset) {
286296
let c = str[token.i];
287297
token.type = (!charset && this._profile.charTypes[c]) || "char";
288298
if (!charset && c === "/") {
289-
token.err = "fwdslash";
299+
token.error = {id: "fwdslash"};
290300
}
291301
if (token.type === "char") {
292302
token.code = c.charCodeAt(0);
@@ -310,12 +320,12 @@ export default class ExpressionLexer {
310320
token.clss = "charclass";
311321
if (match[1] === ":") {
312322
token.type = "posixcharclass";
313-
if (!this._profile.posixCharClasses[match[2]]) { token.err = "posixcharclassbad"; }
314-
else if (!charset) { token.err = "posixcharclassnoset"; }
323+
if (!this._profile.posixCharClasses[match[2]]) { token.error = {id: "posixcharclassbad"}; }
324+
else if (!charset) { token.error = {id: "posixcharclassnoset"}; }
315325
} else {
316326
token.type = "posixcollseq";
317327
// TODO: can this be generalized? Right now, no, because we assign ids that aren't in the profile.
318-
token.err = "notsupported";
328+
token.error = {id: "notsupported"};
319329
}
320330
} else if (!charset) {
321331
// set [a-z] [aeiou]
@@ -449,7 +459,7 @@ export default class ExpressionLexer {
449459
token.capture = true;
450460
}
451461

452-
if (!this._profile.tokens[token.type]) { token.err = "notsupported"; }
462+
if (!this._profile.tokens[token.type]) { token.error = {id: "notsupported"}; }
453463

454464
return token;
455465
};
@@ -459,7 +469,7 @@ export default class ExpressionLexer {
459469
let i = token.i, match, profile = this._profile;
460470
let sub = str.substr(i + 1), c = sub[0], val;
461471
if (i + 1 === (closeIndex || str.length)) {
462-
token.err = "esccharopen";
472+
token.error = {id: "esccharopen"};
463473
return;
464474
}
465475

@@ -502,7 +512,7 @@ export default class ExpressionLexer {
502512
token.l += match[0].length;
503513
val = parseInt(match[1], 16);
504514
// PCRE errors on more than 2 digits (>255). In theory it should allow 4?
505-
if (isNaN(val) || val > 255 || /[^\da-f]/i.test(match[1])) { token.err = "esccharbad"; }
515+
if (isNaN(val) || val > 255 || /[^\da-f]/i.test(match[1])) { token.error = {id: "esccharbad"}; }
506516
else { token.code = val; }
507517
} else if (match = sub.match(/^x([\da-fA-F]{0,2})/)) {
508518
// hex ascii: \xFF
@@ -519,7 +529,7 @@ export default class ExpressionLexer {
519529
token.l += 2;
520530
} else if (profile.config.ctrlcodeerr) {
521531
token.l++;
522-
token.err = "esccharbad";
532+
token.error = {id: "esccharbad"};
523533
} else {
524534
return this.parseChar(str, token, charset); // this builds the "/" token
525535
}
@@ -537,7 +547,7 @@ export default class ExpressionLexer {
537547
token.type = "escoctal";
538548
token.l += match[0].length;
539549
val = parseInt(match[1], 8);
540-
if (isNaN(val) || val > 255 || /[^0-7]/.test(match[1])) { token.err = "esccharbad"; }
550+
if (isNaN(val) || val > 255 || /[^0-7]/.test(match[1])) { token.error = {id: "esccharbad"}; }
541551
else { token.code = val; }
542552
} else {
543553
// single char
@@ -572,7 +582,7 @@ export default class ExpressionLexer {
572582
token.code = c.charCodeAt(0);
573583
token.clss = "esc";
574584
} else {
575-
token.err = "esccharbad";
585+
token.error = {id: "esccharbad"};
576586
}
577587
}
578588

@@ -616,7 +626,7 @@ export default class ExpressionLexer {
616626
val = null;
617627
}
618628
if (not) { token.type = "not"+token.type; }
619-
if (!val) { token.err = "unicodebad"; }
629+
if (!val) { token.error = {id: "unicodebad"}; }
620630
token.value = val;
621631
token.clss = "charclass"
622632
return token;
@@ -651,7 +661,7 @@ export default class ExpressionLexer {
651661
token.l = match[0].length + 2;
652662

653663
if (bad) {
654-
token.err = "modebad";
664+
token.error = {id: "modebad"};
655665
token.errmode = c;
656666
} else {
657667
this._modes = modes;
@@ -669,7 +679,7 @@ export default class ExpressionLexer {
669679
token.min = parseInt(arr[0]);
670680
token.max = (arr[1] === undefined) ? token.min : (arr[1] === "") ? -1 : parseInt(arr[1]);
671681
if (token.max !== -1 && token.min > token.max) {
672-
token.err = "quantrev";
682+
token.error = {id: "quantrev"};
673683
}
674684
return token;
675685
};
@@ -684,7 +694,7 @@ export default class ExpressionLexer {
684694
token.clss = "set";
685695
if (prv.code > next.code) {
686696
// this gets added here because parse has already moved to the next token:
687-
this.errors.push(token.err = "rangerev");
697+
this.errors.push(token.error = {id: "rangerev"});
688698
}
689699
// preserve as separate tokens, but treat as one in the UI:
690700
next.proxy = prv.proxy = token;

dev/src/SubstLexer.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ export default class SubstLexer {
6363
this.token = token;
6464
}
6565

66-
if (token.err) {
66+
if (token.error) {
6767
// SubstLexer currently doesn't generate any errors.
68-
this.errors.push(token.err);
68+
this.errors.push(token.error);
6969
}
7070
prev = token;
7171
}

dev/src/docs/Reference.js

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,11 @@ export default class Reference {
5353
}
5454

5555
idForToken(token) {
56-
if (this._idMap[token.err]) { return token.err; }
56+
let errId = token.error && token.error.id;
57+
if (this._idMap[errId]) { return errId; }
5758
if (this._idMap[token.type]) { return token.type; }
5859
if (this._idMap[token.clss]) { return token.clss; }
59-
return token.err || token.type || token.clss;
60+
return errId || token.type || token.clss;
6061
}
6162

6263
// methods used in fillTags:
@@ -139,7 +140,7 @@ export default class Reference {
139140
140141
Currently only supports a single param.
141142
*/
142-
fillTags(str, data, functs, maxLength, htmlSafe=true) {
143+
fillTags(str, data, functs, maxLength=20, htmlSafe=true) {
143144
let match;
144145
while (match = str.match(/{{~?[\w.()]+}}/)) {
145146
let val, f, safe=false;
@@ -196,27 +197,31 @@ export default class Reference {
196197
return node;
197198
}
198199

199-
getError(id, token) {
200-
// doesn't fill tags.
201-
return this._content.errors[id] || "no docs for error='" + id + "'";
200+
getError(error, token) {
201+
let errId = error && error.id;
202+
let str = this._content.errors[errId] || "no docs for error='" + errId + "'";
203+
if (token) { str = this.fillTags(str, token, this, 20); }
204+
return str;
202205
}
203206

204207
tipForToken(token) {
205208
if (!token) { return null; }
206209

207210
let node = this.getNodeForToken(token), label, tip
208211

209-
if (token.err) {
210-
label = "<span class='error'>ERROR: </span>";
211-
tip = this.getError(token.err);
212+
if (token.error) {
213+
if (token.error.warning) { label = "<span class='error warning'>WARNING: </span>"; }
214+
else { label = "<span class='error'>ERROR: </span>"; }
215+
tip = this.getError(token.error, token);
212216
} else {
213217
label = node ? node.label || node.id || "" : token.type;
214218
tip = this.getVal(node, "tip") || this.getVal(node, "desc");
219+
tip = this.fillTags(tip, token, this, 20);
215220
if (token.type === "group") { label += " #" + token.num; }
216221
label = "<b>" + label[0].toUpperCase() + label.substr(1) + ".</b> ";
217222
}
218223

219-
return tip ? label + this.fillTags(tip, token, this, 20) : "no docs for id='" + this.idForToken(token) + "'";
224+
return tip ? label + tip : "no docs for id='" + this.idForToken(token) + "'";
220225
}
221226

222227
getContent(id) {

0 commit comments

Comments
 (0)