From 18cce6a9abf1cc7ebadd8a5e257462c33c8fe917 Mon Sep 17 00:00:00 2001 From: Jack Wotherspoon Date: Mon, 2 Feb 2026 20:03:28 -0500 Subject: [PATCH] fix: improve `Ctrl+R` reverse search (#18075) --- .../cli/src/ui/components/InputPrompt.test.tsx | 17 +++++++++++++++++ packages/cli/src/ui/components/InputPrompt.tsx | 7 ++++++- .../components/shared/ExpandableText.test.tsx | 2 ++ .../src/ui/components/shared/ExpandableText.tsx | 6 +----- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/ui/components/InputPrompt.test.tsx b/packages/cli/src/ui/components/InputPrompt.test.tsx index 226a086ae9..2dcf9a0d32 100644 --- a/packages/cli/src/ui/components/InputPrompt.test.tsx +++ b/packages/cli/src/ui/components/InputPrompt.test.tsx @@ -2775,6 +2775,23 @@ describe('InputPrompt', () => { }); unmount(); }); + + it('ensures Ctrl+R search results are prioritized newest-to-oldest by reversing userMessages', async () => { + props.shellModeActive = false; + props.userMessages = ['oldest', 'middle', 'newest']; + + renderWithProviders(); + + const calls = vi.mocked(useReverseSearchCompletion).mock.calls; + const commandSearchCall = calls.find( + (call) => + call[1] === props.userMessages || + (Array.isArray(call[1]) && call[1][0] === 'newest'), + ); + + expect(commandSearchCall).toBeDefined(); + expect(commandSearchCall![1]).toEqual(['newest', 'middle', 'oldest']); + }); }); describe('Tab focus toggle', () => { diff --git a/packages/cli/src/ui/components/InputPrompt.tsx b/packages/cli/src/ui/components/InputPrompt.tsx index dbca3917c7..151c5e14b8 100644 --- a/packages/cli/src/ui/components/InputPrompt.tsx +++ b/packages/cli/src/ui/components/InputPrompt.tsx @@ -197,9 +197,14 @@ export const InputPrompt: React.FC = ({ reverseSearchActive, ); + const reversedUserMessages = useMemo( + () => [...userMessages].reverse(), + [userMessages], + ); + const commandSearchCompletion = useReverseSearchCompletion( buffer, - userMessages, + reversedUserMessages, commandSearchActive, ); diff --git a/packages/cli/src/ui/components/shared/ExpandableText.test.tsx b/packages/cli/src/ui/components/shared/ExpandableText.test.tsx index 1e46751f57..68f5a947c6 100644 --- a/packages/cli/src/ui/components/shared/ExpandableText.test.tsx +++ b/packages/cli/src/ui/components/shared/ExpandableText.test.tsx @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import chalk from 'chalk'; import { describe, it, expect } from 'vitest'; import { render } from '../../../test-utils/render.js'; import { ExpandableText, MAX_WIDTH } from './ExpandableText.js'; @@ -76,6 +77,7 @@ describe('ExpandableText', () => { 100, ); expect(lastFrame()).toMatchSnapshot(); + expect(lastFrame()).toContain(chalk.inverse(userInput)); unmount(); }); diff --git a/packages/cli/src/ui/components/shared/ExpandableText.tsx b/packages/cli/src/ui/components/shared/ExpandableText.tsx index 69b4d4c661..f95f2d4b63 100644 --- a/packages/cli/src/ui/components/shared/ExpandableText.tsx +++ b/packages/cli/src/ui/components/shared/ExpandableText.tsx @@ -119,11 +119,7 @@ const _ExpandableText: React.FC = ({ {before} {match ? match.split(/(\s+)/).map((part, index) => ( - + {part} ))