refactor: migrate to useKeyMatchers hook (#21753)

This commit is contained in:
Tommaso Sciortino
2026-03-09 20:48:09 +00:00
committed by GitHub
parent e406dcc249
commit ab64b15d51
34 changed files with 162 additions and 54 deletions
@@ -11,7 +11,8 @@ import {
getAdminErrorMessage,
} from '@google/gemini-cli-core';
import { useKeypress } from './useKeypress.js';
import { keyMatchers, Command } from '../keyMatchers.js';
import { Command } from '../keyMatchers.js';
import { useKeyMatchers } from './useKeyMatchers.js';
import type { HistoryItemWithoutId } from '../types.js';
import { MessageType } from '../types.js';
@@ -30,6 +31,7 @@ export function useApprovalModeIndicator({
isActive = true,
allowPlanMode = false,
}: UseApprovalModeIndicatorArgs): ApprovalMode {
const keyMatchers = useKeyMatchers();
const currentConfigValue = config.getApprovalMode();
const [showApprovalMode, setApprovalMode] = useState(currentConfigValue);
@@ -0,0 +1,17 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { useMemo } from 'react';
import type { KeyMatchers } from '../keyMatchers.js';
import { defaultKeyMatchers } from '../keyMatchers.js';
/**
* Hook to retrieve the currently active key matchers.
* This prepares the codebase for dynamic or custom key bindings in the future.
*/
export function useKeyMatchers(): KeyMatchers {
return useMemo(() => defaultKeyMatchers, []);
}
@@ -6,8 +6,9 @@
import { useReducer, useRef, useEffect, useCallback } from 'react';
import { useKeypress, type Key } from './useKeypress.js';
import { keyMatchers, Command } from '../keyMatchers.js';
import { Command } from '../keyMatchers.js';
import { debugLogger } from '@google/gemini-cli-core';
import { useKeyMatchers } from './useKeyMatchers.js';
export interface SelectionListItem<T> {
key: string;
@@ -290,6 +291,7 @@ export function useSelectionList<T>({
focusKey,
priority,
}: UseSelectionListOptions<T>): UseSelectionListResult {
const keyMatchers = useKeyMatchers();
const baseItems = toBaseItems(items);
const [state, dispatch] = useReducer(selectionListReducer, {
@@ -460,7 +462,7 @@ export function useSelectionList<T>({
}
return false;
},
[dispatch, itemsLength, showNumbers],
[dispatch, itemsLength, showNumbers, keyMatchers],
);
useKeypress(handleKeypress, {
@@ -9,12 +9,18 @@ import { act } from 'react';
import { renderHook } from '../../test-utils/render.js';
import { useTabbedNavigation } from './useTabbedNavigation.js';
import { useKeypress } from './useKeypress.js';
import { useKeyMatchers } from './useKeyMatchers.js';
import type { KeyMatchers } from '../keyMatchers.js';
import type { Key, KeypressHandler } from '../contexts/KeypressContext.js';
vi.mock('./useKeypress.js', () => ({
useKeypress: vi.fn(),
}));
vi.mock('./useKeyMatchers.js', () => ({
useKeyMatchers: vi.fn(),
}));
const createKey = (partial: Partial<Key>): Key => ({
name: partial.name || '',
sequence: partial.sequence || '',
@@ -26,13 +32,14 @@ const createKey = (partial: Partial<Key>): Key => ({
...partial,
});
const mockKeyMatchers = {
'cursor.left': vi.fn((key) => key.name === 'left'),
'cursor.right': vi.fn((key) => key.name === 'right'),
'dialog.next': vi.fn((key) => key.name === 'tab' && !key.shift),
'dialog.previous': vi.fn((key) => key.name === 'tab' && key.shift),
} as unknown as KeyMatchers;
vi.mock('../keyMatchers.js', () => ({
keyMatchers: {
'cursor.left': vi.fn((key) => key.name === 'left'),
'cursor.right': vi.fn((key) => key.name === 'right'),
'dialog.next': vi.fn((key) => key.name === 'tab' && !key.shift),
'dialog.previous': vi.fn((key) => key.name === 'tab' && key.shift),
},
Command: {
MOVE_LEFT: 'cursor.left',
MOVE_RIGHT: 'cursor.right',
@@ -45,6 +52,7 @@ describe('useTabbedNavigation', () => {
let capturedHandler: KeypressHandler;
beforeEach(() => {
vi.mocked(useKeyMatchers).mockReturnValue(mockKeyMatchers);
vi.mocked(useKeypress).mockImplementation((handler) => {
capturedHandler = handler;
});
@@ -6,7 +6,8 @@
import { useReducer, useCallback, useEffect, useRef } from 'react';
import { useKeypress, type Key } from './useKeypress.js';
import { keyMatchers, Command } from '../keyMatchers.js';
import { Command } from '../keyMatchers.js';
import { useKeyMatchers } from './useKeyMatchers.js';
/**
* Options for the useTabbedNavigation hook.
@@ -147,6 +148,7 @@ export function useTabbedNavigation({
isActive = true,
onTabChange,
}: UseTabbedNavigationOptions): UseTabbedNavigationResult {
const keyMatchers = useKeyMatchers();
const [state, dispatch] = useReducer(tabbedNavigationReducer, {
currentIndex: Math.max(0, Math.min(initialIndex, tabCount - 1)),
tabCount,
@@ -231,6 +233,7 @@ export function useTabbedNavigation({
goToNextTab,
goToPrevTab,
isNavigationBlocked,
keyMatchers,
],
);
+5 -2
View File
@@ -9,7 +9,8 @@ import type { Key } from './useKeypress.js';
import type { TextBuffer } from '../components/shared/text-buffer.js';
import { useVimMode } from '../contexts/VimModeContext.js';
import { debugLogger } from '@google/gemini-cli-core';
import { keyMatchers, Command } from '../keyMatchers.js';
import { Command } from '../keyMatchers.js';
import { useKeyMatchers } from './useKeyMatchers.js';
export type VimMode = 'NORMAL' | 'INSERT';
@@ -152,6 +153,7 @@ const vimReducer = (state: VimState, action: VimAction): VimState => {
* @returns Object with vim state and input handler
*/
export function useVim(buffer: TextBuffer, onSubmit?: (value: string) => void) {
const keyMatchers = useKeyMatchers();
const { vimEnabled, vimMode, setVimMode } = useVimMode();
const [state, dispatch] = useReducer(vimReducer, initialVimState);
@@ -439,7 +441,7 @@ export function useVim(buffer: TextBuffer, onSubmit?: (value: string) => void) {
return buffer.handleInput(normalizedKey);
},
[buffer, dispatch, updateMode, onSubmit, checkDoubleEscape],
[buffer, dispatch, updateMode, onSubmit, checkDoubleEscape, keyMatchers],
);
/**
@@ -1202,6 +1204,7 @@ export function useVim(buffer: TextBuffer, onSubmit?: (value: string) => void) {
executeCommand,
updateMode,
checkDoubleEscape,
keyMatchers,
],
);