Merge pull request #4453 from GHolk/hint-exclude

Option to exclude selectors from hint result
This commit is contained in:
Oliver Blanthorn 2023-01-17 14:39:21 +01:00 committed by GitHub
commit 0db566cd87
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 87 additions and 3 deletions

View file

@ -396,6 +396,71 @@ interface Hintables {
hintclasses?: string[]
}
/**
A convenient javascript interface to hint on specified html elements.
The return value is a promise resolving to the selected element,
or an AsyncIterator resolving to the selected elements in rapid mode.
Example usage:
`tri.hinting_content.hintElements(...).then(element => {tri.dom.simulateClick(element))`
`for (await e of tri.hinting_content.hintElements(..., {rapid: true}){tri.dom.simulateClick(e)})`
@param elements a iterator yield html elements
@param option a option object. The `option.rapid` specify whether hint in rapid mode. Default value is false. The `option.callback` is executed when a hint is selected if specified. The default callback is a no-op.
@returns promise resolve to the selected element, or a async iterator in rapid mode.
*/
export function hintElements(elements: Element[], option = {}) {
const hintable = toHintablesArray(Array.from(elements))
const rapid = option["rapid"] ?? false
const callback = typeof option["callback"] === "function" ?
option["callback"] : x => x
if (!rapid) {
return new Promise((resolve, reject) => {
hintPage(hintable, x => x, resolve, reject, rapid)
}).then(x => {
callback(x)
return x
})
} else {
const endDefer = deferCreate()
const endPromise = endDefer.promise.catch(error => error)
let onSelect = deferCreate()
const key = Symbol("select-result")
const hintCallback = element => {
callback(element)
onSelect.resolve({[key]: element})
onSelect = deferCreate()
}
const wrap = async function* () {
while (true) {
const first = await Promise.race([onSelect.promise, endPromise])
if (first && typeof first === "object" && key in first) {
yield first[key]
} else return await endPromise
}
}
const result = wrap()
hintPage(hintable, hintCallback,
endDefer.resolve, endDefer.reject, rapid)
return result
}
function deferCreate() {
const defer = {
resolve: null,
reject: null,
promise: null,
}
defer.promise = new Promise((ok, no) => {
defer.resolve = ok
defer.reject = no
})
return defer
}
}
/** For each hintable element, add a hint
* @hidden
* */

View file

@ -5064,6 +5064,7 @@ const KILL_STACK: Element[] = []
- -c [selector] hint links that match the css selector
- `bind ;c hint -c [class*="expand"],[class="togg"]` works particularly well on reddit and HN
- this works with most other hint modes, with the caveat that if other hint mode takes arguments your selector must contain no spaces, i.e. `hint -c[yourOtherFlag] [selector] [your other flag's arguments, which may contain spaces]`
- -x [selector] exclude the matched elements from hinting
- -f [text] hint links and inputs that display the given text
- `bind <c-e> hint -f Edit`
- Backslashes can escape spaces: `bind <c-s> hint -f Save\ as`

View file

@ -47,6 +47,7 @@ export interface HintOptions {
excmd: null | string
pipeAttribute: null | string
selectors: string[]
selectorsExclude: string[]
warnings: string[]
}
@ -64,6 +65,7 @@ export class HintConfig implements HintOptions {
public excmd = null
public pipeAttribute = null
public selectors = []
public selectorsExclude = []
public warnings = []
public static parse(args: string[]): HintConfig {
@ -78,6 +80,7 @@ export class HintConfig implements HintOptions {
ExpectPipeSelector,
ExpectPipeAttribute,
ExpectSelectorCallback,
ExpectSelectorExclude,
}
const result = new HintConfig()
@ -150,6 +153,9 @@ export class HintConfig implements HintOptions {
case "c":
newState = State.ExpectSelector
break
case "x":
newState = State.ExpectSelectorExclude
break
case "pipe":
newState = State.ExpectPipeSelector
break
@ -304,6 +310,11 @@ export class HintConfig implements HintOptions {
result.selectors.push(arg)
state = State.Initial
break
case State.ExpectSelectorExclude:
// -x, expect a single exclude selector
result.selectorsExclude.push(arg)
state = State.Initial
break
case State.ExpectPipeSelector:
// -pipe, first expect a selector
result.selectors.push(arg)
@ -401,13 +412,20 @@ export class HintConfig implements HintOptions {
)
: this.defaultHintables()
// Do we have text filters to refine this?
if (this.textFilter !== null) {
for (const elements of hintables) {
const textFilter = this.textFilter
const exclude = this.selectorsExclude.join(" ")
for (const elements of hintables) {
// Do we have text filters to refine this?
if (textFilter !== null) {
elements.elements = elements.elements.filter(
hinting.hintByTextFilter(this.textFilter),
)
}
if (exclude) {
elements.elements = elements.elements.filter(
element => !element.matches(exclude)
)
}
}
return hintables