mirror of
https://github.com/gorhill/uBlock.git
synced 2024-09-01 16:49:39 +02:00
Add detection of mismatched!#if
-!#endif
in linter
Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/1712
This commit is contained in:
parent
ac814302e0
commit
9f4b31a96f
@ -289,16 +289,14 @@
|
|||||||
.CodeMirror-lintmarker > * {
|
.CodeMirror-lintmarker > * {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
.CodeMirror-lintmarker[data-lint="error"] {
|
.CodeMirror-lintmarker[data-error="y"] {
|
||||||
background-color: var(--sf-error-ink);
|
background-color: var(--sf-error-ink);
|
||||||
}
|
}
|
||||||
.CodeMirror-lintmarker[data-lint="error"] .msg {
|
.CodeMirror-lintmarker .msg {
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.CodeMirror-lintmarker[data-lint="error"] .msg {
|
|
||||||
background-color: var(--surface-0);
|
background-color: var(--surface-0);
|
||||||
border: 1px solid var(--sf-error-ink);
|
border: 1px solid var(--sf-error-ink);
|
||||||
color: var(--ink-1);
|
color: var(--ink-1);
|
||||||
|
display: none;
|
||||||
filter: drop-shadow(2px 2px 4px #0008);
|
filter: drop-shadow(2px 2px 4px #0008);
|
||||||
left: 100%;
|
left: 100%;
|
||||||
padding: var(--default-gap-xsmall);
|
padding: var(--default-gap-xsmall);
|
||||||
@ -311,6 +309,9 @@
|
|||||||
top: 15%;
|
top: 15%;
|
||||||
width: 70%;
|
width: 70%;
|
||||||
}
|
}
|
||||||
|
.CodeMirror-lintmarker[data-error="y"] svg {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
.CodeMirror-lintmarker[data-fold="start"] {
|
.CodeMirror-lintmarker[data-fold="start"] {
|
||||||
fill: var(--cm-foldmarker-ink);
|
fill: var(--cm-foldmarker-ink);
|
||||||
}
|
}
|
||||||
@ -320,7 +321,7 @@
|
|||||||
.CodeMirror-lintmarker[data-fold="end"] {
|
.CodeMirror-lintmarker[data-fold="end"] {
|
||||||
fill: var(--border-2);
|
fill: var(--border-2);
|
||||||
}
|
}
|
||||||
.CodeMirror-lintmarker[data-lint="error"]:hover > span,
|
.CodeMirror-lintmarker[data-error="y"]:hover > span,
|
||||||
.CodeMirror-lintmarker[data-lint="error"] > span:hover {
|
.CodeMirror-lintmarker[data-error="y"] > span:hover {
|
||||||
display: initial;
|
display: initial;
|
||||||
}
|
}
|
||||||
|
@ -377,7 +377,7 @@ import { i18n$ } from '../i18n.js';
|
|||||||
if ( markers === null ) { return; }
|
if ( markers === null ) { return; }
|
||||||
const marker = markers['CodeMirror-lintgutter'];
|
const marker = markers['CodeMirror-lintgutter'];
|
||||||
if ( marker === undefined ) { return; }
|
if ( marker === undefined ) { return; }
|
||||||
if ( marker.dataset.lint !== 'error' ) { return; }
|
if ( marker.dataset.error !== 'y' ) { return; }
|
||||||
const line = lineHandle.lineNo();
|
const line = lineHandle.lineNo();
|
||||||
if ( dir < 0 ) {
|
if ( dir < 0 ) {
|
||||||
found = line;
|
found = line;
|
||||||
|
@ -699,9 +699,12 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => {
|
|||||||
const includeset = new Set();
|
const includeset = new Set();
|
||||||
let errorCount = 0;
|
let errorCount = 0;
|
||||||
|
|
||||||
|
const ifendifSet = new Set();
|
||||||
|
let ifendifSetChanged = false;
|
||||||
|
|
||||||
const extractMarkerDetails = (doc, lineHandle) => {
|
const extractMarkerDetails = (doc, lineHandle) => {
|
||||||
if ( astParser.isUnsupported() ) {
|
if ( astParser.isUnsupported() ) {
|
||||||
return { value: 'error', msg: 'Unsupported filter syntax' };
|
return { lint: 'error', msg: 'Unsupported filter syntax' };
|
||||||
}
|
}
|
||||||
if ( astParser.hasError() ) {
|
if ( astParser.hasError() ) {
|
||||||
let msg = 'Invalid filter';
|
let msg = 'Invalid filter';
|
||||||
@ -739,7 +742,7 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return { value: 'error', msg };
|
return { lint: 'error', msg };
|
||||||
}
|
}
|
||||||
if ( astParser.astType !== sfp.AST_TYPE_COMMENT ) { return; }
|
if ( astParser.astType !== sfp.AST_TYPE_COMMENT ) { return; }
|
||||||
if ( astParser.astTypeFlavor !== sfp.AST_TYPE_COMMENT_PREPARSER ) {
|
if ( astParser.astTypeFlavor !== sfp.AST_TYPE_COMMENT_PREPARSER ) {
|
||||||
@ -747,15 +750,23 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => {
|
|||||||
for ( const include of includeset ) {
|
for ( const include of includeset ) {
|
||||||
if ( astParser.raw.endsWith(include) === false ) { continue; }
|
if ( astParser.raw.endsWith(include) === false ) { continue; }
|
||||||
includeset.delete(include);
|
includeset.delete(include);
|
||||||
return { value: 'include-end' };
|
return { lint: 'include-end' };
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( /^\s*!#if \S+/.test(astParser.raw) ) {
|
if ( /^\s*!#if \S+/.test(astParser.raw) ) {
|
||||||
return { value: 'if-start' };
|
return {
|
||||||
|
lint: 'if-start',
|
||||||
|
data: {
|
||||||
|
state: sfp.utils.preparser.evaluateExpr(
|
||||||
|
astParser.getTypeString(sfp.NODE_TYPE_PREPARSE_DIRECTIVE_IF_VALUE),
|
||||||
|
preparseDirectiveEnv
|
||||||
|
) ? 'y' : 'n'
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if ( /^\s*!#endif\b/.test(astParser.raw) ) {
|
if ( /^\s*!#endif\b/.test(astParser.raw) ) {
|
||||||
return { value: 'if-end' };
|
return { lint: 'if-end' };
|
||||||
}
|
}
|
||||||
const match = /^\s*!#include\s*(\S+)/.exec(astParser.raw);
|
const match = /^\s*!#include\s*(\S+)/.exec(astParser.raw);
|
||||||
if ( match === null ) { return; }
|
if ( match === null ) { return; }
|
||||||
@ -765,7 +776,7 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => {
|
|||||||
const includeToken = `/${match[1]}`;
|
const includeToken = `/${match[1]}`;
|
||||||
if ( nextLineHandle.text.endsWith(includeToken) === false ) { return; }
|
if ( nextLineHandle.text.endsWith(includeToken) === false ) { return; }
|
||||||
includeset.add(includeToken);
|
includeset.add(includeToken);
|
||||||
return { value: 'include-start' };
|
return { lint: 'include-start' };
|
||||||
};
|
};
|
||||||
|
|
||||||
const extractMarker = lineHandle => {
|
const extractMarker = lineHandle => {
|
||||||
@ -779,7 +790,7 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => {
|
|||||||
'error': {
|
'error': {
|
||||||
node: null,
|
node: null,
|
||||||
html: [
|
html: [
|
||||||
'<div class="CodeMirror-lintmarker" data-lint="error"> ',
|
'<div class="CodeMirror-lintmarker" data-lint="error" data-error="y"> ',
|
||||||
'<span class="msg"></span>',
|
'<span class="msg"></span>',
|
||||||
'</div>',
|
'</div>',
|
||||||
],
|
],
|
||||||
@ -787,10 +798,11 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => {
|
|||||||
'if-start': {
|
'if-start': {
|
||||||
node: null,
|
node: null,
|
||||||
html: [
|
html: [
|
||||||
'<div class="CodeMirror-lintmarker" data-lint="if" data-fold="start"> ',
|
'<div class="CodeMirror-lintmarker" data-lint="if" data-fold="start" data-state=""> ',
|
||||||
'<svg viewBox="0 0 100 100">',
|
'<svg viewBox="0 0 100 100">',
|
||||||
'<polygon points="0,0 100,0 50,100" />',
|
'<polygon points="0,0 100,0 50,100" />',
|
||||||
'</svg>',
|
'</svg>',
|
||||||
|
'<span class="msg">Mismatched if-endif directive</span>',
|
||||||
'</div>',
|
'</div>',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -801,6 +813,7 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => {
|
|||||||
'<svg viewBox="0 0 100 100">',
|
'<svg viewBox="0 0 100 100">',
|
||||||
'<polygon points="50,0 100,100 0,100" />',
|
'<polygon points="50,0 100,100 0,100" />',
|
||||||
'</svg>',
|
'</svg>',
|
||||||
|
'<span class="msg">Mismatched if-endif directive</span>',
|
||||||
'</div>',
|
'</div>',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -826,41 +839,98 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const markerFromTemplate = which => {
|
const markerFromTemplate = details => {
|
||||||
const template = markerTemplates[which];
|
const template = markerTemplates[details.lint];
|
||||||
if ( template.node === null ) {
|
if ( template.node === null ) {
|
||||||
const domParser = new DOMParser();
|
const domParser = new DOMParser();
|
||||||
const doc = domParser.parseFromString(template.html.join(''), 'text/html');
|
const doc = domParser.parseFromString(template.html.join(''), 'text/html');
|
||||||
template.node = document.adoptNode(qs$(doc, '.CodeMirror-lintmarker'));
|
template.node = document.adoptNode(qs$(doc, '.CodeMirror-lintmarker'));
|
||||||
}
|
}
|
||||||
return template.node.cloneNode(true);
|
const node = template.node.cloneNode(true);
|
||||||
|
if ( details.data instanceof Object ) {
|
||||||
|
for ( const [ k, v ] of Object.entries(details.data) ) {
|
||||||
|
node.dataset[k] = `${v}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return node;
|
||||||
};
|
};
|
||||||
|
|
||||||
const addMarker = (doc, lineHandle, marker, details) => {
|
const addMarker = (doc, lineHandle, marker, details) => {
|
||||||
if ( marker !== null && marker.dataset.lint !== details.value ) {
|
if ( marker && marker.dataset.lint !== details.lint ) {
|
||||||
doc.setGutterMarker(lineHandle, 'CodeMirror-lintgutter', null);
|
doc.setGutterMarker(lineHandle, 'CodeMirror-lintgutter', null);
|
||||||
if ( marker.dataset.lint === 'error' ) {
|
if ( marker.dataset.error === 'y' ) {
|
||||||
errorCount -= 1;
|
errorCount -= 1;
|
||||||
}
|
}
|
||||||
|
if ( marker.dataset.lint === 'if' ) {
|
||||||
|
ifendifSet.delete(lineHandle);
|
||||||
|
ifendifSetChanged = true;
|
||||||
|
}
|
||||||
marker = null;
|
marker = null;
|
||||||
}
|
}
|
||||||
if ( marker === null ) {
|
if ( marker === null ) {
|
||||||
marker = markerFromTemplate(details.value);
|
marker = markerFromTemplate(details);
|
||||||
doc.setGutterMarker(lineHandle, 'CodeMirror-lintgutter', marker);
|
doc.setGutterMarker(lineHandle, 'CodeMirror-lintgutter', marker);
|
||||||
if ( details.value === 'error' ) {
|
if ( marker.dataset.error === 'y' ) {
|
||||||
errorCount += 1;
|
errorCount += 1;
|
||||||
}
|
}
|
||||||
|
if ( marker.dataset.lint === 'if' ) {
|
||||||
|
ifendifSet.add(lineHandle);
|
||||||
|
ifendifSetChanged = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if ( typeof details.msg !== 'string' || details.msg === '' ) { return; }
|
||||||
const msgElem = qs$(marker, '.msg');
|
const msgElem = qs$(marker, '.msg');
|
||||||
if ( msgElem === null ) { return; }
|
if ( msgElem === null ) { return; }
|
||||||
msgElem.textContent = details.msg || '';
|
msgElem.textContent = details.msg;
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeMarker = (doc, lineHandle, marker) => {
|
const removeMarker = (doc, lineHandle, marker) => {
|
||||||
doc.setGutterMarker(lineHandle, 'CodeMirror-lintgutter', null);
|
doc.setGutterMarker(lineHandle, 'CodeMirror-lintgutter', null);
|
||||||
if ( marker.dataset.lint === 'error' ) {
|
if ( marker.dataset.error === 'y' ) {
|
||||||
errorCount -= 1;
|
errorCount -= 1;
|
||||||
}
|
}
|
||||||
|
if ( marker.dataset.lint === 'if' ) {
|
||||||
|
ifendifSet.delete(lineHandle);
|
||||||
|
ifendifSetChanged = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Analyze whether all if-endif are properly paired
|
||||||
|
const processIfendifs = ( ) => {
|
||||||
|
if ( ifendifSet.size === 0 ) { return; }
|
||||||
|
if ( ifendifSetChanged !== true ) { return; }
|
||||||
|
const sortFn = (a, b) => a.lineNo() - b.lineNo();
|
||||||
|
const sorted = Array.from(ifendifSet).sort(sortFn);
|
||||||
|
const bad = [];
|
||||||
|
const stack = [];
|
||||||
|
for ( const line of sorted ) {
|
||||||
|
const marker = extractMarker(line);
|
||||||
|
const fold = marker.dataset.fold;
|
||||||
|
if ( fold === 'start' ) {
|
||||||
|
stack.push(line);
|
||||||
|
} else if ( fold === 'end' ) {
|
||||||
|
if ( stack.length !== 0 ) {
|
||||||
|
if ( marker.dataset.error === 'y' ) {
|
||||||
|
marker.dataset.error = '';
|
||||||
|
errorCount -= 1;
|
||||||
|
}
|
||||||
|
const ifstart = extractMarker(stack.pop());
|
||||||
|
if ( ifstart.dataset.error === 'y' ) {
|
||||||
|
ifstart.dataset.error = '';
|
||||||
|
errorCount -= 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bad.push(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bad.push(...stack);
|
||||||
|
for ( const line of bad ) {
|
||||||
|
const marker = extractMarker(line);
|
||||||
|
marker.dataset.error = 'y';
|
||||||
|
errorCount += 1;
|
||||||
|
}
|
||||||
|
ifendifSetChanged = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const processDeletion = (doc, change) => {
|
const processDeletion = (doc, change) => {
|
||||||
@ -868,10 +938,12 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => {
|
|||||||
doc.eachLine(from.line, to.line, lineHandle => {
|
doc.eachLine(from.line, to.line, lineHandle => {
|
||||||
const marker = extractMarker(lineHandle);
|
const marker = extractMarker(lineHandle);
|
||||||
if ( marker === null ) { return; }
|
if ( marker === null ) { return; }
|
||||||
if ( marker.dataset.lint === 'error' ) {
|
if ( marker.dataset.error === 'y' ) {
|
||||||
|
marker.dataset.error = '';
|
||||||
errorCount -= 1;
|
errorCount -= 1;
|
||||||
marker.dataset.lint = 'void';
|
|
||||||
}
|
}
|
||||||
|
ifendifSet.delete(lineHandle);
|
||||||
|
ifendifSetChanged = true;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -881,10 +953,10 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => {
|
|||||||
astParser.parse(lineHandle.text);
|
astParser.parse(lineHandle.text);
|
||||||
const markerDetails = extractMarkerDetails(doc, lineHandle);
|
const markerDetails = extractMarkerDetails(doc, lineHandle);
|
||||||
const marker = extractMarker(lineHandle);
|
const marker = extractMarker(lineHandle);
|
||||||
if ( markerDetails === undefined && marker !== null ) {
|
if ( markerDetails !== undefined ) {
|
||||||
removeMarker(doc, lineHandle, marker);
|
|
||||||
} else if ( markerDetails !== undefined ) {
|
|
||||||
addMarker(doc, lineHandle, marker, markerDetails);
|
addMarker(doc, lineHandle, marker, markerDetails);
|
||||||
|
} else if ( marker !== null ) {
|
||||||
|
removeMarker(doc, lineHandle, marker);
|
||||||
}
|
}
|
||||||
from += 1;
|
from += 1;
|
||||||
if ( (from & 0x0F) !== 0 ) { return; }
|
if ( (from & 0x0F) !== 0 ) { return; }
|
||||||
@ -910,6 +982,7 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => {
|
|||||||
return processChangesetAsync(doc);
|
return processChangesetAsync(doc);
|
||||||
}
|
}
|
||||||
includeset.clear();
|
includeset.clear();
|
||||||
|
processIfendifs(doc);
|
||||||
CodeMirror.signal(doc.getEditor(), 'linterDone', { errorCount });
|
CodeMirror.signal(doc.getEditor(), 'linterDone', { errorCount });
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -922,13 +995,14 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onChanges = (cm, changes) => {
|
const onChanges = (cm, changes) => {
|
||||||
|
if ( changes.length === 0 ) { return; }
|
||||||
const doc = cm.getDoc();
|
const doc = cm.getDoc();
|
||||||
for ( const change of changes ) {
|
for ( const change of changes ) {
|
||||||
const from = change.from.line;
|
const from = change.from.line;
|
||||||
const to = from + change.text.length;
|
const to = from + change.text.length;
|
||||||
changeset.push({ from, to });
|
changeset.push({ from, to });
|
||||||
processChangesetAsync(doc);
|
|
||||||
}
|
}
|
||||||
|
processChangesetAsync(doc);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onBeforeChanges = (cm, change) => {
|
const onBeforeChanges = (cm, change) => {
|
||||||
|
@ -1224,6 +1224,7 @@ export class AstFilterParser {
|
|||||||
? NODE_TYPE_PREPARSE_DIRECTIVE_IF_VALUE
|
? NODE_TYPE_PREPARSE_DIRECTIVE_IF_VALUE
|
||||||
: NODE_TYPE_PREPARSE_DIRECTIVE_VALUE;
|
: NODE_TYPE_PREPARSE_DIRECTIVE_VALUE;
|
||||||
const next = this.allocTypedNode(type, directiveEnd, parentEnd);
|
const next = this.allocTypedNode(type, directiveEnd, parentEnd);
|
||||||
|
this.addNodeToRegister(type, next);
|
||||||
this.linkRight(head, next);
|
this.linkRight(head, next);
|
||||||
if ( type === NODE_TYPE_PREPARSE_DIRECTIVE_IF_VALUE ) {
|
if ( type === NODE_TYPE_PREPARSE_DIRECTIVE_IF_VALUE ) {
|
||||||
const rawToken = this.getNodeString(next).trim();
|
const rawToken = this.getNodeString(next).trim();
|
||||||
|
Loading…
Reference in New Issue
Block a user