From 0d17912972de6b9e8b08aedf141260413c60d7fd Mon Sep 17 00:00:00 2001
From: Shin'ya Ueoka <ueokande@i-beam.org>
Date: Thu, 5 May 2022 07:01:55 +0000
Subject: Use useDebounce

---
 src/console/components/CommandPrompt.tsx | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

(limited to 'src/console/components/CommandPrompt.tsx')

diff --git a/src/console/components/CommandPrompt.tsx b/src/console/components/CommandPrompt.tsx
index 0e2506c..0312fe4 100644
--- a/src/console/components/CommandPrompt.tsx
+++ b/src/console/components/CommandPrompt.tsx
@@ -3,6 +3,7 @@ import Completion from "./console/Completion";
 import Input from "./console//Input";
 import styled from "styled-components";
 import { useCompletions, useSelectCompletion } from "../completion/hooks";
+import useDebounce from "../hooks/useDebounce";
 import useAutoResize from "../hooks/useAutoResize";
 import { CompletionProvider } from "../completion/provider";
 import { useExecCommand, useHide } from "../app/hooks";
@@ -20,6 +21,7 @@ interface Props {
 const CommandPromptInner: React.FC<Props> = ({ initialInputValue }) => {
   const hide = useHide();
   const [inputValue, setInputValue] = React.useState(initialInputValue);
+  const debouncedValue = useDebounce(inputValue, 100);
   const { completions, updateCompletions } = useCompletions();
   const { select, currentValue, selectNext, selectPrev } =
     useSelectCompletion();
@@ -82,8 +84,8 @@ const CommandPromptInner: React.FC<Props> = ({ initialInputValue }) => {
   };
 
   React.useEffect(() => {
-    updateCompletions(inputValue);
-  }, [inputValue]);
+    updateCompletions(debouncedValue);
+  }, [debouncedValue]);
 
   return (
     <ConsoleWrapper>
-- 
cgit v1.2.3


From cc427e967d63523b90ecc1c6aa2c864cb13b4b58 Mon Sep 17 00:00:00 2001
From: Shin'ya Ueoka <ueokande@i-beam.org>
Date: Thu, 5 May 2022 10:17:20 +0000
Subject: Pass source string via argument

---
 src/console/completion/hooks.ts          | 17 ++++++-----------
 src/console/components/CommandPrompt.tsx |  6 +-----
 2 files changed, 7 insertions(+), 16 deletions(-)

(limited to 'src/console/components/CommandPrompt.tsx')

diff --git a/src/console/completion/hooks.ts b/src/console/completion/hooks.ts
index 3a2ec33..a2e8bde 100644
--- a/src/console/completion/hooks.ts
+++ b/src/console/completion/hooks.ts
@@ -170,16 +170,12 @@ export const getPropertyCompletions = async (
   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 updateCompletions = React.useCallback((source: string) => {
-    dispatch(actions.setCompletionSource(source));
-  }, []);
-
   const queryCompletions = React.useCallback(
     (text: string, completionTypes: CompletionType[]) => {
       const phase = commandLineParser.inputPhase(text);
@@ -233,16 +229,15 @@ export const useCompletions = () => {
   );
 
   React.useEffect(() => {
+    dispatch(actions.setCompletionSource(source));
+
     if (typeof completionTypes === "undefined") {
       return;
     }
-    queryCompletions(state.completionSource, completionTypes);
-  }, [state.completionSource, completionTypes]);
+    queryCompletions(source, completionTypes);
+  }, [source, completionTypes]);
 
-  return {
-    completions: state.completions,
-    updateCompletions,
-  };
+  return { completions: state.completions };
 };
 
 export const useSelectCompletion = () => {
diff --git a/src/console/components/CommandPrompt.tsx b/src/console/components/CommandPrompt.tsx
index 0312fe4..89acf57 100644
--- a/src/console/components/CommandPrompt.tsx
+++ b/src/console/components/CommandPrompt.tsx
@@ -22,7 +22,7 @@ const CommandPromptInner: React.FC<Props> = ({ initialInputValue }) => {
   const hide = useHide();
   const [inputValue, setInputValue] = React.useState(initialInputValue);
   const debouncedValue = useDebounce(inputValue, 100);
-  const { completions, updateCompletions } = useCompletions();
+  const { completions } = useCompletions(debouncedValue);
   const { select, currentValue, selectNext, selectPrev } =
     useSelectCompletion();
   const execCommand = useExecCommand();
@@ -83,10 +83,6 @@ const CommandPromptInner: React.FC<Props> = ({ initialInputValue }) => {
     setInputValue(text);
   };
 
-  React.useEffect(() => {
-    updateCompletions(debouncedValue);
-  }, [debouncedValue]);
-
   return (
     <ConsoleWrapper>
       <Completion
-- 
cgit v1.2.3


From 3f347b66be898b54ffa45265ae9cce7b35e04433 Mon Sep 17 00:00:00 2001
From: Shin'ya Ueoka <ueokande@i-beam.org>
Date: Thu, 5 May 2022 14:26:06 +0000
Subject: Await loading completions done on selecting items

---
 src/console/completion/hooks.ts          | 38 +++++++++++++++++++++++---------
 src/console/components/CommandPrompt.tsx | 30 +++++++++++++++++++++----
 2 files changed, 54 insertions(+), 14 deletions(-)

(limited to 'src/console/components/CommandPrompt.tsx')

diff --git a/src/console/completion/hooks.ts b/src/console/completion/hooks.ts
index a2e8bde..cc6cd30 100644
--- a/src/console/completion/hooks.ts
+++ b/src/console/completion/hooks.ts
@@ -175,6 +175,7 @@ export const useCompletions = (source: string) => {
   const dispatch = React.useContext(CompletionDispatchContext);
   const commandLineParser = React.useMemo(() => new CommandLineParser(), []);
   const [completionTypes] = useGetCompletionTypes();
+  const [loading, setLoading] = React.useState(false);
 
   const queryCompletions = React.useCallback(
     (text: string, completionTypes: CompletionType[]) => {
@@ -192,40 +193,57 @@ export const useCompletions = (source: string) => {
             return;
           }
         }
+
+        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))
+              (completions) => {
+                dispatch(actions.setCompletions(completions));
+                setLoading(false);
+              }
             );
             break;
           case Command.Buffer:
             getTabCompletions(cmd.command, cmd.args, false).then(
-              (completions) => dispatch(actions.setCompletions(completions))
+              (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))
+            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))
+              (completions) => {
+                dispatch(actions.setCompletions(completions));
+                setLoading(false);
+              }
             );
             break;
           case Command.Set:
