From a9dc2772dda8d6c814a8f151aeb1e4e796a797d7 Mon Sep 17 00:00:00 2001
From: Taylor Mullen <ntaylormullen@google.com>
Date: Sun, 27 Apr 2025 23:19:08 -0700
Subject: 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.
---
 .../src/ui/components/messages/DiffRenderer.tsx    | 57 +++++++++++++++++++++-
 1 file changed, 55 insertions(+), 2 deletions(-)

(limited to 'packages/cli/src/ui/components/messages/DiffRenderer.tsx')

diff --git a/packages/cli/src/ui/components/messages/DiffRenderer.tsx b/packages/cli/src/ui/components/messages/DiffRenderer.tsx
index 4d196e6d..a9afeca3 100644
--- a/packages/cli/src/ui/components/messages/DiffRenderer.tsx
+++ b/packages/cli/src/ui/components/messages/DiffRenderer.tsx
@@ -8,6 +8,7 @@ import React from 'react';
 import { Box, Text } from 'ink';
 import { Colors } from '../../colors.js';
 import crypto from 'crypto';
+import { colorizeCode } from '../../utils/CodeColorizer.js';
 
 interface DiffLine {
   type: 'add' | 'del' | 'context' | 'hunk' | 'other';
@@ -104,6 +105,42 @@ export const DiffRenderer: React.FC<DiffRendererProps> = ({
 
   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
   const normalizedLines = parsedLines.map((line) => ({
     ...line,
@@ -137,11 +174,11 @@ export const DiffRenderer: React.FC<DiffRendererProps> = ({
   if (!isFinite(baseIndentation)) {
     baseIndentation = 0;
   }
-  // --- End Modification ---
 
   const key = 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 (
     <Box flexDirection="column" key={key}>
       {/* Iterate over the lines that should be displayed (already normalized) */}
@@ -193,3 +230,19 @@ export const DiffRenderer: React.FC<DiffRendererProps> = ({
     </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
+};
-- 
cgit v1.2.3

