aboutsummaryrefslogtreecommitdiff
path: root/src/console/completion
diff options
context:
space:
mode:
authorShin'ya Ueoka <ueokande@i-beam.org>2022-05-08 13:33:46 +0000
committerGitHub <noreply@github.com>2022-05-08 13:33:46 +0000
commit9279fff1351406a1f6c37c074cc1997e6a9e97e3 (patch)
treecc8e53f783efc5eb737af31bb9dd15c9e6474782 /src/console/completion
parent4468afca7a8c9893f71f7e042b25f6c46ba49678 (diff)
parent2a6d6b0967c6f6e269c3eedf4bd6002aee26b9da (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.ts16
-rw-r--r--src/console/completion/hooks.ts238
-rw-r--r--src/console/completion/hooks/clients.ts23
-rw-r--r--src/console/completion/reducer.ts8
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,