diff options
Diffstat (limited to 'packages/cli/src')
| -rw-r--r-- | packages/cli/src/ui/App.test.tsx | 84 | ||||
| -rw-r--r-- | packages/cli/src/ui/App.tsx | 16 | ||||
| -rw-r--r-- | packages/cli/src/ui/components/ContextSummaryDisplay.tsx | 22 | ||||
| -rw-r--r-- | packages/cli/src/ui/components/IDEContextDetailDisplay.tsx | 26 |
4 files changed, 100 insertions, 48 deletions
diff --git a/packages/cli/src/ui/App.test.tsx b/packages/cli/src/ui/App.test.tsx index 93230d1c..f35f8cb7 100644 --- a/packages/cli/src/ui/App.test.tsx +++ b/packages/cli/src/ui/App.test.tsx @@ -153,8 +153,8 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => { }); const ideContextMock = { - getOpenFilesContext: vi.fn(), - subscribeToOpenFiles: vi.fn(() => vi.fn()), // subscribe returns an unsubscribe function + getIdeContext: vi.fn(), + subscribeToIdeContext: vi.fn(() => vi.fn()), // subscribe returns an unsubscribe function }; return { @@ -277,7 +277,7 @@ describe('App UI', () => { // Ensure a theme is set so the theme dialog does not appear. mockSettings = createMockSettings({ workspace: { theme: 'Default' } }); - vi.mocked(ideContext.getOpenFilesContext).mockReturnValue(undefined); + vi.mocked(ideContext.getIdeContext).mockReturnValue(undefined); }); afterEach(() => { @@ -289,10 +289,17 @@ describe('App UI', () => { }); it('should display active file when available', async () => { - vi.mocked(ideContext.getOpenFilesContext).mockReturnValue({ - activeFile: '/path/to/my-file.ts', - recentOpenFiles: [{ filePath: '/path/to/my-file.ts', content: 'hello' }], - selectedText: 'hello', + vi.mocked(ideContext.getIdeContext).mockReturnValue({ + workspaceState: { + openFiles: [ + { + path: '/path/to/my-file.ts', + isActive: true, + selectedText: 'hello', + timestamp: 0, + }, + ], + }, }); const { lastFrame, unmount } = render( @@ -304,12 +311,14 @@ describe('App UI', () => { ); currentUnmount = unmount; await Promise.resolve(); - expect(lastFrame()).toContain('1 recent file (ctrl+e to view)'); + expect(lastFrame()).toContain('1 open file (ctrl+e to view)'); }); - it('should not display active file when not available', async () => { - vi.mocked(ideContext.getOpenFilesContext).mockReturnValue({ - activeFile: '', + it('should not display any files when not available', async () => { + vi.mocked(ideContext.getIdeContext).mockReturnValue({ + workspaceState: { + openFiles: [], + }, }); const { lastFrame, unmount } = render( @@ -324,11 +333,54 @@ describe('App UI', () => { expect(lastFrame()).not.toContain('Open File'); }); + it('should display active file and other open files', async () => { + vi.mocked(ideContext.getIdeContext).mockReturnValue({ + workspaceState: { + openFiles: [ + { + path: '/path/to/my-file.ts', + isActive: true, + selectedText: 'hello', + timestamp: 0, + }, + { + path: '/path/to/another-file.ts', + isActive: false, + timestamp: 1, + }, + { + path: '/path/to/third-file.ts', + isActive: false, + timestamp: 2, + }, + ], + }, + }); + + const { lastFrame, unmount } = render( + <App + config={mockConfig as unknown as ServerConfig} + settings={mockSettings} + version={mockVersion} + />, + ); + currentUnmount = unmount; + await Promise.resolve(); + expect(lastFrame()).toContain('3 open files (ctrl+e to view)'); + }); + it('should display active file and other context', async () => { - vi.mocked(ideContext.getOpenFilesContext).mockReturnValue({ - activeFile: '/path/to/my-file.ts', - recentOpenFiles: [{ filePath: '/path/to/my-file.ts', content: 'hello' }], - selectedText: 'hello', + vi.mocked(ideContext.getIdeContext).mockReturnValue({ + workspaceState: { + openFiles: [ + { + path: '/path/to/my-file.ts', + isActive: true, + selectedText: 'hello', + timestamp: 0, + }, + ], + }, }); mockConfig.getGeminiMdFileCount.mockReturnValue(1); mockConfig.getAllGeminiMdFilenames.mockReturnValue(['GEMINI.md']); @@ -343,7 +395,7 @@ describe('App UI', () => { currentUnmount = unmount; await Promise.resolve(); expect(lastFrame()).toContain( - 'Using: 1 recent file (ctrl+e to view) | 1 GEMINI.md file', + 'Using: 1 open file (ctrl+e to view) | 1 GEMINI.md file', ); }); diff --git a/packages/cli/src/ui/App.tsx b/packages/cli/src/ui/App.tsx index 87a78ac6..aacf45d7 100644 --- a/packages/cli/src/ui/App.tsx +++ b/packages/cli/src/ui/App.tsx @@ -60,7 +60,7 @@ import { FlashFallbackEvent, logFlashFallback, AuthType, - type OpenFiles, + type IdeContext, ideContext, } from '@google/gemini-cli-core'; import { validateAuthMethod } from '../config/auth.js'; @@ -169,13 +169,15 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => { const [modelSwitchedFromQuotaError, setModelSwitchedFromQuotaError] = useState<boolean>(false); const [userTier, setUserTier] = useState<UserTierId | undefined>(undefined); - const [openFiles, setOpenFiles] = useState<OpenFiles | undefined>(); + const [ideContextState, setIdeContextState] = useState< + IdeContext | undefined + >(); const [isProcessing, setIsProcessing] = useState<boolean>(false); useEffect(() => { - const unsubscribe = ideContext.subscribeToOpenFiles(setOpenFiles); + const unsubscribe = ideContext.subscribeToIdeContext(setIdeContextState); // Set the initial value - setOpenFiles(ideContext.getOpenFilesContext()); + setIdeContextState(ideContext.getIdeContext()); return unsubscribe; }, []); @@ -568,7 +570,7 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => { if (Object.keys(mcpServers || {}).length > 0) { handleSlashCommand(newValue ? '/mcp desc' : '/mcp nodesc'); } - } else if (key.ctrl && input === 'e' && ideContext) { + } else if (key.ctrl && input === 'e' && ideContextState) { setShowIDEContextDetail((prev) => !prev); } else if (key.ctrl && (input === 'c' || input === 'C')) { handleExit(ctrlCPressedOnce, setCtrlCPressedOnce, ctrlCTimerRef); @@ -943,7 +945,7 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => { </Text> ) : ( <ContextSummaryDisplay - openFiles={openFiles} + ideContext={ideContextState} geminiMdFileCount={geminiMdFileCount} contextFileNames={contextFileNames} mcpServers={config.getMcpServers()} @@ -963,7 +965,7 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => { </Box> </Box> {showIDEContextDetail && ( - <IDEContextDetailDisplay openFiles={openFiles} /> + <IDEContextDetailDisplay ideContext={ideContextState} /> )} {showErrorDetails && ( <OverflowProvider> diff --git a/packages/cli/src/ui/components/ContextSummaryDisplay.tsx b/packages/cli/src/ui/components/ContextSummaryDisplay.tsx index b166056a..78a19f0d 100644 --- a/packages/cli/src/ui/components/ContextSummaryDisplay.tsx +++ b/packages/cli/src/ui/components/ContextSummaryDisplay.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { Text } from 'ink'; import { Colors } from '../colors.js'; -import { type OpenFiles, type MCPServerConfig } from '@google/gemini-cli-core'; +import { type IdeContext, type MCPServerConfig } from '@google/gemini-cli-core'; interface ContextSummaryDisplayProps { geminiMdFileCount: number; @@ -15,7 +15,7 @@ interface ContextSummaryDisplayProps { mcpServers?: Record<string, MCPServerConfig>; blockedMcpServers?: Array<{ name: string; extensionName: string }>; showToolDescriptions?: boolean; - openFiles?: OpenFiles; + ideContext?: IdeContext; } export const ContextSummaryDisplay: React.FC<ContextSummaryDisplayProps> = ({ @@ -24,26 +24,28 @@ export const ContextSummaryDisplay: React.FC<ContextSummaryDisplayProps> = ({ mcpServers, blockedMcpServers, showToolDescriptions, - openFiles, + ideContext, }) => { const mcpServerCount = Object.keys(mcpServers || {}).length; const blockedMcpServerCount = blockedMcpServers?.length || 0; + const openFileCount = ideContext?.workspaceState?.openFiles?.length ?? 0; if ( geminiMdFileCount === 0 && mcpServerCount === 0 && blockedMcpServerCount === 0 && - (openFiles?.recentOpenFiles?.length ?? 0) === 0 + openFileCount === 0 ) { return <Text> </Text>; // Render an empty space to reserve height } - const recentFilesText = (() => { - const count = openFiles?.recentOpenFiles?.length ?? 0; - if (count === 0) { + const openFilesText = (() => { + if (openFileCount === 0) { return ''; } - return `${count} recent file${count > 1 ? 's' : ''} (ctrl+e to view)`; + return `${openFileCount} open file${ + openFileCount > 1 ? 's' : '' + } (ctrl+e to view)`; })(); const geminiMdText = (() => { @@ -81,8 +83,8 @@ export const ContextSummaryDisplay: React.FC<ContextSummaryDisplayProps> = ({ let summaryText = 'Using: '; const summaryParts = []; - if (recentFilesText) { - summaryParts.push(recentFilesText); + if (openFilesText) { + summaryParts.push(openFilesText); } if (geminiMdText) { summaryParts.push(geminiMdText); diff --git a/packages/cli/src/ui/components/IDEContextDetailDisplay.tsx b/packages/cli/src/ui/components/IDEContextDetailDisplay.tsx index 8d4fb2c9..f535c40a 100644 --- a/packages/cli/src/ui/components/IDEContextDetailDisplay.tsx +++ b/packages/cli/src/ui/components/IDEContextDetailDisplay.tsx @@ -5,25 +5,21 @@ */ import { Box, Text } from 'ink'; -import { type OpenFiles } from '@google/gemini-cli-core'; +import { type File, type IdeContext } from '@google/gemini-cli-core'; import { Colors } from '../colors.js'; import path from 'node:path'; interface IDEContextDetailDisplayProps { - openFiles: OpenFiles | undefined; + ideContext: IdeContext | undefined; } export function IDEContextDetailDisplay({ - openFiles, + ideContext, }: IDEContextDetailDisplayProps) { - if ( - !openFiles || - !openFiles.recentOpenFiles || - openFiles.recentOpenFiles.length === 0 - ) { + const openFiles = ideContext?.workspaceState?.openFiles; + if (!openFiles || openFiles.length === 0) { return null; } - const recentFiles = openFiles.recentOpenFiles || []; return ( <Box @@ -36,13 +32,13 @@ export function IDEContextDetailDisplay({ <Text color={Colors.AccentCyan} bold> IDE Context (ctrl+e to toggle) </Text> - {recentFiles.length > 0 && ( + {openFiles.length > 0 && ( <Box flexDirection="column" marginTop={1}> - <Text bold>Recent files:</Text> - {recentFiles.map((file) => ( - <Text key={file.filePath}> - - {path.basename(file.filePath)} - {file.filePath === openFiles.activeFile ? ' (active)' : ''} + <Text bold>Open files:</Text> + {openFiles.map((file: File) => ( + <Text key={file.path}> + - {path.basename(file.path)} + {file.isActive ? ' (active)' : ''} </Text> ))} </Box> |
