@aaronshaf/ger
Version:
Gerrit CLI and SDK - A modern CLI tool and TypeScript SDK for Gerrit Code Review, built with Effect-TS
174 lines (147 loc) • 4.79 kB
text/typescript
import { colors } from './formatters'
import type { FileDiffContent } from '@/schemas/gerrit'
interface DiffStats {
additions: number
deletions: number
files: number
}
/**
* Format a unified diff for pretty human-readable output
*/
export const formatDiffPretty = (diffContent: string): string => {
if (!diffContent || typeof diffContent !== 'string') {
const emptyStats = { additions: 0, deletions: 0, files: 0 }
return formatDiffSummary(emptyStats) + '\n\n' + 'No diff content available'
}
const lines = diffContent.split('\n')
const formattedLines: string[] = []
let stats: DiffStats = { additions: 0, deletions: 0, files: 0 }
for (const line of lines) {
if (line.startsWith('diff --git')) {
stats.files++
// File header with colors
formattedLines.push(`${colors.bold}${colors.blue}${line}${colors.reset}`)
} else if (line.startsWith('index ')) {
// Index line
formattedLines.push(`${colors.dim}${line}${colors.reset}`)
} else if (line.startsWith('---')) {
// Old file marker
formattedLines.push(`${colors.red}${line}${colors.reset}`)
} else if (line.startsWith('+++')) {
// New file marker
formattedLines.push(`${colors.green}${line}${colors.reset}`)
} else if (line.startsWith('@@')) {
// Hunk header
formattedLines.push(`${colors.cyan}${line}${colors.reset}`)
} else if (line.startsWith('+') && !line.startsWith('+++')) {
// Added lines
stats.additions++
formattedLines.push(`${colors.green}${line}${colors.reset}`)
} else if (line.startsWith('-') && !line.startsWith('---')) {
// Removed lines
stats.deletions++
formattedLines.push(`${colors.red}${line}${colors.reset}`)
} else if (line.startsWith(' ')) {
// Context lines
formattedLines.push(`${colors.dim}${line}${colors.reset}`)
} else {
// Other lines (usually empty or metadata)
formattedLines.push(line)
}
}
// Add summary at the top
const summary = formatDiffSummary(stats)
return summary + '\n\n' + formattedLines.join('\n')
}
/**
* Format diff summary statistics
*/
export const formatDiffSummary = (stats: DiffStats): string => {
const { additions, deletions, files } = stats
const total = additions + deletions
let summary = `${colors.bold}Changes summary:${colors.reset} `
if (files > 0) {
summary += `${files} file${files !== 1 ? 's' : ''} changed`
}
if (additions > 0 || deletions > 0) {
if (files > 0) summary += ', '
if (additions > 0) {
summary += `${colors.green}+${additions} addition${additions !== 1 ? 's' : ''}${colors.reset}`
}
if (additions > 0 && deletions > 0) {
summary += ', '
}
if (deletions > 0) {
summary += `${colors.red}-${deletions} deletion${deletions !== 1 ? 's' : ''}${colors.reset}`
}
}
if (total === 0 && files === 0) {
summary += 'No changes detected'
}
return summary
}
/**
* Format a list of changed files for pretty output
*/
export const formatFilesList = (files: string[]): string => {
if (!files || files.length === 0) {
return 'No files changed'
}
const header = `${colors.bold}Changed files (${files.length}):${colors.reset}\n`
const fileList = files
.map((file) => {
// Simple file status indicators - we could enhance this if we had status info
return ` ${colors.blue}•${colors.reset} ${file}`
})
.join('\n')
return header + fileList
}
/**
* Extract diff statistics from unified diff content
*/
export const extractDiffStats = (diffContent: string): DiffStats => {
if (!diffContent || typeof diffContent !== 'string') {
return { additions: 0, deletions: 0, files: 0 }
}
const lines = diffContent.split('\n')
let additions = 0
let deletions = 0
let files = 0
for (const line of lines) {
if (line.startsWith('diff --git')) {
files++
} else if (line.startsWith('+') && !line.startsWith('+++')) {
additions++
} else if (line.startsWith('-') && !line.startsWith('---')) {
deletions++
}
}
return { additions, deletions, files }
}
export const convertToUnifiedDiff = (diff: FileDiffContent, filePath: string): string => {
const lines: string[] = []
if (diff.diff_header) {
lines.push(...diff.diff_header)
} else {
lines.push(`--- a/${filePath}`)
lines.push(`+++ b/${filePath}`)
}
for (const section of diff.content) {
if (section.ab) {
for (const line of section.ab) {
lines.push(` ${line}`)
}
}
if (section.a) {
for (const line of section.a) {
lines.push(`-${line}`)
}
}
if (section.b) {
for (const line of section.b) {
lines.push(`+${line}`)
}
}
}
return lines.join('\n')
}