|
1 | | -<script lang="ts" generics="K"> |
| 1 | +<script lang="ts"> |
2 | 2 | import { |
3 | | - type ConciseDiffViewProps, |
4 | | - ConciseDiffViewState, |
| 3 | + TextDiffState, |
5 | 4 | type DiffViewerPatchHunk, |
6 | 5 | innerPatchLineTypeProps, |
7 | 6 | type InnerPatchLineTypeProps, |
8 | 7 | makeSearchSegments, |
9 | | - parseSinglePatch, |
10 | 8 | type PatchLine, |
11 | 9 | PatchLineType, |
12 | 10 | type PatchLineTypeProps, |
13 | 11 | patchLineTypeProps, |
14 | 12 | type SearchSegment, |
15 | | - } from "$lib/components/diff/concise-diff-view.svelte"; |
| 13 | + } from "$lib/components/diff/text-diff.svelte"; |
16 | 14 | import Spinner from "$lib/components/Spinner.svelte"; |
| 15 | + import { GlobalOptions } from "$lib/global-options.svelte"; |
| 16 | + import { MultiFileDiffViewerState, type TextFileDetails } from "$lib/diff-viewer.svelte"; |
17 | 17 | import { type MutableValue } from "$lib/util"; |
18 | 18 | import { box } from "svelte-toolbelt"; |
19 | 19 | import { boolAttr } from "runed"; |
20 | 20 |
|
21 | | - let { |
22 | | - rawPatchContent, |
23 | | - patch, |
24 | | - syntaxHighlighting = true, |
25 | | - syntaxHighlightingTheme, |
26 | | - omitPatchHeaderOnlyHunks = true, |
27 | | - wordDiffs = true, |
28 | | - lineWrap = true, |
29 | | - searchQuery, |
30 | | - searchMatchingLines, |
31 | | - activeSearchResult = -1, |
32 | | - jumpToSearchResult = $bindable(false), |
33 | | - cache, |
34 | | - cacheKey, |
35 | | - unresolvedSelection, |
36 | | - selection = $bindable(), |
37 | | - jumpToSelection = $bindable(false), |
38 | | - }: ConciseDiffViewProps<K> = $props(); |
| 21 | + interface Props { |
| 22 | + file: TextFileDetails; |
| 23 | + } |
39 | 24 |
|
40 | | - const uid = $props.id(); |
| 25 | + let { file }: Props = $props(); |
41 | 26 |
|
42 | | - const parsedPatch = $derived.by(() => { |
43 | | - if (rawPatchContent !== undefined) { |
44 | | - return parseSinglePatch(rawPatchContent); |
45 | | - } else if (patch !== undefined) { |
46 | | - return patch; |
47 | | - } |
48 | | - throw Error("Either rawPatchContent or patch must be provided"); |
49 | | - }); |
| 27 | + const uid = $props.id(); |
| 28 | + const viewer = MultiFileDiffViewerState.get(); |
| 29 | + const globalOptions = GlobalOptions.get(); |
50 | 30 |
|
51 | | - const view = new ConciseDiffViewState({ |
| 31 | + const view = new TextDiffState({ |
52 | 32 | rootElementId: uid, |
53 | 33 |
|
54 | | - patch: box.with(() => parsedPatch), |
55 | | - syntaxHighlighting: box.with(() => syntaxHighlighting), |
56 | | - syntaxHighlightingTheme: box.with(() => syntaxHighlightingTheme), |
57 | | - omitPatchHeaderOnlyHunks: box.with(() => omitPatchHeaderOnlyHunks), |
58 | | - wordDiffs: box.with(() => wordDiffs), |
| 34 | + patch: box.with(() => file.structuredPatch), |
| 35 | + syntaxHighlighting: box.with(() => globalOptions.syntaxHighlighting), |
| 36 | + syntaxHighlightingTheme: box.with(() => globalOptions.syntaxHighlightingTheme), |
| 37 | + omitPatchHeaderOnlyHunks: box.with(() => globalOptions.omitPatchHeaderOnlyHunks), |
| 38 | + wordDiffs: box.with(() => globalOptions.wordDiffs), |
59 | 39 |
|
60 | | - unresolvedSelection: box.with(() => unresolvedSelection), |
| 40 | + unresolvedSelection: box.with(() => viewer.getSelection(file)?.unresolvedLines), |
61 | 41 | selection: box.with( |
62 | | - () => selection, |
63 | | - (v) => (selection = v), |
| 42 | + () => viewer.getSelection(file)?.lines, |
| 43 | + (lines) => { |
| 44 | + if (lines === undefined && viewer.selection?.file === file) { |
| 45 | + viewer.clearSelection(); |
| 46 | + } else { |
| 47 | + viewer.setSelection(file, lines); |
| 48 | + } |
| 49 | + }, |
64 | 50 | ), |
65 | 51 |
|
66 | | - cache: box.with(() => cache), |
67 | | - cacheKey: box.with(() => cacheKey), |
| 52 | + cache: box.with(() => viewer.diffViewCache), |
68 | 53 | }); |
69 | 54 |
|
70 | 55 | function getDisplayLineNo(line: PatchLine, num: number | undefined) { |
|
76 | 61 | } |
77 | 62 |
|
78 | 63 | let searchSegments: Promise<SearchSegment[][][]> = $derived.by(async () => { |
79 | | - if (!searchQuery || !searchMatchingLines) { |
| 64 | + const searchQuery = viewer.searchQueryDebounced.current; |
| 65 | + if (!searchQuery) { |
80 | 66 | return []; |
81 | 67 | } |
82 | | - const matchingLines = await searchMatchingLines(); |
| 68 | + const matchingLines = (await viewer.searchResults).lines.get(file); |
83 | 69 | if (!matchingLines || matchingLines.length === 0) { |
84 | 70 | return []; |
85 | 71 | } |
|
117 | 103 | return segments; |
118 | 104 | }); |
119 | 105 |
|
| 106 | + let activeSearchResult = $derived(viewer.activeSearchResult?.file === file ? viewer.activeSearchResult.idx : -1); |
| 107 | + let selection = $derived(viewer.getSelection(file)?.lines); |
120 | 108 | let selectionMidpoint = $derived.by(() => { |
121 | 109 | if (!selection) return null; |
122 | 110 | const startIdx = selection.start.idx; |
|
125 | 113 | }); |
126 | 114 |
|
127 | 115 | let heightEstimateRem = $derived.by(() => { |
128 | | - if (!parsedPatch) return 1.25; |
129 | | - const rawLineCount = parsedPatch.hunks.reduce((sum, hunk) => sum + hunk.lines.length, 0); |
130 | | - const headerAndSpacerLines = parsedPatch.hunks.length * 2; |
| 116 | + const rawLineCount = file.structuredPatch.hunks.reduce((sum, hunk) => sum + hunk.lines.length, 0); |
| 117 | + const headerAndSpacerLines = file.structuredPatch.hunks.length * 2; |
131 | 118 | const totalLines = rawLineCount + headerAndSpacerLines; |
132 | 119 | return totalLines * 1.25; |
133 | 120 | }); |
|
163 | 150 | {#each lineSearchSegments as searchSegment, index (index)} |
164 | 151 | {#if searchSegment.highlighted}<span |
165 | 152 | {@attach (element) => { |
166 | | - if (jumpToSearchResult && searchSegment.id === activeSearchResult) { |
| 153 | + if (viewer.jumpToSearchResult && searchSegment.id === activeSearchResult) { |
167 | 154 | element.scrollIntoView({ block: "center", inline: "center" }); |
168 | | - jumpToSearchResult = false; |
| 155 | + viewer.jumpToSearchResult = false; |
169 | 156 | // See similar code & comment below around jumping to selections |
170 | 157 | //const scheduledJump = setTimeout(() => { |
171 | | - // jumpToSearchResult = false; |
| 158 | + // viewer.jumpToSearchResult = false; |
172 | 159 | // element.scrollIntoView({ block: "center", inline: "center" }); |
173 | 160 | //}, 200); |
174 | 161 | //return () => { |
175 | | - // jumpToSearchResult = false; |
| 162 | + // viewer.jumpToSearchResult = false; |
176 | 163 | // clearTimeout(scheduledJump); |
177 | 164 | //}; |
178 | 165 | } |
|
226 | 213 | data-selection-start={boolAttr(view.isSelectionStart(hunkIndex, lineIndex))} |
227 | 214 | data-selection-end={boolAttr(view.isSelectionEnd(hunkIndex, lineIndex))} |
228 | 215 | {@attach (element) => { |
229 | | - if (jumpToSelection && selection && selection.hunk === hunkIndex && selectionMidpoint === lineIndex) { |
| 216 | + if (viewer.jumpToSelection && selection && selection.hunk === hunkIndex && selectionMidpoint === lineIndex) { |
230 | 217 | element.scrollIntoView({ block: "center", inline: "center" }); |
231 | | - jumpToSelection = false; |
| 218 | + viewer.jumpToSelection = false; |
232 | 219 | // Need to schedule because otherwise the vlist rendering surrounding elements may shift things |
233 | 220 | // and cause the element to scroll to the wrong position |
234 | 221 | // This is not 100% reliable but is good enough for now |
235 | 222 | //const scheduledJump = setTimeout(() => { |
236 | | - // jumpToSelection = false; |
| 223 | + // viewer.jumpToSelection = false; |
237 | 224 | // element.scrollIntoView({ block: "center", inline: "center" }); |
238 | 225 | //}, 200); |
239 | 226 | //return () => { |
240 | 227 | // if (scheduledJump) { |
241 | | - // jumpToSelection = false; |
| 228 | + // viewer.jumpToSelection = false; |
242 | 229 | // clearTimeout(scheduledJump); |
243 | 230 | // } |
244 | 231 | //}; |
|
261 | 248 | id={uid} |
262 | 249 | style={rootStyle} |
263 | 250 | class="diff-content text-patch-line w-full bg-[var(--editor-bg)] font-mono text-xs leading-[1.25rem] text-[var(--editor-fg)] selection:bg-[var(--select-bg)]" |
264 | | - data-wrap={lineWrap} |
| 251 | + data-wrap={globalOptions.lineWrap} |
265 | 252 | > |
266 | 253 | {#each diffViewerPatch.hunks as hunk, hunkIndex (hunkIndex)} |
267 | 254 | {#each hunk.lines as line, lineIndex (lineIndex)} |
|
0 commit comments