mirror of
https://github.com/gorhill/uBlock.git
synced 2024-11-05 18:32:30 +01:00
Support line continuation in filter lists
If a line in a filter list ends with a space (ASCII code 32) followed by a backslash (ASCII code 92), those two characters will be removed, the line will be trimmed and the next line will be trimmed and concatenated to form a new, longer line. The purpose is to give filter list authors a way to visually break apart unduly long filters and thus make maintenance easier. When line continuation is used, it is suggested that the extra lines are prepended with four space so as to make it more visually obvious that the extra line(s) are the continuation of a previous line. Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/943 The filter referenced in the above issue was the motivation to implement this feature: - https://hg.adblockplus.org/ruadlist/rev/f362910bc9a0 I verified and could not find any instance in major filter lists of lines ending with ` \`, thus the change should be safe.
This commit is contained in:
parent
1400b146ec
commit
703c525b01
@ -24,76 +24,113 @@
|
||||
'use strict';
|
||||
|
||||
CodeMirror.defineMode("ubo-static-filtering", function() {
|
||||
const reDirective = /^\s*!#(?:if|endif)\b/;
|
||||
const reDirective = /^\s*!#(?:if|endif|include)\b/;
|
||||
const reComment1 = /^\s*!/;
|
||||
const reComment2 = /^\s*#/;
|
||||
const reExt = /^(\s*[^#]*)(#@?(?:\$\??|\?)?#)(.+)$/;
|
||||
const reNet = /^(.*?)(?:(\$)([^$]+)?)?$/;
|
||||
const reNetAllow = /^\s*@@/;
|
||||
const reExt = /(#@?(?:\$\??|\?)?#)(?!##)/;
|
||||
const reNet = /^\s*(?:@@)?.*(?:(\$)(?:[^$]+)?)?$/;
|
||||
let lineStyle = null;
|
||||
let lineMatches = null;
|
||||
let anchorOptPos = null;
|
||||
|
||||
const lines = [];
|
||||
let iLine = 0;
|
||||
|
||||
const lineStyles = new Map([
|
||||
[ 'staticext', [ '', 'staticOpt', '' ] ],
|
||||
[ 'staticnetAllow', [ '', 'staticOpt', '' ] ],
|
||||
[ 'staticnetBlock', [ '', 'staticOpt', '' ] ],
|
||||
]);
|
||||
const lineFromLineBuffer = function() {
|
||||
return lines.length === 1
|
||||
? lines[0]
|
||||
: lines.filter(a => a.replace(/^\s*|\s+\\$/g, '')).join('');
|
||||
};
|
||||
|
||||
const styleFromStream = function(stream) {
|
||||
for ( let i = 1, l = 0; i < lineMatches.length; i++ ) {
|
||||
if ( typeof lineMatches[i] !== 'string' ) { continue; }
|
||||
l += lineMatches[i].length;
|
||||
if ( stream.pos < l ) {
|
||||
stream.pos = l;
|
||||
let style = lineStyle;
|
||||
const xstyle = lineStyles.get(style)[i-1];
|
||||
if ( xstyle !== '' ) { style += ' ' + xstyle; }
|
||||
return style;
|
||||
const parseExtFilter = function() {
|
||||
lineStyle = 'staticext';
|
||||
for ( let i = 0; i < lines.length; i++ ) {
|
||||
const match = reExt.exec(lines[i]);
|
||||
if ( match === null ) { continue; }
|
||||
anchorOptPos = { y: i, x: match.index, l: match[1].length };
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const parseNetFilter = function() {
|
||||
lineStyle = lineFromLineBuffer().startsWith('@@')
|
||||
? 'staticnetAllow'
|
||||
: 'staticnetBlock';
|
||||
let i = lines.length;
|
||||
while ( i-- ) {
|
||||
const pos = lines[i].lastIndexOf('$');
|
||||
if ( pos === -1 ) { continue; }
|
||||
anchorOptPos = { y: i, x: pos, l: 1 };
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const highlight = function(stream) {
|
||||
if ( anchorOptPos !== null && iLine === anchorOptPos.y ) {
|
||||
if ( stream.pos === anchorOptPos.x ) {
|
||||
stream.pos += anchorOptPos.l;
|
||||
return `${lineStyle} staticOpt`;
|
||||
}
|
||||
if ( stream.pos < anchorOptPos.x ) {
|
||||
stream.pos = anchorOptPos.x;
|
||||
return lineStyle;
|
||||
}
|
||||
}
|
||||
stream.skipToEnd();
|
||||
return '';
|
||||
return lineStyle;
|
||||
};
|
||||
|
||||
const parseMultiLine = function() {
|
||||
anchorOptPos = null;
|
||||
const line = lineFromLineBuffer();
|
||||
if ( reDirective.test(line) ) {
|
||||
lineStyle = 'directive';
|
||||
return;
|
||||
}
|
||||
if ( reComment1.test(line) ) {
|
||||
lineStyle = 'comment';
|
||||
return;
|
||||
}
|
||||
if ( line.indexOf('#') !== -1 ) {
|
||||
if ( reExt.test(line) ) {
|
||||
return parseExtFilter();
|
||||
}
|
||||
if ( reComment2.test(line) ) {
|
||||
lineStyle = 'comment';
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ( reNet.test(line) ) {
|
||||
return parseNetFilter();
|
||||
}
|
||||
lineStyle = null;
|
||||
};
|
||||
|
||||
return {
|
||||
startState: function() {
|
||||
},
|
||||
token: function(stream) {
|
||||
if ( stream.sol() ) {
|
||||
lineStyle = null;
|
||||
lineMatches = null;
|
||||
} else if ( lineStyle !== null ) {
|
||||
return styleFromStream(stream);
|
||||
if ( iLine === lines.length || stream.string !== lines[iLine] ) {
|
||||
iLine = 0;
|
||||
}
|
||||
if ( reDirective.test(stream.string) ) {
|
||||
stream.skipToEnd();
|
||||
return 'directive';
|
||||
}
|
||||
if ( reComment1.test(stream.string) ) {
|
||||
stream.skipToEnd();
|
||||
return 'comment';
|
||||
}
|
||||
if ( stream.string.indexOf('#') !== -1 ) {
|
||||
lineMatches = reExt.exec(stream.string);
|
||||
if (
|
||||
lineMatches !== null &&
|
||||
lineMatches[3].startsWith('##') === false
|
||||
) {
|
||||
lineStyle = 'staticext';
|
||||
return styleFromStream(stream);
|
||||
if ( iLine === 0 ) {
|
||||
if ( lines.length > 1 ) {
|
||||
lines.length = 1;
|
||||
}
|
||||
if ( reComment2.test(stream.string) ) {
|
||||
stream.skipToEnd();
|
||||
return 'comment';
|
||||
let line = stream.string;
|
||||
lines[0] = line;
|
||||
if ( line.endsWith(' \\') ) {
|
||||
do {
|
||||
line = stream.lookAhead(lines.length);
|
||||
lines.push(line);
|
||||
} while ( line.endsWith(' \\') );
|
||||
}
|
||||
parseMultiLine();
|
||||
}
|
||||
lineMatches = reNet.exec(stream.string);
|
||||
if ( lineMatches !== null ) {
|
||||
lineStyle = reNetAllow.test(stream.string) ?
|
||||
'staticnetAllow' :
|
||||
'staticnetBlock';
|
||||
return styleFromStream(stream);
|
||||
const style = highlight(stream);
|
||||
if ( stream.eol() ) {
|
||||
iLine += 1;
|
||||
}
|
||||
stream.skipToEnd();
|
||||
return null;
|
||||
}
|
||||
return style;
|
||||
},
|
||||
};
|
||||
});
|
||||
|
@ -804,12 +804,13 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
|
||||
const lineIter = new this.LineIterator(this.processDirectives(rawText));
|
||||
|
||||
while ( lineIter.eot() === false ) {
|
||||
// rhill 2014-04-18: The trim is important here, as without it there
|
||||
// could be a lingering `\r` which would cause problems in the
|
||||
// following parsing code.
|
||||
let line = lineIter.next().trim();
|
||||
if ( line.length === 0 ) { continue; }
|
||||
|
||||
while ( line.endsWith(' \\') ) {
|
||||
line = line.slice(0, -2).trim() + lineIter.next().trim();
|
||||
}
|
||||
|
||||
// Strip comments
|
||||
const c = line.charAt(0);
|
||||
if ( c === '!' || c === '[' ) { continue; }
|
||||
|
Loading…
Reference in New Issue
Block a user