@pierre/diffs 1.0.7 → 1.1.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/dist/components/File.d.ts +4 -2
  2. package/dist/components/File.d.ts.map +1 -1
  3. package/dist/components/File.js +80 -34
  4. package/dist/components/File.js.map +1 -1
  5. package/dist/components/FileDiff.d.ts +50 -28
  6. package/dist/components/FileDiff.d.ts.map +1 -1
  7. package/dist/components/FileDiff.js +220 -79
  8. package/dist/components/FileDiff.js.map +1 -1
  9. package/dist/components/FileStream.d.ts +1 -0
  10. package/dist/components/FileStream.d.ts.map +1 -1
  11. package/dist/components/FileStream.js +8 -4
  12. package/dist/components/FileStream.js.map +1 -1
  13. package/dist/constants.d.ts +8 -2
  14. package/dist/constants.d.ts.map +1 -1
  15. package/dist/constants.js +10 -1
  16. package/dist/constants.js.map +1 -1
  17. package/dist/index.d.ts +19 -10
  18. package/dist/index.js +14 -5
  19. package/dist/managers/LineSelectionManager.d.ts.map +1 -1
  20. package/dist/managers/LineSelectionManager.js +8 -9
  21. package/dist/managers/LineSelectionManager.js.map +1 -1
  22. package/dist/react/MultiFileDiff.js +2 -2
  23. package/dist/react/MultiFileDiff.js.map +1 -1
  24. package/dist/react/index.d.ts +2 -2
  25. package/dist/react/utils/renderDiffChildren.d.ts +4 -4
  26. package/dist/react/utils/renderDiffChildren.d.ts.map +1 -1
  27. package/dist/react/utils/renderDiffChildren.js +3 -3
  28. package/dist/react/utils/renderDiffChildren.js.map +1 -1
  29. package/dist/react/utils/useFileDiffInstance.js.map +1 -1
  30. package/dist/renderers/DiffHunksRenderer.d.ts +7 -6
  31. package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
  32. package/dist/renderers/DiffHunksRenderer.js +263 -337
  33. package/dist/renderers/DiffHunksRenderer.js.map +1 -1
  34. package/dist/renderers/FileRenderer.d.ts +1 -0
  35. package/dist/renderers/FileRenderer.d.ts.map +1 -1
  36. package/dist/renderers/FileRenderer.js +11 -4
  37. package/dist/renderers/FileRenderer.js.map +1 -1
  38. package/dist/ssr/index.d.ts +2 -2
  39. package/dist/style.js +1 -1
  40. package/dist/style.js.map +1 -1
  41. package/dist/types.d.ts +246 -42
  42. package/dist/types.d.ts.map +1 -1
  43. package/dist/utils/areDiffLineAnnotationsEqual.d.ts +7 -0
  44. package/dist/utils/areDiffLineAnnotationsEqual.d.ts.map +1 -0
  45. package/dist/utils/areDiffLineAnnotationsEqual.js +8 -0
  46. package/dist/utils/areDiffLineAnnotationsEqual.js.map +1 -0
  47. package/dist/utils/areHunkDataEqual.d.ts +7 -0
  48. package/dist/utils/areHunkDataEqual.d.ts.map +1 -0
  49. package/dist/utils/areHunkDataEqual.js +8 -0
  50. package/dist/utils/areHunkDataEqual.js.map +1 -0
  51. package/dist/utils/areLineAnnotationsEqual.d.ts +7 -0
  52. package/dist/utils/areLineAnnotationsEqual.d.ts.map +1 -0
  53. package/dist/utils/areLineAnnotationsEqual.js +8 -0
  54. package/dist/utils/areLineAnnotationsEqual.js.map +1 -0
  55. package/dist/utils/arePrePropertiesEqual.d.ts +7 -0
  56. package/dist/utils/arePrePropertiesEqual.d.ts.map +1 -0
  57. package/dist/utils/arePrePropertiesEqual.js +9 -0
  58. package/dist/utils/arePrePropertiesEqual.js.map +1 -0
  59. package/dist/utils/areRenderRangesEqual.d.ts +7 -0
  60. package/dist/utils/areRenderRangesEqual.d.ts.map +1 -0
  61. package/dist/utils/areRenderRangesEqual.js +9 -0
  62. package/dist/utils/areRenderRangesEqual.js.map +1 -0
  63. package/dist/utils/areVirtualWindowSpecsEqual.d.ts +7 -0
  64. package/dist/utils/areVirtualWindowSpecsEqual.d.ts.map +1 -0
  65. package/dist/utils/areVirtualWindowSpecsEqual.js +9 -0
  66. package/dist/utils/areVirtualWindowSpecsEqual.js.map +1 -0
  67. package/dist/utils/areWorkerStatsEqual.d.ts +8 -0
  68. package/dist/utils/areWorkerStatsEqual.d.ts.map +1 -0
  69. package/dist/utils/areWorkerStatsEqual.js +9 -0
  70. package/dist/utils/areWorkerStatsEqual.js.map +1 -0
  71. package/dist/utils/createTransformerWithState.js +1 -1
  72. package/dist/utils/createTransformerWithState.js.map +1 -1
  73. package/dist/utils/createWindowFromScrollPosition.d.ts +22 -0
  74. package/dist/utils/createWindowFromScrollPosition.d.ts.map +1 -0
  75. package/dist/utils/createWindowFromScrollPosition.js +26 -0
  76. package/dist/utils/createWindowFromScrollPosition.js.map +1 -0
  77. package/dist/utils/diffAcceptRejectHunk.js +36 -21
  78. package/dist/utils/diffAcceptRejectHunk.js.map +1 -1
  79. package/dist/utils/getOrCreateCodeNode.d.ts +14 -0
  80. package/dist/utils/getOrCreateCodeNode.d.ts.map +1 -0
  81. package/dist/utils/getOrCreateCodeNode.js +13 -0
  82. package/dist/utils/getOrCreateCodeNode.js.map +1 -0
  83. package/dist/utils/getTotalLineCountFromHunks.js +1 -1
  84. package/dist/utils/getTotalLineCountFromHunks.js.map +1 -1
  85. package/dist/utils/hast_utils.d.ts +2 -1
  86. package/dist/utils/hast_utils.d.ts.map +1 -1
  87. package/dist/utils/hast_utils.js +10 -1
  88. package/dist/utils/hast_utils.js.map +1 -1
  89. package/dist/utils/isDefaultRenderRange.d.ts +7 -0
  90. package/dist/utils/isDefaultRenderRange.d.ts.map +1 -0
  91. package/dist/utils/isDefaultRenderRange.js +8 -0
  92. package/dist/utils/isDefaultRenderRange.js.map +1 -0
  93. package/dist/utils/iterateOverDiff.d.ts +39 -0
  94. package/dist/utils/iterateOverDiff.d.ts.map +1 -0
  95. package/dist/utils/iterateOverDiff.js +356 -0
  96. package/dist/utils/iterateOverDiff.js.map +1 -0
  97. package/dist/utils/parseDiffFromFile.d.ts.map +1 -1
  98. package/dist/utils/parseDiffFromFile.js +8 -6
  99. package/dist/utils/parseDiffFromFile.js.map +1 -1
  100. package/dist/utils/parsePatchFiles.d.ts +15 -3
  101. package/dist/utils/parsePatchFiles.d.ts.map +1 -1
  102. package/dist/utils/parsePatchFiles.js +207 -158
  103. package/dist/utils/parsePatchFiles.js.map +1 -1
  104. package/dist/utils/processLine.js +4 -3
  105. package/dist/utils/processLine.js.map +1 -1
  106. package/dist/utils/renderDiffWithHighlighter.d.ts +7 -2
  107. package/dist/utils/renderDiffWithHighlighter.d.ts.map +1 -1
  108. package/dist/utils/renderDiffWithHighlighter.js +149 -227
  109. package/dist/utils/renderDiffWithHighlighter.js.map +1 -1
  110. package/dist/utils/setWrapperNodeProps.d.ts +3 -7
  111. package/dist/utils/setWrapperNodeProps.d.ts.map +1 -1
  112. package/dist/utils/setWrapperNodeProps.js +1 -1
  113. package/dist/utils/setWrapperNodeProps.js.map +1 -1
  114. package/dist/worker/WorkerPoolManager.d.ts +9 -2
  115. package/dist/worker/WorkerPoolManager.d.ts.map +1 -1
  116. package/dist/worker/WorkerPoolManager.js +124 -45
  117. package/dist/worker/WorkerPoolManager.js.map +1 -1
  118. package/dist/worker/types.d.ts +7 -0
  119. package/dist/worker/types.d.ts.map +1 -1
  120. package/dist/worker/worker-portable.js +634 -242
  121. package/dist/worker/worker-portable.js.map +1 -1
  122. package/dist/worker/worker.js +511 -231
  123. package/dist/worker/worker.js.map +1 -1
  124. package/package.json +20 -1
  125. package/dist/utils/createCodeNode.d.ts +0 -12
  126. package/dist/utils/createCodeNode.d.ts.map +0 -1
  127. package/dist/utils/createCodeNode.js +0 -12
  128. package/dist/utils/createCodeNode.js.map +0 -1
