From 072d8ba2899f2601dad6d4b0333fdcb80555a7dd Mon Sep 17 00:00:00 2001
From: Ayesha Shafique <79274585+Aisha630@users.noreply.github.com>
Date: Mon, 4 Aug 2025 00:53:24 +0500
Subject: feat: Add reverse search capability for shell commands (#4793)

---
 .../src/ui/hooks/useReverseSearchCompletion.tsx    | 91 ++++++++++++++++++++++
 1 file changed, 91 insertions(+)
 create mode 100644 packages/cli/src/ui/hooks/useReverseSearchCompletion.tsx

(limited to 'packages/cli/src/ui/hooks/useReverseSearchCompletion.tsx')

diff --git a/packages/cli/src/ui/hooks/useReverseSearchCompletion.tsx b/packages/cli/src/ui/hooks/useReverseSearchCompletion.tsx
new file mode 100644
index 00000000..1cc7e602
--- /dev/null
+++ b/packages/cli/src/ui/hooks/useReverseSearchCompletion.tsx
@@ -0,0 +1,91 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { useEffect, useCallback } from 'react';
+import { useCompletion } from './useCompletion.js';
+import { TextBuffer } from '../components/shared/text-buffer.js';
+import { Suggestion } from '../components/SuggestionsDisplay.js';
+
+export interface UseReverseSearchCompletionReturn {
+  suggestions: Suggestion[];
+  activeSuggestionIndex: number;
+  visibleStartIndex: number;
+  showSuggestions: boolean;
+  isLoadingSuggestions: boolean;
+  navigateUp: () => void;
+  navigateDown: () => void;
+  handleAutocomplete: (i: number) => void;
+  resetCompletionState: () => void;
+}
+
+export function useReverseSearchCompletion(
+  buffer: TextBuffer,
+  shellHistory: readonly string[],
+  reverseSearchActive: boolean,
+): UseReverseSearchCompletionReturn {
+  const {
+    suggestions,
+    activeSuggestionIndex,
+    visibleStartIndex,
+    showSuggestions,
+    isLoadingSuggestions,
+
+    setSuggestions,
+    setShowSuggestions,
+    setActiveSuggestionIndex,
+    resetCompletionState,
+    navigateUp,
+    navigateDown,
+  } = useCompletion();
+
+  // whenever reverseSearchActive is on, filter history
+  useEffect(() => {
+    if (!reverseSearchActive) {
+      resetCompletionState();
+      return;
+    }
+    const q = buffer.text.toLowerCase();
+    const matches = shellHistory.reduce<Suggestion[]>((acc, cmd) => {
+      const idx = cmd.toLowerCase().indexOf(q);
+      if (idx !== -1) {
+        acc.push({ label: cmd, value: cmd, matchedIndex: idx });
+      }
+      return acc;
+    }, []);
+    setSuggestions(matches);
+    setShowSuggestions(matches.length > 0);
+    setActiveSuggestionIndex(matches.length > 0 ? 0 : -1);
+  }, [
+    buffer.text,
+    shellHistory,
+    reverseSearchActive,
+    resetCompletionState,
+    setActiveSuggestionIndex,
+    setShowSuggestions,
+    setSuggestions,
+  ]);
+
+  const handleAutocomplete = useCallback(
+    (i: number) => {
+      if (i < 0 || i >= suggestions.length) return;
+      buffer.setText(suggestions[i].value);
+      resetCompletionState();
+    },
+    [buffer, suggestions, resetCompletionState],
+  );
+
+  return {
+    suggestions,
+    activeSuggestionIndex,
+    visibleStartIndex,
+    showSuggestions,
+    isLoadingSuggestions,
+    navigateUp,
+    navigateDown,
+    handleAutocomplete,
+    resetCompletionState,
+  };
+}
-- 
cgit v1.2.3

