completions: Make sure completion computation has ended before resizeArea

Before this commit, Tridactyl had a bug where resizeArea could be called
before completion computation had ended, which resulted in completions
pushing the input field out of the viewport (easy way to reproduce this:
open a lot of tabs and press `b` to open buffer completions).
This happened because for some of the completion sources, `filter`
returned before completion computation had actually ended. This is fixed
by making sure that filter() (and all underlying calls to updateOptions,
onInput, updateChain...) return a promise that will only be resolved
once completion computation has actually ended.
This commit is contained in:
glacambre 2019-03-05 06:43:53 +01:00
parent 5e13b87ebd
commit 5dd1ec3a74
No known key found for this signature in database
GPG key ID: B9625DB1767553AC
13 changed files with 124 additions and 114 deletions

View file

@ -285,7 +285,7 @@ clInput.addEventListener("input", () => {
if (exstr != clInput.value) return
onInputPromise = refresh_completions(exstr)
}, 1)
}, 100)
})
/** @hidden **/

View file

@ -42,9 +42,12 @@ export abstract class CompletionSource {
let commands = aliases.getCmdAliasMapping()
// Now, for each prefix given as argument, add it to the completionsource's prefix list and also add any alias it has
prefixes.map(p => p.trim()).forEach(p => {
prefixes
.map(p => p.trim())
.forEach(p => {
this.prefixes.push(p)
if (commands[p]) this.prefixes = this.prefixes.concat(commands[p])
if (commands[p])
this.prefixes = this.prefixes.concat(commands[p])
})
// Not sure this is necessary but every completion source has it
@ -143,7 +146,7 @@ export abstract class CompletionSourceFuse extends CompletionSource {
public options: CompletionOptionFuse[]
protected lastExstr: string
protected optionContainer = html`<table class="optionContainer">`
protected optionContainer = html`<table class="optionContainer"></table>`
constructor(prefixes, className: string, title?: string) {
super(prefixes)
@ -162,7 +165,7 @@ export abstract class CompletionSourceFuse extends CompletionSource {
public async filter(exstr: string) {
this.lastExstr = exstr
await this.onInput(exstr)
await this.updateChain()
return this.updateChain()
}
updateChain(exstr = this.lastExstr, options = this.options) {

View file

@ -25,9 +25,11 @@ class BmarkCompletionOption extends Completions.CompletionOptionHTML
<td class="prefix">${"".padEnd(2)}</td>
<td class="icon"></td>
<td class="title">${bmark.title}</td>
<td class="content"><a class="url" target="_blank" href=${
bmark.url
}>${bmark.url}</a></td>
<td class="content">
<a class="url" target="_blank" href=${bmark.url}
>${bmark.url}</a
>
</td>
</tr>`
}
}
@ -67,7 +69,7 @@ export class BmarkCompletionSource extends Completions.CompletionSourceFuse {
page => new BmarkCompletionOption(option + page.url, page),
)
this.updateChain()
return this.updateChain()
}
updateChain() {
@ -75,7 +77,7 @@ export class BmarkCompletionSource extends Completions.CompletionSourceFuse {
this.options.forEach(option => (option.state = "normal"))
// Call concrete class
this.updateDisplay()
return this.updateDisplay()
}
onInput() {}

View file

@ -35,7 +35,7 @@ export class ExcmdCompletionSource extends Completions.CompletionSourceFuse {
}
async onInput(exstr) {
await this.updateOptions(exstr)
return this.updateOptions(exstr)
}
updateChain(exstr = this.lastExstr, options = this.options) {
@ -82,7 +82,7 @@ export class ExcmdCompletionSource extends Completions.CompletionSourceFuse {
}
this.options.forEach(o => (o.state = "normal"))
this.updateChain()
return this.updateChain()
}
private scoreOptions(options: ExcmdCompletionOption[]) {

View file

@ -9,9 +9,11 @@ class FileSystemCompletionOption extends Completions.CompletionOptionHTML
constructor(public value: string) {
super()
this.fuseKeys = [value]
this.html = html`<tr class="FileSystemCompletionOption option">
this.html = html`
<tr class="FileSystemCompletionOption option">
<td class="value">${value}</td>
</tr>`
</tr>
`
}
}
@ -25,7 +27,7 @@ export class FileSystemCompletionSource extends Completions.CompletionSourceFuse
}
public async onInput(exstr) {
this.filter(exstr)
return this.filter(exstr)
}
public async filter(exstr: string) {
@ -71,6 +73,6 @@ export class FileSystemCompletionSource extends Completions.CompletionSourceFuse
)
this.state = "normal"
this.updateChain()
return this.updateChain()
}
}

View file

@ -5,10 +5,7 @@ class GuisetCompletionOption extends Completions.CompletionOptionHTML
implements Completions.CompletionOptionFuse {
public fuseKeys = []
constructor(
public value: string,
displayValue: string
) {
constructor(public value: string, displayValue: string) {
super()
this.fuseKeys.push(value)
@ -56,13 +53,19 @@ export class GuisetCompletionSource extends Completions.CompletionSourceFuse {
this.options = this.options.concat(
Object.keys(metaRules[ruleName])
.filter(s => s.startsWith(subRule))
.map(s => new GuisetCompletionOption(`${ruleName} ${s}`, s)))
.map(
s => new GuisetCompletionOption(`${ruleName} ${s}`, s),
),
)
}
if (potentialRules[ruleName]) {
this.options = this.options.concat(
Object.keys(potentialRules[ruleName].options)
.filter(s => s.startsWith(subRule))
.map(s => new GuisetCompletionOption(`${ruleName} ${s}`, s)))
.map(
s => new GuisetCompletionOption(`${ruleName} ${s}`, s),
),
)
}
if (this.options.length == 0) {
this.options = Object.keys(metaRules)
@ -71,7 +74,7 @@ export class GuisetCompletionSource extends Completions.CompletionSourceFuse {
.map(s => new GuisetCompletionOption(s, s))
}
this.updateChain()
return this.updateChain()
}
updateChain() {
@ -79,10 +82,10 @@ export class GuisetCompletionSource extends Completions.CompletionSourceFuse {
this.options.forEach(option => (option.state = "normal"))
// Call concrete class
this.updateDisplay()
return this.updateDisplay()
}
onInput(arg) {
this.filter(arg)
return this.filter(arg)
}
}

View file

@ -136,7 +136,7 @@ export class HelpCompletionSource extends Completions.CompletionSourceFuse {
this.options = opts.sort((compopt1, compopt2) =>
compopt1.name.localeCompare(compopt2.name),
)
this.updateChain()
return this.updateChain()
}
updateChain() {
@ -144,7 +144,7 @@ export class HelpCompletionSource extends Completions.CompletionSourceFuse {
this.options.forEach(option => (option.state = "normal"))
// Call concrete class
this.updateDisplay()
return this.updateDisplay()
}
onInput() {}

View file

@ -23,9 +23,11 @@ class HistoryCompletionOption extends Completions.CompletionOptionHTML
<td class="prefix">${"".padEnd(2)}</td>
<td class="icon"></td>
<td class="title">${page.title}</td>
<td class="content"><a class="url" target="_blank" href=${
page.url
}>${page.url}</a></td>
<td class="content">
<a class="url" target="_blank" href=${page.url}
>${page.url}</a
>
</td>
</tr>`
}
}
@ -85,7 +87,7 @@ export class HistoryCompletionSource extends Completions.CompletionSourceFuse {
page => new HistoryCompletionOption(options + page.url, page),
)
this.updateChain()
return this.updateChain()
}
updateChain() {
@ -93,7 +95,7 @@ export class HistoryCompletionSource extends Completions.CompletionSourceFuse {
this.options.forEach(option => (option.state = "normal"))
// Call concrete class
this.updateDisplay()
return this.updateDisplay()
}
onInput() {}

View file

@ -5,10 +5,7 @@ class PreferenceCompletionOption extends Completions.CompletionOptionHTML
implements Completions.CompletionOptionFuse {
public fuseKeys = []
constructor(
public value: string,
public prefvalue: string
) {
constructor(public value: string, public prefvalue: string) {
super()
this.fuseKeys.push(value)
this.html = html`<tr class="PreferenceCompletionOption option">
@ -22,11 +19,7 @@ export class PreferenceCompletionSource extends Completions.CompletionSourceFuse
public options: PreferenceCompletionOption[]
constructor(private _parent) {
super(
["setpref"],
"PreferenceCompletionSource",
"Preference",
)
super(["setpref"], "PreferenceCompletionSource", "Preference")
this._parent.appendChild(this.node)
}
@ -50,8 +43,7 @@ export class PreferenceCompletionSource extends Completions.CompletionSourceFuse
this.options = Object.keys(preferences)
.filter(key => key.startsWith(pref))
.map(key => new PreferenceCompletionOption(key, preferences[key]))
if (this.options.length > 0)
this.state = "normal"
this.updateChain()
if (this.options.length > 0) this.state = "normal"
return this.updateChain()
}
}

View file

@ -13,7 +13,9 @@ class RssCompletionOption extends Completions.CompletionOptionHTML
this.html = html`<tr class="RssCompletionOption option">
<td class="title">${title}</td>
<td class="content"><a class="url" target="_blank" href=${url}>${url}</a></td>
<td class="content">
<a class="url" target="_blank" href=${url}>${url}</a>
</td>
<td class="type">${type}</td>
</tr>`
}
@ -64,6 +66,6 @@ export class RssCompletionSource extends Completions.CompletionSourceFuse {
return opt
})
}
this.updateChain()
return this.updateChain()
}
}

View file

@ -62,9 +62,11 @@ export class SettingsCompletionSource extends Completions.CompletionSourceFuse {
options += options ? " " : ""
let file, default_config, settings
if (!(file = metadata.everything.getFile("src/lib/config.ts"))
|| !(default_config = file.getClass("default_config"))
|| !(settings = config.get()))
if (
!(file = metadata.everything.getFile("src/lib/config.ts")) ||
!(default_config = file.getClass("default_config")) ||
!(settings = config.get())
)
return
this.options = Object.keys(settings)
@ -74,7 +76,7 @@ export class SettingsCompletionSource extends Completions.CompletionSourceFuse {
let md = undefined
let doc = ""
let type = ""
if (md = default_config.getMember(setting)) {
if ((md = default_config.getMember(setting))) {
doc = md.doc
type = md.type.toString()
}
@ -86,7 +88,7 @@ export class SettingsCompletionSource extends Completions.CompletionSourceFuse {
})
})
this.updateChain()
return this.updateChain()
}
updateChain() {
@ -94,7 +96,7 @@ export class SettingsCompletionSource extends Completions.CompletionSourceFuse {
this.options.forEach(option => (option.state = "normal"))
// Call concrete class
this.updateDisplay()
return this.updateDisplay()
}
onInput() {}

View file

@ -35,16 +35,17 @@ class BufferCompletionOption extends Completions.CompletionOptionHTML
const favIconUrl = tab.favIconUrl
? tab.favIconUrl
: Completions.DEFAULT_FAVICON
this.html = html`<tr class="BufferCompletionOption option container_${
container.color
} container_${container.icon} container_${container.name}">
this.html = html`<tr class="BufferCompletionOption option container_${container.color} container_${container.icon} container_${container.name}"
>
<td class="prefix">${pre.padEnd(2)}</td>
<td class="container"></td>
<td class="icon"><img src="${favIconUrl}" /></td>
<td class="title">${tab.index + 1}: ${tab.title}</td>
<td class="content"><a class="url" target="_blank" href=${
tab.url
}>${tab.url}</a></td>
<td class="content">
<a class="url" target="_blank" href=${tab.url}
>${tab.url}</a
>
</td>
</tr>`
}
}
@ -122,18 +123,18 @@ export class BufferCompletionSource extends Completions.CompletionSourceFuse {
} else {
this.options.forEach(option => (option.state = "normal"))
}
this.updateDisplay()
return this.updateDisplay()
}
async onInput(exstr) {
// Schedule an update, if you like. Not very useful for tabs, but
// will be for other things.
this.updateOptions(exstr)
return this.updateOptions(exstr)
}
async filter(exstr) {
this.lastExstr = exstr
await this.onInput(exstr)
return this.onInput(exstr)
}
setStateFromScore(scoredOpts: Completions.ScoredOption[]) {

View file

@ -22,19 +22,20 @@ class TabAllCompletionOption extends Completions.CompletionOptionHTML
const favIconUrl = tab.favIconUrl
? tab.favIconUrl
: Completions.DEFAULT_FAVICON
this.html = html`<tr class="BufferAllCompletionOption option container_${
container.color
} container_${container.icon} container_${container.name} ${
incognito ? "incognito" : ""
}">
this.html = html`<tr class="BufferAllCompletionOption option container_${container.color} container_${container.icon} container_${container.name} ${incognito
? "incognito"
: ""}"
>
<td class="prefix"></td>
<td class="privatewindow"></td>
<td class="container"></td>
<td class="icon"><img src="${favIconUrl}" /></td>
<td class="title">${this.value}: ${tab.title}</td>
<td class="content"><a class="url" target="_blank" href=${
tab.url
}>${tab.url}</a></td>
<td class="content">
<a class="url" target="_blank" href=${tab.url}
>${tab.url}</a
>
</td>
</tr>`
}
}
@ -50,7 +51,7 @@ export class TabAllCompletionSource extends Completions.CompletionSourceFuse {
}
async onInput(exstr) {
await this.updateOptions(exstr)
return this.updateOptions(exstr)
}
/**
@ -112,7 +113,7 @@ export class TabAllCompletionSource extends Completions.CompletionSourceFuse {
this.completion = undefined
this.options = options
this.updateChain()
return this.updateChain()
}
setStateFromScore(scoredOpts: Completions.ScoredOption[]) {