@@ -5,16 +5,20 @@ import { ResizeManager } from "../managers/ResizeManager.js";
5
5
  import { getLineAnnotationName } from "../utils/getLineAnnotationName.js";
6
6
  import { SVGSpriteSheet } from "../sprite.js";
7
7
  import { areFilesEqual } from "../utils/areFilesEqual.js";
8
+ import { arePrePropertiesEqual } from "../utils/arePrePropertiesEqual.js";
8
9
  import { createAnnotationWrapperNode } from "../utils/createAnnotationWrapperNode.js";
9
- import { createCodeNode } from "../utils/createCodeNode.js";
10
10
  import { createHoverContentNode } from "../utils/createHoverContentNode.js";
11
11
  import { createUnsafeCSSStyleNode } from "../utils/createUnsafeCSSStyleNode.js";
12
12
  import { wrapUnsafeCSS } from "../utils/cssWrappers.js";
13
+ import { getOrCreateCodeNode } from "../utils/getOrCreateCodeNode.js";
13
14
  import { prerenderHTMLIfNecessary } from "../utils/prerenderHTMLIfNecessary.js";
14
15
  import { setPreNodeProperties } from "../utils/setWrapperNodeProps.js";
15
16
  import { DiffsContainerLoaded } from "./web-components.js";
