summaryrefslogtreecommitdiff
path: root/packages/cli/src/ui/hooks/useCompletion.ts
diff options
context:
space:
mode:
authorAllen Hutchison <[email protected]>2025-04-30 08:31:32 -0700
committerGitHub <[email protected]>2025-04-30 08:31:32 -0700
commit9f20c5f95e43bccee21b1d89e33fbc3c61a70650 (patch)
tree5bb6e262a998f0e8e0ca084082a64ee5872df596 /packages/cli/src/ui/hooks/useCompletion.ts
parent28fc2d0de348a796801fe19e3c5ced4634113162 (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.ts86
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]);