mirror of
https://github.com/tridactyl/tridactyl.git
synced 2026-09-10 07:16:33 -04:00
Merge branch 'bookmark-folder-completions' into develop
This commit is contained in:
commit
203a90df89
|
|
@ -22,7 +22,10 @@ import { CompletionSourceFuse } from "@src/completions"
|
|||
import { AproposCompletionSource } from "@src/completions/Apropos"
|
||||
import { AutocmdCompletionSource } from "@src/completions/Autocmd"
|
||||
import { BindingsCompletionSource } from "@src/completions/Bindings"
|
||||
import { BmarkCompletionSource } from "@src/completions/Bmark"
|
||||
import {
|
||||
BmarkCompletionSource,
|
||||
BookmarkFolderCompletionSource,
|
||||
} from "@src/completions/Bmark"
|
||||
import { CompositeCompletionSource } from "@src/completions/Composite"
|
||||
import { ExcmdCompletionSource } from "@src/completions/Excmd"
|
||||
import { ExtensionsCompletionSource } from "@src/completions/Extensions"
|
||||
|
|
@ -70,6 +73,7 @@ const commandline_state = {
|
|||
completionsDiv: window.document.getElementById("completions"),
|
||||
fns: undefined as ReturnType<typeof getCommandlineFns>,
|
||||
getCompletion,
|
||||
getActiveCompletionSource,
|
||||
history,
|
||||
/** @hidden
|
||||
* This is to handle Escape key which, while the cmdline is focused,
|
||||
|
|
@ -100,16 +104,21 @@ function resizeArea() {
|
|||
* This is a bit loosely defined at the moment.
|
||||
* Should work so long as there's only one completion source per prefix.
|
||||
*/
|
||||
function getCompletion(args_only = false) {
|
||||
function getActiveCompletionSource(): CompletionSourceFuse | undefined {
|
||||
if (!commandline_state.activeCompletions) return undefined
|
||||
|
||||
for (const comp of commandline_state.activeCompletions) {
|
||||
if (comp.state === "normal" && comp.completion !== undefined) {
|
||||
return args_only ? comp.args : comp.completion
|
||||
}
|
||||
}
|
||||
return commandline_state.activeCompletions.filter(
|
||||
({ state, completion }) =>
|
||||
state === "normal" && completion !== undefined,
|
||||
)[0]
|
||||
}
|
||||
|
||||
/** @hidden **/
|
||||
function getCompletion(args_only = false): string | undefined {
|
||||
const activeSource = getActiveCompletionSource()
|
||||
if (!activeSource) return undefined
|
||||
return args_only ? activeSource.args : activeSource.completion
|
||||
}
|
||||
commandline_state.getCompletion = getCompletion
|
||||
|
||||
/** @hidden **/
|
||||
export function enableCompletions() {
|
||||
|
|
@ -119,6 +128,7 @@ export function enableCompletions() {
|
|||
// FindCompletionSource,
|
||||
BindingsCompletionSource,
|
||||
BmarkCompletionSource,
|
||||
BookmarkFolderCompletionSource,
|
||||
TabAllCompletionSource,
|
||||
BufferCompletionSource,
|
||||
ExcmdCompletionSource,
|
||||
|
|
@ -163,21 +173,28 @@ const noblur = () => setTimeout(() => commandline_state.clInput.focus(), 0)
|
|||
/** @hidden **/
|
||||
export function focus() {
|
||||
function consumeBufferedPageKeys(bufferedPageKeys: string[]) {
|
||||
const clInputStillFocused = window.document.activeElement === commandline_state.clInput;
|
||||
logger.debug("stop_buffering_page_keys response received, bufferedPageKeys = ", bufferedPageKeys,
|
||||
"clInputStillFocused = " + clInputStillFocused)
|
||||
const clInputStillFocused =
|
||||
window.document.activeElement === commandline_state.clInput
|
||||
logger.debug(
|
||||
"stop_buffering_page_keys response received, bufferedPageKeys = ",
|
||||
bufferedPageKeys,
|
||||
"clInputStillFocused = " + clInputStillFocused,
|
||||
)
|
||||
if (bufferedPageKeys.length !== 0) {
|
||||
const currentClInputValue = commandline_state.clInput.value;
|
||||
const initialClInputValue = commandline_state.initialClInputValue;
|
||||
logger.debug("Consuming buffered page keys", bufferedPageKeys,
|
||||
const currentClInputValue = commandline_state.clInput.value
|
||||
const initialClInputValue = commandline_state.initialClInputValue
|
||||
logger.debug(
|
||||
"Consuming buffered page keys",
|
||||
bufferedPageKeys,
|
||||
"initialClInputValue = " + initialClInputValue,
|
||||
"currentClInputValue = " + currentClInputValue);
|
||||
"currentClInputValue = " + currentClInputValue,
|
||||
)
|
||||
// Native events are assumed to be character keydown events,
|
||||
// i.e. characters appended at the end of clInput.
|
||||
commandline_state.clInput.value =
|
||||
initialClInputValue
|
||||
+ bufferedPageKeys.join("")
|
||||
+ currentClInputValue.substring(initialClInputValue.length)
|
||||
initialClInputValue +
|
||||
bufferedPageKeys.join("") +
|
||||
currentClInputValue.substring(initialClInputValue.length)
|
||||
// Update completion.
|
||||
clInputValueChanged()
|
||||
}
|
||||
|
|
@ -185,8 +202,13 @@ export function focus() {
|
|||
commandline_state.clInput.focus()
|
||||
commandline_state.clInput.removeEventListener("blur", noblur)
|
||||
commandline_state.clInput.addEventListener("blur", noblur)
|
||||
logger.debug("commandline_frame clInput focus(), activeElement is clInput: " + (window.document.activeElement === commandline_state.clInput))
|
||||
Messaging.messageOwnTab("stop_buffering_page_keys").then(consumeBufferedPageKeys)
|
||||
logger.debug(
|
||||
"commandline_frame clInput focus(), activeElement is clInput: " +
|
||||
(window.document.activeElement === commandline_state.clInput),
|
||||
)
|
||||
Messaging.messageOwnTab("stop_buffering_page_keys").then(
|
||||
consumeBufferedPageKeys,
|
||||
)
|
||||
}
|
||||
|
||||
/** @hidden **/
|
||||
|
|
@ -210,7 +232,10 @@ commandline_state.clInput.addEventListener(
|
|||
"keydown",
|
||||
function (keyevent: KeyboardEvent) {
|
||||
if (!keyevent.isTrusted) return
|
||||
logger.debug("commandline_frame clInput keydown event listener", keyevent)
|
||||
logger.debug(
|
||||
"commandline_frame clInput keydown event listener",
|
||||
keyevent,
|
||||
)
|
||||
commandline_state.keyEvents.push(minimalKeyFromKeyboardEvent(keyevent))
|
||||
const response = keyParser(commandline_state.keyEvents)
|
||||
if (response.isMatch) {
|
||||
|
|
@ -288,7 +313,7 @@ let onInputPromise: Promise<any> = Promise.resolve()
|
|||
/** @hidden **/
|
||||
commandline_state.clInput.addEventListener("input", () => {
|
||||
logger.debug("commandline_frame clInput input event listener")
|
||||
clInputValueChanged();
|
||||
clInputValueChanged()
|
||||
})
|
||||
|
||||
/** @hidden **/
|
||||
|
|
@ -370,7 +395,15 @@ export function fillcmdline(
|
|||
trailspace = true,
|
||||
ffocus = true,
|
||||
) {
|
||||
logger.debug("commandline_frame fillcmdline(newcommand = " + newcommand + " trailspace = " + trailspace + " ffocus = " + ffocus + ")")
|
||||
logger.debug(
|
||||
"commandline_frame fillcmdline(newcommand = " +
|
||||
newcommand +
|
||||
" trailspace = " +
|
||||
trailspace +
|
||||
" ffocus = " +
|
||||
ffocus +
|
||||
")",
|
||||
)
|
||||
if (trailspace) commandline_state.clInput.value = newcommand + " "
|
||||
else commandline_state.clInput.value = newcommand
|
||||
commandline_state.initialClInputValue = commandline_state.clInput.value
|
||||
|
|
|
|||
|
|
@ -37,12 +37,13 @@ export abstract class CompletionSource {
|
|||
node: HTMLElement
|
||||
public completion: string
|
||||
public args: string
|
||||
public trailingSpace: boolean
|
||||
protected prefixes: string[] = []
|
||||
protected lastFocused: CompletionOption
|
||||
private _state: OptionState
|
||||
private _prevState: OptionState
|
||||
|
||||
constructor(prefixes) {
|
||||
constructor(prefixes, options = { trailingSpace: true }) {
|
||||
const 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
|
||||
|
|
@ -56,6 +57,7 @@ export abstract class CompletionSource {
|
|||
|
||||
// Not sure this is necessary but every completion source has it
|
||||
this.prefixes = this.prefixes.map(p => p + " ")
|
||||
this.trailingSpace = options.trailingSpace
|
||||
}
|
||||
|
||||
/** Control presentation of Source */
|
||||
|
|
@ -174,8 +176,13 @@ export abstract class CompletionSourceFuse extends CompletionSource {
|
|||
|
||||
protected optionContainer = html`<table class="optionContainer"></table>`
|
||||
|
||||
constructor(prefixes, className: string, title?: string) {
|
||||
super(prefixes)
|
||||
constructor(
|
||||
prefixes,
|
||||
className: string,
|
||||
title?: string,
|
||||
options = { trailingSpace: true },
|
||||
) {
|
||||
super(prefixes, options)
|
||||
this.node = html`<div class="${className} hidden">
|
||||
<div class="sectionHeader">${title || className}</div>
|
||||
</div>`
|
||||
|
|
|
|||
|
|
@ -107,3 +107,56 @@ export class BmarkCompletionSource extends Completions.CompletionSourceFuse {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class BookmarkFolderCompletionSource extends Completions.CompletionSourceFuse {
|
||||
constructor(private _parent) {
|
||||
super(["bmark"], "BookmarkFolderCompletionSource", "Bookmark Folders", {
|
||||
trailingSpace: false,
|
||||
})
|
||||
}
|
||||
|
||||
async onInput(exstr: string) {
|
||||
const [_command, _url, path] = this.parseArgs(exstr)
|
||||
if (path == undefined) {
|
||||
this.options = undefined
|
||||
return
|
||||
}
|
||||
this.options = (await providers.getBookmarkFolders(path))
|
||||
.slice(0, 10)
|
||||
.map(path => new BookmarkFolderCompletionOption(path))
|
||||
}
|
||||
|
||||
splitOnPrefix(exstr: string): string[] {
|
||||
const [command, url, path] = this.parseArgs(exstr)
|
||||
return [`${command} ${url}`, path]
|
||||
}
|
||||
|
||||
private parseArgs(exstr: string): string[] {
|
||||
const [command, args] = super.splitOnPrefix(exstr)
|
||||
if (!args) {
|
||||
return [command]
|
||||
}
|
||||
const spaceIndex = args.search(/\s+/)
|
||||
const url = args.slice(0, spaceIndex)
|
||||
if (spaceIndex == -1) {
|
||||
return [command, url]
|
||||
}
|
||||
const path = args.slice(spaceIndex + 1)
|
||||
return [command, url, path]
|
||||
}
|
||||
}
|
||||
|
||||
class BookmarkFolderCompletionOption
|
||||
extends Completions.CompletionOptionHTML
|
||||
implements Completions.CompletionOptionFuse {
|
||||
fuseKeys = []
|
||||
|
||||
constructor(public value: string) {
|
||||
super()
|
||||
this.fuseKeys.push(value)
|
||||
this.html = html`<tr class="BookmarkFolderCompletionOption option">
|
||||
<td class="prefix">${"".padEnd(2)}</td>
|
||||
<td class="title">${value}</td>
|
||||
</tr>`
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,17 +66,7 @@ async function fuseBookmarksSearch(query: string): Promise<Bookmark[]> {
|
|||
}
|
||||
|
||||
async function collectBookmarks(): Promise<Bookmark[]> {
|
||||
const root = await browserBg.bookmarks.getTree()
|
||||
const bookmarks = root.flatMap(flattenChildren)
|
||||
const bookmarksDictionary = bookmarks.reduce((dict, bookmark) => {
|
||||
dict[bookmark.id] = bookmark
|
||||
return dict
|
||||
}, {})
|
||||
return bookmarks
|
||||
.map(bookmark => ({
|
||||
path: buildBookmarkPath("", bookmark, bookmarksDictionary),
|
||||
...bookmark,
|
||||
}))
|
||||
return (await collectBookmarksAndFolders())
|
||||
.filter(isValidBookmark)
|
||||
.sort((a, b) => b.dateAdded - a.dateAdded)
|
||||
}
|
||||
|
|
@ -117,6 +107,34 @@ function isValidBookmark(bookmark: Bookmark): boolean {
|
|||
}
|
||||
}
|
||||
|
||||
let bookmarkPaths: string[]
|
||||
|
||||
export async function getBookmarkFolders(query: string) {
|
||||
bookmarkPaths = bookmarkPaths || [
|
||||
...new Set(
|
||||
(await collectBookmarksAndFolders())
|
||||
.filter(bookmark => bookmark.path && bookmark.path != "/")
|
||||
.map(bookmark => bookmark.path),
|
||||
),
|
||||
]
|
||||
return query
|
||||
? bookmarkPaths.filter(path => path != query && path.includes(query))
|
||||
: bookmarkPaths
|
||||
}
|
||||
|
||||
async function collectBookmarksAndFolders(): Promise<Bookmark[]> {
|
||||
const root = await browserBg.bookmarks.getTree()
|
||||
const bookmarks = root.flatMap(flattenChildren)
|
||||
const bookmarksDictionary = bookmarks.reduce((dict, bookmark) => {
|
||||
dict[bookmark.id] = bookmark
|
||||
return dict
|
||||
}, {})
|
||||
return bookmarks.map(bookmark => ({
|
||||
path: buildBookmarkPath("", bookmark, bookmarksDictionary),
|
||||
...bookmark,
|
||||
}))
|
||||
}
|
||||
|
||||
export async function getSearchUrls(query: string) {
|
||||
const suconf = config.get("searchurls")
|
||||
|
||||
|
|
|
|||
|
|
@ -78,15 +78,17 @@ export function getCommandlineFns(cmdline_state: {
|
|||
"current_cmdline",
|
||||
"cmdline_filter",
|
||||
)
|
||||
const command = cmdline_state.getCompletion()
|
||||
const completionSource = cmdline_state.getActiveCompletionSource()
|
||||
const completion = completionSource?.completion
|
||||
if (cmdline_state.activeCompletions) {
|
||||
cmdline_state.activeCompletions.forEach(
|
||||
comp => (comp.completion = undefined),
|
||||
)
|
||||
}
|
||||
let result = Promise.resolve([])
|
||||
if (command) {
|
||||
cmdline_state.clInput.value = command + " "
|
||||
if (completion) {
|
||||
cmdline_state.clInput.value =
|
||||
completion + (completionSource?.trailingSpace ? " " : "")
|
||||
result = cmdline_state.refresh_completions(
|
||||
cmdline_state.clInput.value,
|
||||
)
|
||||
|
|
@ -99,14 +101,16 @@ export function getCommandlineFns(cmdline_state: {
|
|||
* If no completion is selected, inserts a space where the caret is.
|
||||
*/
|
||||
insert_space_or_completion: () => {
|
||||
const command = cmdline_state.getCompletion()
|
||||
const completionSource = cmdline_state.getActiveCompletionSource()
|
||||
const completion = completionSource?.completion
|
||||
if (cmdline_state.activeCompletions) {
|
||||
cmdline_state.activeCompletions.forEach(
|
||||
comp => (comp.completion = undefined),
|
||||
)
|
||||
}
|
||||
if (command) {
|
||||
cmdline_state.clInput.value = command + " "
|
||||
if (completion) {
|
||||
cmdline_state.clInput.value =
|
||||
completion + (completionSource?.trailingSpace ? " " : "")
|
||||
} else {
|
||||
space(cmdline_state)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue