diff options
| author | Allen Hutchison <[email protected]> | 2025-04-30 08:31:32 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-04-30 08:31:32 -0700 |
| commit | 9f20c5f95e43bccee21b1d89e33fbc3c61a70650 (patch) | |
| tree | 5bb6e262a998f0e8e0ca084082a64ee5872df596 /packages/cli/src/ui/hooks/useCompletion.ts | |
| parent | 28fc2d0de348a796801fe19e3c5ced4634113162 (diff) | |
Add @ command suggestions in the UI. (#219)
Diffstat (limited to 'packages/cli/src/ui/hooks/useCompletion.ts')
| -rw-r--r-- | packages/cli/src/ui/hooks/useCompletion.ts | 86 |
1 files changed, 48 insertions, 38 deletions
diff --git a/packages/cli/src/ui/hooks/useCompletion.ts b/packages/cli/src/ui/hooks/useCompletion.ts index 92841028..36aed0d1 100644 --- a/packages/cli/src/ui/hooks/useCompletion.ts +++ b/packages/cli/src/ui/hooks/useCompletion.ts @@ -8,8 +8,7 @@ import { useState, useEffect, useCallback } from 'react'; import * as fs from 'fs/promises'; import * as path from 'path'; import { isNodeError } from '@gemini-code/server'; - -const MAX_SUGGESTIONS_TO_SHOW = 8; +import { MAX_SUGGESTIONS_TO_SHOW } from '../components/SuggestionsDisplay.js'; export interface UseCompletionReturn { suggestions: string[]; @@ -45,51 +44,64 @@ export function useCompletion( setIsLoadingSuggestions(false); }, []); - // --- Navigation Logic --- const navigateUp = useCallback(() => { if (suggestions.length === 0) return; - setActiveSuggestionIndex((prevIndex) => { - const newIndex = prevIndex <= 0 ? suggestions.length - 1 : prevIndex - 1; + setActiveSuggestionIndex((prevActiveIndex) => { + // Calculate new active index, handling wrap-around + const newActiveIndex = + prevActiveIndex <= 0 ? suggestions.length - 1 : prevActiveIndex - 1; - // Adjust visible window if needed (scrolling up) - if (newIndex < visibleStartIndex) { - setVisibleStartIndex(newIndex); - } else if ( - newIndex === suggestions.length - 1 && - suggestions.length > MAX_SUGGESTIONS_TO_SHOW - ) { - // Handle wrapping from first to last item - setVisibleStartIndex( - Math.max(0, suggestions.length - MAX_SUGGESTIONS_TO_SHOW), - ); - } + // Adjust scroll position based on the new active index + setVisibleStartIndex((prevVisibleStart) => { + // Case 1: Wrapped around to the last item + if ( + newActiveIndex === suggestions.length - 1 && + suggestions.length > MAX_SUGGESTIONS_TO_SHOW + ) { + return Math.max(0, suggestions.length - MAX_SUGGESTIONS_TO_SHOW); + } + // Case 2: Scrolled above the current visible window + if (newActiveIndex < prevVisibleStart) { + return newActiveIndex; + } + // Otherwise, keep the current scroll position + return prevVisibleStart; + }); - return newIndex; + return newActiveIndex; }); - }, [suggestions.length, visibleStartIndex]); + }, [suggestions.length]); const navigateDown = useCallback(() => { if (suggestions.length === 0) return; - setActiveSuggestionIndex((prevIndex) => { - const newIndex = prevIndex >= suggestions.length - 1 ? 0 : prevIndex + 1; + setActiveSuggestionIndex((prevActiveIndex) => { + // Calculate new active index, handling wrap-around + const newActiveIndex = + prevActiveIndex >= suggestions.length - 1 ? 0 : prevActiveIndex + 1; - // Adjust visible window if needed (scrolling down) - if (newIndex >= visibleStartIndex + MAX_SUGGESTIONS_TO_SHOW) { - setVisibleStartIndex(visibleStartIndex + 1); - } else if ( - newIndex === 0 && - suggestions.length > MAX_SUGGESTIONS_TO_SHOW - ) { - // Handle wrapping from last to first item - setVisibleStartIndex(0); - } + // Adjust scroll position based on the new active index + setVisibleStartIndex((prevVisibleStart) => { + // Case 1: Wrapped around to the first item + if ( + newActiveIndex === 0 && + suggestions.length > MAX_SUGGESTIONS_TO_SHOW + ) { + return 0; + } + // Case 2: Scrolled below the current visible window + const visibleEndIndex = prevVisibleStart + MAX_SUGGESTIONS_TO_SHOW; + if (newActiveIndex >= visibleEndIndex) { + return newActiveIndex - MAX_SUGGESTIONS_TO_SHOW + 1; + } + // Otherwise, keep the current scroll position + return prevVisibleStart; + }); - return newIndex; + return newActiveIndex; }); - }, [suggestions.length, visibleStartIndex]); - // --- End Navigation Logic --- + }, [suggestions.length]); useEffect(() => { if (!isActive) { @@ -137,8 +149,8 @@ export function useCompletion( if (isMounted) { setSuggestions(filteredSuggestions); setShowSuggestions(filteredSuggestions.length > 0); - setActiveSuggestionIndex(-1); // Reset selection on new suggestions - setVisibleStartIndex(0); // Reset scroll on new suggestions + setActiveSuggestionIndex(-1); + setVisibleStartIndex(0); } } catch (error) { if (isNodeError(error) && error.code === 'ENOENT') { @@ -162,13 +174,11 @@ export function useCompletion( } }; - // Debounce the fetch slightly const debounceTimeout = setTimeout(fetchSuggestions, 100); return () => { isMounted = false; clearTimeout(debounceTimeout); - // Don't reset loading state here, let the next effect handle it or resetCompletionState }; }, [query, cwd, isActive, resetCompletionState]); |