-            getPropertyCompletions(cmd.command, cmd.args).then((completions) =>
-              dispatch(actions.setCompletions(completions))
+            getPropertyCompletions(cmd.command, cmd.args).then(
+              (completions) => {
+                dispatch(actions.setCompletions(completions));
+                setLoading(false);
+              }
             );
             break;
         }
       }
     },
-    [dispatch]
+    [dispatch, source]
   );
 
   React.useEffect(() => {
@@ -237,7 +255,7 @@ export const useCompletions = (source: string) => {
     queryCompletions(source, completionTypes);
   }, [source, completionTypes]);
 
-  return { completions: state.completions };
+  return { completions: state.completions, loading };
 };
 
 export const useSelectCompletion = () => {
@@ -257,7 +275,7 @@ export const useSelectCompletion = () => {
     }
     const items = state.completions.map((g) => g.items).flat();
     return items[state.select]?.value || "";
-  }, [state.completionSource, state.select]);
+  }, [state.completionSource, state.select, state.completions]);
 
   return {
     select: state.select,
diff --git a/src/console/components/CommandPrompt.tsx b/src/console/components/CommandPrompt.tsx
index 89acf57..5d4cb6e 100644
--- a/src/console/components/CommandPrompt.tsx
+++ b/src/console/components/CommandPrompt.tsx
@@ -18,15 +18,23 @@ interface Props {
   initialInputValue: string;
 }
 
+enum SelectQueueType {
+  SelectNext,
+  SelectPrev,
+}
+
 const CommandPromptInner: React.FC<Props> = ({ initialInputValue }) => {
   const hide = useHide();
   const [inputValue, setInputValue] = React.useState(initialInputValue);
   const debouncedValue = useDebounce(inputValue, 100);
-  const { completions } = useCompletions(debouncedValue);
+  const { completions, loading } = useCompletions(debouncedValue);
   const { select, currentValue, selectNext, selectPrev } =
     useSelectCompletion();
   const execCommand = useExecCommand();
 
+  // The value is set after the user presses Tab (or Shift+Tab) key and waiting the completion
+  const [selecting, setSelecting] = React.useState<SelectQueueType>();
+
   useAutoResize();
 
   const onBlur = () => {
@@ -67,9 +75,9 @@ const CommandPromptInner: React.FC<Props> = ({ initialInputValue }) => {
       execCommand(value);
       hide();
     } else if (isNextKey(e)) {
-      selectNext();
+      setSelecting(SelectQueueType.SelectNext);
     } else if (isPrevKey(e)) {
-      selectPrev();
+      setSelecting(SelectQueueType.SelectPrev);
     } else {
       return;
     }
@@ -78,6 +86,20 @@ const CommandPromptInner: React.FC<Props> = ({ initialInputValue }) => {
     e.preventDefault();
   };
 
+  React.useEffect(() => {
+    if (inputValue !== debouncedValue || loading) {
+      // The completions of the latest input value are not fetched
+      return;
+    }
+    if (selecting === SelectQueueType.SelectNext) {
+      selectNext();
+      setSelecting(undefined);
+    } else if (selecting === SelectQueueType.SelectPrev) {
+      selectPrev();
+      setSelecting(undefined);
+    }
+  }, [inputValue, debouncedValue, selecting, loading]);
+
   const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
     const text = e.target.value;
     setInputValue(text);
@@ -95,7 +117,7 @@ const CommandPromptInner: React.FC<Props> = ({ initialInputValue }) => {
         onBlur={onBlur}
         onKeyDown={onKeyDown}
         onChange={onChange}
-        value={select == -1 ? inputValue : currentValue}
+        value={select == -1 || loading ? inputValue : currentValue}
       />
     </ConsoleWrapper>
   );
-- 
cgit v1.2.3