16
17
  import { ScrollSyncManager } from "../managers/ScrollSyncManager.js";
18
+ import { areRenderRangesEqual } from "../utils/areRenderRangesEqual.js";
17
19
  import { DiffHunksRenderer } from "../renderers/DiffHunksRenderer.js";
20
+ import { areDiffLineAnnotationsEqual } from "../utils/areDiffLineAnnotationsEqual.js";
21
+ import { areHunkDataEqual } from "../utils/areHunkDataEqual.js";
18
22
  import { parseDiffFromFile } from "../utils/parseDiffFromFile.js";
19
23
  import { toHtml } from "hast-util-to-html";
20
24
 
@@ -22,26 +26,33 @@ import { toHtml } from "hast-util-to-html";
22
26
  let instanceId = -1;
23
27
  var FileDiff = class {
24
28
  static LoadedCustomComponent = DiffsContainerLoaded;
25
- __id = ++instanceId;
29
+ __id = `file-diff:${++instanceId}`;
26
30
  fileContainer;
27
31
  spriteSVG;
28
32
  pre;
33
+ codeUnified;
34
+ codeDeletions;
35
+ codeAdditions;
29
36
  unsafeCSSStyle;
30
37
  hoverContent;
31
38
  headerElement;
32
39
  headerMetadata;
33
- customHunkElements = [];
40
+ separatorCache = /* @__PURE__ */ new Map();
34
41
  errorWrapper;
35
42
  hunksRenderer;
36
43
  resizeManager;
37
44
  scrollSyncManager;
38
45
  mouseEventManager;
39
46
  lineSelectionManager;
40
- annotationElements = [];
47
+ annotationCache = /* @__PURE__ */ new Map();
41
48
  lineAnnotations = [];
42
- oldFile;
43
- newFile;
49
+ deletionFile;
50
+ additionFile;
44
51
  fileDiff;
52
+ renderRange;
53
+ appliedPreAttributes;
54
+ lastRenderedHeaderHTML;
55
+ enabled = true;
45
56
  constructor(options = { theme: DEFAULT_THEMES }, workerManager, isContainerManaged = false) {
46
57
  this.options = options;
47
58
  this.workerManager = workerManager;
@@ -55,6 +66,7 @@ var FileDiff = class {
55
66
  this.mouseEventManager = new MouseEventManager("diff", pluckMouseEventOptions(options, typeof options.hunkSeparators === "function" || (options.hunkSeparators ?? "line-info") === "line-info" ? this.handleExpandHunk : void 0));
56
67
  this.lineSelectionManager = new LineSelectionManager(pluckLineSelectionOptions(options));
57
68
  this.workerManager?.subscribeToThemeChanges(this);
69
+ this.enabled = true;
58
70
  }
59
71
  handleHighlightRender = () => {
60
72
  this.rerender();
@@ -100,28 +112,46 @@ var FileDiff = class {
100
112
  setSelectedLines(range) {
101
113
  this.lineSelectionManager.setSelection(range);
102
114
  }
103
- cleanUp() {
104
- this.hunksRenderer.cleanUp();
115
+ cleanUp(recycle = false) {
105
116
  this.resizeManager.cleanUp();
106
117
  this.mouseEventManager.cleanUp();
107
118
  this.scrollSyncManager.cleanUp();
108
119
  this.lineSelectionManager.cleanUp();
109
120
  this.workerManager?.unsubscribeToThemeChanges(this);
110
- this.workerManager = void 0;
111
- this.fileDiff = void 0;
112
- this.oldFile = void 0;
113
- this.newFile = void 0;
121
+ this.renderRange = void 0;
114
122
  if (!this.isContainerManaged) this.fileContainer?.parentNode?.removeChild(this.fileContainer);
115
123
  if (this.fileContainer?.shadowRoot != null) this.fileContainer.shadowRoot.innerHTML = "";
116
124
  this.fileContainer = void 0;
117
- this.pre = void 0;
125
+ if (this.pre != null) {
126
+ this.pre.innerHTML = "";
127
+ this.pre = void 0;
128
+ }
129
+ this.codeUnified = void 0;
130
+ this.codeDeletions = void 0;
131
+ this.codeAdditions = void 0;
132
+ this.appliedPreAttributes = void 0;
118
133
  this.headerElement = void 0;
134
+ this.lastRenderedHeaderHTML = void 0;
119
135
  this.errorWrapper = void 0;
136
+ this.spriteSVG = void 0;
137
+ if (recycle) this.hunksRenderer.recycle();
138
+ else {
139
+ this.hunksRenderer.cleanUp();
140
+ this.workerManager = void 0;
141
+ this.fileDiff = void 0;
142
+ this.deletionFile = void 0;
143
+ this.additionFile = void 0;
144
+ }
145
+ this.enabled = false;
146
+ }
147
+ virtualizedSetup() {
148
+ this.enabled = true;
149
+ this.workerManager?.subscribeToThemeChanges(this);
120
150
  }
121
151
  hydrate(props) {
122
152
  const { fileContainer, prerenderedHTML } = props;
123
153
  prerenderHTMLIfNecessary(fileContainer, prerenderedHTML);
124
- for (const element of Array.from(fileContainer.shadowRoot?.children ?? [])) {
154
+ for (const element of fileContainer.shadowRoot?.children ?? []) {
125
155
  if (element instanceof SVGElement) {
126
156
  this.spriteSVG = element;
127
157
  continue;
@@ -129,6 +159,12 @@ var FileDiff = class {
129
159
  if (!(element instanceof HTMLElement)) continue;
130
160
  if (element instanceof HTMLPreElement) {
131
161
  this.pre = element;
162
+ for (const code of element.children) {
163
+ if (!(code instanceof HTMLElement) || code.tagName.toLowerCase() !== "code") continue;
164
+ if ("deletions" in code.dataset) this.codeDeletions = code;
165
+ if ("additions" in code.dataset) this.codeAdditions = code;
166
+ if ("unified" in code.dataset) this.codeUnified = code;
167
+ }
132
168
  continue;
133
169
  }
134
170
  if ("diffsHeader" in element.dataset) {
@@ -140,14 +176,15 @@ var FileDiff = class {
140
176
  continue;
141
177
  }
142
178
  }
179
+ if (this.pre != null) this.syncCodeNodesFromPre(this.pre);
143
180
  if (this.pre == null) this.render(props);
144
181
  else {
145
182
  const { lineAnnotations, oldFile, newFile, fileDiff } = props;
146
183
  this.fileContainer = fileContainer;
147
184
  delete this.pre.dataset.dehydrated;
148
185
  this.lineAnnotations = lineAnnotations ?? this.lineAnnotations;
149
- this.newFile = newFile;
150
- this.oldFile = oldFile;
186
+ this.additionFile = newFile;
187
+ this.deletionFile = oldFile;
151
188
  this.fileDiff = fileDiff ?? (oldFile != null && newFile != null ? parseDiffFromFile(oldFile, newFile) : void 0);
152
189
  this.hunksRenderer.hydrate(this.fileDiff);
153
190
  this.renderAnnotations();
@@ -157,17 +194,18 @@ var FileDiff = class {
157
194
  this.lineSelectionManager.setup(this.pre);
158
195
  if ((this.options.overflow ?? "scroll") === "scroll") {
159
196
  this.resizeManager.setup(this.pre);
160
- if ((this.options.diffStyle ?? "split") === "split") this.scrollSyncManager.setup(this.pre);
197
+ if ((this.options.diffStyle ?? "split") === "split") this.scrollSyncManager.setup(this.pre, this.codeDeletions, this.codeAdditions);
161
198
  }
162
199
  }
163
200
  }
164
201
  rerender() {
165
- if (this.fileDiff == null && this.newFile == null && this.oldFile == null) return;
202
+ if (!this.enabled || this.fileDiff == null && this.additionFile == null && this.deletionFile == null) return;
166
203
  this.render({
167
- oldFile: this.oldFile,
168
- newFile: this.newFile,
204
+ oldFile: this.deletionFile,
205
+ newFile: this.additionFile,
169
206
  fileDiff: this.fileDiff,
170
- forceRender: true
207
+ forceRender: true,
208
+ renderRange: this.renderRange
171
209
  });
172
210
  }
173
211
  handleExpandHunk = (hunkIndex, direction) => {
@@ -177,12 +215,14 @@ var FileDiff = class {
177
215
  this.hunksRenderer.expandHunk(hunkIndex, direction);
178
216
  this.rerender();
179
217
  }
180
- render({ oldFile, newFile, fileDiff, forceRender = false, lineAnnotations, fileContainer, containerWrapper }) {
181
- const filesDidChange = oldFile != null && newFile != null && (!areFilesEqual(oldFile, this.oldFile) || !areFilesEqual(newFile, this.newFile));
218
+ render({ oldFile, newFile, fileDiff, forceRender = false, lineAnnotations, fileContainer, containerWrapper, renderRange }) {
219
+ if (!this.enabled) throw new Error("FileDiff.render: attempting to call render after cleaned up");
220
+ const filesDidChange = oldFile != null && newFile != null && (!areFilesEqual(oldFile, this.deletionFile) || !areFilesEqual(newFile, this.additionFile));
182
221
  const annotationsChanged = lineAnnotations != null && (lineAnnotations.length > 0 || this.lineAnnotations.length > 0) ? lineAnnotations !== this.lineAnnotations : false;
183
- if (!forceRender && !annotationsChanged && (fileDiff != null && fileDiff === this.fileDiff || fileDiff == null && !filesDidChange)) return;
184
- this.oldFile = oldFile;
185
- this.newFile = newFile;
222
+ if (areRenderRangesEqual(renderRange, this.renderRange) && !forceRender && !annotationsChanged && (fileDiff != null && fileDiff === this.fileDiff || fileDiff == null && !filesDidChange)) return;
223
+ this.renderRange = renderRange;
224
+ this.deletionFile = oldFile;
225
+ this.additionFile = newFile;
186
226
  if (fileDiff != null) this.fileDiff = fileDiff;
187
227
  else if (oldFile != null && newFile != null && filesDidChange) this.fileDiff = parseDiffFromFile(oldFile, newFile);
188
228
  if (lineAnnotations != null) this.setLineAnnotations(lineAnnotations);
@@ -197,66 +237,116 @@ var FileDiff = class {
197
237
  if (this.headerElement != null) {
198
238
  this.headerElement.parentNode?.removeChild(this.headerElement);
199
239
  this.headerElement = void 0;
240
+ this.lastRenderedHeaderHTML = void 0;
200
241
  }
201
242
  }
202
243
  fileContainer = this.getOrCreateFileContainer(fileContainer, containerWrapper);
203
244
  try {
204
- const hunksResult = this.hunksRenderer.renderDiff(this.fileDiff);
245
+ const hunksResult = this.hunksRenderer.renderDiff(this.fileDiff, renderRange);
205
246
  if (hunksResult == null) {
206
247
  if (this.workerManager != null && !this.workerManager.isInitialized()) this.workerManager.initialize().then(() => this.rerender());
207
248
  return;
208
249
  }
209
250
  if (hunksResult.headerElement != null) this.applyHeaderToDOM(hunksResult.headerElement, fileContainer);
210
- const pre = this.getOrCreatePreNode(fileContainer);
211
- this.applyHunksToDOM(pre, hunksResult);
251
+ if (hunksResult.additionsAST != null || hunksResult.deletionsAST != null || hunksResult.unifiedAST != null) {
252
+ const pre = this.getOrCreatePreNode(fileContainer);
253
+ this.applyHunksToDOM(pre, hunksResult);
254
+ } else if (this.pre != null) {
255
+ this.pre.parentNode?.removeChild(this.pre);
256
+ this.pre = void 0;
257
+ }
212
258
  this.renderSeparators(hunksResult.hunkData);
213
259
  this.renderAnnotations();
214
260
  this.renderHoverUtility();
215
261
  } catch (error) {
216
- if (error instanceof Error) this.applyErrorToDOM(error, fileContainer);
262
+ if (error instanceof Error) {
263
+ console.error(error);
264
+ this.applyErrorToDOM(error, fileContainer);
265
+ }
217
266
  }
218
267
  }
219
268
  renderSeparators(hunkData) {
220
269
  const { hunkSeparators } = this.options;
221
- if (this.isContainerManaged || this.fileContainer == null || typeof hunkSeparators !== "function") return;
222
- for (const element of this.customHunkElements) element.parentNode?.removeChild(element);
223
- this.customHunkElements.length = 0;
270
+ if (this.isContainerManaged || this.fileContainer == null || typeof hunkSeparators !== "function") {
271
+ for (const { element } of this.separatorCache.values()) element.parentNode?.removeChild(element);
272
+ this.separatorCache.clear();
273
+ return;
274
+ }
275
+ const staleSeparators = new Map(this.separatorCache);
224
276
  for (const hunk of hunkData) {
225
- const element = document.createElement("div");
226
- element.style.display = "contents";
227
- element.slot = hunk.slotName;
228
- element.appendChild(hunkSeparators(hunk, this));
229
- this.fileContainer.appendChild(element);
230
- this.customHunkElements.push(element);
277
+ const id = hunk.slotName;
278
+ let cache = this.separatorCache.get(id);
279
+ if (cache == null || !areHunkDataEqual(hunk, cache.hunkData)) {
280
+ cache?.element.parentNode?.removeChild(cache.element);
281
+ const element = document.createElement("div");
282
+ element.style.display = "contents";
283
+ element.slot = hunk.slotName;
284
+ element.appendChild(hunkSeparators(hunk, this));
285
+ this.fileContainer.appendChild(element);
286
+ cache = {
287
+ element,
288
+ hunkData: hunk
289
+ };
290
+ this.separatorCache.set(id, cache);
291
+ }
292
+ staleSeparators.delete(id);
293
+ }
294
+ for (const [id, { element }] of staleSeparators.entries()) {
295
+ this.separatorCache.delete(id);
296
+ element.parentNode?.removeChild(element);
231
297
  }
232
298
  }
233
299
  renderAnnotations() {
234
- if (this.isContainerManaged || this.fileContainer == null) return;
235
- for (const element of this.annotationElements) element.parentNode?.removeChild(element);
236
- this.annotationElements.length = 0;
300
+ if (this.isContainerManaged || this.fileContainer == null) {
301
+ for (const { element } of this.annotationCache.values()) element.parentNode?.removeChild(element);
302
+ this.annotationCache.clear();
303
+ return;
304
+ }
305
+ const staleAnnotations = new Map(this.annotationCache);
237
306
  const { renderAnnotation } = this.options;
238
- if (renderAnnotation != null && this.lineAnnotations.length > 0) for (const annotation of this.lineAnnotations) {
239
- const content = renderAnnotation(annotation);
240
- if (content == null) continue;
241
- const el = createAnnotationWrapperNode(getLineAnnotationName(annotation));
242
- el.appendChild(content);
243
- this.annotationElements.push(el);
244
- this.fileContainer.appendChild(el);
307
+ if (renderAnnotation != null && this.lineAnnotations.length > 0) for (const [index, annotation] of this.lineAnnotations.entries()) {
308
+ const id = `${index}-${getLineAnnotationName(annotation)}`;
309
+ let cache = this.annotationCache.get(id);
310
+ if (cache == null || !areDiffLineAnnotationsEqual(annotation, cache.annotation)) {
311
+ cache?.element.parentElement?.removeChild(cache.element);
312
+ const content = renderAnnotation(annotation);
313
+ if (content == null) continue;
314
+ cache = {
315
+ element: createAnnotationWrapperNode(getLineAnnotationName(annotation)),
316
+ annotation
317
+ };
318
+ cache.element.appendChild(content);
319
+ this.fileContainer.appendChild(cache.element);
320
+ this.annotationCache.set(id, cache);
321
+ }
322
+ staleAnnotations.delete(id);
323
+ }
324
+ for (const [id, { element }] of staleAnnotations.entries()) {
325
+ this.annotationCache.delete(id);
326
+ element.parentNode?.removeChild(element);
245
327
  }
246
328
  }
247
329
  renderHoverUtility() {
248
330
  const { renderHoverUtility } = this.options;
249
331
  if (this.fileContainer == null || renderHoverUtility == null) return;
250
- if (this.hoverContent == null) {
251
- this.hoverContent = createHoverContentNode();
252
- this.fileContainer.appendChild(this.hoverContent);
253
- }
254
332
  const element = renderHoverUtility(this.mouseEventManager.getHoveredLine);
255
- this.hoverContent.innerHTML = "";
256
- if (element != null) this.hoverContent.appendChild(element);
333
+ if (element != null && this.hoverContent != null) return;
334
+ else if (element == null) {
335
+ this.hoverContent?.parentNode?.removeChild(this.hoverContent);
336
+ this.hoverContent = void 0;
337
+ return;
338
+ }
339
+ this.hoverContent = createHoverContentNode();
340
+ this.hoverContent.appendChild(element);
341
+ this.fileContainer.appendChild(this.hoverContent);
257
342
  }
258
343
  getOrCreateFileContainer(fileContainer, parentNode) {
344
+ const previousContainer = this.fileContainer;
259
345
  this.fileContainer = fileContainer ?? this.fileContainer ?? document.createElement(DIFFS_TAG_NAME);
346
+ if (previousContainer != null && previousContainer !== this.fileContainer) {
347
+ this.lastRenderedHeaderHTML = void 0;
348
+ this.headerElement = void 0;
349
+ }
260
350
  if (parentNode != null && this.fileContainer.parentNode !== parentNode) parentNode.appendChild(this.fileContainer);
261
351
  if (this.spriteSVG == null) {
262
352
  const fragment = document.createElement("div");
@@ -273,27 +363,50 @@ var FileDiff = class {
273
363
  return this.fileContainer;
274
364
  }
275
365
  getOrCreatePreNode(container) {
366
+ const shadowRoot = container.shadowRoot ?? container.attachShadow({ mode: "open" });
276
367
  if (this.pre == null) {
277
368
  this.pre = document.createElement("pre");
278
- container.shadowRoot?.appendChild(this.pre);
279
- } else if (this.pre.parentNode !== container) container.shadowRoot?.appendChild(this.pre);
369
+ this.appliedPreAttributes = void 0;
370
+ this.codeUnified = void 0;
371
+ this.codeDeletions = void 0;
372
+ this.codeAdditions = void 0;
373
+ shadowRoot.appendChild(this.pre);
374
+ } else if (this.pre.parentNode !== shadowRoot) {
375
+ shadowRoot.appendChild(this.pre);
376
+ this.appliedPreAttributes = void 0;
377
+ }
280
378
  return this.pre;
281
379
  }
380
+ syncCodeNodesFromPre(pre) {
381
+ this.codeUnified = void 0;
382
+ this.codeDeletions = void 0;
383
+ this.codeAdditions = void 0;
384
+ for (const child of Array.from(pre.children)) {
385
+ if (!(child instanceof HTMLElement)) continue;
386
+ if ("unified" in child.dataset) this.codeUnified = child;
387
+ else if ("deletions" in child.dataset) this.codeDeletions = child;
388
+ else if ("additions" in child.dataset) this.codeAdditions = child;
389
+ }
390
+ }
282
391
  applyHeaderToDOM(headerAST, container) {
283
392
  this.cleanupErrorWrapper();
284
- const tempDiv = document.createElement("div");
285
- tempDiv.innerHTML = toHtml(headerAST);
286
- const newHeader = tempDiv.firstElementChild;
287
- if (!(newHeader instanceof HTMLElement)) return;
288
- if (this.headerElement != null) container.shadowRoot?.replaceChild(newHeader, this.headerElement);
289
- else container.shadowRoot?.prepend(newHeader);
290
- this.headerElement = newHeader;
393
+ const headerHTML = toHtml(headerAST);
394
+ if (headerHTML !== this.lastRenderedHeaderHTML) {
395
+ const tempDiv = document.createElement("div");
396
+ tempDiv.innerHTML = headerHTML;
397
+ const newHeader = tempDiv.firstElementChild;
398
+ if (!(newHeader instanceof HTMLElement)) return;
399
+ if (this.headerElement != null) container.shadowRoot?.replaceChild(newHeader, this.headerElement);
400
+ else container.shadowRoot?.prepend(newHeader);
401
+ this.headerElement = newHeader;
402
+ this.lastRenderedHeaderHTML = headerHTML;
403
+ }
291
404
  if (this.isContainerManaged) return;
292
405
  const { renderHeaderMetadata } = this.options;
293
406
  if (this.headerMetadata != null) this.headerMetadata.parentNode?.removeChild(this.headerMetadata);
294
407
  const content = renderHeaderMetadata?.({
295
- oldFile: this.oldFile,
296
- newFile: this.newFile,
408
+ deletionFile: this.deletionFile,
409
+ additionFile: this.additionFile,
297
410
  fileDiff: this.fileDiff
298
411
  }) ?? void 0;
299
412
  if (content != null) {
@@ -317,25 +430,50 @@ var FileDiff = class {
317
430
  applyHunksToDOM(pre, result) {
318
431
  this.cleanupErrorWrapper();
319
432
  this.applyPreNodeAttributes(pre, result);
320
- pre.innerHTML = "";
433
+ let shouldReplace = false;
321
434
  let codeDeletions;
322
435
  let codeAdditions;
436
+ const codeElements = [];
323
437
  if (result.unifiedAST != null) {
324
- const codeUnified = createCodeNode({ columnType: "unified" });
325
- codeUnified.innerHTML = this.hunksRenderer.renderPartialHTML(result.unifiedAST);
326
- pre.appendChild(codeUnified);
438
+ shouldReplace = this.codeUnified == null || this.codeAdditions != null || this.codeDeletions != null;
439
+ this.codeDeletions = void 0;
440
+ this.codeAdditions = void 0;
441
+ if (result.unifiedAST.length > 0) {
442
+ this.codeUnified = getOrCreateCodeNode({
443
+ code: this.codeUnified,
444
+ columnType: "unified"
445
+ });
446
+ this.codeUnified.innerHTML = this.hunksRenderer.renderPartialHTML(result.unifiedAST);
447
+ codeElements.push(this.codeUnified);
448
+ } else this.codeUnified = void 0;
327
449
  } else {
328
450
  if (result.deletionsAST != null) {
329
- codeDeletions = createCodeNode({ columnType: "deletions" });
330
- codeDeletions.innerHTML = this.hunksRenderer.renderPartialHTML(result.deletionsAST);
331
- pre.appendChild(codeDeletions);
451
+ shouldReplace = this.codeDeletions == null || this.codeUnified != null;
452
+ this.codeUnified = void 0;
453
+ if (result.deletionsAST.length > 0) {
454
+ this.codeDeletions = getOrCreateCodeNode({
455
+ code: this.codeDeletions,
456
+ columnType: "deletions"
457
+ });
458
+ this.codeDeletions.innerHTML = this.hunksRenderer.renderPartialHTML(result.deletionsAST);
459
+ codeElements.push(this.codeDeletions);
460
+ } else this.codeDeletions = void 0;
332
461
  }
333
462
  if (result.additionsAST != null) {
334
- codeAdditions = createCodeNode({ columnType: "additions" });
335
- codeAdditions.innerHTML = this.hunksRenderer.renderPartialHTML(result.additionsAST);
336
- pre.appendChild(codeAdditions);
463
+ shouldReplace = shouldReplace || this.codeAdditions == null || this.codeUnified != null;
464
+ this.codeUnified = void 0;
465
+ if (result.additionsAST.length > 0) {
466
+ this.codeAdditions = getOrCreateCodeNode({
467
+ code: this.codeAdditions,
468
+ columnType: "additions"
469
+ });
470
+ this.codeAdditions.innerHTML = this.hunksRenderer.renderPartialHTML(result.additionsAST);
471
+ codeElements.push(this.codeAdditions);
472
+ } else this.codeAdditions = void 0;
337
473
  }
338
474
  }
475
+ if (codeElements.length === 0) pre.textContent = "";
476
+ else if (shouldReplace) pre.replaceChildren(...codeElements);
339
477
  this.injectUnsafeCSS();
340
478
  this.mouseEventManager.setup(pre);
341
479
  this.lineSelectionManager.setup(pre);
@@ -350,8 +488,7 @@ var FileDiff = class {
350
488
  }
351
489
  applyPreNodeAttributes(pre, { themeStyles, baseThemeType, additionsAST, deletionsAST, totalLines }) {
352
490
  const { diffIndicators = "bars", disableBackground = false, disableLineNumbers = false, overflow = "scroll", themeType = "system", diffStyle = "split" } = this.options;
353
- setPreNodeProperties({
354
- pre,
491
+ const preProperties = {
355
492
  diffIndicators,
356
493
  disableBackground,
357
494
  disableLineNumbers,
@@ -360,7 +497,10 @@ var FileDiff = class {
360
497
  themeStyles,
361
498
  themeType: baseThemeType ?? themeType,
362
499
  totalLines
363
- });
500
+ };
501
+ if (arePrePropertiesEqual(preProperties, this.appliedPreAttributes)) return;
502
+ setPreNodeProperties(pre, preProperties);
503
+ this.appliedPreAttributes = preProperties;
364
504
  }
365
505
  applyErrorToDOM(error, container) {
366
506
  this.cleanupErrorWrapper();
@@ -368,6 +508,7 @@ var FileDiff = class {
368
508
  pre.innerHTML = "";
369
509
  pre.parentNode?.removeChild(pre);
370
510
  this.pre = void 0;
511
+ this.appliedPreAttributes = void 0;
371
512
  const shadowRoot = container.shadowRoot ?? container.attachShadow({ mode: "open" });
372
513
  this.errorWrapper ??= document.createElement("div");
373
514
  this.errorWrapper.dataset.errorWrapper = "";