Add support to auto-complete values of domain lists

The auto-complete feature in the _"My filters"_ pane will
use hostname/domain from the set of opened tabs to assist
in entering values for `domain=` option. This also works
for the implict `domain=` option ṗrepending static extended
filters.
This commit is contained in:
Raymond Hill 2020-11-21 09:57:54 -05:00
parent 8d3c4916b0
commit daf464b3c3
No known key found for this signature in database
GPG key ID: 25E1490B761470C2
3 changed files with 75 additions and 19 deletions

View file

@ -54,6 +54,7 @@ vAPI.messaging.send('dashboard', {
}).then(response => {
if ( response instanceof Object === false ) { return; }
const mode = cmEditor.getMode();
// TODO: listen to changes in currently opened set of tabs?
if ( mode.setHints instanceof Function ) {
mode.setHints(response);
}

View file

@ -34,6 +34,7 @@ const redirectNames = new Map();
const scriptletNames = new Map();
const preparseDirectiveTokens = new Map();
const preparseDirectiveHints = [];
const originHints = [];
/******************************************************************************/
@ -362,6 +363,9 @@ CodeMirror.defineMode('ubo-static-filtering', function() {
preparseDirectiveTokens.set(a, b);
});
preparseDirectiveHints.push(...details.preparseDirectiveHints);
for ( const hint of details.originHints ) {
originHints.push(hint);
}
initHints();
},
get parser() {
@ -418,7 +422,23 @@ const initHints = function() {
};
};
const getNetOptionHints = function(cursor, isNegated, seedLeft, seedRight) {
const getOriginHints = function(cursor, line) {
const beg = cursor.ch;
const matchLeft = /[^,|=~]*$/.exec(line.slice(0, beg));
const matchRight = /^[^#,|]*/.exec(line.slice(beg));
if ( matchLeft === null || matchRight === null ) { return; }
const hints = [];
for ( const text of originHints ) {
hints.push(text);
}
return pickBestHints(cursor, matchLeft[0], matchRight[0], hints);
};
const getNetOptionHints = function(cursor, seedLeft, seedRight) {
const isNegated = seedLeft.startsWith('~');
if ( isNegated ) {
seedLeft = seedLeft.slice(1);
}
const assignPos = seedRight.indexOf('=');
if ( assignPos !== -1 ) { seedRight = seedRight.slice(0, assignPos); }
const isException = parser.isException();
@ -448,26 +468,32 @@ const initHints = function() {
const getNetHints = function(cursor, line) {
const beg = cursor.ch;
if ( beg < parser.optionsSpan ) { return; }
if ( parser.optionsSpan.len === 0 ) {
if ( /[^\w\x80-\xF4#,.-]/.test(line) === false ) {
return getOriginHints(cursor, line);
}
return;
}
if ( beg < parser.slices[parser.optionsSpan.i+1] ) { return; }
const lineBefore = line.slice(0, beg);
const lineAfter = line.slice(beg);
let matchLeft = /~?([^$,~]*)$/.exec(lineBefore);
let matchRight = /^([^,]*)/.exec(lineAfter);
let matchLeft = /[^$,]*$/.exec(lineBefore);
let matchRight = /^[^,]*/.exec(lineAfter);
if ( matchLeft === null || matchRight === null ) { return; }
let pos = matchLeft[1].indexOf('=');
if ( pos === -1 ) {
return getNetOptionHints(
const assignPos = matchLeft[0].indexOf('=');
if ( assignPos === -1 ) {
return getNetOptionHints(cursor, matchLeft[0], matchRight[0]);
}
if ( /^redirect(-rule)?=/.test(matchLeft[0]) ) {
return getNetRedirectHints(
cursor,
matchLeft[0].startsWith('~'),
matchLeft[1],
matchRight[1]
matchLeft[0].slice(assignPos + 1),
matchRight[0]
);
}
return getNetRedirectHints(
cursor,
matchLeft[1].slice(pos + 1),
matchRight[1]
);
if ( matchLeft[0].startsWith('domain=') ) {
return getOriginHints(cursor, line);
}
};
const getExtSelectorHints = function(cursor, line) {
@ -527,10 +553,15 @@ const initHints = function() {
const line = cm.getLine(cursor.line);
parser.analyze(line);
if ( parser.category === parser.CATStaticExtFilter ) {
if ( parser.hasFlavor(parser.BITFlavorExtScriptlet) ) {
return getExtScriptletHints(cursor, line);
let hints;
if ( cursor.ch <= parser.slices[parser.optionsAnchorSpan.i+1] ) {
hints = getOriginHints(cursor, line);
} else {
hints = parser.hasFlavor(parser.BITFlavorExtScriptlet)
? getExtScriptletHints(cursor, line)
: getExtSelectorHints(cursor, line);
}
return getExtSelectorHints(cursor, line);
return hints;
}
if ( parser.category === parser.CATStaticNetFilter ) {
return getNetHints(cursor, line);
@ -538,6 +569,9 @@ const initHints = function() {
if ( parser.category === parser.CATComment ) {
return getCommentHints(cursor, line);
}
if ( parser.category === parser.CATNone ) {
return getOriginHints(cursor, line);
}
});
};

View file

@ -1000,7 +1000,7 @@ const resetUserData = async function() {
vAPI.app.restart();
};
// 3rd-party filters
// Filter lists
const prepListEntries = function(entries) {
const µburi = µb.URI;
for ( const k in entries ) {
@ -1041,6 +1041,26 @@ const getLists = async function(callback) {
callback(r);
};
// My filters
// TODO: also return origin of embedded frames?
const getOriginHints = function() {
const punycode = self.punycode;
const out = [];
for ( const tabId of µb.pageStores.keys() ) {
if ( tabId === -1 ) { continue; }
const tabContext = µb.tabContextManager.lookup(tabId);
if ( tabContext === null ) { continue; }
let { rootDomain, rootHostname } = tabContext;
if ( rootDomain.endsWith('-scheme') ) { continue; }
const isPunycode = rootHostname.includes('xn--');
out.push(isPunycode ? punycode.toUnicode(rootDomain) : rootDomain);
if ( rootHostname === rootDomain ) { continue; }
out.push(isPunycode ? punycode.toUnicode(rootHostname) : rootHostname);
}
return out;
};
// My rules
const getRules = function() {
return {
@ -1185,6 +1205,7 @@ const onMessage = function(request, sender, callback) {
redirectResources: µb.redirectEngine.getResourceDetails(),
preparseDirectiveTokens: µb.preparseDirectives.getTokens(),
preparseDirectiveHints: µb.preparseDirectives.getHints(),
originHints: getOriginHints(),
expertMode: µb.hiddenSettings.filterAuthorMode,
};
break;