diff options
author | Shin'ya Ueoka <ueokande@i-beam.org> | 2022-05-08 13:33:46 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-08 13:33:46 +0000 |
commit | 9279fff1351406a1f6c37c074cc1997e6a9e97e3 (patch) | |
tree | cc8e53f783efc5eb737af31bb9dd15c9e6474782 /src/console/completion | |
parent | 4468afca7a8c9893f71f7e042b25f6c46ba49678 (diff) | |
parent | 2a6d6b0967c6f6e269c3eedf4bd6002aee26b9da (diff) |
Merge pull request #1418 from ueokande/await-completions
Await fetching completions done completely
Diffstat (limited to 'src/console/completion')
-rw-r--r-- | src/console/completion/actions.ts | 16 | ||||
-rw-r--r-- | src/console/completion/hooks.ts | 238 | ||||
-rw-r--r-- | src/console/completion/hooks/clients.ts | 23 | ||||
-rw-r--r-- | src/console/completion/reducer.ts | 8 |
4 files changed, 125 insertions, 160 deletions
diff --git a/src/console/completion/actions.ts b/src/console/completion/actions.ts index 59d1a04..0c5e1f1 100644 --- a/src/console/completion/actions.ts +++ b/src/console/completion/actions.ts @@ -1,17 +1,10 @@ -import CompletionType from "../../shared/CompletionType"; import Completions from "../Completions"; -export const INIT_COMPLETIONS = "reset.completions"; export const SET_COMPLETION_SOURCE = "set.completion.source"; export const SET_COMPLETIONS = "set.completions"; export const COMPLETION_NEXT = "completion.next"; export const COMPLETION_PREV = "completion.prev"; -export interface InitCompletionAction { - type: typeof INIT_COMPLETIONS; - completionTypes: CompletionType[]; -} - export interface SetCompletionSourceAction { type: typeof SET_COMPLETION_SOURCE; completionSource: string; @@ -31,20 +24,11 @@ export interface CompletionPrevAction { } export type CompletionAction = - | InitCompletionAction | SetCompletionSourceAction | SetCompletionsAction | CompletionNextAction | CompletionPrevAction; -export const initCompletion = ( - completionTypes: CompletionType[] -): InitCompletionAction => { - return { - type: INIT_COMPLETIONS, - completionTypes, - }; -}; export const setCompletionSource = ( query: string ): SetCompletionSourceAction => { diff --git a/src/console/completion/hooks.ts b/src/console/completion/hooks.ts index 4402b70..cc6cd30 100644 --- a/src/console/completion/hooks.ts +++ b/src/console/completion/hooks.ts @@ -11,6 +11,7 @@ import CommandLineParser, { import { UnknownCommandError } from "../commandline/CommandParser"; import Completions from "../Completions"; import CompletionType from "../../shared/CompletionType"; +import { useGetCompletionTypes } from "./hooks/clients"; const commandDocs = { [Command.Set]: "Set a value of the property", @@ -35,48 +36,13 @@ const propertyDocs: { [key: string]: string } = { const completionClient = new CompletionClient(); -const useDelayedCallback = <T extends unknown, U extends unknown>( - callback: (arg1: T, arg2: U) => void, - timeout: number -) => { - const [timer, setTimer] = React.useState< - ReturnType<typeof setTimeout> | undefined - >(); - const [enabled, setEnabled] = React.useState(false); - - const enableDelay = React.useCallback(() => { - setEnabled(true); - }, [setEnabled]); - - const delayedCallback = React.useCallback( - (arg1: T, arg2: U) => { - if (enabled) { - if (typeof timer !== "undefined") { - clearTimeout(timer); - } - const id = setTimeout(() => { - callback(arg1, arg2); - clearTimeout(timer!); - setTimer(undefined); - }, timeout); - setTimer(id); - } else { - callback(arg1, arg2); - } - }, - [enabled, timer] - ); - - return { enableDelay, delayedCallback }; -}; - const getCommandCompletions = async (query: string): Promise<Completions> => { const items = Object.entries(commandDocs) .filter(([name]) => name.startsWith(query)) .map(([name, doc]) => ({ - caption: name, - content: name, - url: doc, + primary: name, + secondary: doc, + value: name, })); return [ { @@ -102,8 +68,8 @@ const getOpenCompletions = async ( completions.push({ name: "Search Engines", items: items.map((key) => ({ - caption: key.title, - content: command + " " + key.title, + primary: key.title, + value: command + " " + key.title, })), }); break; @@ -116,9 +82,9 @@ const getOpenCompletions = async ( completions.push({ name: "History", items: items.map((item) => ({ - caption: item.title, - content: command + " " + item.url, - url: item.url, + primary: item.title, + secondary: item.url, + value: command + " " + item.url, })), }); break; @@ -131,9 +97,9 @@ const getOpenCompletions = async ( completions.push({ name: "Bookmarks", items: items.map((item) => ({ - caption: item.title, - content: command + " " + item.url, - url: item.url, + primary: item.title, + secondary: item.url, + value: command + " " + item.url, })), }); break; @@ -157,11 +123,11 @@ export const getTabCompletions = async ( { name: "Buffers", items: items.map((item) => ({ - content: command + " " + item.url, - caption: `${item.index}: ${ + primary: `${item.index}: ${ item.flag != TabFlag.None ? item.flag : " " } ${item.title}`, - url: item.url, + secondary: item.url, + value: command + " " + item.url, icon: item.faviconUrl, })), }, @@ -179,117 +145,117 @@ export const getPropertyCompletions = async ( if (item.type === "boolean") { return [ { - caption: item.name, - content: command + " " + item.name, - url: "Enable " + desc, + primary: item.name, + secondary: "Enable " + desc, + value: command + " " + item.name, }, { - caption: "no" + item.name, - content: command + " no" + item.name, - url: "Disable " + desc, + primary: "no" + item.name, + secondary: "Disable " + desc, + value: command + " no" + item.name, }, ]; } else { return [ { - caption: item.name, - content: command + " " + item.name, - url: "Set " + desc, + primary: item.name, + secondary: "Set " + desc, + value: command + " " + item.name, }, ]; } }) .reduce((acc, val) => acc.concat(val), []) - .filter((item) => item.caption.startsWith(query)); + .filter((item) => item.primary.startsWith(query)); return [{ name: "Properties", items }]; }; -export const useCompletions = () => { +export const useCompletions = (source: string) => { const state = React.useContext(CompletionStateContext); const dispatch = React.useContext(CompletionDispatchContext); const commandLineParser = React.useMemo(() => new CommandLineParser(), []); + const [completionTypes] = useGetCompletionTypes(); + const [loading, setLoading] = React.useState(false); - const updateCompletions = React.useCallback((source: string) => { - dispatch(actions.setCompletionSource(source)); - }, []); - - const initCompletion = React.useCallback((source: string) => { - completionClient.getCompletionTypes().then((completionTypes) => { - dispatch(actions.initCompletion(completionTypes)); - dispatch(actions.setCompletionSource(source)); - }); - }, []); - - const { delayedCallback: queryCompletions, enableDelay } = useDelayedCallback( - React.useCallback( - (text: string, completionTypes?: CompletionType[]) => { - const phase = commandLineParser.inputPhase(text); - if (phase === InputPhase.OnCommand) { - getCommandCompletions(text).then((completions) => - dispatch(actions.setCompletions(completions)) - ); - } else { - let cmd: CommandLine | null = null; - try { - cmd = commandLineParser.parse(text); - } catch (e) { - if (e instanceof UnknownCommandError) { - return; - } + const queryCompletions = React.useCallback( + (text: string, completionTypes: CompletionType[]) => { + const phase = commandLineParser.inputPhase(text); + if (phase === InputPhase.OnCommand) { + getCommandCompletions(text).then((completions) => + dispatch(actions.setCompletions(completions)) + ); + } else { + let cmd: CommandLine | null = null; + try { + cmd = commandLineParser.parse(text); + } catch (e) { + if (e instanceof UnknownCommandError) { + return; } - switch (cmd?.command) { - case Command.Open: - case Command.TabOpen: - case Command.WindowOpen: - if (!completionTypes) { - initCompletion(text); - return; - } + } - getOpenCompletions(cmd.command, cmd.args, completionTypes).then( - (completions) => dispatch(actions.setCompletions(completions)) - ); - break; - case Command.Buffer: - getTabCompletions(cmd.command, cmd.args, false).then( - (completions) => dispatch(actions.setCompletions(completions)) - ); - break; - case Command.BufferDelete: - case Command.BuffersDelete: - getTabCompletions(cmd.command, cmd.args, true).then( - (completions) => dispatch(actions.setCompletions(completions)) - ); - break; - case Command.BufferDeleteForce: - case Command.BuffersDeleteForce: - getTabCompletions(cmd.command, cmd.args, false).then( - (completions) => dispatch(actions.setCompletions(completions)) - ); - break; - case Command.Set: - getPropertyCompletions(cmd.command, cmd.args).then( - (completions) => dispatch(actions.setCompletions(completions)) - ); - break; - } - enableDelay(); + setLoading(true); + switch (cmd?.command) { + case Command.Open: + case Command.TabOpen: + case Command.WindowOpen: + getOpenCompletions(cmd.command, cmd.args, completionTypes).then( + (completions) => { + dispatch(actions.setCompletions(completions)); + setLoading(false); + } + ); + break; + case Command.Buffer: + getTabCompletions(cmd.command, cmd.args, false).then( + (completions) => { + dispatch(actions.setCompletions(completions)); + setLoading(false); + } + ); + break; + case Command.BufferDelete: + case Command.BuffersDelete: + getTabCompletions(cmd.command, cmd.args, true).then( + (completions) => { + dispatch(actions.setCompletions(completions)); + setLoading(false); + } + ); + break; + case Command.BufferDeleteForce: + case Command.BuffersDeleteForce: + getTabCompletions(cmd.command, cmd.args, false).then( + (completions) => { + dispatch(actions.setCompletions(completions)); + setLoading(false); + } + ); + break; + case Command.Set: + getPropertyCompletions(cmd.command, cmd.args).then( + (completions) => { + dispatch(actions.setCompletions(completions)); + setLoading(false); + } + ); + break; } - }, - [dispatch] - ), - 100 + } + }, + [dispatch, source] ); React.useEffect(() => { - queryCompletions(state.completionSource, state.completionTypes); - }, [state.completionSource, state.completionTypes]); + dispatch(actions.setCompletionSource(source)); - return { - completions: state.completions, - updateCompletions, - initCompletion, - }; + if (typeof completionTypes === "undefined") { + return; + } + queryCompletions(source, completionTypes); + }, [source, completionTypes]); + + return { completions: state.completions, loading }; }; export const useSelectCompletion = () => { @@ -308,8 +274,8 @@ export const useSelectCompletion = () => { return state.completionSource; } const items = state.completions.map((g) => g.items).flat(); - return items[state.select]?.content || ""; - }, [state.completionSource, state.select]); + return items[state.select]?.value || ""; + }, [state.completionSource, state.select, state.completions]); return { select: state.select, diff --git a/src/console/completion/hooks/clients.ts b/src/console/completion/hooks/clients.ts new file mode 100644 index 0000000..49deb0c --- /dev/null +++ b/src/console/completion/hooks/clients.ts @@ -0,0 +1,23 @@ +import React from "react"; +import CompletionClient from "../../clients/CompletionClient"; +import CompletionType from "../../../shared/CompletionType"; + +const completionClient = new CompletionClient(); + +export const useGetCompletionTypes = (): [ + CompletionType[] | undefined, + boolean +] => { + type State = { + loading: boolean; + result?: CompletionType[]; + }; + const [state, setState] = React.useState<State>({ loading: true }); + + React.useEffect(() => { + completionClient.getCompletionTypes().then((result) => { + setState({ loading: false, result }); + }); + }, []); + return [state.result, state.loading]; +}; diff --git a/src/console/completion/reducer.ts b/src/console/completion/reducer.ts index 905451f..0b34114 100644 --- a/src/console/completion/reducer.ts +++ b/src/console/completion/reducer.ts @@ -1,7 +1,6 @@ import Completions from "../Completions"; import CompletionType from "../../shared/CompletionType"; import { - INIT_COMPLETIONS, SET_COMPLETION_SOURCE, SET_COMPLETIONS, COMPLETION_NEXT, @@ -58,13 +57,6 @@ export default function reducer( action: CompletionAction ): State { switch (action.type) { - case INIT_COMPLETIONS: - return { - ...state, - completionTypes: action.completionTypes, - completions: [], - select: -1, - }; case SET_COMPLETION_SOURCE: return { ...state, |