diff options
Diffstat (limited to 'packages/cli/src/ui/components/messages')
4 files changed, 150 insertions, 12 deletions
diff --git a/packages/cli/src/ui/components/messages/DiffRenderer.test.tsx b/packages/cli/src/ui/components/messages/DiffRenderer.test.tsx new file mode 100644 index 00000000..335ee20a --- /dev/null +++ b/packages/cli/src/ui/components/messages/DiffRenderer.test.tsx @@ -0,0 +1,117 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { render } from 'ink-testing-library'; +import { DiffRenderer } from './DiffRenderer.js'; +import * as CodeColorizer from '../../utils/CodeColorizer.js'; +import { vi } from 'vitest'; + +describe('<DiffRenderer />', () => { + const mockColorizeCode = vi.spyOn(CodeColorizer, 'colorizeCode'); + + beforeEach(() => { + mockColorizeCode.mockClear(); + }); + + it('should call colorizeCode with correct language for new file with known extension', () => { + const newFileDiffContent = ` +diff --git a/test.py b/test.py +new file mode 100644 +index 0000000..e69de29 +--- /dev/null ++++ b/test.py +@@ -0,0 +1 @@ ++print("hello world") +`; + render( + <DiffRenderer diffContent={newFileDiffContent} filename="test.py" />, + ); + expect(mockColorizeCode).toHaveBeenCalledWith( + 'print("hello world")', + 'python', + ); + }); + + it('should call colorizeCode with null language for new file with unknown extension', () => { + const newFileDiffContent = ` +diff --git a/test.unknown b/test.unknown +new file mode 100644 +index 0000000..e69de29 +--- /dev/null ++++ b/test.unknown +@@ -0,0 +1 @@ ++some content +`; + render( + <DiffRenderer diffContent={newFileDiffContent} filename="test.unknown" />, + ); + expect(mockColorizeCode).toHaveBeenCalledWith('some content', null); + }); + + it('should call colorizeCode with null language for new file if no filename is provided', () => { + const newFileDiffContent = ` +diff --git a/test.txt b/test.txt +new file mode 100644 +index 0000000..e69de29 +--- /dev/null ++++ b/test.txt +@@ -0,0 +1 @@ ++some text content +`; + render(<DiffRenderer diffContent={newFileDiffContent} />); + expect(mockColorizeCode).toHaveBeenCalledWith('some text content', null); + }); + + it('should render diff content for existing file (not calling colorizeCode directly for the whole block)', () => { + const existingFileDiffContent = ` +diff --git a/test.txt b/test.txt +index 0000001..0000002 100644 +--- a/test.txt ++++ b/test.txt +@@ -1 +1 @@ +-old line ++new line +`; + const { lastFrame } = render( + <DiffRenderer + diffContent={existingFileDiffContent} + filename="test.txt" + />, + ); + // colorizeCode is used internally by the line-by-line rendering, not for the whole block + expect(mockColorizeCode).not.toHaveBeenCalledWith( + expect.stringContaining('old line'), + expect.anything(), + ); + expect(mockColorizeCode).not.toHaveBeenCalledWith( + expect.stringContaining('new line'), + expect.anything(), + ); + const output = lastFrame(); + const lines = output!.split('\n'); + expect(lines[0]).toBe('1 - old line'); + expect(lines[1]).toBe('1 + new line'); + }); + + it('should handle diff with only header and no changes', () => { + const noChangeDiff = `diff --git a/file.txt b/file.txt +index 1234567..1234567 100644 +--- a/file.txt ++++ b/file.txt +`; + const { lastFrame } = render( + <DiffRenderer diffContent={noChangeDiff} filename="file.txt" />, + ); + expect(lastFrame()).toContain('No changes detected'); + expect(mockColorizeCode).not.toHaveBeenCalled(); + }); + + it('should handle empty diff content', () => { + const { lastFrame } = render(<DiffRenderer diffContent="" />); + expect(lastFrame()).toContain('No diff content'); + expect(mockColorizeCode).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/cli/src/ui/components/messages/DiffRenderer.tsx b/packages/cli/src/ui/components/messages/DiffRenderer.tsx index a9afeca3..4baed3e8 100644 --- a/packages/cli/src/ui/components/messages/DiffRenderer.tsx +++ b/packages/cli/src/ui/components/messages/DiffRenderer.tsx @@ -105,6 +105,14 @@ export const DiffRenderer: React.FC<DiffRendererProps> = ({ const parsedLines = parseDiffWithLineNumbers(diffContent); + if (parsedLines.length === 0) { + return ( + <Box borderStyle="round" borderColor={Colors.SubtleComment} padding={1}> + <Text dimColor>No changes detected.</Text> + </Box> + ); + } + // Check if the diff represents a new file (only additions and header lines) const isNewFile = parsedLines.every( (line) => @@ -233,16 +241,21 @@ const renderDiffContent = ( const getLanguageFromExtension = (extension: string): string | null => { const languageMap: { [key: string]: string } = { - '.js': 'javascript', - '.ts': 'typescript', - '.py': 'python', - '.json': 'json', - '.css': 'css', - '.html': 'html', - '.sh': 'bash', - '.md': 'markdown', - '.yaml': 'yaml', - '.yml': 'yaml', + js: 'javascript', + ts: 'typescript', + py: 'python', + json: 'json', + css: 'css', + html: 'html', + sh: 'bash', + md: 'markdown', + yaml: 'yaml', + yml: 'yaml', + txt: 'plaintext', + java: 'java', + c: 'c', + cpp: 'cpp', + rb: 'ruby', }; return languageMap[extension] || null; // Return null if extension not found }; diff --git a/packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx b/packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx index b43f6843..19b1841a 100644 --- a/packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx +++ b/packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx @@ -52,7 +52,12 @@ export const ToolConfirmationMessage: React.FC< if (isEditDetails(confirmationDetails)) { // Body content is now the DiffRenderer, passing filename to it // The bordered box is removed from here and handled within DiffRenderer - bodyContent = <DiffRenderer diffContent={confirmationDetails.fileDiff} />; + bodyContent = ( + <DiffRenderer + diffContent={confirmationDetails.fileDiff} + filename={confirmationDetails.fileName} + /> + ); question = `Apply this change?`; options.push( diff --git a/packages/cli/src/ui/components/messages/ToolMessage.tsx b/packages/cli/src/ui/components/messages/ToolMessage.tsx index 091785f2..32b23b9e 100644 --- a/packages/cli/src/ui/components/messages/ToolMessage.tsx +++ b/packages/cli/src/ui/components/messages/ToolMessage.tsx @@ -90,7 +90,10 @@ export const ToolMessage: React.FC<ToolMessageProps> = ({ </Box> )} {typeof displayableResult === 'object' && ( - <DiffRenderer diffContent={displayableResult.fileDiff} /> + <DiffRenderer + diffContent={displayableResult.fileDiff} + filename={displayableResult.fileName} + /> )} {hiddenLines > 0 && ( <Box> |
