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, | 
