Update diff colors (#4747)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
This commit is contained in:
parent
e21b5c95aa
commit
2e28bb90a0
|
@ -38,6 +38,12 @@ export const Colors: ColorsTheme = {
|
|||
get AccentRed() {
|
||||
return themeManager.getActiveTheme().colors.AccentRed;
|
||||
},
|
||||
get DiffAdded() {
|
||||
return themeManager.getActiveTheme().colors.DiffAdded;
|
||||
},
|
||||
get DiffRemoved() {
|
||||
return themeManager.getActiveTheme().colors.DiffRemoved;
|
||||
},
|
||||
get Comment() {
|
||||
return themeManager.getActiveTheme().colors.Comment;
|
||||
},
|
||||
|
|
|
@ -279,18 +279,23 @@ export function ThemeDialog({
|
|||
>
|
||||
{colorizeCode(
|
||||
`# function
|
||||
-def fibonacci(n):
|
||||
- a, b = 0, 1
|
||||
- for _ in range(n):
|
||||
- a, b = b, a + b
|
||||
- return a`,
|
||||
def fibonacci(n):
|
||||
a, b = 0, 1
|
||||
for _ in range(n):
|
||||
a, b = b, a + b
|
||||
return a`,
|
||||
'python',
|
||||
codeBlockHeight,
|
||||
colorizeCodeWidth,
|
||||
)}
|
||||
<Box marginTop={1} />
|
||||
<DiffRenderer
|
||||
diffContent={`--- a/old_file.txt\n+++ b/new_file.txt\n@@ -1,6 +1,7 @@\n # function\n-def fibonacci(n):\n- a, b = 0, 1\n- for _ in range(n):\n- a, b = b, a + b\n- return a\n+def fibonacci(n):\n+ a, b = 0, 1\n+ for _ in range(n):\n+ a, b = b, a + b\n+ return a\n+\n+print(fibonacci(10))\n`}
|
||||
diffContent={`--- a/util.py
|
||||
+++ b/util.py
|
||||
@@ -1,2 +1,2 @@
|
||||
- print("Hello, " + name)
|
||||
+ print(f"Hello, {name}!")
|
||||
`}
|
||||
availableTerminalHeight={diffHeight}
|
||||
terminalWidth={colorizeCodeWidth}
|
||||
theme={previewTheme}
|
||||
|
|
|
@ -130,8 +130,8 @@ index 0000001..0000002 100644
|
|||
);
|
||||
const output = lastFrame();
|
||||
const lines = output!.split('\n');
|
||||
expect(lines[0]).toBe('1 - old line');
|
||||
expect(lines[1]).toBe('1 + new line');
|
||||
expect(lines[0]).toBe('1 - old line');
|
||||
expect(lines[1]).toBe('1 + new line');
|
||||
});
|
||||
|
||||
it('should handle diff with only header and no changes', () => {
|
||||
|
@ -253,35 +253,35 @@ index 123..789 100644
|
|||
{
|
||||
terminalWidth: 80,
|
||||
height: undefined,
|
||||
expected: `1 console.log('first hunk');
|
||||
2 - const oldVar = 1;
|
||||
2 + const newVar = 1;
|
||||
3 console.log('end of first hunk');
|
||||
expected: ` 1 console.log('first hunk');
|
||||
2 - const oldVar = 1;
|
||||
2 + const newVar = 1;
|
||||
3 console.log('end of first hunk');
|
||||
════════════════════════════════════════════════════════════════════════════════
|
||||
20 console.log('second hunk');
|
||||
21 - const anotherOld = 'test';
|
||||
21 + const anotherNew = 'test';
|
||||
22 console.log('end of second hunk');`,
|
||||
20 console.log('second hunk');
|
||||
21 - const anotherOld = 'test';
|
||||
21 + const anotherNew = 'test';
|
||||
22 console.log('end of second hunk');`,
|
||||
},
|
||||
{
|
||||
terminalWidth: 80,
|
||||
height: 6,
|
||||
expected: `... first 4 lines hidden ...
|
||||
════════════════════════════════════════════════════════════════════════════════
|
||||
20 console.log('second hunk');
|
||||
21 - const anotherOld = 'test';
|
||||
21 + const anotherNew = 'test';
|
||||
22 console.log('end of second hunk');`,
|
||||
20 console.log('second hunk');
|
||||
21 - const anotherOld = 'test';
|
||||
21 + const anotherNew = 'test';
|
||||
22 console.log('end of second hunk');`,
|
||||
},
|
||||
{
|
||||
terminalWidth: 30,
|
||||
height: 6,
|
||||
expected: `... first 10 lines hidden ...
|
||||
'test';
|
||||
21 + const anotherNew =
|
||||
'test';
|
||||
22 console.log('end of
|
||||
second hunk');`,
|
||||
;
|
||||
21 + const anotherNew = 'test'
|
||||
;
|
||||
22 console.log('end of
|
||||
second hunk');`,
|
||||
},
|
||||
])(
|
||||
'with terminalWidth $terminalWidth and height $height',
|
||||
|
@ -329,11 +329,11 @@ fileDiff Index: file.txt
|
|||
);
|
||||
const output = lastFrame();
|
||||
|
||||
expect(output).toEqual(`1 - const oldVar = 1;
|
||||
1 + const newVar = 1;
|
||||
expect(output).toEqual(` 1 - const oldVar = 1;
|
||||
1 + const newVar = 1;
|
||||
════════════════════════════════════════════════════════════════════════════════
|
||||
20 - const anotherOld = 'test';
|
||||
20 + const anotherNew = 'test';`);
|
||||
20 - const anotherOld = 'test';
|
||||
20 + const anotherNew = 'test';`);
|
||||
});
|
||||
|
||||
it('should correctly render a new file with no file extension correctly', () => {
|
||||
|
|
|
@ -8,7 +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';
|
||||
import { colorizeCode, colorizeLine } from '../../utils/CodeColorizer.js';
|
||||
import { MaxSizedBox } from '../shared/MaxSizedBox.js';
|
||||
|
||||
interface DiffLine {
|
||||
|
@ -157,7 +157,6 @@ export const DiffRenderer: React.FC<DiffRendererProps> = ({
|
|||
tabWidth,
|
||||
availableTerminalHeight,
|
||||
terminalWidth,
|
||||
theme,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -170,7 +169,6 @@ const renderDiffContent = (
|
|||
tabWidth = DEFAULT_TAB_WIDTH,
|
||||
availableTerminalHeight: number | undefined,
|
||||
terminalWidth: number,
|
||||
theme?: import('../../themes/theme.js').Theme,
|
||||
) => {
|
||||
// 1. Normalize whitespace (replace tabs with spaces) *before* further processing
|
||||
const normalizedLines = parsedLines.map((line) => ({
|
||||
|
@ -191,6 +189,18 @@ const renderDiffContent = (
|
|||
);
|
||||
}
|
||||
|
||||
const maxLineNumber = Math.max(
|
||||
0,
|
||||
...displayableLines.map((l) => l.oldLine ?? 0),
|
||||
...displayableLines.map((l) => l.newLine ?? 0),
|
||||
);
|
||||
const gutterWidth = Math.max(1, maxLineNumber.toString().length);
|
||||
|
||||
const fileExtension = filename?.split('.').pop() || null;
|
||||
const language = fileExtension
|
||||
? getLanguageFromExtension(fileExtension)
|
||||
: null;
|
||||
|
||||
// Calculate the minimum indentation across all displayable lines
|
||||
let baseIndentation = Infinity; // Start high to find the minimum
|
||||
for (const line of displayableLines) {
|
||||
|
@ -237,27 +247,25 @@ const renderDiffContent = (
|
|||
) {
|
||||
acc.push(
|
||||
<Box key={`gap-${index}`}>
|
||||
<Text wrap="truncate">{'═'.repeat(terminalWidth)}</Text>
|
||||
<Text wrap="truncate" color={Colors.Gray}>
|
||||
{'═'.repeat(terminalWidth)}
|
||||
</Text>
|
||||
</Box>,
|
||||
);
|
||||
}
|
||||
|
||||
const lineKey = `diff-line-${index}`;
|
||||
let gutterNumStr = '';
|
||||
let color: string | undefined = undefined;
|
||||
let prefixSymbol = ' ';
|
||||
let dim = false;
|
||||
|
||||
switch (line.type) {
|
||||
case 'add':
|
||||
gutterNumStr = (line.newLine ?? '').toString();
|
||||
color = theme?.colors?.AccentGreen || 'green';
|
||||
prefixSymbol = '+';
|
||||
lastLineNumber = line.newLine ?? null;
|
||||
break;
|
||||
case 'del':
|
||||
gutterNumStr = (line.oldLine ?? '').toString();
|
||||
color = theme?.colors?.AccentRed || 'red';
|
||||
prefixSymbol = '-';
|
||||
// For deletions, update lastLineNumber based on oldLine if it's advancing.
|
||||
// This helps manage gaps correctly if there are multiple consecutive deletions
|
||||
|
@ -268,7 +276,6 @@ const renderDiffContent = (
|
|||
break;
|
||||
case 'context':
|
||||
gutterNumStr = (line.newLine ?? '').toString();
|
||||
dim = true;
|
||||
prefixSymbol = ' ';
|
||||
lastLineNumber = line.newLine ?? null;
|
||||
break;
|
||||
|
@ -280,13 +287,26 @@ const renderDiffContent = (
|
|||
|
||||
acc.push(
|
||||
<Box key={lineKey} flexDirection="row">
|
||||
<Text color={Colors.Gray}>{gutterNumStr.padEnd(4)} </Text>
|
||||
<Text color={color} dimColor={dim}>
|
||||
{prefixSymbol}{' '}
|
||||
</Text>
|
||||
<Text color={color} dimColor={dim} wrap="wrap">
|
||||
{displayContent}
|
||||
<Text color={Colors.Gray}>
|
||||
{gutterNumStr.padStart(gutterWidth)}{' '}
|
||||
</Text>
|
||||
{line.type === 'context' ? (
|
||||
<>
|
||||
<Text>{prefixSymbol} </Text>
|
||||
<Text wrap="wrap">
|
||||
{colorizeLine(displayContent, language)}
|
||||
</Text>
|
||||
</>
|
||||
) : (
|
||||
<Text
|
||||
backgroundColor={
|
||||
line.type === 'add' ? Colors.DiffAdded : Colors.DiffRemoved
|
||||
}
|
||||
wrap="wrap"
|
||||
>
|
||||
{prefixSymbol} {colorizeLine(displayContent, language)}
|
||||
</Text>
|
||||
)}
|
||||
</Box>,
|
||||
);
|
||||
return acc;
|
||||
|
|
|
@ -17,6 +17,8 @@ const ansiLightColors: ColorsTheme = {
|
|||
AccentGreen: 'green',
|
||||
AccentYellow: 'orange',
|
||||
AccentRed: 'red',
|
||||
DiffAdded: '#E5F2E5',
|
||||
DiffRemoved: '#FFE5E5',
|
||||
Comment: 'gray',
|
||||
Gray: 'gray',
|
||||
GradientColors: ['blue', 'green'],
|
||||
|
|
|
@ -17,6 +17,8 @@ const ansiColors: ColorsTheme = {
|
|||
AccentGreen: 'green',
|
||||
AccentYellow: 'yellow',
|
||||
AccentRed: 'red',
|
||||
DiffAdded: '#003300',
|
||||
DiffRemoved: '#4D0000',
|
||||
Comment: 'gray',
|
||||
Gray: 'gray',
|
||||
GradientColors: ['cyan', 'green'],
|
||||
|
|
|
@ -17,6 +17,8 @@ const atomOneDarkColors: ColorsTheme = {
|
|||
AccentGreen: '#98c379',
|
||||
AccentYellow: '#e6c07b',
|
||||
AccentRed: '#e06c75',
|
||||
DiffAdded: '#39544E',
|
||||
DiffRemoved: '#562B2F',
|
||||
Comment: '#5c6370',
|
||||
Gray: '#5c6370',
|
||||
GradientColors: ['#61aeee', '#98c379'],
|
||||
|
|
|
@ -17,8 +17,10 @@ const ayuLightColors: ColorsTheme = {
|
|||
AccentGreen: '#86b300',
|
||||
AccentYellow: '#f2ae49',
|
||||
AccentRed: '#f07171',
|
||||
DiffAdded: '#C6EAD8',
|
||||
DiffRemoved: '#FFCCCC',
|
||||
Comment: '#ABADB1',
|
||||
Gray: '#CCCFD3',
|
||||
Gray: '#a6aaaf',
|
||||
GradientColors: ['#399ee6', '#86b300'],
|
||||
};
|
||||
|
||||
|
|
|
@ -17,8 +17,10 @@ const ayuDarkColors: ColorsTheme = {
|
|||
AccentGreen: '#AAD94C',
|
||||
AccentYellow: '#FFB454',
|
||||
AccentRed: '#F26D78',
|
||||
DiffAdded: '#293022',
|
||||
DiffRemoved: '#3D1215',
|
||||
Comment: '#646A71',
|
||||
Gray: '##3D4149',
|
||||
Gray: '#3D4149',
|
||||
GradientColors: ['#FFB454', '#F26D78'],
|
||||
};
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ const draculaColors: ColorsTheme = {
|
|||
AccentGreen: '#50fa7b',
|
||||
AccentYellow: '#f1fa8c',
|
||||
AccentRed: '#ff5555',
|
||||
DiffAdded: '#11431d',
|
||||
DiffRemoved: '#6e1818',
|
||||
Comment: '#6272a4',
|
||||
Gray: '#6272a4',
|
||||
GradientColors: ['#ff79c6', '#8be9fd'],
|
||||
|
|
|
@ -17,6 +17,8 @@ const githubDarkColors: ColorsTheme = {
|
|||
AccentGreen: '#85E89D',
|
||||
AccentYellow: '#FFAB70',
|
||||
AccentRed: '#F97583',
|
||||
DiffAdded: '#3C4636',
|
||||
DiffRemoved: '#502125',
|
||||
Comment: '#6A737D',
|
||||
Gray: '#6A737D',
|
||||
GradientColors: ['#79B8FF', '#85E89D'],
|
||||
|
|
|
@ -17,6 +17,8 @@ const githubLightColors: ColorsTheme = {
|
|||
AccentGreen: '#008080',
|
||||
AccentYellow: '#990073',
|
||||
AccentRed: '#d14',
|
||||
DiffAdded: '#C6EAD8',
|
||||
DiffRemoved: '#FFCCCC',
|
||||
Comment: '#998',
|
||||
Gray: '#999',
|
||||
GradientColors: ['#458', '#008080'],
|
||||
|
|
|
@ -17,6 +17,8 @@ const googleCodeColors: ColorsTheme = {
|
|||
AccentGreen: '#080',
|
||||
AccentYellow: '#660',
|
||||
AccentRed: '#800',
|
||||
DiffAdded: '#C6EAD8',
|
||||
DiffRemoved: '#FEDEDE',
|
||||
Comment: '#5f6368',
|
||||
Gray: lightTheme.Gray,
|
||||
GradientColors: ['#066', '#606'],
|
||||
|
|
|
@ -17,6 +17,8 @@ const noColorColorsTheme: ColorsTheme = {
|
|||
AccentGreen: '',
|
||||
AccentYellow: '',
|
||||
AccentRed: '',
|
||||
DiffAdded: '',
|
||||
DiffRemoved: '',
|
||||
Comment: '',
|
||||
Gray: '',
|
||||
};
|
||||
|
|
|
@ -22,6 +22,8 @@ const shadesOfPurpleColors: ColorsTheme = {
|
|||
AccentGreen: '#A5FF90', // Strings and many others
|
||||
AccentYellow: '#fad000', // Title, main yellow
|
||||
AccentRed: '#ff628c', // Error/deletion accent
|
||||
DiffAdded: '#383E45',
|
||||
DiffRemoved: '#572244',
|
||||
Comment: '#B362FF', // Comment color (same as AccentPurple)
|
||||
Gray: '#726c86', // Gray color
|
||||
GradientColors: ['#4d21fc', '#847ace', '#ff628c'],
|
||||
|
|
|
@ -23,10 +23,12 @@ const validCustomTheme: CustomTheme = {
|
|||
AccentPurple: '#8B5CF6',
|
||||
AccentCyan: '#06B6D4',
|
||||
AccentGreen: '#3CA84B',
|
||||
AccentYellow: '#D5A40A',
|
||||
AccentRed: '#DD4C4C',
|
||||
Comment: '#008000',
|
||||
Gray: '#B7BECC',
|
||||
AccentYellow: 'yellow',
|
||||
AccentRed: 'red',
|
||||
DiffAdded: 'green',
|
||||
DiffRemoved: 'red',
|
||||
Comment: 'gray',
|
||||
Gray: 'gray',
|
||||
};
|
||||
|
||||
describe('ThemeManager', () => {
|
||||
|
|
|
@ -20,6 +20,8 @@ export interface ColorsTheme {
|
|||
AccentGreen: string;
|
||||
AccentYellow: string;
|
||||
AccentRed: string;
|
||||
DiffAdded: string;
|
||||
DiffRemoved: string;
|
||||
Comment: string;
|
||||
Gray: string;
|
||||
GradientColors?: string[];
|
||||
|
@ -41,8 +43,10 @@ export const lightTheme: ColorsTheme = {
|
|||
AccentGreen: '#3CA84B',
|
||||
AccentYellow: '#D5A40A',
|
||||
AccentRed: '#DD4C4C',
|
||||
DiffAdded: '#C6EAD8',
|
||||
DiffRemoved: '#FFCCCC',
|
||||
Comment: '#008000',
|
||||
Gray: '#B7BECC',
|
||||
Gray: '#97a0b0',
|
||||
GradientColors: ['#4796E4', '#847ACE', '#C3677F'],
|
||||
};
|
||||
|
||||
|
@ -57,6 +61,8 @@ export const darkTheme: ColorsTheme = {
|
|||
AccentGreen: '#A6E3A1',
|
||||
AccentYellow: '#F9E2AF',
|
||||
AccentRed: '#F38BA8',
|
||||
DiffAdded: '#28350B',
|
||||
DiffRemoved: '#430000',
|
||||
Comment: '#6C7086',
|
||||
Gray: '#6C7086',
|
||||
GradientColors: ['#4796E4', '#847ACE', '#C3677F'],
|
||||
|
@ -73,6 +79,8 @@ export const ansiTheme: ColorsTheme = {
|
|||
AccentGreen: 'green',
|
||||
AccentYellow: 'yellow',
|
||||
AccentRed: 'red',
|
||||
DiffAdded: 'green',
|
||||
DiffRemoved: 'red',
|
||||
Comment: 'gray',
|
||||
Gray: 'gray',
|
||||
};
|
||||
|
@ -328,6 +336,8 @@ export function validateCustomTheme(customTheme: Partial<CustomTheme>): {
|
|||
'AccentGreen',
|
||||
'AccentYellow',
|
||||
'AccentRed',
|
||||
'DiffAdded',
|
||||
'DiffRemoved',
|
||||
'Comment',
|
||||
'Gray',
|
||||
];
|
||||
|
@ -352,6 +362,8 @@ export function validateCustomTheme(customTheme: Partial<CustomTheme>): {
|
|||
'AccentGreen',
|
||||
'AccentYellow',
|
||||
'AccentRed',
|
||||
'DiffAdded',
|
||||
'DiffRemoved',
|
||||
'Comment',
|
||||
'Gray',
|
||||
];
|
||||
|
|
|
@ -17,6 +17,8 @@ const xcodeColors: ColorsTheme = {
|
|||
AccentGreen: '#007400',
|
||||
AccentYellow: '#836C28',
|
||||
AccentRed: '#c41a16',
|
||||
DiffAdded: '#C6EAD8',
|
||||
DiffRemoved: '#FEDEDE',
|
||||
Comment: '#007400',
|
||||
Gray: '#c0c0c0',
|
||||
GradientColors: ['#1c00cf', '#007400'],
|
||||
|
|
|
@ -88,6 +88,34 @@ function renderHastNode(
|
|||
return null;
|
||||
}
|
||||
|
||||
function highlightAndRenderLine(
|
||||
line: string,
|
||||
language: string | null,
|
||||
theme: Theme,
|
||||
): React.ReactNode {
|
||||
try {
|
||||
const getHighlightedLine = () =>
|
||||
!language || !lowlight.registered(language)
|
||||
? lowlight.highlightAuto(line)
|
||||
: lowlight.highlight(language, line);
|
||||
|
||||
const renderedNode = renderHastNode(getHighlightedLine(), theme, undefined);
|
||||
|
||||
return renderedNode !== null ? renderedNode : line;
|
||||
} catch (_error) {
|
||||
return line;
|
||||
}
|
||||
}
|
||||
|
||||
export function colorizeLine(
|
||||
line: string,
|
||||
language: string | null,
|
||||
theme?: Theme,
|
||||
): React.ReactNode {
|
||||
const activeTheme = theme || themeManager.getActiveTheme();
|
||||
return highlightAndRenderLine(line, language, activeTheme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders syntax-highlighted code for Ink applications using a selected theme.
|
||||
*
|
||||
|
@ -123,11 +151,6 @@ export function colorizeCode(
|
|||
}
|
||||
}
|
||||
|
||||
const getHighlightedLines = (line: string) =>
|
||||
!language || !lowlight.registered(language)
|
||||
? lowlight.highlightAuto(line)
|
||||
: lowlight.highlight(language, line);
|
||||
|
||||
return (
|
||||
<MaxSizedBox
|
||||
maxHeight={availableHeight}
|
||||
|
@ -136,17 +159,19 @@ export function colorizeCode(
|
|||
overflowDirection="top"
|
||||
>
|
||||
{lines.map((line, index) => {
|
||||
const renderedNode = renderHastNode(
|
||||
getHighlightedLines(line),
|
||||
const contentToRender = highlightAndRenderLine(
|
||||
line,
|
||||
language,
|
||||
activeTheme,
|
||||
undefined,
|
||||
);
|
||||
|
||||
const contentToRender = renderedNode !== null ? renderedNode : line;
|
||||
return (
|
||||
<Box key={index}>
|
||||
<Text color={activeTheme.colors.Gray}>
|
||||
{`${String(index + 1 + hiddenLinesCount).padStart(padWidth, ' ')} `}
|
||||
{`${String(index + 1 + hiddenLinesCount).padStart(
|
||||
padWidth,
|
||||
' ',
|
||||
)} `}
|
||||
</Text>
|
||||
<Text color={activeTheme.defaultColor} wrap="wrap">
|
||||
{contentToRender}
|
||||
|
|
Loading…
Reference in New Issue