1
0
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:
Raymond Hill 2020-03-14 13:19:41 -04:00
parent 1400b146ec
commit 703c525b01
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
2 changed files with 95 additions and 57 deletions

View File

@ -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;
},
};
});

View File

@ -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; }