Skip to content

Commit 5a673e0

Browse files
fix
1 parent 68b2252 commit 5a673e0

27 files changed

+966
-34
lines changed

packages/cli/package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,18 @@
3535
"cli diff component"
3636
],
3737
"dependencies": {
38-
"@git-diff-view/core": "^0.0.29",
38+
"@git-diff-view/core": "^0.0.30",
3939
"@types/hast": "^3.0.0",
4040
"highlight.js": "^11.11.0",
4141
"lowlight": "^3.3.0",
4242
"fast-diff": "^1.3.0",
43-
"reactivity-store": "^0.3.11"
43+
"reactivity-store": "^0.3.11",
44+
"use-sync-external-store": "^1.4.0"
4445
},
4546
"devDependencies": {
4647
"@my-react/react": "^0.3.15",
47-
"@my-react/react-terminal": "^0.0.8"
48+
"@my-react/react-terminal": "^0.0.8",
49+
"@types/react": "^19.1.8"
4850
},
4951
"peerDependencies": {
5052
"react": "^19.1.0",
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
/* eslint-disable max-lines */
2+
import { DiffLineType } from "@git-diff-view/core";
3+
import { memoFunc, getSymbol, NewLineSymbol } from "@git-diff-view/utils";
4+
import { Box, Text } from "ink";
5+
import * as React from "react";
6+
7+
import { diffAddContentHighlight, diffDelContentHighlight, GitHubDark, GitHubLight } from "./color";
8+
import { DiffNoNewLine } from "./DiffNoNewLine";
9+
10+
import type { DiffFile, DiffLine, File } from "@git-diff-view/core";
11+
12+
// for shiki highlighter
13+
export const getStyleObjectFromString = memoFunc((str: string) => {
14+
const re = { light: {}, dark: {} };
15+
if (!str) return re;
16+
str.split(";").forEach((el) => {
17+
const [property, value] = el.split(":");
18+
if (!property) return;
19+
if (property.trim()?.endsWith("light")) {
20+
re["light"]["color"] = value.trim();
21+
return;
22+
}
23+
if (property.trim()?.endsWith("dark")) {
24+
re["dark"]["color"] = value.trim();
25+
return;
26+
}
27+
});
28+
return re;
29+
});
30+
31+
// for lowlight highlighter
32+
export const getStyleFromClassName = memoFunc((className: string) => {
33+
const re = { light: {}, dark: {} };
34+
if (!className) return re;
35+
className.split(" ").forEach((name) => {
36+
const dark = GitHubDark[name] || {};
37+
const light = GitHubLight[name] || {};
38+
Object.assign(re.dark, dark);
39+
Object.assign(re.light, light);
40+
});
41+
return re;
42+
});
43+
44+
const DiffString = ({
45+
theme,
46+
rawLine,
47+
diffLine,
48+
operator,
49+
}: {
50+
theme: "light" | "dark";
51+
rawLine: string;
52+
diffLine?: DiffLine;
53+
operator?: "add" | "del";
54+
plainLine?: File["plainFile"][number];
55+
}) => {
56+
const changes = diffLine?.changes;
57+
58+
if (changes?.hasLineChange) {
59+
const isNewLineSymbolChanged = changes.newLineSymbol;
60+
61+
const range = changes.range;
62+
const str1 = rawLine.slice(0, range.location);
63+
const str2 = rawLine.slice(range.location, range.location + range.length);
64+
const str3 = rawLine.slice(range.location + range.length);
65+
const isLast = str2.includes("\n");
66+
const _str2 = isLast ? str2.replace("\n", "").replace("\r", "") : str2;
67+
return (
68+
<Box>
69+
<Box data-range-start={range.location} data-range-end={range.location + range.length}>
70+
<Text>{str1}</Text>
71+
<Text
72+
data-diff-highlight
73+
backgroundColor={
74+
operator === "add"
75+
? theme === "light"
76+
? diffAddContentHighlight.light
77+
: diffAddContentHighlight.dark
78+
: theme === "light"
79+
? diffDelContentHighlight.light
80+
: diffDelContentHighlight.dark
81+
}
82+
>
83+
{isLast ? (
84+
<>
85+
{_str2}
86+
<Text data-newline-symbol>{getSymbol(isNewLineSymbolChanged)}</Text>
87+
</>
88+
) : (
89+
str2
90+
)}
91+
</Text>
92+
<Text>{str3}</Text>
93+
</Box>
94+
{isNewLineSymbolChanged === NewLineSymbol.NEWLINE && <DiffNoNewLine />}
95+
</Box>
96+
);
97+
}
98+
99+
return (
100+
<Box>
101+
<Text>{rawLine}</Text>
102+
</Box>
103+
);
104+
};
105+
106+
const DiffSyntax = ({
107+
theme,
108+
rawLine,
109+
diffLine,
110+
operator,
111+
syntaxLine,
112+
}: {
113+
theme: "light" | "dark";
114+
rawLine: string;
115+
diffLine?: DiffLine;
116+
syntaxLine?: File["syntaxFile"][number];
117+
operator?: "add" | "del";
118+
}) => {
119+
if (!syntaxLine) {
120+
return <DiffString theme={theme} rawLine={rawLine} diffLine={diffLine} operator={operator} />;
121+
}
122+
123+
const changes = diffLine?.changes;
124+
125+
if (changes?.hasLineChange) {
126+
const isNewLineSymbolChanged = changes.newLineSymbol;
127+
128+
const range = changes.range;
129+
130+
return (
131+
<Box>
132+
<Box data-range-start={range.location} data-range-end={range.location + range.length}>
133+
{syntaxLine.nodeList?.map(({ node, wrapper }, index) => {
134+
const lowlightStyles = getStyleFromClassName(wrapper?.properties?.className?.join(" ") || "");
135+
const lowlightStyle = theme === "dark" ? lowlightStyles.dark : lowlightStyles.light;
136+
const shikiStyles = getStyleObjectFromString(wrapper?.properties?.style || "");
137+
const shikiStyle = theme === "dark" ? shikiStyles.dark : shikiStyles.light;
138+
if (node.endIndex < range.location || range.location + range.length < node.startIndex) {
139+
return (
140+
<Text
141+
key={index}
142+
data-start={node.startIndex}
143+
data-end={node.endIndex}
144+
{...lowlightStyle}
145+
{...shikiStyle}
146+
>
147+
{node.value}
148+
</Text>
149+
);
150+
} else {
151+
const index1 = range.location - node.startIndex;
152+
const index2 = index1 < 0 ? 0 : index1;
153+
const str1 = node.value.slice(0, index2);
154+
const str2 = node.value.slice(index2, index1 + range.length);
155+
const str3 = node.value.slice(index1 + range.length);
156+
const isLast = str2.includes("\n");
157+
const _str2 = isLast ? str2.replace("\n", "").replace("\r", "") : str2;
158+
return (
159+
<Text
160+
key={index}
161+
data-start={node.startIndex}
162+
data-end={node.endIndex}
163+
{...lowlightStyle}
164+
{...shikiStyle}
165+
>
166+
<Text>{str1}</Text>
167+
<Text
168+
data-diff-highlight
169+
backgroundColor={
170+
operator === "add"
171+
? theme === "light"
172+
? diffAddContentHighlight.light
173+
: diffAddContentHighlight.dark
174+
: theme === "light"
175+
? diffDelContentHighlight.light
176+
: diffDelContentHighlight.dark
177+
}
178+
>
179+
{isLast ? (
180+
<>
181+
{_str2}
182+
<Text data-newline-symbol>{getSymbol(isNewLineSymbolChanged)}</Text>
183+
</>
184+
) : (
185+
str2
186+
)}
187+
</Text>
188+
<Text>{str3}</Text>
189+
</Text>
190+
);
191+
}
192+
})}
193+
</Box>
194+
{isNewLineSymbolChanged === NewLineSymbol.NEWLINE && <DiffNoNewLine />}
195+
</Box>
196+
);
197+
}
198+
199+
return (
200+
<Box>
201+
{syntaxLine?.nodeList?.map(({ node, wrapper }, index) => {
202+
const lowlightStyles = getStyleFromClassName(wrapper?.properties?.className?.join(" ") || "");
203+
const lowlightStyle = theme === "dark" ? lowlightStyles.dark : lowlightStyles.light;
204+
const shikiStyles = getStyleObjectFromString(wrapper?.properties?.style || "");
205+
const shikiStyle = theme === "dark" ? shikiStyles.dark : shikiStyles.light;
206+
return (
207+
<Text key={index} data-start={node.startIndex} data-end={node.endIndex} {...lowlightStyle} {...shikiStyle}>
208+
{node.value}
209+
</Text>
210+
);
211+
})}
212+
</Box>
213+
);
214+
};
215+
216+
export const DiffContent = ({
217+
theme,
218+
diffLine,
219+
rawLine,
220+
plainLine,
221+
syntaxLine,
222+
enableHighlight,
223+
}: {
224+
rawLine: string;
225+
theme: "light" | "dark";
226+
plainLine?: File["plainFile"][number];
227+
syntaxLine?: File["syntaxFile"][number];
228+
diffLine?: DiffLine;
229+
diffFile: DiffFile;
230+
enableHighlight: boolean;
231+
}) => {
232+
const isAdded = diffLine?.type === DiffLineType.Add;
233+
234+
const isDelete = diffLine?.type === DiffLineType.Delete;
235+
236+
const isMaxLineLengthToIgnoreSyntax = syntaxLine?.nodeList?.length > 150;
237+
238+
return (
239+
<Box>
240+
<Text data-operator={isAdded ? "+" : isDelete ? "-" : undefined}>{isAdded ? "+" : isDelete ? "-" : " "}</Text>
241+
{enableHighlight && syntaxLine && !isMaxLineLengthToIgnoreSyntax ? (
242+
<DiffSyntax
243+
theme={theme}
244+
operator={isAdded ? "add" : isDelete ? "del" : undefined}
245+
rawLine={rawLine}
246+
diffLine={diffLine}
247+
syntaxLine={syntaxLine}
248+
/>
249+
) : (
250+
<DiffString
251+
theme={theme}
252+
operator={isAdded ? "add" : isDelete ? "del" : undefined}
253+
rawLine={rawLine}
254+
diffLine={diffLine}
255+
plainLine={plainLine}
256+
/>
257+
)}
258+
</Box>
259+
);
260+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { Text } from "ink";
2+
import * as React from "react";
3+
4+
export const DiffNoNewLine = () => {
5+
return <Text>\ No newline at end of file</Text>;
6+
};

0 commit comments

Comments
 (0)