Skip to content

Commit 0d8bb3a

Browse files
add rangeMode
1 parent 8621f3e commit 0d8bb3a

File tree

9 files changed

+286
-30
lines changed

9 files changed

+286
-30
lines changed

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,41 @@ setEnableFastDiffTemplate(true);
138138
|---------|----------|
139139
| ![default](https://raw.githubusercontent.com/MrWangJustToDo/git-diff-view/main/default.png) | ![fastdiff](https://raw.githubusercontent.com/MrWangJustToDo/git-diff-view/main/enableFastDiffTemplate.png) |
140140

141+
### Range Mode
142+
143+
Display only a portion of the diff by specifying a line number range. This is useful when you want to focus on a specific section of a large diff.
144+
145+
```tsx
146+
import { DiffFile, SplitSide } from "@git-diff-view/core";
147+
148+
// Create the full diff instance
149+
const diffFile = new DiffFile(/* ... */);
150+
diffFile.init();
151+
diffFile.buildSplitDiffLines();
152+
153+
// Generate a new instance containing only lines 10-50 (based on new file line numbers)
154+
const rangeDiffFile = diffFile.generateInstanceFromLineNumberRange(10, 50);
155+
156+
// Or specify which side's line numbers to use
157+
const rangeDiffFileOld = diffFile.generateInstanceFromLineNumberRange(10, 50, SplitSide.old);
158+
const rangeDiffFileNew = diffFile.generateInstanceFromLineNumberRange(10, 50, SplitSide.new);
159+
160+
<DiffView diffFile={rangeDiffFile} />
161+
```
162+
163+
**API: `generateInstanceFromLineNumberRange(start, end, side?)`**
164+
165+
| Parameter | Type | Default | Description |
166+
|-----------|------|---------|-------------|
167+
| `start` | `number` | - | Start line number (must be less than `end`) |
168+
| `end` | `number` | - | End line number |
169+
| `side` | `SplitSide` | `SplitSide.new` | Which file's line numbers to use (`SplitSide.old` or `SplitSide.new`) |
170+
171+
This method creates a new `DiffFile` instance containing only the diff lines within the specified range, making it ideal for:
172+
- Displaying relevant portions of large diffs
173+
- Creating focused code review experiences
174+
- Building paginated diff views
175+
141176
### Template Mode
142177

143178
Optimized rendering mode enabled by default for better performance. [Learn more](https://github.com/MrWangJustToDo/git-diff-view/blob/main/packages/core/src/parse/template.ts)

packages/core/index.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,14 @@ export declare class DiffFile {
204204
isFullMerge: boolean;
205205
};
206206
mergeBundle: (data: ReturnType<DiffFile["getBundle"]>, notifyUpdate?: boolean) => void;
207+
/**
208+
*
209+
* @param start start lineNumber
210+
* @param end end lineNumber
211+
* @param side range side
212+
* @returns a new instance can only show the content from start to end
213+
*/
214+
generateInstanceFromLineNumberRange: (start: number, end: number, side?: SplitSide) => DiffFile;
207215
_getHighlighterName: () => string;
208216
_getHighlighterType: () => string;
209217
_getIsPureDiffRender: () => boolean;

packages/core/src/diff-file.ts

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1682,14 +1682,80 @@ export class DiffFile {
16821682
}
16831683

16841684
if (__DEV__ && !data.hasInitRaw) {
1685-
console.error("[@git-diff-view/core] Invalid bundle data. Try calling the 'initRaw' function before merge / getBundle.");
1685+
console.error(
1686+
"[@git-diff-view/core] Invalid bundle data. Try calling the 'initRaw' function before merge / getBundle."
1687+
);
16861688
}
16871689

16881690
if (notifyUpdate) {
16891691
this.notifyAll();
16901692
}
16911693
};
16921694

1695+
/**
1696+
*
1697+
* @param start start lineNumber
1698+
* @param end end lineNumber
1699+
* @param side range side
1700+
* @returns a new instance can only show the content from start to end
1701+
*/
1702+
generateInstanceFromLineNumberRange = (start: number, end: number, side = SplitSide.new) => {
1703+
if (start >= end) {
1704+
if (__DEV__) {
1705+
console.warn(`[@git-diff-view/core] The end line must gt start line`);
1706+
}
1707+
1708+
return this;
1709+
}
1710+
1711+
const splitStart = this.getSplitLineIndexByLineNumber(start, side);
1712+
1713+
const splitEnd = this.getSplitLineIndexByLineNumber(end, side);
1714+
1715+
const unifiedStart = this.getUnifiedLineIndexByLineNumber(start, side);
1716+
1717+
const unifiedEnd = this.getUnifiedLineIndexByLineNumber(end, side);
1718+
1719+
const l: Array<SplitLineItem> = [];
1720+
const r: Array<SplitLineItem> = [];
1721+
const u: Array<UnifiedLineItem> = [];
1722+
1723+
for (let i = splitStart; i <= splitEnd; i++) {
1724+
const _l = this.getSplitLeftLine(i);
1725+
const _r = this.getSplitRightLine(i);
1726+
1727+
if (_l.value || _r.value) {
1728+
l.push({ ..._l, isHidden: false });
1729+
r.push({ ..._r, isHidden: false });
1730+
}
1731+
}
1732+
1733+
for (let i = unifiedStart; i <= unifiedEnd; i++) {
1734+
const _u = this.getUnifiedLine(i);
1735+
1736+
if (_u.value) {
1737+
u.push({ ..._u, isHidden: false });
1738+
}
1739+
}
1740+
1741+
const contextDiffFile = DiffFile.createInstance(
1742+
{},
1743+
{
1744+
...this.getBundle(),
1745+
composeByDiff: true,
1746+
splitHunkLines: {},
1747+
splitLeftLines: l,
1748+
splitRightLines: r,
1749+
splitLineLength: l.length,
1750+
unifiedHunkLines: {},
1751+
unifiedLines: u,
1752+
unifiedLineLength: u.length,
1753+
}
1754+
);
1755+
1756+
return contextDiffFile;
1757+
};
1758+
16931759
_getHighlighterName = () => this.#highlighterName || "";
16941760

16951761
_getHighlighterType = () => this.#highlighterType || "";

packages/file/index.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,14 @@ export declare class DiffFile {
202202
isFullMerge: boolean;
203203
};
204204
mergeBundle: (data: ReturnType<DiffFile["getBundle"]>, notifyUpdate?: boolean) => void;
205+
/**
206+
*
207+
* @param start start lineNumber
208+
* @param end end lineNumber
209+
* @param side range side
210+
* @returns a new instance can only show the content from start to end
211+
*/
212+
generateInstanceFromLineNumberRange: (start: number, end: number, side?: SplitSide) => DiffFile;
205213
_getHighlighterName: () => string;
206214
_getHighlighterType: () => string;
207215
_getIsPureDiffRender: () => boolean;

packages/react/index.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,14 @@ export declare class DiffFile {
202202
isFullMerge: boolean;
203203
};
204204
mergeBundle: (data: ReturnType<DiffFile["getBundle"]>, notifyUpdate?: boolean) => void;
205+
/**
206+
*
207+
* @param start start lineNumber
208+
* @param end end lineNumber
209+
* @param side range side
210+
* @returns a new instance can only show the content from start to end
211+
*/
212+
generateInstanceFromLineNumberRange: (start: number, end: number, side?: SplitSide) => DiffFile;
205213
_getHighlighterName: () => string;
206214
_getHighlighterType: () => string;
207215
_getIsPureDiffRender: () => boolean;

packages/solid/index.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,14 @@ export declare class DiffFile {
202202
isFullMerge: boolean;
203203
};
204204
mergeBundle: (data: ReturnType<DiffFile["getBundle"]>, notifyUpdate?: boolean) => void;
205+
/**
206+
*
207+
* @param start start lineNumber
208+
* @param end end lineNumber
209+
* @param side range side
210+
* @returns a new instance can only show the content from start to end
211+
*/
212+
generateInstanceFromLineNumberRange: (start: number, end: number, side?: SplitSide) => DiffFile;
205213
_getHighlighterName: () => string;
206214
_getHighlighterType: () => string;
207215
_getIsPureDiffRender: () => boolean;

packages/vue/index.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,14 @@ export declare class DiffFile {
202202
isFullMerge: boolean;
203203
};
204204
mergeBundle: (data: ReturnType<DiffFile["getBundle"]>, notifyUpdate?: boolean) => void;
205+
/**
206+
*
207+
* @param start start lineNumber
208+
* @param end end lineNumber
209+
* @param side range side
210+
* @returns a new instance can only show the content from start to end
211+
*/
212+
generateInstanceFromLineNumberRange: (start: number, end: number, side?: SplitSide) => DiffFile;
205213
_getHighlighterName: () => string;
206214
_getHighlighterType: () => string;
207215
_getIsPureDiffRender: () => boolean;

ui/react-example/src/views/PlayGround/PlayGroundFileDiff.tsx

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
getMaxLengthToIgnoreLineDiff,
77
changeMaxLengthToIgnoreLineDiff,
88
} from "@git-diff-view/react";
9-
import { useMantineColorScheme, Code, Button, Switch, NumberInput, Tooltip } from "@mantine/core";
9+
import { useMantineColorScheme, Code, Button, Switch, NumberInput, Tooltip, Divider, Collapse } from "@mantine/core";
1010
import { useCallbackRef } from "@mantine/hooks";
1111
import { debounce } from "lodash";
1212
import { useState, useCallback, useEffect, useMemo } from "react";
@@ -48,6 +48,14 @@ export const PlayGroundFileDiff = ({ onClick }: { onClick: () => void }) => {
4848

4949
const [ignoreWhiteSpace, setIgnoreWhiteSpace] = useState(false);
5050

51+
const [rangeDiffInstance, setRangeDiffInstance] = useState<DiffFile>();
52+
53+
const [rangeMode, setRangeMode] = useState(false);
54+
55+
const [start, setStart] = useState(0);
56+
57+
const [end, setEnd] = useState(0);
58+
5159
const [fastDiffTemplate, setFastDiffTemplate] = useState(getEnableFastDiffTemplate());
5260

5361
const [file1, setFile1] = useState(initialState.file1);
@@ -71,6 +79,10 @@ export const PlayGroundFileDiff = ({ onClick }: { onClick: () => void }) => {
7179

7280
const setDiffInstanceCb = useCallback(
7381
debounce((lang1, lang2, file1, file2, ignoreWhiteSpace) => {
82+
setRangeMode(false);
83+
setStart(0);
84+
setEnd(0);
85+
setRangeDiffInstance(undefined);
7486
if (!file1 && !file2) {
7587
setDiffInstance(undefined);
7688
return;
@@ -103,6 +115,12 @@ export const PlayGroundFileDiff = ({ onClick }: { onClick: () => void }) => {
103115
reloadDiffInstance();
104116
}, [fastDiffTemplate]);
105117

118+
useEffect(() => {
119+
if (diffInstance && start && end) {
120+
setRangeDiffInstance(diffInstance.generateInstanceFromLineNumberRange(start, end));
121+
}
122+
}, [diffInstance, start, end]);
123+
106124
const handleShare = useCallback(async () => {
107125
try {
108126
const success = await copyToClipboard(window.location.href);
@@ -121,11 +139,32 @@ export const PlayGroundFileDiff = ({ onClick }: { onClick: () => void }) => {
121139

122140
return (
123141
<div className="m-auto mb-[1em] mt-[1em] w-[90%]">
142+
<div className="flex items-center gap-x-6">
143+
<Button onClick={onClick}>Go to `Git diff` mode</Button>
144+
<Tooltip
145+
label={
146+
shareStatus === "copied"
147+
? "Copied!"
148+
: shareStatus === "error"
149+
? "Failed to copy"
150+
: "Copy share URL to clipboard"
151+
}
152+
>
153+
<Button
154+
variant={shareStatus === "copied" ? "filled" : "outline"}
155+
color={shareStatus === "error" ? "red" : shareStatus === "copied" ? "green" : undefined}
156+
onClick={handleShare}
157+
>
158+
{shareStatus === "copied" ? "Copied!" : shareStatus === "error" ? "Error" : "Share"}
159+
</Button>
160+
</Tooltip>
161+
</div>
162+
<Divider className="my-2" />
124163
<h2 className="flex flex-wrap gap-x-8 gap-y-4 text-[24px]">
125164
<span>
126-
<Code className="text-[24px]">File diff</Code> mode
165+
<Code className="text-[24px] font-medium">File diff</Code> mode
127166
</span>
128-
<div className="inline-flex gap-x-2 text-[14px]">
167+
{/* <div className="inline-flex gap-x-2 text-[14px]">
129168
<Button onClick={onClick}>Go to `Git diff` mode</Button>
130169
<Tooltip
131170
label={
@@ -144,7 +183,7 @@ export const PlayGroundFileDiff = ({ onClick }: { onClick: () => void }) => {
144183
{shareStatus === "copied" ? "Copied!" : shareStatus === "error" ? "Error" : "Share"}
145184
</Button>
146185
</Tooltip>
147-
</div>
186+
</div> */}
148187
<div className="inline-flex items-center gap-x-4">
149188
<Switch
150189
checked={ignoreWhiteSpace}
@@ -156,6 +195,11 @@ export const PlayGroundFileDiff = ({ onClick }: { onClick: () => void }) => {
156195
onChange={(e) => setFastDiffTemplate(e.target.checked)}
157196
label="Fast Diff Template (better line diff)"
158197
/>
198+
<Switch
199+
checked={rangeMode}
200+
onChange={(e) => setRangeMode(e.target.checked)}
201+
label="RangeMode (show part of diff)"
202+
/>
159203
<Tooltip label="Ignore line diff when line length over this value">
160204
<NumberInput
161205
value={getMaxLengthToIgnoreLineDiff()}
@@ -169,6 +213,28 @@ export const PlayGroundFileDiff = ({ onClick }: { onClick: () => void }) => {
169213
</Tooltip>
170214
</div>
171215
</h2>
216+
<Collapse in={rangeMode}>
217+
<div className="flex items-center gap-x-6 py-2">
218+
<NumberInput
219+
value={start}
220+
min={0}
221+
label="Range Start"
222+
max={diffInstance?.splitLineLength}
223+
onChange={(n) => {
224+
setStart(Number(n));
225+
}}
226+
/>
227+
<NumberInput
228+
value={end}
229+
min={0}
230+
label="Range End"
231+
max={diffInstance?.splitLineLength}
232+
onChange={(n) => {
233+
setEnd(Number(n));
234+
}}
235+
/>
236+
</div>
237+
</Collapse>
172238
<div className="mt-[10px] flex gap-x-[10px]">
173239
<div className="flex w-[50%] flex-col gap-y-[10px]">
174240
<span className="border-color border-b p-[3px]">Lang: </span>
@@ -195,10 +261,10 @@ export const PlayGroundFileDiff = ({ onClick }: { onClick: () => void }) => {
195261
</div>
196262
</div>
197263

198-
{diffInstance ? (
264+
{(rangeMode ? rangeDiffInstance : diffInstance) ? (
199265
<DiffView<string>
200266
className="border-color mt-[10px] overflow-hidden rounded-[4px] border"
201-
diffFile={diffInstance}
267+
diffFile={(rangeMode ? rangeDiffInstance : diffInstance)!}
202268
diffViewFontSize={13}
203269
diffViewTheme={colorScheme === "dark" ? "dark" : "light"}
204270
diffViewHighlight={true}

0 commit comments

Comments
 (0)