feat(cli): Improve new file diff rendering with syntax highlighting
- Enhance the component to provide better readability for newly created files. - Instead of displaying a standard line-by-line diff for new files, extract the added content and render it with syntax highlighting based on the file extension. - Refactor the existing diff rendering logic into a separate function. - Add a helper function to map common file extensions to language names for syntax highlighting. Fixes: https://b.corp.google.com/issues/414279447 Signed-off-by: Gemini, your friendly neighborhood code agent.
This commit is contained in:
parent
a6e9bcb52d
commit
a9dc2772dd
|
@ -8,6 +8,7 @@ import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { Colors } from '../../colors.js';
|
import { Colors } from '../../colors.js';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
|
import { colorizeCode } from '../../utils/CodeColorizer.js';
|
||||||
|
|
||||||
interface DiffLine {
|
interface DiffLine {
|
||||||
type: 'add' | 'del' | 'context' | 'hunk' | 'other';
|
type: 'add' | 'del' | 'context' | 'hunk' | 'other';
|
||||||
|
@ -104,6 +105,42 @@ export const DiffRenderer: React.FC<DiffRendererProps> = ({
|
||||||
|
|
||||||
const parsedLines = parseDiffWithLineNumbers(diffContent);
|
const parsedLines = parseDiffWithLineNumbers(diffContent);
|
||||||
|
|
||||||
|
// Check if the diff represents a new file (only additions and header lines)
|
||||||
|
const isNewFile = parsedLines.every(
|
||||||
|
(line) =>
|
||||||
|
line.type === 'add' ||
|
||||||
|
line.type === 'hunk' ||
|
||||||
|
line.type === 'other' ||
|
||||||
|
line.content.startsWith('diff --git') ||
|
||||||
|
line.content.startsWith('new file mode'),
|
||||||
|
);
|
||||||
|
|
||||||
|
let renderedOutput;
|
||||||
|
|
||||||
|
if (isNewFile) {
|
||||||
|
// Extract only the added lines' content
|
||||||
|
const addedContent = parsedLines
|
||||||
|
.filter((line) => line.type === 'add')
|
||||||
|
.map((line) => line.content)
|
||||||
|
.join('\n');
|
||||||
|
// Attempt to infer language from filename, default to plain text if no filename
|
||||||
|
const fileExtension = filename?.split('.').pop() || null;
|
||||||
|
const language = fileExtension
|
||||||
|
? getLanguageFromExtension(fileExtension)
|
||||||
|
: null;
|
||||||
|
renderedOutput = colorizeCode(addedContent, language);
|
||||||
|
} else {
|
||||||
|
renderedOutput = renderDiffContent(parsedLines, filename, tabWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderedOutput;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderDiffContent = (
|
||||||
|
parsedLines: DiffLine[],
|
||||||
|
filename?: string,
|
||||||
|
tabWidth = DEFAULT_TAB_WIDTH,
|
||||||
|
) => {
|
||||||
// 1. Normalize whitespace (replace tabs with spaces) *before* further processing
|
// 1. Normalize whitespace (replace tabs with spaces) *before* further processing
|
||||||
const normalizedLines = parsedLines.map((line) => ({
|
const normalizedLines = parsedLines.map((line) => ({
|
||||||
...line,
|
...line,
|
||||||
|
@ -137,11 +174,11 @@ export const DiffRenderer: React.FC<DiffRendererProps> = ({
|
||||||
if (!isFinite(baseIndentation)) {
|
if (!isFinite(baseIndentation)) {
|
||||||
baseIndentation = 0;
|
baseIndentation = 0;
|
||||||
}
|
}
|
||||||
// --- End Modification ---
|
|
||||||
|
|
||||||
const key = filename
|
const key = filename
|
||||||
? `diff-box-${filename}`
|
? `diff-box-${filename}`
|
||||||
: `diff-box-${crypto.createHash('sha1').update(diffContent).digest('hex')}`;
|
: `diff-box-${crypto.createHash('sha1').update(JSON.stringify(parsedLines)).digest('hex')}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="column" key={key}>
|
<Box flexDirection="column" key={key}>
|
||||||
{/* Iterate over the lines that should be displayed (already normalized) */}
|
{/* Iterate over the lines that should be displayed (already normalized) */}
|
||||||
|
@ -193,3 +230,19 @@ export const DiffRenderer: React.FC<DiffRendererProps> = ({
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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',
|
||||||
|
};
|
||||||
|
return languageMap[extension] || null; // Return null if extension not found
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue