@git-diff-view/react 0.0.3 → 0.0.6

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 (27) hide show
  1. package/dist/cjs/index.development.js +1998 -346
  2. package/dist/cjs/index.development.js.map +1 -1
  3. package/dist/cjs/index.production.js +1989 -356
  4. package/dist/cjs/index.production.js.map +1 -1
  5. package/dist/css/diff-view.css +1 -1
  6. package/dist/esm/index.mjs +2003 -350
  7. package/dist/esm/index.mjs.map +1 -1
  8. package/dist/types/components/DiffContent.d.ts.map +1 -1
  9. package/dist/types/components/DiffSplitHunkLineNormal.d.ts +0 -4
  10. package/dist/types/components/DiffSplitHunkLineNormal.d.ts.map +1 -1
  11. package/dist/types/components/DiffSplitHunkLineWrap.d.ts +0 -3
  12. package/dist/types/components/DiffSplitHunkLineWrap.d.ts.map +1 -1
  13. package/dist/types/components/DiffSplitView.d.ts.map +1 -1
  14. package/dist/types/components/DiffSplitViewLineNormal.d.ts +9 -0
  15. package/dist/types/components/DiffSplitViewLineNormal.d.ts.map +1 -0
  16. package/dist/types/components/DiffSplitViewLineWrap.d.ts +7 -0
  17. package/dist/types/components/DiffSplitViewLineWrap.d.ts.map +1 -0
  18. package/dist/types/components/DiffSplitViewNormal.d.ts +1 -1
  19. package/dist/types/components/DiffSplitViewNormal.d.ts.map +1 -1
  20. package/dist/types/components/DiffSplitViewWrap.d.ts.map +1 -1
  21. package/dist/types/components/DiffUnifiedHunkLine.d.ts +0 -3
  22. package/dist/types/components/DiffUnifiedHunkLine.d.ts.map +1 -1
  23. package/dist/types/components/DiffUnifiedView.d.ts.map +1 -1
  24. package/dist/types/components/DiffView.d.ts +9 -3
  25. package/dist/types/components/DiffView.d.ts.map +1 -1
  26. package/package.json +4 -4
  27. package/readme.md +68 -1
@@ -25,6 +25,1447 @@ function _interopNamespaceDefault(e) {
25
25
 
26
26
  var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
27
27
 
28
+ /**
29
+ * @module LRUCache
30
+ */
31
+ const perf = typeof performance === 'object' &&
32
+ performance &&
33
+ typeof performance.now === 'function'
34
+ ? performance
35
+ : Date;
36
+ const warned = new Set();
37
+ /* c8 ignore start */
38
+ const PROCESS = (typeof process === 'object' && !!process ? process : {});
39
+ /* c8 ignore start */
40
+ const emitWarning = (msg, type, code, fn) => {
41
+ typeof PROCESS.emitWarning === 'function'
42
+ ? PROCESS.emitWarning(msg, type, code, fn)
43
+ : console.error(`[${code}] ${type}: ${msg}`);
44
+ };
45
+ let AC = globalThis.AbortController;
46
+ let AS = globalThis.AbortSignal;
47
+ /* c8 ignore start */
48
+ if (typeof AC === 'undefined') {
49
+ //@ts-ignore
50
+ AS = class AbortSignal {
51
+ onabort;
52
+ _onabort = [];
53
+ reason;
54
+ aborted = false;
55
+ addEventListener(_, fn) {
56
+ this._onabort.push(fn);
57
+ }
58
+ };
59
+ //@ts-ignore
60
+ AC = class AbortController {
61
+ constructor() {
62
+ warnACPolyfill();
63
+ }
64
+ signal = new AS();
65
+ abort(reason) {
66
+ if (this.signal.aborted)
67
+ return;
68
+ //@ts-ignore
69
+ this.signal.reason = reason;
70
+ //@ts-ignore
71
+ this.signal.aborted = true;
72
+ //@ts-ignore
73
+ for (const fn of this.signal._onabort) {
74
+ fn(reason);
75
+ }
76
+ this.signal.onabort?.(reason);
77
+ }
78
+ };
79
+ let printACPolyfillWarning = PROCESS.env?.LRU_CACHE_IGNORE_AC_WARNING !== '1';
80
+ const warnACPolyfill = () => {
81
+ if (!printACPolyfillWarning)
82
+ return;
83
+ printACPolyfillWarning = false;
84
+ emitWarning('AbortController is not defined. If using lru-cache in ' +
85
+ 'node 14, load an AbortController polyfill from the ' +
86
+ '`node-abort-controller` package. A minimal polyfill is ' +
87
+ 'provided for use by LRUCache.fetch(), but it should not be ' +
88
+ 'relied upon in other contexts (eg, passing it to other APIs that ' +
89
+ 'use AbortController/AbortSignal might have undesirable effects). ' +
90
+ 'You may disable this with LRU_CACHE_IGNORE_AC_WARNING=1 in the env.', 'NO_ABORT_CONTROLLER', 'ENOTSUP', warnACPolyfill);
91
+ };
92
+ }
93
+ /* c8 ignore stop */
94
+ const shouldWarn = (code) => !warned.has(code);
95
+ const isPosInt = (n) => n && n === Math.floor(n) && n > 0 && isFinite(n);
96
+ /* c8 ignore start */
97
+ // This is a little bit ridiculous, tbh.
98
+ // The maximum array length is 2^32-1 or thereabouts on most JS impls.
99
+ // And well before that point, you're caching the entire world, I mean,
100
+ // that's ~32GB of just integers for the next/prev links, plus whatever
101
+ // else to hold that many keys and values. Just filling the memory with
102
+ // zeroes at init time is brutal when you get that big.
103
+ // But why not be complete?
104
+ // Maybe in the future, these limits will have expanded.
105
+ const getUintArray = (max) => !isPosInt(max)
106
+ ? null
107
+ : max <= Math.pow(2, 8)
108
+ ? Uint8Array
109
+ : max <= Math.pow(2, 16)
110
+ ? Uint16Array
111
+ : max <= Math.pow(2, 32)
112
+ ? Uint32Array
113
+ : max <= Number.MAX_SAFE_INTEGER
114
+ ? ZeroArray
115
+ : null;
116
+ /* c8 ignore stop */
117
+ class ZeroArray extends Array {
118
+ constructor(size) {
119
+ super(size);
120
+ this.fill(0);
121
+ }
122
+ }
123
+ class Stack {
124
+ heap;
125
+ length;
126
+ // private constructor
127
+ static #constructing = false;
128
+ static create(max) {
129
+ const HeapCls = getUintArray(max);
130
+ if (!HeapCls)
131
+ return [];
132
+ Stack.#constructing = true;
133
+ const s = new Stack(max, HeapCls);
134
+ Stack.#constructing = false;
135
+ return s;
136
+ }
137
+ constructor(max, HeapCls) {
138
+ /* c8 ignore start */
139
+ if (!Stack.#constructing) {
140
+ throw new TypeError('instantiate Stack using Stack.create(n)');
141
+ }
142
+ /* c8 ignore stop */
143
+ this.heap = new HeapCls(max);
144
+ this.length = 0;
145
+ }
146
+ push(n) {
147
+ this.heap[this.length++] = n;
148
+ }
149
+ pop() {
150
+ return this.heap[--this.length];
151
+ }
152
+ }
153
+ /**
154
+ * Default export, the thing you're using this module to get.
155
+ *
156
+ * All properties from the options object (with the exception of
157
+ * {@link OptionsBase.max} and {@link OptionsBase.maxSize}) are added as
158
+ * normal public members. (`max` and `maxBase` are read-only getters.)
159
+ * Changing any of these will alter the defaults for subsequent method calls,
160
+ * but is otherwise safe.
161
+ */
162
+ class LRUCache {
163
+ // properties coming in from the options of these, only max and maxSize
164
+ // really *need* to be protected. The rest can be modified, as they just
165
+ // set defaults for various methods.
166
+ #max;
167
+ #maxSize;
168
+ #dispose;
169
+ #disposeAfter;
170
+ #fetchMethod;
171
+ /**
172
+ * {@link LRUCache.OptionsBase.ttl}
173
+ */
174
+ ttl;
175
+ /**
176
+ * {@link LRUCache.OptionsBase.ttlResolution}
177
+ */
178
+ ttlResolution;
179
+ /**
180
+ * {@link LRUCache.OptionsBase.ttlAutopurge}
181
+ */
182
+ ttlAutopurge;
183
+ /**
184
+ * {@link LRUCache.OptionsBase.updateAgeOnGet}
185
+ */
186
+ updateAgeOnGet;
187
+ /**
188
+ * {@link LRUCache.OptionsBase.updateAgeOnHas}
189
+ */
190
+ updateAgeOnHas;
191
+ /**
192
+ * {@link LRUCache.OptionsBase.allowStale}
193
+ */
194
+ allowStale;
195
+ /**
196
+ * {@link LRUCache.OptionsBase.noDisposeOnSet}
197
+ */
198
+ noDisposeOnSet;
199
+ /**
200
+ * {@link LRUCache.OptionsBase.noUpdateTTL}
201
+ */
202
+ noUpdateTTL;
203
+ /**
204
+ * {@link LRUCache.OptionsBase.maxEntrySize}
205
+ */
206
+ maxEntrySize;
207
+ /**
208
+ * {@link LRUCache.OptionsBase.sizeCalculation}
209
+ */
210
+ sizeCalculation;
211
+ /**
212
+ * {@link LRUCache.OptionsBase.noDeleteOnFetchRejection}
213
+ */
214
+ noDeleteOnFetchRejection;
215
+ /**
216
+ * {@link LRUCache.OptionsBase.noDeleteOnStaleGet}
217
+ */
218
+ noDeleteOnStaleGet;
219
+ /**
220
+ * {@link LRUCache.OptionsBase.allowStaleOnFetchAbort}
221
+ */
222
+ allowStaleOnFetchAbort;
223
+ /**
224
+ * {@link LRUCache.OptionsBase.allowStaleOnFetchRejection}
225
+ */
226
+ allowStaleOnFetchRejection;
227
+ /**
228
+ * {@link LRUCache.OptionsBase.ignoreFetchAbort}
229
+ */
230
+ ignoreFetchAbort;
231
+ // computed properties
232
+ #size;
233
+ #calculatedSize;
234
+ #keyMap;
235
+ #keyList;
236
+ #valList;
237
+ #next;
238
+ #prev;
239
+ #head;
240
+ #tail;
241
+ #free;
242
+ #disposed;
243
+ #sizes;
244
+ #starts;
245
+ #ttls;
246
+ #hasDispose;
247
+ #hasFetchMethod;
248
+ #hasDisposeAfter;
249
+ /**
250
+ * Do not call this method unless you need to inspect the
251
+ * inner workings of the cache. If anything returned by this
252
+ * object is modified in any way, strange breakage may occur.
253
+ *
254
+ * These fields are private for a reason!
255
+ *
256
+ * @internal
257
+ */
258
+ static unsafeExposeInternals(c) {
259
+ return {
260
+ // properties
261
+ starts: c.#starts,
262
+ ttls: c.#ttls,
263
+ sizes: c.#sizes,
264
+ keyMap: c.#keyMap,
265
+ keyList: c.#keyList,
266
+ valList: c.#valList,
267
+ next: c.#next,
268
+ prev: c.#prev,
269
+ get head() {
270
+ return c.#head;
271
+ },
272
+ get tail() {
273
+ return c.#tail;
274
+ },
275
+ free: c.#free,
276
+ // methods
277
+ isBackgroundFetch: (p) => c.#isBackgroundFetch(p),
278
+ backgroundFetch: (k, index, options, context) => c.#backgroundFetch(k, index, options, context),
279
+ moveToTail: (index) => c.#moveToTail(index),
280
+ indexes: (options) => c.#indexes(options),
281
+ rindexes: (options) => c.#rindexes(options),
282
+ isStale: (index) => c.#isStale(index),
283
+ };
284
+ }
285
+ // Protected read-only members
286
+ /**
287
+ * {@link LRUCache.OptionsBase.max} (read-only)
288
+ */
289
+ get max() {
290
+ return this.#max;
291
+ }
292
+ /**
293
+ * {@link LRUCache.OptionsBase.maxSize} (read-only)
294
+ */
295
+ get maxSize() {
296
+ return this.#maxSize;
297
+ }
298
+ /**
299
+ * The total computed size of items in the cache (read-only)
300
+ */
301
+ get calculatedSize() {
302
+ return this.#calculatedSize;
303
+ }
304
+ /**
305
+ * The number of items stored in the cache (read-only)
306
+ */
307
+ get size() {
308
+ return this.#size;
309
+ }
310
+ /**
311
+ * {@link LRUCache.OptionsBase.fetchMethod} (read-only)
312
+ */
313
+ get fetchMethod() {
314
+ return this.#fetchMethod;
315
+ }
316
+ /**
317
+ * {@link LRUCache.OptionsBase.dispose} (read-only)
318
+ */
319
+ get dispose() {
320
+ return this.#dispose;
321
+ }
322
+ /**
323
+ * {@link LRUCache.OptionsBase.disposeAfter} (read-only)
324
+ */
325
+ get disposeAfter() {
326
+ return this.#disposeAfter;
327
+ }
328
+ constructor(options) {
329
+ const { max = 0, ttl, ttlResolution = 1, ttlAutopurge, updateAgeOnGet, updateAgeOnHas, allowStale, dispose, disposeAfter, noDisposeOnSet, noUpdateTTL, maxSize = 0, maxEntrySize = 0, sizeCalculation, fetchMethod, noDeleteOnFetchRejection, noDeleteOnStaleGet, allowStaleOnFetchRejection, allowStaleOnFetchAbort, ignoreFetchAbort, } = options;
330
+ if (max !== 0 && !isPosInt(max)) {
331
+ throw new TypeError('max option must be a nonnegative integer');
332
+ }
333
+ const UintArray = max ? getUintArray(max) : Array;
334
+ if (!UintArray) {
335
+ throw new Error('invalid max value: ' + max);
336
+ }
337
+ this.#max = max;
338
+ this.#maxSize = maxSize;
339
+ this.maxEntrySize = maxEntrySize || this.#maxSize;
340
+ this.sizeCalculation = sizeCalculation;
341
+ if (this.sizeCalculation) {
342
+ if (!this.#maxSize && !this.maxEntrySize) {
343
+ throw new TypeError('cannot set sizeCalculation without setting maxSize or maxEntrySize');
344
+ }
345
+ if (typeof this.sizeCalculation !== 'function') {
346
+ throw new TypeError('sizeCalculation set to non-function');
347
+ }
348
+ }
349
+ if (fetchMethod !== undefined &&
350
+ typeof fetchMethod !== 'function') {
351
+ throw new TypeError('fetchMethod must be a function if specified');
352
+ }
353
+ this.#fetchMethod = fetchMethod;
354
+ this.#hasFetchMethod = !!fetchMethod;
355
+ this.#keyMap = new Map();
356
+ this.#keyList = new Array(max).fill(undefined);
357
+ this.#valList = new Array(max).fill(undefined);
358
+ this.#next = new UintArray(max);
359
+ this.#prev = new UintArray(max);
360
+ this.#head = 0;
361
+ this.#tail = 0;
362
+ this.#free = Stack.create(max);
363
+ this.#size = 0;
364
+ this.#calculatedSize = 0;
365
+ if (typeof dispose === 'function') {
366
+ this.#dispose = dispose;
367
+ }
368
+ if (typeof disposeAfter === 'function') {
369
+ this.#disposeAfter = disposeAfter;
370
+ this.#disposed = [];
371
+ }
372
+ else {
373
+ this.#disposeAfter = undefined;
374
+ this.#disposed = undefined;
375
+ }
376
+ this.#hasDispose = !!this.#dispose;
377
+ this.#hasDisposeAfter = !!this.#disposeAfter;
378
+ this.noDisposeOnSet = !!noDisposeOnSet;
379
+ this.noUpdateTTL = !!noUpdateTTL;
380
+ this.noDeleteOnFetchRejection = !!noDeleteOnFetchRejection;
381
+ this.allowStaleOnFetchRejection = !!allowStaleOnFetchRejection;
382
+ this.allowStaleOnFetchAbort = !!allowStaleOnFetchAbort;
383
+ this.ignoreFetchAbort = !!ignoreFetchAbort;
384
+ // NB: maxEntrySize is set to maxSize if it's set
385
+ if (this.maxEntrySize !== 0) {
386
+ if (this.#maxSize !== 0) {
387
+ if (!isPosInt(this.#maxSize)) {
388
+ throw new TypeError('maxSize must be a positive integer if specified');
389
+ }
390
+ }
391
+ if (!isPosInt(this.maxEntrySize)) {
392
+ throw new TypeError('maxEntrySize must be a positive integer if specified');
393
+ }
394
+ this.#initializeSizeTracking();
395
+ }
396
+ this.allowStale = !!allowStale;
397
+ this.noDeleteOnStaleGet = !!noDeleteOnStaleGet;
398
+ this.updateAgeOnGet = !!updateAgeOnGet;
399
+ this.updateAgeOnHas = !!updateAgeOnHas;
400
+ this.ttlResolution =
401
+ isPosInt(ttlResolution) || ttlResolution === 0
402
+ ? ttlResolution
403
+ : 1;
404
+ this.ttlAutopurge = !!ttlAutopurge;
405
+ this.ttl = ttl || 0;
406
+ if (this.ttl) {
407
+ if (!isPosInt(this.ttl)) {
408
+ throw new TypeError('ttl must be a positive integer if specified');
409
+ }
410
+ this.#initializeTTLTracking();
411
+ }
412
+ // do not allow completely unbounded caches
413
+ if (this.#max === 0 && this.ttl === 0 && this.#maxSize === 0) {
414
+ throw new TypeError('At least one of max, maxSize, or ttl is required');
415
+ }
416
+ if (!this.ttlAutopurge && !this.#max && !this.#maxSize) {
417
+ const code = 'LRU_CACHE_UNBOUNDED';
418
+ if (shouldWarn(code)) {
419
+ warned.add(code);
420
+ const msg = 'TTL caching without ttlAutopurge, max, or maxSize can ' +
421
+ 'result in unbounded memory consumption.';
422
+ emitWarning(msg, 'UnboundedCacheWarning', code, LRUCache);
423
+ }
424
+ }
425
+ }
426
+ /**
427
+ * Return the remaining TTL time for a given entry key
428
+ */
429
+ getRemainingTTL(key) {
430
+ return this.#keyMap.has(key) ? Infinity : 0;
431
+ }
432
+ #initializeTTLTracking() {
433
+ const ttls = new ZeroArray(this.#max);
434
+ const starts = new ZeroArray(this.#max);
435
+ this.#ttls = ttls;
436
+ this.#starts = starts;
437
+ this.#setItemTTL = (index, ttl, start = perf.now()) => {
438
+ starts[index] = ttl !== 0 ? start : 0;
439
+ ttls[index] = ttl;
440
+ if (ttl !== 0 && this.ttlAutopurge) {
441
+ const t = setTimeout(() => {
442
+ if (this.#isStale(index)) {
443
+ this.delete(this.#keyList[index]);
444
+ }
445
+ }, ttl + 1);
446
+ // unref() not supported on all platforms
447
+ /* c8 ignore start */
448
+ if (t.unref) {
449
+ t.unref();
450
+ }
451
+ /* c8 ignore stop */
452
+ }
453
+ };
454
+ this.#updateItemAge = index => {
455
+ starts[index] = ttls[index] !== 0 ? perf.now() : 0;
456
+ };
457
+ this.#statusTTL = (status, index) => {
458
+ if (ttls[index]) {
459
+ const ttl = ttls[index];
460
+ const start = starts[index];
461
+ /* c8 ignore next */
462
+ if (!ttl || !start)
463
+ return;
464
+ status.ttl = ttl;
465
+ status.start = start;
466
+ status.now = cachedNow || getNow();
467
+ const age = status.now - start;
468
+ status.remainingTTL = ttl - age;
469
+ }
470
+ };
471
+ // debounce calls to perf.now() to 1s so we're not hitting
472
+ // that costly call repeatedly.
473
+ let cachedNow = 0;
474
+ const getNow = () => {
475
+ const n = perf.now();
476
+ if (this.ttlResolution > 0) {
477
+ cachedNow = n;
478
+ const t = setTimeout(() => (cachedNow = 0), this.ttlResolution);
479
+ // not available on all platforms
480
+ /* c8 ignore start */
481
+ if (t.unref) {
482
+ t.unref();
483
+ }
484
+ /* c8 ignore stop */
485
+ }
486
+ return n;
487
+ };
488
+ this.getRemainingTTL = key => {
489
+ const index = this.#keyMap.get(key);
490
+ if (index === undefined) {
491
+ return 0;
492
+ }
493
+ const ttl = ttls[index];
494
+ const start = starts[index];
495
+ if (!ttl || !start) {
496
+ return Infinity;
497
+ }
498
+ const age = (cachedNow || getNow()) - start;
499
+ return ttl - age;
500
+ };
501
+ this.#isStale = index => {
502
+ const s = starts[index];
503
+ const t = ttls[index];
504
+ return !!t && !!s && (cachedNow || getNow()) - s > t;
505
+ };
506
+ }
507
+ // conditionally set private methods related to TTL
508
+ #updateItemAge = () => { };
509
+ #statusTTL = () => { };
510
+ #setItemTTL = () => { };
511
+ /* c8 ignore stop */
512
+ #isStale = () => false;
513
+ #initializeSizeTracking() {
514
+ const sizes = new ZeroArray(this.#max);
515
+ this.#calculatedSize = 0;
516
+ this.#sizes = sizes;
517
+ this.#removeItemSize = index => {
518
+ this.#calculatedSize -= sizes[index];
519
+ sizes[index] = 0;
520
+ };
521
+ this.#requireSize = (k, v, size, sizeCalculation) => {
522
+ // provisionally accept background fetches.
523
+ // actual value size will be checked when they return.
524
+ if (this.#isBackgroundFetch(v)) {
525
+ return 0;
526
+ }
527
+ if (!isPosInt(size)) {
528
+ if (sizeCalculation) {
529
+ if (typeof sizeCalculation !== 'function') {
530
+ throw new TypeError('sizeCalculation must be a function');
531
+ }
532
+ size = sizeCalculation(v, k);
533
+ if (!isPosInt(size)) {
534
+ throw new TypeError('sizeCalculation return invalid (expect positive integer)');
535
+ }
536
+ }
537
+ else {
538
+ throw new TypeError('invalid size value (must be positive integer). ' +
539
+ 'When maxSize or maxEntrySize is used, sizeCalculation ' +
540
+ 'or size must be set.');
541
+ }
542
+ }
543
+ return size;
544
+ };
545
+ this.#addItemSize = (index, size, status) => {
546
+ sizes[index] = size;
547
+ if (this.#maxSize) {
548
+ const maxSize = this.#maxSize - sizes[index];
549
+ while (this.#calculatedSize > maxSize) {
550
+ this.#evict(true);
551
+ }
552
+ }
553
+ this.#calculatedSize += sizes[index];
554
+ if (status) {
555
+ status.entrySize = size;
556
+ status.totalCalculatedSize = this.#calculatedSize;
557
+ }
558
+ };
559
+ }
560
+ #removeItemSize = _i => { };
561
+ #addItemSize = (_i, _s, _st) => { };
562
+ #requireSize = (_k, _v, size, sizeCalculation) => {
563
+ if (size || sizeCalculation) {
564
+ throw new TypeError('cannot set size without setting maxSize or maxEntrySize on cache');
565
+ }
566
+ return 0;
567
+ };
568
+ *#indexes({ allowStale = this.allowStale } = {}) {
569
+ if (this.#size) {
570
+ for (let i = this.#tail; true;) {
571
+ if (!this.#isValidIndex(i)) {
572
+ break;
573
+ }
574
+ if (allowStale || !this.#isStale(i)) {
575
+ yield i;
576
+ }
577
+ if (i === this.#head) {
578
+ break;
579
+ }
580
+ else {
581
+ i = this.#prev[i];
582
+ }
583
+ }
584
+ }
585
+ }
586
+ *#rindexes({ allowStale = this.allowStale } = {}) {
587
+ if (this.#size) {
588
+ for (let i = this.#head; true;) {
589
+ if (!this.#isValidIndex(i)) {
590
+ break;
591
+ }
592
+ if (allowStale || !this.#isStale(i)) {
593
+ yield i;
594
+ }
595
+ if (i === this.#tail) {
596
+ break;
597
+ }
598
+ else {
599
+ i = this.#next[i];
600
+ }
601
+ }
602
+ }
603
+ }
604
+ #isValidIndex(index) {
605
+ return (index !== undefined &&
606
+ this.#keyMap.get(this.#keyList[index]) === index);
607
+ }
608
+ /**
609
+ * Return a generator yielding `[key, value]` pairs,
610
+ * in order from most recently used to least recently used.
611
+ */
612
+ *entries() {
613
+ for (const i of this.#indexes()) {
614
+ if (this.#valList[i] !== undefined &&
615
+ this.#keyList[i] !== undefined &&
616
+ !this.#isBackgroundFetch(this.#valList[i])) {
617
+ yield [this.#keyList[i], this.#valList[i]];
618
+ }
619
+ }
620
+ }
621
+ /**
622
+ * Inverse order version of {@link LRUCache.entries}
623
+ *
624
+ * Return a generator yielding `[key, value]` pairs,
625
+ * in order from least recently used to most recently used.
626
+ */
627
+ *rentries() {
628
+ for (const i of this.#rindexes()) {
629
+ if (this.#valList[i] !== undefined &&
630
+ this.#keyList[i] !== undefined &&
631
+ !this.#isBackgroundFetch(this.#valList[i])) {
632
+ yield [this.#keyList[i], this.#valList[i]];
633
+ }
634
+ }
635
+ }
636
+ /**
637
+ * Return a generator yielding the keys in the cache,
638
+ * in order from most recently used to least recently used.
639
+ */
640
+ *keys() {
641
+ for (const i of this.#indexes()) {
642
+ const k = this.#keyList[i];
643
+ if (k !== undefined &&
644
+ !this.#isBackgroundFetch(this.#valList[i])) {
645
+ yield k;
646
+ }
647
+ }
648
+ }
649
+ /**
650
+ * Inverse order version of {@link LRUCache.keys}
651
+ *
652
+ * Return a generator yielding the keys in the cache,
653
+ * in order from least recently used to most recently used.
654
+ */
655
+ *rkeys() {
656
+ for (const i of this.#rindexes()) {
657
+ const k = this.#keyList[i];
658
+ if (k !== undefined &&
659
+ !this.#isBackgroundFetch(this.#valList[i])) {
660
+ yield k;
661
+ }
662
+ }
663
+ }
664
+ /**
665
+ * Return a generator yielding the values in the cache,
666
+ * in order from most recently used to least recently used.
667
+ */
668
+ *values() {
669
+ for (const i of this.#indexes()) {
670
+ const v = this.#valList[i];
671
+ if (v !== undefined &&
672
+ !this.#isBackgroundFetch(this.#valList[i])) {
673
+ yield this.#valList[i];
674
+ }
675
+ }
676
+ }
677
+ /**
678
+ * Inverse order version of {@link LRUCache.values}
679
+ *
680
+ * Return a generator yielding the values in the cache,
681
+ * in order from least recently used to most recently used.
682
+ */
683
+ *rvalues() {
684
+ for (const i of this.#rindexes()) {
685
+ const v = this.#valList[i];
686
+ if (v !== undefined &&
687
+ !this.#isBackgroundFetch(this.#valList[i])) {
688
+ yield this.#valList[i];
689
+ }
690
+ }
691
+ }
692
+ /**
693
+ * Iterating over the cache itself yields the same results as
694
+ * {@link LRUCache.entries}
695
+ */
696
+ [Symbol.iterator]() {
697
+ return this.entries();
698
+ }
699
+ /**
700
+ * A String value that is used in the creation of the default string description of an object.
701
+ * Called by the built-in method Object.prototype.toString.
702
+ */
703
+ [Symbol.toStringTag] = 'LRUCache';
704
+ /**
705
+ * Find a value for which the supplied fn method returns a truthy value,
706
+ * similar to Array.find(). fn is called as fn(value, key, cache).
707
+ */
708
+ find(fn, getOptions = {}) {
709
+ for (const i of this.#indexes()) {
710
+ const v = this.#valList[i];
711
+ const value = this.#isBackgroundFetch(v)
712
+ ? v.__staleWhileFetching
713
+ : v;
714
+ if (value === undefined)
715
+ continue;
716
+ if (fn(value, this.#keyList[i], this)) {
717
+ return this.get(this.#keyList[i], getOptions);
718
+ }
719
+ }
720
+ }
721
+ /**
722
+ * Call the supplied function on each item in the cache, in order from
723
+ * most recently used to least recently used. fn is called as
724
+ * fn(value, key, cache). Does not update age or recenty of use.
725
+ * Does not iterate over stale values.
726
+ */
727
+ forEach(fn, thisp = this) {
728
+ for (const i of this.#indexes()) {
729
+ const v = this.#valList[i];
730
+ const value = this.#isBackgroundFetch(v)
731
+ ? v.__staleWhileFetching
732
+ : v;
733
+ if (value === undefined)
734
+ continue;
735
+ fn.call(thisp, value, this.#keyList[i], this);
736
+ }
737
+ }
738
+ /**
739
+ * The same as {@link LRUCache.forEach} but items are iterated over in
740
+ * reverse order. (ie, less recently used items are iterated over first.)
741
+ */
742
+ rforEach(fn, thisp = this) {
743
+ for (const i of this.#rindexes()) {
744
+ const v = this.#valList[i];
745
+ const value = this.#isBackgroundFetch(v)
746
+ ? v.__staleWhileFetching
747
+ : v;
748
+ if (value === undefined)
749
+ continue;
750
+ fn.call(thisp, value, this.#keyList[i], this);
751
+ }
752
+ }
753
+ /**
754
+ * Delete any stale entries. Returns true if anything was removed,
755
+ * false otherwise.
756
+ */
757
+ purgeStale() {
758
+ let deleted = false;
759
+ for (const i of this.#rindexes({ allowStale: true })) {
760
+ if (this.#isStale(i)) {
761
+ this.delete(this.#keyList[i]);
762
+ deleted = true;
763
+ }
764
+ }
765
+ return deleted;
766
+ }
767
+ /**
768
+ * Get the extended info about a given entry, to get its value, size, and
769
+ * TTL info simultaneously. Like {@link LRUCache#dump}, but just for a
770
+ * single key. Always returns stale values, if their info is found in the
771
+ * cache, so be sure to check for expired TTLs if relevant.
772
+ */
773
+ info(key) {
774
+ const i = this.#keyMap.get(key);
775
+ if (i === undefined)
776
+ return undefined;
777
+ const v = this.#valList[i];
778
+ const value = this.#isBackgroundFetch(v)
779
+ ? v.__staleWhileFetching
780
+ : v;
781
+ if (value === undefined)
782
+ return undefined;
783
+ const entry = { value };
784
+ if (this.#ttls && this.#starts) {
785
+ const ttl = this.#ttls[i];
786
+ const start = this.#starts[i];
787
+ if (ttl && start) {
788
+ const remain = ttl - (perf.now() - start);
789
+ entry.ttl = remain;
790
+ entry.start = Date.now();
791
+ }
792
+ }
793
+ if (this.#sizes) {
794
+ entry.size = this.#sizes[i];
795
+ }
796
+ return entry;
797
+ }
798
+ /**
799
+ * Return an array of [key, {@link LRUCache.Entry}] tuples which can be
800
+ * passed to cache.load()
801
+ */
802
+ dump() {
803
+ const arr = [];
804
+ for (const i of this.#indexes({ allowStale: true })) {
805
+ const key = this.#keyList[i];
806
+ const v = this.#valList[i];
807
+ const value = this.#isBackgroundFetch(v)
808
+ ? v.__staleWhileFetching
809
+ : v;
810
+ if (value === undefined || key === undefined)
811
+ continue;
812
+ const entry = { value };
813
+ if (this.#ttls && this.#starts) {
814
+ entry.ttl = this.#ttls[i];
815
+ // always dump the start relative to a portable timestamp
816
+ // it's ok for this to be a bit slow, it's a rare operation.
817
+ const age = perf.now() - this.#starts[i];
818
+ entry.start = Math.floor(Date.now() - age);
819
+ }
820
+ if (this.#sizes) {
821
+ entry.size = this.#sizes[i];
822
+ }
823
+ arr.unshift([key, entry]);
824
+ }
825
+ return arr;
826
+ }
827
+ /**
828
+ * Reset the cache and load in the items in entries in the order listed.
829
+ * Note that the shape of the resulting cache may be different if the
830
+ * same options are not used in both caches.
831
+ */
832
+ load(arr) {
833
+ this.clear();
834
+ for (const [key, entry] of arr) {
835
+ if (entry.start) {
836
+ // entry.start is a portable timestamp, but we may be using
837
+ // node's performance.now(), so calculate the offset, so that
838
+ // we get the intended remaining TTL, no matter how long it's
839
+ // been on ice.
840
+ //
841
+ // it's ok for this to be a bit slow, it's a rare operation.
842
+ const age = Date.now() - entry.start;
843
+ entry.start = perf.now() - age;
844
+ }
845
+ this.set(key, entry.value, entry);
846
+ }
847
+ }
848
+ /**
849
+ * Add a value to the cache.
850
+ *
851
+ * Note: if `undefined` is specified as a value, this is an alias for
852
+ * {@link LRUCache#delete}
853
+ */
854
+ set(k, v, setOptions = {}) {
855
+ if (v === undefined) {
856
+ this.delete(k);
857
+ return this;
858
+ }
859
+ const { ttl = this.ttl, start, noDisposeOnSet = this.noDisposeOnSet, sizeCalculation = this.sizeCalculation, status, } = setOptions;
860
+ let { noUpdateTTL = this.noUpdateTTL } = setOptions;
861
+ const size = this.#requireSize(k, v, setOptions.size || 0, sizeCalculation);
862
+ // if the item doesn't fit, don't do anything
863
+ // NB: maxEntrySize set to maxSize by default
864
+ if (this.maxEntrySize && size > this.maxEntrySize) {
865
+ if (status) {
866
+ status.set = 'miss';
867
+ status.maxEntrySizeExceeded = true;
868
+ }
869
+ // have to delete, in case something is there already.
870
+ this.delete(k);
871
+ return this;
872
+ }
873
+ let index = this.#size === 0 ? undefined : this.#keyMap.get(k);
874
+ if (index === undefined) {
875
+ // addition
876
+ index = (this.#size === 0
877
+ ? this.#tail
878
+ : this.#free.length !== 0
879
+ ? this.#free.pop()
880
+ : this.#size === this.#max
881
+ ? this.#evict(false)
882
+ : this.#size);
883
+ this.#keyList[index] = k;
884
+ this.#valList[index] = v;
885
+ this.#keyMap.set(k, index);
886
+ this.#next[this.#tail] = index;
887
+ this.#prev[index] = this.#tail;
888
+ this.#tail = index;
889
+ this.#size++;
890
+ this.#addItemSize(index, size, status);
891
+ if (status)
892
+ status.set = 'add';
893
+ noUpdateTTL = false;
894
+ }
895
+ else {
896
+ // update
897
+ this.#moveToTail(index);
898
+ const oldVal = this.#valList[index];
899
+ if (v !== oldVal) {
900
+ if (this.#hasFetchMethod && this.#isBackgroundFetch(oldVal)) {
901
+ oldVal.__abortController.abort(new Error('replaced'));
902
+ const { __staleWhileFetching: s } = oldVal;
903
+ if (s !== undefined && !noDisposeOnSet) {
904
+ if (this.#hasDispose) {
905
+ this.#dispose?.(s, k, 'set');
906
+ }
907
+ if (this.#hasDisposeAfter) {
908
+ this.#disposed?.push([s, k, 'set']);
909
+ }
910
+ }
911
+ }
912
+ else if (!noDisposeOnSet) {
913
+ if (this.#hasDispose) {
914
+ this.#dispose?.(oldVal, k, 'set');
915
+ }
916
+ if (this.#hasDisposeAfter) {
917
+ this.#disposed?.push([oldVal, k, 'set']);
918
+ }
919
+ }
920
+ this.#removeItemSize(index);
921
+ this.#addItemSize(index, size, status);
922
+ this.#valList[index] = v;
923
+ if (status) {
924
+ status.set = 'replace';
925
+ const oldValue = oldVal && this.#isBackgroundFetch(oldVal)
926
+ ? oldVal.__staleWhileFetching
927
+ : oldVal;
928
+ if (oldValue !== undefined)
929
+ status.oldValue = oldValue;
930
+ }
931
+ }
932
+ else if (status) {
933
+ status.set = 'update';
934
+ }
935
+ }
936
+ if (ttl !== 0 && !this.#ttls) {
937
+ this.#initializeTTLTracking();
938
+ }
939
+ if (this.#ttls) {
940
+ if (!noUpdateTTL) {
941
+ this.#setItemTTL(index, ttl, start);
942
+ }
943
+ if (status)
944
+ this.#statusTTL(status, index);
945
+ }
946
+ if (!noDisposeOnSet && this.#hasDisposeAfter && this.#disposed) {
947
+ const dt = this.#disposed;
948
+ let task;
949
+ while ((task = dt?.shift())) {
950
+ this.#disposeAfter?.(...task);
951
+ }
952
+ }
953
+ return this;
954
+ }
955
+ /**
956
+ * Evict the least recently used item, returning its value or
957
+ * `undefined` if cache is empty.
958
+ */
959
+ pop() {
960
+ try {
961
+ while (this.#size) {
962
+ const val = this.#valList[this.#head];
963
+ this.#evict(true);
964
+ if (this.#isBackgroundFetch(val)) {
965
+ if (val.__staleWhileFetching) {
966
+ return val.__staleWhileFetching;
967
+ }
968
+ }
969
+ else if (val !== undefined) {
970
+ return val;
971
+ }
972
+ }
973
+ }
974
+ finally {
975
+ if (this.#hasDisposeAfter && this.#disposed) {
976
+ const dt = this.#disposed;
977
+ let task;
978
+ while ((task = dt?.shift())) {
979
+ this.#disposeAfter?.(...task);
980
+ }
981
+ }
982
+ }
983
+ }
984
+ #evict(free) {
985
+ const head = this.#head;
986
+ const k = this.#keyList[head];
987
+ const v = this.#valList[head];
988
+ if (this.#hasFetchMethod && this.#isBackgroundFetch(v)) {
989
+ v.__abortController.abort(new Error('evicted'));
990
+ }
991
+ else if (this.#hasDispose || this.#hasDisposeAfter) {
992
+ if (this.#hasDispose) {
993
+ this.#dispose?.(v, k, 'evict');
994
+ }
995
+ if (this.#hasDisposeAfter) {
996
+ this.#disposed?.push([v, k, 'evict']);
997
+ }
998
+ }
999
+ this.#removeItemSize(head);
1000
+ // if we aren't about to use the index, then null these out
1001
+ if (free) {
1002
+ this.#keyList[head] = undefined;
1003
+ this.#valList[head] = undefined;
1004
+ this.#free.push(head);
1005
+ }
1006
+ if (this.#size === 1) {
1007
+ this.#head = this.#tail = 0;
1008
+ this.#free.length = 0;
1009
+ }
1010
+ else {
1011
+ this.#head = this.#next[head];
1012
+ }
1013
+ this.#keyMap.delete(k);
1014
+ this.#size--;
1015
+ return head;
1016
+ }
1017
+ /**
1018
+ * Check if a key is in the cache, without updating the recency of use.
1019
+ * Will return false if the item is stale, even though it is technically
1020
+ * in the cache.
1021
+ *
1022
+ * Will not update item age unless
1023
+ * {@link LRUCache.OptionsBase.updateAgeOnHas} is set.
1024
+ */
1025
+ has(k, hasOptions = {}) {
1026
+ const { updateAgeOnHas = this.updateAgeOnHas, status } = hasOptions;
1027
+ const index = this.#keyMap.get(k);
1028
+ if (index !== undefined) {
1029
+ const v = this.#valList[index];
1030
+ if (this.#isBackgroundFetch(v) &&
1031
+ v.__staleWhileFetching === undefined) {
1032
+ return false;
1033
+ }
1034
+ if (!this.#isStale(index)) {
1035
+ if (updateAgeOnHas) {
1036
+ this.#updateItemAge(index);
1037
+ }
1038
+ if (status) {
1039
+ status.has = 'hit';
1040
+ this.#statusTTL(status, index);
1041
+ }
1042
+ return true;
1043
+ }
1044
+ else if (status) {
1045
+ status.has = 'stale';
1046
+ this.#statusTTL(status, index);
1047
+ }
1048
+ }
1049
+ else if (status) {
1050
+ status.has = 'miss';
1051
+ }
1052
+ return false;
1053
+ }
1054
+ /**
1055
+ * Like {@link LRUCache#get} but doesn't update recency or delete stale
1056
+ * items.
1057
+ *
1058
+ * Returns `undefined` if the item is stale, unless
1059
+ * {@link LRUCache.OptionsBase.allowStale} is set.
1060
+ */
1061
+ peek(k, peekOptions = {}) {
1062
+ const { allowStale = this.allowStale } = peekOptions;
1063
+ const index = this.#keyMap.get(k);
1064
+ if (index === undefined ||
1065
+ (!allowStale && this.#isStale(index))) {
1066
+ return;
1067
+ }
1068
+ const v = this.#valList[index];
1069
+ // either stale and allowed, or forcing a refresh of non-stale value
1070
+ return this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
1071
+ }
1072
+ #backgroundFetch(k, index, options, context) {
1073
+ const v = index === undefined ? undefined : this.#valList[index];
1074
+ if (this.#isBackgroundFetch(v)) {
1075
+ return v;
1076
+ }
1077
+ const ac = new AC();
1078
+ const { signal } = options;
1079
+ // when/if our AC signals, then stop listening to theirs.
1080
+ signal?.addEventListener('abort', () => ac.abort(signal.reason), {
1081
+ signal: ac.signal,
1082
+ });
1083
+ const fetchOpts = {
1084
+ signal: ac.signal,
1085
+ options,
1086
+ context,
1087
+ };
1088
+ const cb = (v, updateCache = false) => {
1089
+ const { aborted } = ac.signal;
1090
+ const ignoreAbort = options.ignoreFetchAbort && v !== undefined;
1091
+ if (options.status) {
1092
+ if (aborted && !updateCache) {
1093
+ options.status.fetchAborted = true;
1094
+ options.status.fetchError = ac.signal.reason;
1095
+ if (ignoreAbort)
1096
+ options.status.fetchAbortIgnored = true;
1097
+ }
1098
+ else {
1099
+ options.status.fetchResolved = true;
1100
+ }
1101
+ }
1102
+ if (aborted && !ignoreAbort && !updateCache) {
1103
+ return fetchFail(ac.signal.reason);
1104
+ }
1105
+ // either we didn't abort, and are still here, or we did, and ignored
1106
+ const bf = p;
1107
+ if (this.#valList[index] === p) {
1108
+ if (v === undefined) {
1109
+ if (bf.__staleWhileFetching) {
1110
+ this.#valList[index] = bf.__staleWhileFetching;
1111
+ }
1112
+ else {
1113
+ this.delete(k);
1114
+ }
1115
+ }
1116
+ else {
1117
+ if (options.status)
1118
+ options.status.fetchUpdated = true;
1119
+ this.set(k, v, fetchOpts.options);
1120
+ }
1121
+ }
1122
+ return v;
1123
+ };
1124
+ const eb = (er) => {
1125
+ if (options.status) {
1126
+ options.status.fetchRejected = true;
1127
+ options.status.fetchError = er;
1128
+ }
1129
+ return fetchFail(er);
1130
+ };
1131
+ const fetchFail = (er) => {
1132
+ const { aborted } = ac.signal;
1133
+ const allowStaleAborted = aborted && options.allowStaleOnFetchAbort;
1134
+ const allowStale = allowStaleAborted || options.allowStaleOnFetchRejection;
1135
+ const noDelete = allowStale || options.noDeleteOnFetchRejection;
1136
+ const bf = p;
1137
+ if (this.#valList[index] === p) {
1138
+ // if we allow stale on fetch rejections, then we need to ensure that
1139
+ // the stale value is not removed from the cache when the fetch fails.
1140
+ const del = !noDelete || bf.__staleWhileFetching === undefined;
1141
+ if (del) {
1142
+ this.delete(k);
1143
+ }
1144
+ else if (!allowStaleAborted) {
1145
+ // still replace the *promise* with the stale value,
1146
+ // since we are done with the promise at this point.
1147
+ // leave it untouched if we're still waiting for an
1148
+ // aborted background fetch that hasn't yet returned.
1149
+ this.#valList[index] = bf.__staleWhileFetching;
1150
+ }
1151
+ }
1152
+ if (allowStale) {
1153
+ if (options.status && bf.__staleWhileFetching !== undefined) {
1154
+ options.status.returnedStale = true;
1155
+ }
1156
+ return bf.__staleWhileFetching;
1157
+ }
1158
+ else if (bf.__returned === bf) {
1159
+ throw er;
1160
+ }
1161
+ };
1162
+ const pcall = (res, rej) => {
1163
+ const fmp = this.#fetchMethod?.(k, v, fetchOpts);
1164
+ if (fmp && fmp instanceof Promise) {
1165
+ fmp.then(v => res(v === undefined ? undefined : v), rej);
1166
+ }
1167
+ // ignored, we go until we finish, regardless.
1168
+ // defer check until we are actually aborting,
1169
+ // so fetchMethod can override.
1170
+ ac.signal.addEventListener('abort', () => {
1171
+ if (!options.ignoreFetchAbort ||
1172
+ options.allowStaleOnFetchAbort) {
1173
+ res(undefined);
1174
+ // when it eventually resolves, update the cache.
1175
+ if (options.allowStaleOnFetchAbort) {
1176
+ res = v => cb(v, true);
1177
+ }
1178
+ }
1179
+ });
1180
+ };
1181
+ if (options.status)
1182
+ options.status.fetchDispatched = true;
1183
+ const p = new Promise(pcall).then(cb, eb);
1184
+ const bf = Object.assign(p, {
1185
+ __abortController: ac,
1186
+ __staleWhileFetching: v,
1187
+ __returned: undefined,
1188
+ });
1189
+ if (index === undefined) {
1190
+ // internal, don't expose status.
1191
+ this.set(k, bf, { ...fetchOpts.options, status: undefined });
1192
+ index = this.#keyMap.get(k);
1193
+ }
1194
+ else {
1195
+ this.#valList[index] = bf;
1196
+ }
1197
+ return bf;
1198
+ }
1199
+ #isBackgroundFetch(p) {
1200
+ if (!this.#hasFetchMethod)
1201
+ return false;
1202
+ const b = p;
1203
+ return (!!b &&
1204
+ b instanceof Promise &&
1205
+ b.hasOwnProperty('__staleWhileFetching') &&
1206
+ b.__abortController instanceof AC);
1207
+ }
1208
+ async fetch(k, fetchOptions = {}) {
1209
+ const {
1210
+ // get options
1211
+ allowStale = this.allowStale, updateAgeOnGet = this.updateAgeOnGet, noDeleteOnStaleGet = this.noDeleteOnStaleGet,
1212
+ // set options
1213
+ ttl = this.ttl, noDisposeOnSet = this.noDisposeOnSet, size = 0, sizeCalculation = this.sizeCalculation, noUpdateTTL = this.noUpdateTTL,
1214
+ // fetch exclusive options
1215
+ noDeleteOnFetchRejection = this.noDeleteOnFetchRejection, allowStaleOnFetchRejection = this.allowStaleOnFetchRejection, ignoreFetchAbort = this.ignoreFetchAbort, allowStaleOnFetchAbort = this.allowStaleOnFetchAbort, context, forceRefresh = false, status, signal, } = fetchOptions;
1216
+ if (!this.#hasFetchMethod) {
1217
+ if (status)
1218
+ status.fetch = 'get';
1219
+ return this.get(k, {
1220
+ allowStale,
1221
+ updateAgeOnGet,
1222
+ noDeleteOnStaleGet,
1223
+ status,
1224
+ });
1225
+ }
1226
+ const options = {
1227
+ allowStale,
1228
+ updateAgeOnGet,
1229
+ noDeleteOnStaleGet,
1230
+ ttl,
1231
+ noDisposeOnSet,
1232
+ size,
1233
+ sizeCalculation,
1234
+ noUpdateTTL,
1235
+ noDeleteOnFetchRejection,
1236
+ allowStaleOnFetchRejection,
1237
+ allowStaleOnFetchAbort,
1238
+ ignoreFetchAbort,
1239
+ status,
1240
+ signal,
1241
+ };
1242
+ let index = this.#keyMap.get(k);
1243
+ if (index === undefined) {
1244
+ if (status)
1245
+ status.fetch = 'miss';
1246
+ const p = this.#backgroundFetch(k, index, options, context);
1247
+ return (p.__returned = p);
1248
+ }
1249
+ else {
1250
+ // in cache, maybe already fetching
1251
+ const v = this.#valList[index];
1252
+ if (this.#isBackgroundFetch(v)) {
1253
+ const stale = allowStale && v.__staleWhileFetching !== undefined;
1254
+ if (status) {
1255
+ status.fetch = 'inflight';
1256
+ if (stale)
1257
+ status.returnedStale = true;
1258
+ }
1259
+ return stale ? v.__staleWhileFetching : (v.__returned = v);
1260
+ }
1261
+ // if we force a refresh, that means do NOT serve the cached value,
1262
+ // unless we are already in the process of refreshing the cache.
1263
+ const isStale = this.#isStale(index);
1264
+ if (!forceRefresh && !isStale) {
1265
+ if (status)
1266
+ status.fetch = 'hit';
1267
+ this.#moveToTail(index);
1268
+ if (updateAgeOnGet) {
1269
+ this.#updateItemAge(index);
1270
+ }
1271
+ if (status)
1272
+ this.#statusTTL(status, index);
1273
+ return v;
1274
+ }
1275
+ // ok, it is stale or a forced refresh, and not already fetching.
1276
+ // refresh the cache.
1277
+ const p = this.#backgroundFetch(k, index, options, context);
1278
+ const hasStale = p.__staleWhileFetching !== undefined;
1279
+ const staleVal = hasStale && allowStale;
1280
+ if (status) {
1281
+ status.fetch = isStale ? 'stale' : 'refresh';
1282
+ if (staleVal && isStale)
1283
+ status.returnedStale = true;
1284
+ }
1285
+ return staleVal ? p.__staleWhileFetching : (p.__returned = p);
1286
+ }
1287
+ }
1288
+ /**
1289
+ * Return a value from the cache. Will update the recency of the cache
1290
+ * entry found.
1291
+ *
1292
+ * If the key is not found, get() will return `undefined`.
1293
+ */
1294
+ get(k, getOptions = {}) {
1295
+ const { allowStale = this.allowStale, updateAgeOnGet = this.updateAgeOnGet, noDeleteOnStaleGet = this.noDeleteOnStaleGet, status, } = getOptions;
1296
+ const index = this.#keyMap.get(k);
1297
+ if (index !== undefined) {
1298
+ const value = this.#valList[index];
1299
+ const fetching = this.#isBackgroundFetch(value);
1300
+ if (status)
1301
+ this.#statusTTL(status, index);
1302
+ if (this.#isStale(index)) {
1303
+ if (status)
1304
+ status.get = 'stale';
1305
+ // delete only if not an in-flight background fetch
1306
+ if (!fetching) {
1307
+ if (!noDeleteOnStaleGet) {
1308
+ this.delete(k);
1309
+ }
1310
+ if (status && allowStale)
1311
+ status.returnedStale = true;
1312
+ return allowStale ? value : undefined;
1313
+ }
1314
+ else {
1315
+ if (status &&
1316
+ allowStale &&
1317
+ value.__staleWhileFetching !== undefined) {
1318
+ status.returnedStale = true;
1319
+ }
1320
+ return allowStale ? value.__staleWhileFetching : undefined;
1321
+ }
1322
+ }
1323
+ else {
1324
+ if (status)
1325
+ status.get = 'hit';
1326
+ // if we're currently fetching it, we don't actually have it yet
1327
+ // it's not stale, which means this isn't a staleWhileRefetching.
1328
+ // If it's not stale, and fetching, AND has a __staleWhileFetching
1329
+ // value, then that means the user fetched with {forceRefresh:true},
1330
+ // so it's safe to return that value.
1331
+ if (fetching) {
1332
+ return value.__staleWhileFetching;
1333
+ }
1334
+ this.#moveToTail(index);
1335
+ if (updateAgeOnGet) {
1336
+ this.#updateItemAge(index);
1337
+ }
1338
+ return value;
1339
+ }
1340
+ }
1341
+ else if (status) {
1342
+ status.get = 'miss';
1343
+ }
1344
+ }
1345
+ #connect(p, n) {
1346
+ this.#prev[n] = p;
1347
+ this.#next[p] = n;
1348
+ }
1349
+ #moveToTail(index) {
1350
+ // if tail already, nothing to do
1351
+ // if head, move head to next[index]
1352
+ // else
1353
+ // move next[prev[index]] to next[index] (head has no prev)
1354
+ // move prev[next[index]] to prev[index]
1355
+ // prev[index] = tail
1356
+ // next[tail] = index
1357
+ // tail = index
1358
+ if (index !== this.#tail) {
1359
+ if (index === this.#head) {
1360
+ this.#head = this.#next[index];
1361
+ }
1362
+ else {
1363
+ this.#connect(this.#prev[index], this.#next[index]);
1364
+ }
1365
+ this.#connect(this.#tail, index);
1366
+ this.#tail = index;
1367
+ }
1368
+ }
1369
+ /**
1370
+ * Deletes a key out of the cache.
1371
+ * Returns true if the key was deleted, false otherwise.
1372
+ */
1373
+ delete(k) {
1374
+ let deleted = false;
1375
+ if (this.#size !== 0) {
1376
+ const index = this.#keyMap.get(k);
1377
+ if (index !== undefined) {
1378
+ deleted = true;
1379
+ if (this.#size === 1) {
1380
+ this.clear();
1381
+ }
1382
+ else {
1383
+ this.#removeItemSize(index);
1384
+ const v = this.#valList[index];
1385
+ if (this.#isBackgroundFetch(v)) {
1386
+ v.__abortController.abort(new Error('deleted'));
1387
+ }
1388
+ else if (this.#hasDispose || this.#hasDisposeAfter) {
1389
+ if (this.#hasDispose) {
1390
+ this.#dispose?.(v, k, 'delete');
1391
+ }
1392
+ if (this.#hasDisposeAfter) {
1393
+ this.#disposed?.push([v, k, 'delete']);
1394
+ }
1395
+ }
1396
+ this.#keyMap.delete(k);
1397
+ this.#keyList[index] = undefined;
1398
+ this.#valList[index] = undefined;
1399
+ if (index === this.#tail) {
1400
+ this.#tail = this.#prev[index];
1401
+ }
1402
+ else if (index === this.#head) {
1403
+ this.#head = this.#next[index];
1404
+ }
1405
+ else {
1406
+ const pi = this.#prev[index];
1407
+ this.#next[pi] = this.#next[index];
1408
+ const ni = this.#next[index];
1409
+ this.#prev[ni] = this.#prev[index];
1410
+ }
1411
+ this.#size--;
1412
+ this.#free.push(index);
1413
+ }
1414
+ }
1415
+ }
1416
+ if (this.#hasDisposeAfter && this.#disposed?.length) {
1417
+ const dt = this.#disposed;
1418
+ let task;
1419
+ while ((task = dt?.shift())) {
1420
+ this.#disposeAfter?.(...task);
1421
+ }
1422
+ }
1423
+ return deleted;
1424
+ }
1425
+ /**
1426
+ * Clear the cache entirely, throwing away all values.
1427
+ */
1428
+ clear() {
1429
+ for (const index of this.#rindexes({ allowStale: true })) {
1430
+ const v = this.#valList[index];
1431
+ if (this.#isBackgroundFetch(v)) {
1432
+ v.__abortController.abort(new Error('deleted'));
1433
+ }
1434
+ else {
1435
+ const k = this.#keyList[index];
1436
+ if (this.#hasDispose) {
1437
+ this.#dispose?.(v, k, 'delete');
1438
+ }
1439
+ if (this.#hasDisposeAfter) {
1440
+ this.#disposed?.push([v, k, 'delete']);
1441
+ }
1442
+ }
1443
+ }
1444
+ this.#keyMap.clear();
1445
+ this.#valList.fill(undefined);
1446
+ this.#keyList.fill(undefined);
1447
+ if (this.#ttls && this.#starts) {
1448
+ this.#ttls.fill(0);
1449
+ this.#starts.fill(0);
1450
+ }
1451
+ if (this.#sizes) {
1452
+ this.#sizes.fill(0);
1453
+ }
1454
+ this.#head = 0;
1455
+ this.#tail = 0;
1456
+ this.#free.length = 0;
1457
+ this.#calculatedSize = 0;
1458
+ this.#size = 0;
1459
+ if (this.#hasDisposeAfter && this.#disposed) {
1460
+ const dt = this.#disposed;
1461
+ let task;
1462
+ while ((task = dt?.shift())) {
1463
+ this.#disposeAfter?.(...task);
1464
+ }
1465
+ }
1466
+ }
1467
+ }
1468
+
28
1469
  const lowlight = lowlight$1.createLowlight(lowlight$1.all);
29
1470
  lowlight.register("vue", function hljsDefineVue(hljs) {
30
1471
  return {
@@ -74,6 +1515,7 @@ lowlight.register("vue", function hljsDefineVue(hljs) {
74
1515
  const highlighter = lowlight;
75
1516
  let _autoDetectLang = true;
76
1517
  let _maxLineToIgnoreSyntax = 2000;
1518
+ const _ignoreSyntaxHighlightList = [];
77
1519
  Object.defineProperty(highlighter, "maxLineToIgnoreSyntax", {
78
1520
  get: () => _maxLineToIgnoreSyntax,
79
1521
  });
@@ -90,12 +1532,21 @@ Object.defineProperty(highlighter, "setAutoDetectLang", {
90
1532
  _autoDetectLang = v;
91
1533
  },
92
1534
  });
1535
+ Object.defineProperty(highlighter, "ignoreSyntaxHighlightList", {
1536
+ get: () => _ignoreSyntaxHighlightList,
1537
+ });
1538
+ Object.defineProperty(highlighter, "setIgnoreSyntaxHighlightList", {
1539
+ value: (v) => {
1540
+ _ignoreSyntaxHighlightList.length = 0;
1541
+ _ignoreSyntaxHighlightList.push(...v);
1542
+ },
1543
+ });
93
1544
 
94
- // TODO LRU Cache
95
- const map = {};
1545
+ const map = new LRUCache({ max: 30 });
96
1546
  class File {
97
1547
  raw;
98
1548
  lang;
1549
+ fileName;
99
1550
  ast;
100
1551
  rawFile = {};
101
1552
  hasDoRaw = false;
@@ -104,12 +1555,13 @@ class File {
104
1555
  hasDoSyntax = false;
105
1556
  syntaxLength;
106
1557
  maxLineNumber = 0;
107
- constructor(raw, lang) {
1558
+ constructor(raw, lang, fileName) {
108
1559
  this.raw = raw;
109
1560
  this.lang = lang;
1561
+ this.fileName = fileName;
110
1562
  Object.defineProperty(this, "__v_skip", { value: true });
111
1563
  }
112
- doSyntax({ autoDetectLang, registerHighlighter }) {
1564
+ doSyntax({ autoDetectLang, registerHighlighter, }) {
113
1565
  if (!this.raw || this.hasDoSyntax)
114
1566
  return;
115
1567
  let hasRegisteredLang = true;
@@ -129,6 +1581,11 @@ class File {
129
1581
  console.warn(`ignore syntax for current file, because the rawLength is too long: ${this.rawLength}`);
130
1582
  return;
131
1583
  }
1584
+ if (this.fileName &&
1585
+ _highlighter.ignoreSyntaxHighlightList.some((item) => item instanceof RegExp ? item.test(this.fileName) : this.fileName === item)) {
1586
+ console.warn(`ignore syntax for current file, because the fileName is in the ignoreSyntaxHighlightList: ${this.fileName}`);
1587
+ return;
1588
+ }
132
1589
  if (hasRegisteredLang) {
133
1590
  this.ast = _highlighter.highlight(this.lang, this.raw);
134
1591
  }
@@ -148,10 +1605,18 @@ class File {
148
1605
  const rawArray = rawString.split("\n");
149
1606
  this.rawLength = rawArray.length;
150
1607
  this.maxLineNumber = rawArray.length;
151
- this.rawFile = rawArray.reduce((p, item, index) => ({
152
- ...p,
153
- [index + 1]: index < rawArray.length - 1 ? item + "\n" : item,
154
- }), {});
1608
+ this.rawFile = {};
1609
+ for (let i = 0; i < rawArray.length; i++) {
1610
+ this.rawFile[i + 1] = i < rawArray.length - 1 ? rawArray[i] + "\n" : rawArray[i];
1611
+ }
1612
+ // reduce 对于大数组性能很差
1613
+ // this.rawFile = rawArray.reduce(
1614
+ // (p, item, index) => ({
1615
+ // ...p,
1616
+ // [index + 1]: index < rawArray.length - 1 ? item + "\n" : item,
1617
+ // }),
1618
+ // {}
1619
+ // );
155
1620
  this.hasDoRaw = true;
156
1621
  }
157
1622
  #doAST() {
@@ -242,15 +1707,90 @@ class File {
242
1707
  }
243
1708
  }
244
1709
  }
245
- const getFile = (raw, lang) => {
246
- const key = raw + "--" + "0.0.3" + "--" + lang;
247
- if (map[key])
248
- return map[key];
249
- const file = new File(raw, lang);
250
- map[key] = file;
1710
+ const getFile = (raw, lang, fileName) => {
1711
+ const key = raw + "--" + "0.0.6" + "--" + lang;
1712
+ if (map.has(key))
1713
+ return map.get(key);
1714
+ const file = new File(raw, lang, fileName);
1715
+ map.set(key, file);
251
1716
  return file;
252
1717
  };
253
1718
 
1719
+ const maxLength = 1000;
1720
+ /** Get the maximum position in the range. */
1721
+ function rangeMax(range) {
1722
+ return range.location + range.length;
1723
+ }
1724
+ /** Get the length of the common substring between the two strings. */
1725
+ function commonLength(stringA, rangeA, stringB, rangeB, reverse) {
1726
+ const max = Math.min(rangeA.length, rangeB.length);
1727
+ const startA = reverse ? rangeMax(rangeA) - 1 : rangeA.location;
1728
+ const startB = reverse ? rangeMax(rangeB) - 1 : rangeB.location;
1729
+ const stride = reverse ? -1 : 1;
1730
+ let length = 0;
1731
+ while (Math.abs(length) < max) {
1732
+ if (stringA[startA + length] !== stringB[startB + length]) {
1733
+ break;
1734
+ }
1735
+ length += stride;
1736
+ }
1737
+ return Math.abs(length);
1738
+ }
1739
+ function isInValidString(s) {
1740
+ return s.trim().length === 0 || s.length >= maxLength;
1741
+ }
1742
+ /** Get the changed ranges in the strings, relative to each other. */
1743
+ function relativeChanges(stringA, stringB) {
1744
+ let bRange = { location: 0, length: stringB.length };
1745
+ let aRange = { location: 0, length: stringA.length };
1746
+ const _stringA = stringA.trimEnd();
1747
+ const _stringB = stringB.trimEnd();
1748
+ const aEndStr = stringA.slice(-2);
1749
+ const bEndStr = stringB.slice(-2);
1750
+ if (_stringA === _stringB && aEndStr !== bEndStr && (aEndStr === "\r\n" || bEndStr === "\r\n")) {
1751
+ return {
1752
+ stringARange: {
1753
+ location: _stringA.length,
1754
+ length: stringA.length - _stringA.length,
1755
+ isNewLineSymbolChanged: true,
1756
+ },
1757
+ stringBRange: {
1758
+ location: _stringB.length,
1759
+ length: stringB.length - _stringB.length,
1760
+ isNewLineSymbolChanged: true,
1761
+ },
1762
+ };
1763
+ }
1764
+ if (isInValidString(stringA) || isInValidString(stringB)) {
1765
+ aRange.length = 0;
1766
+ bRange.length = 0;
1767
+ return { stringARange: aRange, stringBRange: bRange };
1768
+ }
1769
+ const prefixLength = commonLength(stringB, bRange, stringA, aRange, false);
1770
+ bRange = {
1771
+ location: bRange.location + prefixLength,
1772
+ length: bRange.length - prefixLength,
1773
+ };
1774
+ aRange = {
1775
+ location: aRange.location + prefixLength,
1776
+ length: aRange.length - prefixLength,
1777
+ };
1778
+ const suffixLength = commonLength(stringB, bRange, stringA, aRange, true);
1779
+ bRange.length -= suffixLength;
1780
+ aRange.length -= suffixLength;
1781
+ return { stringARange: aRange, stringBRange: bRange };
1782
+ }
1783
+ /** Check two string have a diff range */
1784
+ function hasRelativeChange(stringA, stringB) {
1785
+ const _stringA = stringA.trim();
1786
+ const _stringB = stringB.trim();
1787
+ const { stringARange, stringBRange } = relativeChanges(_stringA, _stringB);
1788
+ return (stringARange.location > 0 ||
1789
+ stringBRange.location > 0 ||
1790
+ stringARange.length < _stringA.length ||
1791
+ stringBRange.length < _stringB.length);
1792
+ }
1793
+
254
1794
  /** indicate what a line in the diff represents */
255
1795
  var DiffLineType;
256
1796
  (function (DiffLineType) {
@@ -446,72 +1986,39 @@ function getHunkHeaderExpansionType(hunkIndex, hunkHeader, previousHunk) {
446
1986
  else if (distanceToPrevious <= DefaultDiffExpansionStep) {
447
1987
  return DiffHunkExpansionType.Short;
448
1988
  }
449
- else {
450
- return DiffHunkExpansionType.Both;
451
- }
452
- }
453
- const numIterator = (num, cb) => {
454
- const re = [];
455
- for (let i = 0; i < num; i++) {
456
- re.push(cb(i));
457
- }
458
- return re;
459
- };
460
-
461
- const maxLength = 1000;
462
- /** Get the maximum position in the range. */
463
- function rangeMax(range) {
464
- return range.location + range.length;
465
- }
466
- /** Get the length of the common substring between the two strings. */
467
- function commonLength(stringA, rangeA, stringB, rangeB, reverse) {
468
- const max = Math.min(rangeA.length, rangeB.length);
469
- const startA = reverse ? rangeMax(rangeA) - 1 : rangeA.location;
470
- const startB = reverse ? rangeMax(rangeB) - 1 : rangeB.location;
471
- const stride = reverse ? -1 : 1;
472
- let length = 0;
473
- while (Math.abs(length) < max) {
474
- if (stringA[startA + length] !== stringB[startB + length]) {
475
- break;
476
- }
477
- length += stride;
478
- }
479
- return Math.abs(length);
480
- }
481
- function isInValidString(s) {
482
- return s.trim().length === 0 || s.length >= maxLength;
483
- }
484
- /** Get the changed ranges in the strings, relative to each other. */
485
- function relativeChanges(stringA, stringB) {
486
- let bRange = { location: 0, length: stringB.length };
487
- let aRange = { location: 0, length: stringA.length };
488
- const _stringA = stringA.trimEnd();
489
- const _stringB = stringB.trimEnd();
490
- if (_stringA === _stringB) {
491
- return {
492
- stringARange: { location: _stringA.length, length: stringA.length - _stringA.length },
493
- stringBRange: { location: _stringB.length, length: stringB.length - _stringB.length },
494
- };
495
- }
496
- if (isInValidString(stringA) || isInValidString(stringB)) {
497
- aRange.length = 0;
498
- bRange.length = 0;
499
- return { stringARange: aRange, stringBRange: bRange };
500
- }
501
- const prefixLength = commonLength(stringB, bRange, stringA, aRange, false);
502
- bRange = {
503
- location: bRange.location + prefixLength,
504
- length: bRange.length - prefixLength,
505
- };
506
- aRange = {
507
- location: aRange.location + prefixLength,
508
- length: aRange.length - prefixLength,
509
- };
510
- const suffixLength = commonLength(stringB, bRange, stringA, aRange, true);
511
- bRange.length -= suffixLength;
512
- aRange.length -= suffixLength;
513
- return { stringARange: aRange, stringBRange: bRange };
1989
+ else {
1990
+ return DiffHunkExpansionType.Both;
1991
+ }
514
1992
  }
1993
+ const numIterator = (num, cb) => {
1994
+ const re = [];
1995
+ for (let i = 0; i < num; i++) {
1996
+ re.push(cb(i));
1997
+ }
1998
+ return re;
1999
+ };
2000
+ const getLang = (fileName) => {
2001
+ const dotIndex = fileName.lastIndexOf(".");
2002
+ const extension = fileName.slice(dotIndex + 1);
2003
+ return extension;
2004
+ };
2005
+ const getDiffRange = (additions, deletions) => {
2006
+ if (additions.length === deletions.length) {
2007
+ const len = additions.length;
2008
+ for (let i = 0; i < len; i++) {
2009
+ const addition = additions[i];
2010
+ const deletion = deletions[i];
2011
+ const hasDiffRange = hasRelativeChange(addition.text, deletion.text);
2012
+ if (hasDiffRange) {
2013
+ const { stringARange, stringBRange } = relativeChanges(addition.text, deletion.text);
2014
+ addition.needRematch = true;
2015
+ addition.range = stringARange;
2016
+ deletion.needRematch = true;
2017
+ deletion.range = stringBRange;
2018
+ }
2019
+ }
2020
+ }
2021
+ };
515
2022
 
516
2023
  /* eslint-disable max-lines */
517
2024
  // NODE: ALL of the Diff parse logic from desktop, SEE https://github.com/desktop/desktop
@@ -866,11 +2373,6 @@ const parseInstance = new DiffParser();
866
2373
  /* eslint-disable max-lines */
867
2374
  const composeLen = 40;
868
2375
  const idSet = new Set();
869
- const getLang = (fileName) => {
870
- const dotIndex = fileName.lastIndexOf(".");
871
- const extension = fileName.slice(dotIndex + 1);
872
- return extension;
873
- };
874
2376
  class DiffFile {
875
2377
  _oldFileName;
876
2378
  _newFileName;
@@ -888,10 +2390,8 @@ class DiffFile {
888
2390
  #splitLeftLines = [];
889
2391
  #splitRightLines = [];
890
2392
  #splitHunksLines;
891
- #splitLastStartIndex;
892
2393
  #unifiedLines = [];
893
2394
  #unifiedHunksLines;
894
- #unifiedLastStartIndex;
895
2395
  #listeners = [];
896
2396
  #hasInitRaw = false;
897
2397
  #hasInitSyntax = false;
@@ -899,6 +2399,7 @@ class DiffFile {
899
2399
  #hasBuildUnified = false;
900
2400
  #updateCount = 0;
901
2401
  #composeByDiff = false;
2402
+ _version_ = "0.0.6";
902
2403
  _oldFileContent = "";
903
2404
  _oldFileLang = "";
904
2405
  _newFileContent = "";
@@ -949,10 +2450,10 @@ class DiffFile {
949
2450
  if (!this._oldFileContent && !this._newFileContent)
950
2451
  return;
951
2452
  if (this._oldFileContent) {
952
- this.#oldFileResult = getFile(this._oldFileContent, this._oldFileLang);
2453
+ this.#oldFileResult = getFile(this._oldFileContent, this._oldFileLang, this._oldFileName);
953
2454
  }
954
2455
  if (this._newFileContent) {
955
- this.#newFileResult = getFile(this._newFileContent, this._newFileLang);
2456
+ this.#newFileResult = getFile(this._newFileContent, this._newFileLang, this._newFileName);
956
2457
  }
957
2458
  }
958
2459
  #composeRaw() {
@@ -994,8 +2495,8 @@ class DiffFile {
994
2495
  return;
995
2496
  this._oldFileContent = oldFileContent;
996
2497
  this._newFileContent = newFileContent;
997
- this.#oldFileResult = getFile(this._oldFileContent, this._oldFileLang);
998
- this.#newFileResult = getFile(this._newFileContent, this._newFileLang);
2498
+ this.#oldFileResult = getFile(this._oldFileContent, this._oldFileLang, this._oldFileName);
2499
+ this.#newFileResult = getFile(this._newFileContent, this._newFileLang, this._newFileName);
999
2500
  // all of the file just compose by diff, so we can not do the expand action
1000
2501
  this.#composeByDiff = true;
1001
2502
  }
@@ -1016,7 +2517,7 @@ class DiffFile {
1016
2517
  if (newFileContent === this._oldFileContent)
1017
2518
  return;
1018
2519
  this._newFileContent = newFileContent;
1019
- this.#newFileResult = getFile(this._newFileContent, this._newFileLang);
2520
+ this.#newFileResult = getFile(this._newFileContent, this._newFileLang, this._newFileName);
1020
2521
  }
1021
2522
  else if (this.#newFileResult) {
1022
2523
  let oldLineNumber = 1;
@@ -1035,7 +2536,7 @@ class DiffFile {
1035
2536
  if (oldFileContent === this._newFileContent)
1036
2537
  return;
1037
2538
  this._oldFileContent = oldFileContent;
1038
- this.#oldFileResult = getFile(this._oldFileContent, this._oldFileLang);
2539
+ this.#oldFileResult = getFile(this._oldFileContent, this._oldFileLang, this._oldFileName);
1039
2540
  }
1040
2541
  this.#composeRaw();
1041
2542
  }
@@ -1055,27 +2556,22 @@ class DiffFile {
1055
2556
  deletions.push(line);
1056
2557
  }
1057
2558
  else {
1058
- if (additions.length === deletions.length) {
1059
- const len = additions.length;
1060
- for (let i = 0; i < len; i++) {
1061
- const addition = additions[i];
1062
- const deletion = deletions[i];
1063
- const { stringARange, stringBRange } = relativeChanges(addition.text, deletion.text);
1064
- addition.needRematch = true;
1065
- addition.range = stringARange;
1066
- deletion.needRematch = true;
1067
- deletion.range = stringBRange;
1068
- }
1069
- }
2559
+ getDiffRange(additions, deletions);
1070
2560
  additions = [];
1071
2561
  deletions = [];
1072
2562
  }
1073
2563
  });
2564
+ getDiffRange(additions, deletions);
2565
+ });
2566
+ });
2567
+ this.#diffLines = [];
2568
+ const tmp = [];
2569
+ this.#diffListResults.forEach((item) => {
2570
+ item.hunks.forEach((_item) => {
2571
+ tmp.push(..._item.lines);
1074
2572
  });
1075
2573
  });
1076
- this.#diffLines = this.#diffListResults
1077
- .reduce((p, c) => p.concat(...c.hunks.reduce((_p, _c) => _p.concat(..._c.lines), [])), [])
1078
- .map((i, index) => {
2574
+ this.#diffLines = tmp.map((i, index) => {
1079
2575
  const typedI = i;
1080
2576
  typedI.index = index;
1081
2577
  if (typedI.type === DiffLineType.Hunk) {
@@ -1090,28 +2586,49 @@ class DiffFile {
1090
2586
  oldLength: Number(oldNumLength),
1091
2587
  newStartIndex: +Number(newNumStartIndex),
1092
2588
  newLength: Number(newNumLength),
2589
+ _oldStartIndex: -Number(oldNumStartIndex),
2590
+ _oldLength: Number(oldNumLength),
2591
+ _newStartIndex: +Number(newNumStartIndex),
2592
+ _newLength: Number(newNumLength),
1093
2593
  };
1094
2594
  }
1095
2595
  return typedI;
1096
2596
  });
1097
- this.#oldFileDiffLines = this.#diffLines.reduce((p, c) => {
1098
- if (c.oldLineNumber) {
1099
- this.diffLineLength = Math.max(this.diffLineLength, c.oldLineNumber);
1100
- return { ...p, [c.oldLineNumber]: c };
2597
+ // this.#diffLines = this.#diffListResults
2598
+ // .reduce<DiffLine[]>((p, c) => p.concat(...c.hunks.reduce<DiffLine[]>((_p, _c) => _p.concat(..._c.lines), [])), [])
2599
+ // .map<DiffLineItem>((i, index) => {
2600
+ // const typedI = i as DiffLineItem;
2601
+ // typedI.index = index;
2602
+ // if (typedI.type === DiffLineType.Hunk) {
2603
+ // const numInfo = typedI.text.split("@@")?.[1].split(" ").filter(Boolean);
2604
+ // const oldNumInfo = numInfo?.[0] || "";
2605
+ // const newNumInfo = numInfo?.[1] || "";
2606
+ // const [oldNumStartIndex, oldNumLength] = oldNumInfo.split(",");
2607
+ // const [newNumStartIndex, newNumLength] = newNumInfo.split(",");
2608
+ // const typedTypeI = typedI as DiffHunkItem;
2609
+ // typedTypeI.hunkInfo = {
2610
+ // oldStartIndex: -Number(oldNumStartIndex),
2611
+ // oldLength: Number(oldNumLength),
2612
+ // newStartIndex: +Number(newNumStartIndex),
2613
+ // newLength: Number(newNumLength),
2614
+ // };
2615
+ // }
2616
+ // return typedI;
2617
+ // });
2618
+ this.#oldFileDiffLines = {};
2619
+ this.#diffLines.forEach((item) => {
2620
+ if (item.oldLineNumber) {
2621
+ this.diffLineLength = Math.max(this.diffLineLength, item.oldLineNumber);
2622
+ this.#oldFileDiffLines[item.oldLineNumber] = item;
1101
2623
  }
1102
- else {
1103
- return p;
1104
- }
1105
- }, {});
1106
- this.#newFileDiffLines = this.#diffLines.reduce((p, c) => {
1107
- if (c.newLineNumber) {
1108
- this.diffLineLength = Math.max(this.diffLineLength, c.newLineNumber);
1109
- return { ...p, [c.newLineNumber]: c };
1110
- }
1111
- else {
1112
- return p;
2624
+ });
2625
+ this.#newFileDiffLines = {};
2626
+ this.#diffLines.forEach((item) => {
2627
+ if (item.newLineNumber) {
2628
+ this.diffLineLength = Math.max(this.diffLineLength, item.newLineNumber);
2629
+ this.#newFileDiffLines[item.newLineNumber] = item;
1113
2630
  }
1114
- }, {});
2631
+ });
1115
2632
  }
1116
2633
  #composeSyntax({ autoDetectLang, registerHighlighter, }) {
1117
2634
  this.#oldFileResult?.doSyntax({ autoDetectLang, registerHighlighter });
@@ -1179,14 +2696,27 @@ class DiffFile {
1179
2696
  const maxOldFileLineNumber = this.#oldFileResult?.maxLineNumber || 0;
1180
2697
  const maxNewFileLineNumber = this.#newFileResult?.maxLineNumber || 0;
1181
2698
  while (oldFileLineNumber <= maxOldFileLineNumber || newFileLineNumber <= maxNewFileLineNumber) {
1182
- const oldRawLine = this.#getOldRawLine(oldFileLineNumber);
1183
2699
  const oldDiffLine = this.#getOldDiffLine(oldFileLineNumber);
1184
- const newRawLine = this.#getNewRawLine(newFileLineNumber);
1185
2700
  const newDiffLine = this.#getNewDiffLine(newFileLineNumber);
2701
+ const oldRawLine = this.#getOldRawLine(oldFileLineNumber);
2702
+ const newRawLine = this.#getNewRawLine(newFileLineNumber);
1186
2703
  const oldLineHasChange = oldDiffLine?.isIncludeableLine();
1187
2704
  const newLineHasChange = newDiffLine?.isIncludeableLine();
1188
2705
  const len = this.#splitRightLines.length;
1189
2706
  const isHidden = !oldDiffLine && !newDiffLine;
2707
+ if ((oldDiffLine && !newDiffLine && oldDiffLine.newLineNumber && oldDiffLine.newLineNumber > newFileLineNumber) ||
2708
+ (!oldDiffLine && newDiffLine && newDiffLine.oldLineNumber && newDiffLine.oldLineNumber > oldFileLineNumber)) {
2709
+ if (this.#composeByDiff) {
2710
+ oldDiffLine && newFileLineNumber++;
2711
+ newDiffLine && oldFileLineNumber++;
2712
+ continue;
2713
+ }
2714
+ else {
2715
+ {
2716
+ console.error("some error happen for generate diff line!");
2717
+ }
2718
+ }
2719
+ }
1190
2720
  if (!oldDiffLine && !newRawLine && !oldDiffLine && !newDiffLine)
1191
2721
  break;
1192
2722
  if ((oldLineHasChange && newLineHasChange) || (!oldLineHasChange && !newLineHasChange)) {
@@ -1195,12 +2725,14 @@ class DiffFile {
1195
2725
  value: oldRawLine,
1196
2726
  diff: oldDiffLine,
1197
2727
  isHidden,
2728
+ _isHidden: isHidden,
1198
2729
  });
1199
2730
  this.#splitRightLines.push({
1200
2731
  lineNumber: newFileLineNumber++,
1201
2732
  value: newRawLine,
1202
2733
  diff: newDiffLine,
1203
2734
  isHidden,
2735
+ _isHidden: isHidden,
1204
2736
  });
1205
2737
  }
1206
2738
  else if (oldLineHasChange) {
@@ -1208,6 +2740,8 @@ class DiffFile {
1208
2740
  lineNumber: oldFileLineNumber++,
1209
2741
  value: oldRawLine,
1210
2742
  diff: oldDiffLine,
2743
+ isHidden,
2744
+ _isHidden: isHidden,
1211
2745
  });
1212
2746
  this.#splitRightLines.push({});
1213
2747
  }
@@ -1217,6 +2751,8 @@ class DiffFile {
1217
2751
  lineNumber: newFileLineNumber++,
1218
2752
  value: newRawLine,
1219
2753
  diff: newDiffLine,
2754
+ isHidden,
2755
+ _isHidden: isHidden,
1220
2756
  });
1221
2757
  }
1222
2758
  if (!prevIsHidden && isHidden) {
@@ -1234,18 +2770,48 @@ class DiffFile {
1234
2770
  startHiddenIndex: hideStart,
1235
2771
  endHiddenIndex: len,
1236
2772
  plainText: typedPrevious.text,
2773
+ _startHiddenIndex: hideStart,
2774
+ _endHiddenIndex: len,
2775
+ _plainText: typedPrevious.text,
1237
2776
  };
1238
2777
  hideStart = Infinity;
1239
2778
  }
1240
2779
  this.#splitHunksLines = {
1241
2780
  ...this.#splitHunksLines,
1242
- [len]: previous,
2781
+ [len]: typedPrevious,
1243
2782
  };
1244
2783
  }
1245
2784
  }
1246
2785
  }
2786
+ // have last hunk
2787
+ if (Number.isFinite(hideStart)) {
2788
+ const lastDiff = new DiffLine("", DiffLineType.Hunk, null, null, null);
2789
+ const lastHunk = lastDiff;
2790
+ lastHunk.isLast = true;
2791
+ lastHunk.splitInfo = {
2792
+ startHiddenIndex: hideStart,
2793
+ endHiddenIndex: this.#splitRightLines.length,
2794
+ _startHiddenIndex: hideStart,
2795
+ _endHiddenIndex: this.#splitRightLines.length,
2796
+ // just for placeholder
2797
+ plainText: "",
2798
+ oldStartIndex: 0,
2799
+ newStartIndex: 0,
2800
+ oldLength: 0,
2801
+ newLength: 0,
2802
+ _plainText: "",
2803
+ _oldStartIndex: 0,
2804
+ _newStartIndex: 0,
2805
+ _oldLength: 0,
2806
+ _newLength: 0,
2807
+ };
2808
+ this.#splitHunksLines = {
2809
+ ...this.#splitHunksLines,
2810
+ [this.#splitRightLines.length]: lastHunk,
2811
+ };
2812
+ hideStart = Infinity;
2813
+ }
1247
2814
  this.splitLineLength = this.#splitRightLines.length;
1248
- this.#splitLastStartIndex = hideStart;
1249
2815
  this.#hasBuildSplit = true;
1250
2816
  this.notifyAll();
1251
2817
  }
@@ -1267,6 +2833,19 @@ class DiffFile {
1267
2833
  const newLineHasChange = newDiffLine?.isIncludeableLine();
1268
2834
  const len = this.#unifiedLines.length;
1269
2835
  const isHidden = !oldDiffLine && !newDiffLine;
2836
+ if ((oldDiffLine && !newDiffLine && oldDiffLine.newLineNumber && oldDiffLine.newLineNumber > newFileLineNumber) ||
2837
+ (!oldDiffLine && newDiffLine && newDiffLine.oldLineNumber && newDiffLine.oldLineNumber > oldFileLineNumber)) {
2838
+ if (this.#composeByDiff) {
2839
+ oldDiffLine && newFileLineNumber++;
2840
+ newDiffLine && oldFileLineNumber++;
2841
+ continue;
2842
+ }
2843
+ else {
2844
+ {
2845
+ console.error("some error happen for generate diff line!");
2846
+ }
2847
+ }
2848
+ }
1270
2849
  if (!oldRawLine && !newRawLine && !newDiffLine && !oldDiffLine)
1271
2850
  break;
1272
2851
  if (!oldLineHasChange && !newLineHasChange) {
@@ -1276,6 +2855,7 @@ class DiffFile {
1276
2855
  value: newRawLine,
1277
2856
  diff: newDiffLine,
1278
2857
  isHidden,
2858
+ _isHidden: isHidden,
1279
2859
  });
1280
2860
  }
1281
2861
  else if (oldLineHasChange) {
@@ -1283,6 +2863,8 @@ class DiffFile {
1283
2863
  oldLineNumber: oldFileLineNumber++,
1284
2864
  value: oldRawLine,
1285
2865
  diff: oldDiffLine,
2866
+ isHidden,
2867
+ _isHidden: isHidden,
1286
2868
  });
1287
2869
  }
1288
2870
  else if (newLineHasChange) {
@@ -1290,6 +2872,8 @@ class DiffFile {
1290
2872
  newLineNumber: newFileLineNumber++,
1291
2873
  value: newRawLine,
1292
2874
  diff: newDiffLine,
2875
+ isHidden,
2876
+ _isHidden: isHidden,
1293
2877
  });
1294
2878
  }
1295
2879
  if (!prevIsHidden && isHidden) {
@@ -1307,18 +2891,48 @@ class DiffFile {
1307
2891
  startHiddenIndex: hideStart,
1308
2892
  endHiddenIndex: len,
1309
2893
  plainText: typedPrevious.text,
2894
+ _startHiddenIndex: hideStart,
2895
+ _endHiddenIndex: len,
2896
+ _plainText: typedPrevious.text,
1310
2897
  };
1311
2898
  hideStart = Infinity;
1312
2899
  }
1313
2900
  this.#unifiedHunksLines = {
1314
2901
  ...this.#unifiedHunksLines,
1315
- [len]: previous,
2902
+ [len]: typedPrevious,
1316
2903
  };
1317
2904
  }
1318
2905
  }
1319
2906
  }
2907
+ // have last hunk
2908
+ if (Number.isFinite(hideStart)) {
2909
+ const lastDiff = new DiffLine("", DiffLineType.Hunk, null, null, null);
2910
+ const lastHunk = lastDiff;
2911
+ lastHunk.isLast = true;
2912
+ lastHunk.unifiedInfo = {
2913
+ startHiddenIndex: hideStart,
2914
+ endHiddenIndex: this.#unifiedLines.length,
2915
+ _startHiddenIndex: hideStart,
2916
+ _endHiddenIndex: this.#unifiedLines.length,
2917
+ // just for placeholder
2918
+ plainText: "",
2919
+ oldStartIndex: 0,
2920
+ newStartIndex: 0,
2921
+ oldLength: 0,
2922
+ newLength: 0,
2923
+ _plainText: "",
2924
+ _oldStartIndex: 0,
2925
+ _newStartIndex: 0,
2926
+ _oldLength: 0,
2927
+ _newLength: 0,
2928
+ };
2929
+ this.#unifiedHunksLines = {
2930
+ ...this.#unifiedHunksLines,
2931
+ [this.#unifiedLines.length]: lastHunk,
2932
+ };
2933
+ hideStart = Infinity;
2934
+ }
1320
2935
  this.unifiedLineLength = this.#unifiedLines.length;
1321
- this.#unifiedLastStartIndex = hideStart;
1322
2936
  this.#hasBuildUnified = true;
1323
2937
  this.notifyAll();
1324
2938
  }
@@ -1331,9 +2945,9 @@ class DiffFile {
1331
2945
  getSplitHunkLine = (index) => {
1332
2946
  return this.#splitHunksLines?.[index];
1333
2947
  };
1334
- onSplitHunkExpand = (dir, index) => {
2948
+ onSplitHunkExpand = (dir, index, needTrigger = true) => {
1335
2949
  const current = this.#splitHunksLines?.[index];
1336
- if (!current)
2950
+ if (!current || !current.splitInfo)
1337
2951
  return;
1338
2952
  if (dir === "all") {
1339
2953
  for (let i = current.splitInfo.startHiddenIndex; i < current.splitInfo.endHiddenIndex; i++) {
@@ -1344,7 +2958,6 @@ class DiffFile {
1344
2958
  if (rightLine?.isHidden)
1345
2959
  rightLine.isHidden = false;
1346
2960
  }
1347
- current.splitInfo.plainText = current.text;
1348
2961
  current.splitInfo = {
1349
2962
  ...current.splitInfo,
1350
2963
  ...current.hunkInfo,
@@ -1361,13 +2974,25 @@ class DiffFile {
1361
2974
  if (rightLine?.isHidden)
1362
2975
  rightLine.isHidden = false;
1363
2976
  }
1364
- current.splitInfo = {
1365
- ...current.splitInfo,
1366
- startHiddenIndex: current.splitInfo.startHiddenIndex + composeLen,
1367
- plainText: `@@ -${current.splitInfo.oldStartIndex},${current.splitInfo.oldLength} +${current.splitInfo.newStartIndex},${current.splitInfo.newLength}`,
1368
- };
2977
+ if (current.isLast) {
2978
+ current.splitInfo = {
2979
+ ...current.splitInfo,
2980
+ startHiddenIndex: current.splitInfo.startHiddenIndex + composeLen,
2981
+ };
2982
+ }
2983
+ else {
2984
+ current.splitInfo = {
2985
+ ...current.splitInfo,
2986
+ startHiddenIndex: current.splitInfo.startHiddenIndex + composeLen,
2987
+ plainText: `@@ -${current.splitInfo.oldStartIndex},${current.splitInfo.oldLength} +${current.splitInfo.newStartIndex},${current.splitInfo.newLength}`,
2988
+ };
2989
+ }
1369
2990
  }
1370
2991
  else {
2992
+ if (current.isLast) {
2993
+ console.error("the last hunk can not expand up!");
2994
+ return;
2995
+ }
1371
2996
  for (let i = current.splitInfo.endHiddenIndex - composeLen; i < current.splitInfo.endHiddenIndex; i++) {
1372
2997
  const leftLine = this.#splitLeftLines[i];
1373
2998
  const rightLine = this.#splitRightLines[i];
@@ -1392,25 +3017,7 @@ class DiffFile {
1392
3017
  delete this.#splitHunksLines?.[index];
1393
3018
  this.#splitHunksLines[current.splitInfo.endHiddenIndex] = current;
1394
3019
  }
1395
- this.notifyAll();
1396
- };
1397
- onSplitLastExpand = (expandAll) => {
1398
- if (!this.#splitLastStartIndex || !Number.isFinite(this.#splitLastStartIndex))
1399
- return;
1400
- const start = this.#splitLastStartIndex;
1401
- const end = expandAll ? this.splitLineLength : this.#splitLastStartIndex + composeLen;
1402
- for (let i = start; i < end; i++) {
1403
- const leftLine = this.#splitLeftLines[i];
1404
- const rightLine = this.#splitRightLines[i];
1405
- if (leftLine?.isHidden)
1406
- leftLine.isHidden = false;
1407
- if (rightLine?.isHidden)
1408
- rightLine.isHidden = false;
1409
- }
1410
- this.#splitLastStartIndex = end;
1411
- this.#splitLastStartIndex =
1412
- this.#splitLastStartIndex >= this.splitLineLength ? Infinity : this.#splitLastStartIndex;
1413
- this.notifyAll();
3020
+ needTrigger && this.notifyAll();
1414
3021
  };
1415
3022
  getUnifiedLine = (index) => {
1416
3023
  return this.#unifiedLines[index];
@@ -1418,9 +3025,9 @@ class DiffFile {
1418
3025
  getUnifiedHunkLine = (index) => {
1419
3026
  return this.#unifiedHunksLines?.[index];
1420
3027
  };
1421
- onUnifiedHunkExpand = (dir, index) => {
3028
+ onUnifiedHunkExpand = (dir, index, needTrigger = true) => {
1422
3029
  const current = this.#unifiedHunksLines?.[index];
1423
- if (!current)
3030
+ if (!current || !current.unifiedInfo)
1424
3031
  return;
1425
3032
  if (dir === "all") {
1426
3033
  for (let i = current.unifiedInfo.startHiddenIndex; i < current.unifiedInfo.endHiddenIndex; i++) {
@@ -1429,7 +3036,6 @@ class DiffFile {
1429
3036
  unifiedLine.isHidden = false;
1430
3037
  }
1431
3038
  }
1432
- current.unifiedInfo.plainText = current.text;
1433
3039
  current.unifiedInfo = {
1434
3040
  ...current.unifiedInfo,
1435
3041
  ...current.hunkInfo,
@@ -1443,13 +3049,25 @@ class DiffFile {
1443
3049
  if (unifiedLine?.isHidden)
1444
3050
  unifiedLine.isHidden = false;
1445
3051
  }
1446
- current.unifiedInfo = {
1447
- ...current.unifiedInfo,
1448
- startHiddenIndex: current.unifiedInfo.startHiddenIndex + composeLen,
1449
- plainText: `@@ -${current.unifiedInfo.oldStartIndex},${current.unifiedInfo.oldLength} +${current.unifiedInfo.newStartIndex},${current.unifiedInfo.newLength}`,
1450
- };
3052
+ if (current.isLast) {
3053
+ current.unifiedInfo = {
3054
+ ...current.unifiedInfo,
3055
+ startHiddenIndex: current.unifiedInfo.startHiddenIndex + composeLen,
3056
+ };
3057
+ }
3058
+ else {
3059
+ current.unifiedInfo = {
3060
+ ...current.unifiedInfo,
3061
+ startHiddenIndex: current.unifiedInfo.startHiddenIndex + composeLen,
3062
+ plainText: `@@ -${current.unifiedInfo.oldStartIndex},${current.unifiedInfo.oldLength} +${current.unifiedInfo.newStartIndex},${current.unifiedInfo.newLength}`,
3063
+ };
3064
+ }
1451
3065
  }
1452
3066
  else {
3067
+ if (current.isLast) {
3068
+ console.error("the last hunk can not expand up!");
3069
+ return;
3070
+ }
1453
3071
  for (let i = current.unifiedInfo.endHiddenIndex - composeLen; i < current.unifiedInfo.endHiddenIndex; i++) {
1454
3072
  const unifiedLine = this.#unifiedLines[i];
1455
3073
  if (unifiedLine?.isHidden)
@@ -1471,21 +3089,87 @@ class DiffFile {
1471
3089
  delete this.#unifiedHunksLines?.[index];
1472
3090
  this.#unifiedHunksLines[current.unifiedInfo.endHiddenIndex] = current;
1473
3091
  }
3092
+ needTrigger && this.notifyAll();
3093
+ };
3094
+ onAllExpand = (mode) => {
3095
+ if (mode === "split") {
3096
+ Object.keys(this.#splitHunksLines || {}).forEach((key) => {
3097
+ this.onSplitHunkExpand("all", +key, false);
3098
+ });
3099
+ }
3100
+ else {
3101
+ Object.keys(this.#unifiedHunksLines || {}).forEach((key) => {
3102
+ this.onUnifiedHunkExpand("all", +key, false);
3103
+ });
3104
+ }
1474
3105
  this.notifyAll();
1475
3106
  };
1476
- onUnifiedLastExpand = (expandAll) => {
1477
- if (!this.#unifiedLastStartIndex || !Number.isFinite(this.#unifiedLastStartIndex))
1478
- return;
1479
- const start = this.#unifiedLastStartIndex;
1480
- const end = expandAll ? this.unifiedLineLength : this.#unifiedLastStartIndex + composeLen;
1481
- for (let i = start; i < end; i++) {
1482
- const unifiedLine = this.#unifiedLines[i];
1483
- if (unifiedLine?.isHidden)
1484
- unifiedLine.isHidden = false;
1485
- }
1486
- this.#unifiedLastStartIndex = end;
1487
- this.#unifiedLastStartIndex =
1488
- this.#unifiedLastStartIndex >= this.unifiedLineLength ? Infinity : this.#unifiedLastStartIndex;
3107
+ onAllCollapse = (mode) => {
3108
+ if (mode === "split") {
3109
+ Object.values(this.#splitLeftLines || {}).forEach((item) => {
3110
+ if (!item.isHidden && item._isHidden) {
3111
+ item.isHidden = item._isHidden;
3112
+ }
3113
+ });
3114
+ Object.values(this.#splitRightLines || {}).forEach((item) => {
3115
+ if (!item.isHidden && item._isHidden) {
3116
+ item.isHidden = item._isHidden;
3117
+ }
3118
+ });
3119
+ Object.values(this.#splitHunksLines || {}).forEach((item) => {
3120
+ if (!item.splitInfo)
3121
+ return;
3122
+ item.splitInfo = {
3123
+ ...item.splitInfo,
3124
+ oldStartIndex: item.splitInfo._oldStartIndex,
3125
+ oldLength: item.splitInfo._oldLength,
3126
+ newStartIndex: item.splitInfo._newStartIndex,
3127
+ newLength: item.splitInfo._newLength,
3128
+ startHiddenIndex: item.splitInfo._startHiddenIndex,
3129
+ endHiddenIndex: item.splitInfo._endHiddenIndex,
3130
+ plainText: item.splitInfo._plainText,
3131
+ };
3132
+ });
3133
+ Object.keys(this.#splitHunksLines || {}).forEach((key) => {
3134
+ const item = this.#splitHunksLines[key];
3135
+ if (!item.splitInfo)
3136
+ return;
3137
+ if (item.splitInfo.endHiddenIndex !== +key) {
3138
+ delete this.#splitHunksLines[key];
3139
+ this.#splitHunksLines[item.splitInfo.endHiddenIndex] = item;
3140
+ }
3141
+ });
3142
+ }
3143
+ else {
3144
+ Object.values(this.#unifiedLines || {}).forEach((item) => {
3145
+ if (!item.isHidden && item._isHidden) {
3146
+ item.isHidden = item._isHidden;
3147
+ }
3148
+ });
3149
+ Object.values(this.#unifiedHunksLines || {}).forEach((item) => {
3150
+ if (!item.unifiedInfo)
3151
+ return;
3152
+ item.unifiedInfo = {
3153
+ ...item.unifiedInfo,
3154
+ oldStartIndex: item.unifiedInfo._oldStartIndex,
3155
+ oldLength: item.unifiedInfo._oldLength,
3156
+ newStartIndex: item.unifiedInfo._newStartIndex,
3157
+ newLength: item.unifiedInfo._newLength,
3158
+ startHiddenIndex: item.unifiedInfo._startHiddenIndex,
3159
+ endHiddenIndex: item.unifiedInfo._endHiddenIndex,
3160
+ plainText: item.unifiedInfo._plainText,
3161
+ };
3162
+ });
3163
+ Object.keys(this.#unifiedHunksLines || {}).forEach((key) => {
3164
+ const item = this.#unifiedHunksLines[key];
3165
+ if (!item.unifiedInfo)
3166
+ return;
3167
+ if (item.unifiedInfo.endHiddenIndex !== +key) {
3168
+ delete this.#unifiedHunksLines[key];
3169
+ this.#unifiedHunksLines[item.unifiedInfo.endHiddenIndex] = item;
3170
+ }
3171
+ });
3172
+ }
1489
3173
  this.notifyAll();
1490
3174
  };
1491
3175
  getOldSyntaxLine = (lineNumber) => {
@@ -1510,19 +3194,6 @@ class DiffFile {
1510
3194
  });
1511
3195
  };
1512
3196
  getUpdateCount = () => this.#updateCount;
1513
- getNeedShowExpandAll = (mode) => {
1514
- if (mode === "split") {
1515
- return (this.#splitLastStartIndex &&
1516
- Number.isFinite(this.#splitLastStartIndex) &&
1517
- (this.getSplitLeftLine(this.splitLineLength - 1)?.isHidden ||
1518
- this.getSplitRightLine(this.splitLineLength - 1)?.isHidden));
1519
- }
1520
- else {
1521
- return (this.#unifiedLastStartIndex &&
1522
- Number.isFinite(this.#unifiedLastStartIndex) &&
1523
- this.getUnifiedLine(this.unifiedLineLength - 1)?.isHidden);
1524
- }
1525
- };
1526
3197
  getExpandEnabled = () => !this.#composeByDiff;
1527
3198
  getBundle = () => {
1528
3199
  // common
@@ -1543,11 +3214,10 @@ class DiffFile {
1543
3214
  const splitLeftLines = this.#splitLeftLines;
1544
3215
  const splitRightLines = this.#splitRightLines;
1545
3216
  const splitHunkLines = this.#splitHunksLines;
1546
- const splitLastStartIndex = this.#splitLastStartIndex;
1547
3217
  // unified
1548
3218
  const unifiedLines = this.#unifiedLines;
1549
3219
  const unifiedHunkLines = this.#unifiedHunksLines;
1550
- const unifiedLastStartIndex = this.#unifiedLastStartIndex;
3220
+ const version = this._version_;
1551
3221
  return {
1552
3222
  hasInitRaw,
1553
3223
  hasInitSyntax,
@@ -1564,11 +3234,10 @@ class DiffFile {
1564
3234
  splitLeftLines,
1565
3235
  splitRightLines,
1566
3236
  splitHunkLines,
1567
- splitLastStartIndex,
1568
3237
  unifiedLines,
1569
3238
  unifiedHunkLines,
1570
- unifiedLastStartIndex,
1571
3239
  composeByDiff,
3240
+ version
1572
3241
  };
1573
3242
  };
1574
3243
  mergeBundle = (data) => {
@@ -1588,10 +3257,11 @@ class DiffFile {
1588
3257
  this.#splitLeftLines = data.splitLeftLines;
1589
3258
  this.#splitRightLines = data.splitRightLines;
1590
3259
  this.#splitHunksLines = data.splitHunkLines;
1591
- this.#splitLastStartIndex = data.splitLastStartIndex;
1592
3260
  this.#unifiedLines = data.unifiedLines;
1593
3261
  this.#unifiedHunksLines = data.unifiedHunkLines;
1594
- this.#unifiedLastStartIndex = data.unifiedLastStartIndex;
3262
+ if (this._version_ !== data.version) {
3263
+ console.error('the version of the `diffInstance` is not match, some error may happen!');
3264
+ }
1595
3265
  this.notifyAll();
1596
3266
  };
1597
3267
  _addClonedInstance = (instance) => {
@@ -1641,6 +3311,24 @@ class DiffFile {
1641
3311
  this.#clonedInstance.forEach((v) => v());
1642
3312
  this.#clonedInstance.clear();
1643
3313
  };
3314
+ clear = () => {
3315
+ this._destroy();
3316
+ this.#oldFileResult = null;
3317
+ this.#newFileResult = null;
3318
+ this.#diffLines = null;
3319
+ this.#diffListResults = null;
3320
+ this.#newFileDiffLines = null;
3321
+ this.#oldFileDiffLines = null;
3322
+ this.#newFileLines = null;
3323
+ this.#oldFileLines = null;
3324
+ this.#newFileSyntaxLines = null;
3325
+ this.#oldFileSyntaxLines = null;
3326
+ this.#splitHunksLines = null;
3327
+ this.#splitLeftLines = null;
3328
+ this.#splitRightLines = null;
3329
+ this.#unifiedHunksLines = null;
3330
+ this.#unifiedLines = null;
3331
+ };
1644
3332
  }
1645
3333
 
1646
3334
  var DiffFileLineType;
@@ -1649,7 +3337,6 @@ var DiffFileLineType;
1649
3337
  DiffFileLineType[DiffFileLineType["content"] = 2] = "content";
1650
3338
  DiffFileLineType[DiffFileLineType["widget"] = 3] = "widget";
1651
3339
  DiffFileLineType[DiffFileLineType["extend"] = 4] = "extend";
1652
- DiffFileLineType[DiffFileLineType["lastHunk"] = 5] = "lastHunk";
1653
3340
  })(DiffFileLineType || (DiffFileLineType = {}));
1654
3341
  const getSplitLines = (diffFile, options) => {
1655
3342
  const splitLineLength = diffFile.splitLineLength;
@@ -1677,15 +3364,6 @@ const getSplitLines = (diffFile, options) => {
1677
3364
  extendLine &&
1678
3365
  splitLines.push({ type: DiffFileLineType.extend, index, lineNumber: index + 1, extendLine: extendLine });
1679
3366
  });
1680
- const lastHunkLine = diffFile.getNeedShowExpandAll("split");
1681
- const expandEnabled = diffFile.getExpandEnabled();
1682
- if (lastHunkLine && expandEnabled) {
1683
- splitLines.push({
1684
- type: DiffFileLineType.lastHunk,
1685
- index: splitLineLength,
1686
- lineNumber: splitLineLength + 1,
1687
- });
1688
- }
1689
3367
  return splitLines;
1690
3368
  };
1691
3369
  const getSplitContentLines = (diffFile) => {
@@ -1711,15 +3389,6 @@ const getUnifiedLines = (diffFile, options) => {
1711
3389
  extendLine &&
1712
3390
  unifiedLines.push({ type: DiffFileLineType.extend, index, lineNumber: index + 1, extendLine: extendLine });
1713
3391
  });
1714
- const lastHunkLine = diffFile.getNeedShowExpandAll("unified");
1715
- const expandEnabled = diffFile.getExpandEnabled();
1716
- if (lastHunkLine && expandEnabled) {
1717
- unifiedLines.push({
1718
- type: DiffFileLineType.lastHunk,
1719
- index: unifiedLineLength,
1720
- lineNumber: unifiedLineLength + 1,
1721
- });
1722
- }
1723
3392
  return unifiedLines;
1724
3393
  };
1725
3394
  const getUnifiedContentLine = (diffFile) => {
@@ -1733,6 +3402,47 @@ const useUnmount = (cb, deps) => {
1733
3402
  React.useEffect(() => ref.current, deps);
1734
3403
  };
1735
3404
 
3405
+ const isClient = typeof window !== "undefined";
3406
+ const useSafeLayout = isClient ? React.useLayoutEffect : React.useEffect;
3407
+
3408
+ let canvasCtx = null;
3409
+ class TextMeasure {
3410
+ #key = "";
3411
+ #map = {};
3412
+ #getInstance() {
3413
+ canvasCtx = canvasCtx || document.createElement("canvas").getContext("2d");
3414
+ return canvasCtx;
3415
+ }
3416
+ measure(text, font) {
3417
+ const currentKey = `${font?.fontFamily}-${font?.fontStyle}-${font?.fontSize}-${text}`;
3418
+ if (this.#map[currentKey]) {
3419
+ return this.#map[currentKey];
3420
+ }
3421
+ const instance = this.#getInstance();
3422
+ if (font) {
3423
+ const currentFontKey = `${font.fontFamily}-${font.fontStyle}-${font.fontSize}`;
3424
+ if (this.#key !== currentFontKey) {
3425
+ this.#key = currentFontKey;
3426
+ instance.font = `${font.fontStyle || ""} ${font.fontSize || ""} ${font.fontFamily || ""}`;
3427
+ }
3428
+ }
3429
+ else {
3430
+ instance.font = "";
3431
+ }
3432
+ const textWidth = instance.measureText(text).width;
3433
+ return textWidth;
3434
+ }
3435
+ }
3436
+ const measureInstance = new TextMeasure();
3437
+ const useTextWidth = ({ text, font, }) => {
3438
+ const [width, setWidth] = React.useState(0);
3439
+ useSafeLayout(() => {
3440
+ const width = measureInstance.measure(text, font);
3441
+ setWidth(width);
3442
+ }, [text, font]);
3443
+ return width;
3444
+ };
3445
+
1736
3446
  const useDomWidth = ({ selector, enable }) => {
1737
3447
  const [width, setWidth] = React.useState(0);
1738
3448
  const { useDiffContext } = useDiffViewContext();
@@ -1783,9 +3493,6 @@ const DiffViewContext = React.createContext(null);
1783
3493
  DiffViewContext.displayName = "DiffViewContext";
1784
3494
  const useDiffViewContext = () => React.useContext(DiffViewContext);
1785
3495
 
1786
- const isClient = typeof window !== "undefined";
1787
- const useSafeLayout = isClient ? React.useLayoutEffect : React.useEffect;
1788
-
1789
3496
  const useSyncHeight = ({ selector, side, enable, }) => {
1790
3497
  const { useDiffContext } = useDiffViewContext();
1791
3498
  const id = useDiffContext(React.useCallback((s) => s.id, []));
@@ -1934,22 +3641,26 @@ const _DiffSplitHunkLine = ({ index, diffFile, side, lineNumber, }) => {
1934
3641
  currentHunk.splitInfo &&
1935
3642
  currentHunk.splitInfo.endHiddenIndex - currentHunk.splitInfo.startHiddenIndex < composeLen;
1936
3643
  const isFirstLine = currentHunk && currentHunk.index === 0;
3644
+ const isLastLine = currentHunk && currentHunk.isLast;
1937
3645
  return (React__namespace.createElement("tr", { "data-line": `${lineNumber}-hunk`, "data-state": "hunk", "data-side": exports.SplitSide[side], className: "diff-line diff-line-hunk" }, enableHunkAction ? (React__namespace.createElement(React__namespace.Fragment, null,
1938
3646
  React__namespace.createElement("td", { className: "diff-line-hunk-action sticky left-0 p-[1px] w-[1%] min-w-[40px] select-none", style: {
1939
3647
  backgroundColor: `var(${hunkLineNumberBGName})`,
1940
3648
  color: `var(${plainLineNumberColorName})`,
1941
3649
  } }, expandEnabled ? (isFirstLine ? (React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[6px] cursor-pointer rounded-[2px]", title: "Expand Up", "data-title": "Expand Up", onClick: () => diffFile.onSplitHunkExpand("up", index) },
1942
- React__namespace.createElement(ExpandUp, { className: "fill-current" }))) : isExpandAll ? (React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[6px] cursor-pointer rounded-[2px]", title: "Expand All", "data-title": "Expand All", onClick: () => diffFile.onSplitHunkExpand("all", index) },
3650
+ React__namespace.createElement(ExpandUp, { className: "fill-current" }))) : isLastLine ? (React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[6px] cursor-pointer rounded-[2px] relative", title: "Expand Down", "data-title": "Expand Down", onClick: () => {
3651
+ diffFile.onSplitHunkExpand("down", index);
3652
+ } },
3653
+ React__namespace.createElement(ExpandDown, { className: "fill-current" }))) : isExpandAll ? (React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[6px] cursor-pointer rounded-[2px]", title: "Expand All", "data-title": "Expand All", onClick: () => diffFile.onSplitHunkExpand("all", index) },
1943
3654
  React__namespace.createElement(ExpandAll, { className: "fill-current" }))) : (React__namespace.createElement(React__namespace.Fragment, null,
1944
3655
  React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[2px] cursor-pointer rounded-[2px]", title: "Expand Down", "data-title": "Expand Down", onClick: () => diffFile.onSplitHunkExpand("down", index) },
1945
3656
  React__namespace.createElement(ExpandDown, { className: "fill-current" })),
1946
3657
  React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[2px] cursor-pointer rounded-[2px]", title: "Expand Up", "data-title": "Expand Up", onClick: () => diffFile.onSplitHunkExpand("up", index) },
1947
- React__namespace.createElement(ExpandUp, { className: "fill-current" }))))) : (React__namespace.createElement("span", null, "\u2002"))),
3658
+ React__namespace.createElement(ExpandUp, { className: "fill-current" }))))) : (React__namespace.createElement("div", { className: "min-h-[28px]" }, "\u2002"))),
1948
3659
  React__namespace.createElement("td", { className: "diff-line-hunk-content pr-[10px] align-middle", style: { backgroundColor: `var(${hunkContentBGName})` } },
1949
3660
  React__namespace.createElement("div", { className: "pl-[1.5em]", style: {
1950
3661
  color: `var(${hunkContentColorName})`,
1951
3662
  } }, currentHunk.splitInfo.plainText)))) : (React__namespace.createElement("td", { className: "diff-line-hunk-placeholder select-none", colSpan: 2, style: { backgroundColor: `var(${hunkContentBGName})` } },
1952
- React__namespace.createElement("span", null, "\u2002")))));
3663
+ React__namespace.createElement("div", { className: "min-h-[28px]" }, "\u2002")))));
1953
3664
  };
1954
3665
  const DiffSplitHunkLine$1 = ({ index, diffFile, side, lineNumber, }) => {
1955
3666
  const currentHunk = diffFile.getSplitHunkLine(index);
@@ -1960,33 +3671,6 @@ const DiffSplitHunkLine$1 = ({ index, diffFile, side, lineNumber, }) => {
1960
3671
  return null;
1961
3672
  return React__namespace.createElement(_DiffSplitHunkLine, { index: index, diffFile: diffFile, side: side, lineNumber: lineNumber });
1962
3673
  };
1963
- const _DiffSplitLastHunkLine = ({ diffFile, side }) => {
1964
- const enableHunkAction = side === exports.SplitSide.old;
1965
- useSyncHeight({
1966
- selector: `tr[data-line="last-hunk"]`,
1967
- side: exports.SplitSide[exports.SplitSide.old],
1968
- enable: side === exports.SplitSide.new,
1969
- });
1970
- return (React__namespace.createElement("tr", { "data-line": "last-hunk", "data-state": "hunk", "data-side": exports.SplitSide[side], className: "diff-line diff-line-hunk" }, enableHunkAction ? (React__namespace.createElement(React__namespace.Fragment, null,
1971
- React__namespace.createElement("td", { className: "diff-line-hunk-action sticky left-0 p-[1px] w-[1%] min-w-[40px] select-none", style: {
1972
- backgroundColor: `var(${hunkLineNumberBGName})`,
1973
- color: `var(${plainLineNumberColorName})`,
1974
- } },
1975
- React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[6px] cursor-pointer rounded-[2px] relative", title: "Expand Down", "data-title": "Expand Down", onClick: () => {
1976
- diffFile.onSplitLastExpand();
1977
- } },
1978
- React__namespace.createElement(ExpandDown, { className: "fill-current" }))),
1979
- React__namespace.createElement("td", { className: "diff-line-hunk-content pr-[10px] align-middle select-none", style: { backgroundColor: `var(${hunkContentBGName})` } },
1980
- React__namespace.createElement("span", null, "\u2002")))) : (React__namespace.createElement("td", { className: "diff-line-hunk-placeholder select-none", colSpan: 2, style: { backgroundColor: `var(${hunkContentBGName})` } },
1981
- React__namespace.createElement("span", null, "\u2002")))));
1982
- };
1983
- const DiffSplitLastHunkLine$1 = ({ diffFile, side }) => {
1984
- const currentIsShow = diffFile.getNeedShowExpandAll("split");
1985
- const expandEnabled = diffFile.getExpandEnabled();
1986
- if (!currentIsShow || !expandEnabled)
1987
- return null;
1988
- return React__namespace.createElement(_DiffSplitLastHunkLine, { diffFile: diffFile, side: side });
1989
- };
1990
3674
 
1991
3675
  const DiffSplitAddWidget = ({ side, className, lineNumber, onWidgetClick, onOpenAddWidget, }) => {
1992
3676
  return (React__namespace.createElement("div", { className: "diff-add-widget-wrapper" + (className ? " " + className : ""), style: {
@@ -2021,16 +3705,25 @@ const DiffUnifiedAddWidget = ({ lineNumber, side, onWidgetClick, onOpenAddWidget
2021
3705
 
2022
3706
  const DiffString = ({ rawLine, diffLine, operator, }) => {
2023
3707
  const range = diffLine?.range;
2024
- if (range && range.length > 0 && range.length < rawLine.length) {
3708
+ if (range) {
2025
3709
  const str1 = rawLine.slice(0, range.location);
2026
3710
  const str2 = rawLine.slice(range.location, range.location + range.length);
2027
3711
  const str3 = rawLine.slice(range.location + range.length);
3712
+ const isNewLineSymbolChanged = range.isNewLineSymbolChanged;
2028
3713
  return (React__namespace.createElement("span", { className: "diff-line-content-raw" },
2029
3714
  React__namespace.createElement("span", { "data-range-start": range.location, "data-range-end": range.location + range.length },
2030
3715
  str1,
2031
3716
  React__namespace.createElement("span", { "data-diff-highlight": true, className: "rounded-[0.2em]", style: {
2032
3717
  backgroundColor: operator === "add" ? `var(${addContentHighlightBGName})` : `var(${delContentHighlightBGName})`,
2033
- } }, str2 === "\r" ? "␍" : str2 === "\n" ? "␊" : str2 === "\r\n" ? "␍␊" : str2),
3718
+ } }, isNewLineSymbolChanged
3719
+ ? str2 === "\r"
3720
+ ? "␍"
3721
+ : str2 === "\n"
3722
+ ? "␊"
3723
+ : str2 === "\r\n"
3724
+ ? "␍␊"
3725
+ : str2
3726
+ : str2),
2034
3727
  str3)));
2035
3728
  }
2036
3729
  return React__namespace.createElement("span", { className: "diff-line-content-raw" }, rawLine);
@@ -2040,7 +3733,7 @@ const DiffSyntax = ({ rawLine, diffLine, operator, syntaxLine, }) => {
2040
3733
  return React__namespace.createElement(DiffString, { rawLine: rawLine, diffLine: diffLine, operator: operator });
2041
3734
  }
2042
3735
  const range = diffLine?.range;
2043
- if (range && range.length > 0 && range.length < syntaxLine.valueLength) {
3736
+ if (range) {
2044
3737
  return (React__namespace.createElement("span", { className: "diff-line-syntax-raw" },
2045
3738
  React__namespace.createElement("span", { "data-range-start": range.location, "data-range-end": range.location + range.length }, syntaxLine.nodeList?.map(({ node, wrapper }, index) => {
2046
3739
  if (node.endIndex < range.location || range.location + range.length < node.startIndex) {
@@ -2054,6 +3747,7 @@ const DiffSyntax = ({ rawLine, diffLine, operator, syntaxLine, }) => {
2054
3747
  const str3 = node.value.slice(index1 + range.length);
2055
3748
  const isStart = str1.length || range.location === node.startIndex;
2056
3749
  const isEnd = str3.length || node.endIndex === range.location + range.length - 1;
3750
+ const isNewLineSymbolChanged = range.isNewLineSymbolChanged;
2057
3751
  return (React__namespace.createElement("span", { key: index, "data-start": node.startIndex, "data-end": node.endIndex, className: wrapper?.properties?.className?.join(" ") },
2058
3752
  str1,
2059
3753
  React__namespace.createElement("span", { "data-diff-highlight": true, style: {
@@ -2062,7 +3756,15 @@ const DiffSyntax = ({ rawLine, diffLine, operator, syntaxLine, }) => {
2062
3756
  borderBottomLeftRadius: isStart ? "0.2em" : undefined,
2063
3757
  borderTopRightRadius: isEnd ? "0.2em" : undefined,
2064
3758
  borderBottomRightRadius: isEnd ? "0.2em" : undefined,
2065
- } }, str2 === "\r" ? "␍" : str2 === "\n" ? "␊" : str2 === "\r\n" ? "␍␊" : str2),
3759
+ } }, isNewLineSymbolChanged
3760
+ ? str2 === "\r"
3761
+ ? "␍"
3762
+ : str2 === "\n"
3763
+ ? "␊"
3764
+ : str2 === "\r\n"
3765
+ ? "␍␊"
3766
+ : str2
3767
+ : str2),
2066
3768
  str3));
2067
3769
  }
2068
3770
  }))));
@@ -2072,12 +3774,13 @@ const DiffSyntax = ({ rawLine, diffLine, operator, syntaxLine, }) => {
2072
3774
  const DiffContent = ({ diffLine, rawLine, syntaxLine, enableWrap, enableHighlight, }) => {
2073
3775
  const isAdded = diffLine?.type === DiffLineType.Add;
2074
3776
  const isDelete = diffLine?.type === DiffLineType.Delete;
3777
+ const isMaxLineLengthToIgnoreSyntax = syntaxLine?.nodeList?.length > 150;
2075
3778
  return (React__namespace.createElement("div", { className: "diff-line-content-item pl-[2.0em]", "data-val": rawLine, style: {
2076
3779
  whiteSpace: enableWrap ? "pre-wrap" : "pre",
2077
3780
  wordBreak: enableWrap ? "break-all" : "initial",
2078
3781
  } },
2079
3782
  React__namespace.createElement("span", { "data-operator": isAdded ? "+" : isDelete ? "-" : undefined, className: "diff-line-content-operator inline-block w-[1.5em] ml-[-1.5em] indent-[0.2em] select-none" }, isAdded ? "+" : isDelete ? "-" : " "),
2080
- enableHighlight && syntaxLine ? (React__namespace.createElement(DiffSyntax, { operator: isAdded ? "add" : isDelete ? "del" : undefined, rawLine: rawLine, diffLine: diffLine, syntaxLine: syntaxLine })) : (React__namespace.createElement(DiffString, { operator: isAdded ? "add" : isDelete ? "del" : undefined, rawLine: rawLine, diffLine: diffLine }))));
3783
+ enableHighlight && syntaxLine && !isMaxLineLengthToIgnoreSyntax ? (React__namespace.createElement(DiffSyntax, { operator: isAdded ? "add" : isDelete ? "del" : undefined, rawLine: rawLine, diffLine: diffLine, syntaxLine: syntaxLine })) : (React__namespace.createElement(DiffString, { operator: isAdded ? "add" : isDelete ? "del" : undefined, rawLine: rawLine, diffLine: diffLine }))));
2081
3784
  };
2082
3785
 
2083
3786
  const DiffWidgetContext = React.createContext(null);
@@ -2203,12 +3906,12 @@ const onMouseDown$1 = (e) => {
2203
3906
  return;
2204
3907
  }
2205
3908
  };
2206
- const DiffSplitViewTable = ({ side, diffFile }) => {
3909
+ const DiffSplitViewTable = ({ side, diffFile, width }) => {
2207
3910
  const className = side === exports.SplitSide.new ? "new-diff-table" : "old-diff-table";
2208
3911
  const lines = getSplitContentLines(diffFile);
2209
3912
  return (React__namespace.createElement("table", { className: className + " border-collapse w-full", "data-mode": exports.SplitSide[side] },
2210
3913
  React__namespace.createElement("colgroup", null,
2211
- React__namespace.createElement("col", { className: `diff-table-${exports.SplitSide[side]}-num-col` }),
3914
+ React__namespace.createElement("col", { className: `diff-table-${exports.SplitSide[side]}-num-col`, style: { minWidth: Math.round(width) + 25 } }),
2212
3915
  React__namespace.createElement("col", { className: `diff-table-${exports.SplitSide[side]}-content-col` })),
2213
3916
  React__namespace.createElement("thead", { className: "hidden" },
2214
3917
  React__namespace.createElement("tr", null,
@@ -2224,11 +3927,14 @@ const DiffSplitViewTable = ({ side, diffFile }) => {
2224
3927
  React__namespace.createElement(DiffSplitLine$1, { index: line.index, side: side, lineNumber: line.lineNumber, diffFile: diffFile }),
2225
3928
  React__namespace.createElement(DiffSplitWidgetLine$1, { index: line.index, side: side, lineNumber: line.lineNumber, diffFile: diffFile }),
2226
3929
  React__namespace.createElement(DiffSplitExtendLine$1, { index: line.index, side: side, lineNumber: line.lineNumber, diffFile: diffFile })))),
2227
- React__namespace.createElement(DiffSplitLastHunkLine$1, { side: side, diffFile: diffFile }))));
3930
+ React__namespace.createElement(DiffSplitHunkLine$1, { side: side, index: diffFile.splitLineLength, lineNumber: diffFile.splitLineLength, diffFile: diffFile }))));
2228
3931
  };
2229
3932
  const DiffSplitViewNormal = React.memo(({ diffFile }) => {
2230
3933
  const ref1 = React.useRef(null);
2231
3934
  const ref2 = React.useRef(null);
3935
+ const splitLineLength = diffFile.splitLineLength;
3936
+ const { useDiffContext } = useDiffViewContext();
3937
+ const fontSize = useDiffContext(React.useCallback((s) => s.fontSize, []));
2232
3938
  shim.useSyncExternalStore(diffFile.subscribe, diffFile.getUpdateCount);
2233
3939
  React.useEffect(() => {
2234
3940
  const left = ref1.current;
@@ -2237,61 +3943,27 @@ const DiffSplitViewNormal = React.memo(({ diffFile }) => {
2237
3943
  return;
2238
3944
  return syncScroll(left, right);
2239
3945
  }, []);
3946
+ const width = useTextWidth({
3947
+ text: splitLineLength.toString(),
3948
+ font: { fontSize: fontSize + "px", fontFamily: "Menlo, Consolas, monospace" },
3949
+ });
2240
3950
  return (React__namespace.createElement("div", { className: "split-diff-view split-diff-view-wrap w-full flex basis-[50%]" },
2241
3951
  React__namespace.createElement("div", { className: "old-diff-table-wrapper overflow-auto w-full scrollbar-hide scrollbar-disable", ref: ref1, style: {
2242
3952
  overscrollBehaviorX: "none",
2243
3953
  fontFamily: "Menlo, Consolas, monospace",
2244
3954
  fontSize: "var(--diff-font-size--)",
2245
3955
  } },
2246
- React__namespace.createElement(DiffSplitViewTable, { side: exports.SplitSide.old, diffFile: diffFile })),
3956
+ React__namespace.createElement(DiffSplitViewTable, { side: exports.SplitSide.old, diffFile: diffFile, width: width })),
2247
3957
  React__namespace.createElement("div", { className: "diff-split-line w-[1.5px] bg-[#ccc]" }),
2248
3958
  React__namespace.createElement("div", { className: "new-diff-table-wrapper overflow-auto w-full scrollbar-hide scrollbar-disable", ref: ref2, style: {
2249
3959
  overscrollBehaviorX: "none",
2250
3960
  fontFamily: "Menlo, Consolas, monospace",
2251
3961
  fontSize: "var(--diff-font-size--)",
2252
3962
  } },
2253
- React__namespace.createElement(DiffSplitViewTable, { side: exports.SplitSide.new, diffFile: diffFile }))));
3963
+ React__namespace.createElement(DiffSplitViewTable, { side: exports.SplitSide.new, diffFile: diffFile, width: width }))));
2254
3964
  });
2255
3965
  DiffSplitViewNormal.displayName = "DiffSplitViewNormal";
2256
3966
 
2257
- let canvasCtx = null;
2258
- class TextMeasure {
2259
- #key = "";
2260
- #map = {};
2261
- #getInstance() {
2262
- canvasCtx = canvasCtx || document.createElement("canvas").getContext("2d");
2263
- return canvasCtx;
2264
- }
2265
- measure(text, font) {
2266
- const currentKey = `${font?.fontFamily}-${font?.fontStyle}-${font?.fontSize}-${text}`;
2267
- if (this.#map[currentKey]) {
2268
- return this.#map[currentKey];
2269
- }
2270
- const instance = this.#getInstance();
2271
- if (font) {
2272
- const currentFontKey = `${font.fontFamily}-${font.fontStyle}-${font.fontSize}`;
2273
- if (this.#key !== currentFontKey) {
2274
- this.#key = currentFontKey;
2275
- instance.font = `${font.fontStyle || ""} ${font.fontSize || ""} ${font.fontFamily || ""}`;
2276
- }
2277
- }
2278
- else {
2279
- instance.font = "";
2280
- }
2281
- const textWidth = instance.measureText(text).width;
2282
- return textWidth;
2283
- }
2284
- }
2285
- const measureInstance = new TextMeasure();
2286
- const useTextWidth = ({ text, font, }) => {
2287
- const [width, setWidth] = React.useState(0);
2288
- useSafeLayout(() => {
2289
- const width = measureInstance.measure(text, font);
2290
- setWidth(width);
2291
- }, [text, font]);
2292
- return width;
2293
- };
2294
-
2295
3967
  const _DiffSplitExtendLine = ({ index, diffFile, lineNumber, }) => {
2296
3968
  const { useDiffContext } = useDiffViewContext();
2297
3969
  // 需要显示的时候才进行方法订阅,可以大幅度提高性能
@@ -2345,42 +4017,26 @@ const DiffSplitHunkLine = ({ index, diffFile, lineNumber, }) => {
2345
4017
  currentHunk.splitInfo &&
2346
4018
  currentHunk.splitInfo.startHiddenIndex < currentHunk.splitInfo.endHiddenIndex;
2347
4019
  const isFirstLine = currentHunk && currentHunk.index === 0;
4020
+ const isLastLine = currentHunk && currentHunk.isLast;
2348
4021
  if (!currentIsShow)
2349
4022
  return null;
2350
4023
  return (React__namespace.createElement("tr", { "data-line": `${lineNumber}-hunk`, "data-state": "hunk", className: "diff-line diff-line-hunk" },
2351
4024
  React__namespace.createElement("td", { className: "diff-line-hunk-action p-[1px] w-[1%] min-w-[40px] select-none", style: {
2352
4025
  backgroundColor: `var(${hunkLineNumberBGName})`,
2353
4026
  color: `var(${plainLineNumberColorName})`,
2354
- } }, expandEnabled &&
2355
- (isFirstLine ? (React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[6px] cursor-pointer rounded-[2px]", title: "Expand Up", "data-title": "Expand Up", onClick: () => diffFile.onSplitHunkExpand("up", index) },
2356
- React__namespace.createElement(ExpandUp, { className: "fill-current" }))) : isExpandAll ? (React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[6px] cursor-pointer rounded-[2px]", title: "Expand All", "data-title": "Expand All", onClick: () => diffFile.onSplitHunkExpand("all", index) },
2357
- React__namespace.createElement(ExpandAll, { className: "fill-current" }))) : (React__namespace.createElement(React__namespace.Fragment, null,
2358
- React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[2px] cursor-pointer rounded-[2px]", title: "Expand Down", "data-title": "Expand Down", onClick: () => diffFile.onSplitHunkExpand("down", index) },
2359
- React__namespace.createElement(ExpandDown, { className: "fill-current" })),
2360
- React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[2px] cursor-pointer rounded-[2px]", title: "Expand Up", "data-title": "Expand Up", onClick: () => diffFile.onSplitHunkExpand("up", index) },
2361
- React__namespace.createElement(ExpandUp, { className: "fill-current" })))))),
4027
+ } }, expandEnabled ? (isFirstLine ? (React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[6px] cursor-pointer rounded-[2px]", title: "Expand Up", "data-title": "Expand Up", onClick: () => diffFile.onSplitHunkExpand("up", index) },
4028
+ React__namespace.createElement(ExpandUp, { className: "fill-current" }))) : isLastLine ? (React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[6px] cursor-pointer rounded-[2px]", title: "Expand Down", "data-title": "Expand Down", onClick: () => diffFile.onSplitHunkExpand("down", index) },
4029
+ React__namespace.createElement(ExpandDown, { className: "fill-current" }))) : isExpandAll ? (React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[6px] cursor-pointer rounded-[2px]", title: "Expand All", "data-title": "Expand All", onClick: () => diffFile.onSplitHunkExpand("all", index) },
4030
+ React__namespace.createElement(ExpandAll, { className: "fill-current" }))) : (React__namespace.createElement(React__namespace.Fragment, null,
4031
+ React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[2px] cursor-pointer rounded-[2px]", title: "Expand Down", "data-title": "Expand Down", onClick: () => diffFile.onSplitHunkExpand("down", index) },
4032
+ React__namespace.createElement(ExpandDown, { className: "fill-current" })),
4033
+ React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[2px] cursor-pointer rounded-[2px]", title: "Expand Up", "data-title": "Expand Up", onClick: () => diffFile.onSplitHunkExpand("up", index) },
4034
+ React__namespace.createElement(ExpandUp, { className: "fill-current" }))))) : (React__namespace.createElement("div", { className: "min-h-[28px]" }, "\u2002"))),
2362
4035
  React__namespace.createElement("td", { className: "diff-line-hunk-content pr-[10px] align-middle", style: { backgroundColor: `var(${hunkContentBGName})` }, colSpan: 3 },
2363
4036
  React__namespace.createElement("div", { className: "pl-[1.5em]", style: {
2364
4037
  color: `var(${hunkContentColorName})`,
2365
4038
  } }, currentHunk.splitInfo.plainText))));
2366
4039
  };
2367
- const DiffSplitLastHunkLine = ({ diffFile }) => {
2368
- const currentIsShow = diffFile.getNeedShowExpandAll("split");
2369
- const expandEnabled = diffFile.getExpandEnabled();
2370
- if (!currentIsShow || !expandEnabled)
2371
- return null;
2372
- return (React__namespace.createElement("tr", { "data-line": "last-hunk", "data-state": "hunk", className: "diff-line diff-line-hunk" },
2373
- React__namespace.createElement("td", { className: "diff-line-hunk-action p-[1px] w-[1%] min-w-[40px] select-none", style: {
2374
- backgroundColor: `var(${hunkLineNumberBGName})`,
2375
- color: `var(${plainLineNumberColorName})`,
2376
- } },
2377
- React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[6px] cursor-pointer rounded-[2px]", title: "Expand Down", "data-title": "Expand Down", onClick: () => {
2378
- diffFile.onSplitLastExpand();
2379
- } },
2380
- React__namespace.createElement(ExpandDown, { className: "fill-current" }))),
2381
- React__namespace.createElement("td", { className: "diff-line-hunk-content pr-[10px] align-middle select-none", colSpan: 3, style: { backgroundColor: `var(${hunkContentBGName})` } },
2382
- React__namespace.createElement("span", null, "\u2002"))));
2383
- };
2384
4040
 
2385
4041
  const _DiffSplitLine = ({ index, diffFile, lineNumber }) => {
2386
4042
  const oldLine = diffFile.getSplitLeftLine(index);
@@ -2473,7 +4129,6 @@ const DiffSplitWidgetLine = ({ index, diffFile, lineNumber, }) => {
2473
4129
  return React__namespace.createElement(_DiffSplitWidgetLine, { index: index, diffFile: diffFile, lineNumber: lineNumber });
2474
4130
  };
2475
4131
 
2476
- /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
2477
4132
  const Style = ({ useSelector, id, }) => {
2478
4133
  const splitRef = useSelector((s) => s.splitRef);
2479
4134
  return (React__namespace.createElement("style", null, splitRef === exports.SplitSide.old
@@ -2544,7 +4199,7 @@ const DiffSplitViewWrap = React.memo(({ diffFile }) => {
2544
4199
  React__namespace.createElement(DiffSplitLine, { index: line.index, lineNumber: line.lineNumber, diffFile: diffFile }),
2545
4200
  React__namespace.createElement(DiffSplitWidgetLine, { index: line.index, lineNumber: line.lineNumber, diffFile: diffFile }),
2546
4201
  React__namespace.createElement(DiffSplitExtendLine, { index: line.index, lineNumber: line.lineNumber, diffFile: diffFile })))),
2547
- React__namespace.createElement(DiffSplitLastHunkLine, { diffFile: diffFile }))))));
4202
+ React__namespace.createElement(DiffSplitHunkLine, { index: diffFile.splitLineLength, lineNumber: diffFile.splitLineLength, diffFile: diffFile }))))));
2548
4203
  });
2549
4204
  DiffSplitViewWrap.displayName = "DiffSplitViewWrap";
2550
4205
 
@@ -2556,11 +4211,14 @@ const DiffSplitView = React.memo(({ diffFile }) => {
2556
4211
  const widgetSide = reactivityStore.ref(undefined);
2557
4212
  const widgetLineNumber = reactivityStore.ref(undefined);
2558
4213
  const setWidget = ({ side, lineNumber }) => {
4214
+ const { renderWidgetLine } = useDiffContext.getReadonlyState();
4215
+ if (typeof renderWidgetLine !== "function")
4216
+ return;
2559
4217
  widgetSide.value = side;
2560
4218
  widgetLineNumber.value = lineNumber;
2561
4219
  };
2562
4220
  return { widgetSide, widgetLineNumber, setWidget };
2563
- }), []);
4221
+ }), [useDiffContext]);
2564
4222
  const contextValue = React.useMemo(() => ({ useWidget }), [useWidget]);
2565
4223
  shim.useSyncExternalStore(diffFile.subscribe, diffFile.getUpdateCount);
2566
4224
  React.useEffect(() => {
@@ -2623,17 +4281,19 @@ const _DiffUnifiedHunkLine = ({ index, diffFile, lineNumber, }) => {
2623
4281
  currentHunk.unifiedInfo &&
2624
4282
  currentHunk.unifiedInfo.endHiddenIndex - currentHunk.unifiedInfo.startHiddenIndex < composeLen;
2625
4283
  const isFirstLine = currentHunk && currentHunk.index === 0;
4284
+ const isLastLine = currentHunk && currentHunk.isLast;
2626
4285
  return (React__namespace.createElement("tr", { "data-line": `${lineNumber}-hunk`, "data-state": "hunk", className: "diff-line diff-line-hunk" },
2627
4286
  React__namespace.createElement("td", { className: "diff-line-hunk-action sticky left-0 p-[1px] w-[1%] min-w-[100px] select-none", style: {
2628
4287
  backgroundColor: `var(${hunkLineNumberBGName})`,
2629
4288
  color: `var(${plainLineNumberColorName})`,
2630
4289
  } }, expandEnabled ? (isFirstLine ? (React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[6px] cursor-pointer rounded-[2px]", title: "Expand Up", "data-title": "Expand Up", onClick: () => diffFile.onUnifiedHunkExpand("up", index) },
2631
- React__namespace.createElement(ExpandUp, { className: "fill-current" }))) : isExpandAll ? (React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[6px] cursor-pointer rounded-[2px]", title: "Expand All", "data-title": "Expand All", onClick: () => diffFile.onUnifiedHunkExpand("all", index) },
4290
+ React__namespace.createElement(ExpandUp, { className: "fill-current" }))) : isLastLine ? (React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[6px] cursor-pointer rounded-[2px]", title: "Expand Down", "data-title": "Expand Down", onClick: () => diffFile.onUnifiedHunkExpand("down", index) },
4291
+ React__namespace.createElement(ExpandDown, { className: "fill-current" }))) : isExpandAll ? (React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[6px] cursor-pointer rounded-[2px]", title: "Expand All", "data-title": "Expand All", onClick: () => diffFile.onUnifiedHunkExpand("all", index) },
2632
4292
  React__namespace.createElement(ExpandAll, { className: "fill-current" }))) : (React__namespace.createElement(React__namespace.Fragment, null,
2633
4293
  React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[2px] cursor-pointer rounded-[2px]", title: "Expand Down", "data-title": "Expand Down", onClick: () => diffFile.onUnifiedHunkExpand("down", index) },
2634
4294
  React__namespace.createElement(ExpandDown, { className: "fill-current" })),
2635
4295
  React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[2px] cursor-pointer rounded-[2px]", title: "Expand Up", "data-title": "Expand Up", onClick: () => diffFile.onUnifiedHunkExpand("up", index) },
2636
- React__namespace.createElement(ExpandUp, { className: "fill-current" }))))) : null),
4296
+ React__namespace.createElement(ExpandUp, { className: "fill-current" }))))) : (React__namespace.createElement("div", { className: "min-h-[28px]" }, "\u2002"))),
2637
4297
  React__namespace.createElement("td", { className: "diff-line-hunk-content pr-[10px] align-middle", style: { backgroundColor: `var(${hunkContentBGName})` } },
2638
4298
  React__namespace.createElement("div", { className: "pl-[1.5em]", style: {
2639
4299
  whiteSpace: enableWrap ? "pre-wrap" : "pre",
@@ -2650,21 +4310,6 @@ const DiffUnifiedHunkLine = ({ index, diffFile, lineNumber, }) => {
2650
4310
  return null;
2651
4311
  return React__namespace.createElement(_DiffUnifiedHunkLine, { index: index, diffFile: diffFile, lineNumber: lineNumber });
2652
4312
  };
2653
- const DiffUnifiedLastHunkLine = ({ diffFile }) => {
2654
- const currentIsShow = diffFile.getNeedShowExpandAll("unified");
2655
- const expandEnabled = diffFile.getExpandEnabled();
2656
- if (!currentIsShow || !expandEnabled)
2657
- return null;
2658
- return (React__namespace.createElement("tr", { "data-line": "last-hunk", "data-state": "hunk", className: "diff-line diff-line-hunk" },
2659
- React__namespace.createElement("td", { className: "diff-line-hunk-action sticky left-0 w-[1%] min-w-[100px] select-none", style: {
2660
- backgroundColor: `var(${hunkLineNumberBGName})`,
2661
- color: `var(${plainLineNumberColorName})`,
2662
- } },
2663
- React__namespace.createElement("button", { className: "w-full diff-widget-tooltip hover:bg-blue-300 flex justify-center items-center py-[6px] cursor-pointer rounded-[2px]", title: "Expand Down", "data-title": "Expand Down", onClick: () => diffFile.onUnifiedLastExpand() },
2664
- React__namespace.createElement(ExpandDown, { className: "fill-current" }))),
2665
- React__namespace.createElement("td", { className: "diff-line-hunk-content pr-[10px] align-middle", style: { backgroundColor: `var(${hunkContentBGName})` } },
2666
- React__namespace.createElement("span", null, "\u2002"))));
2667
- };
2668
4313
 
2669
4314
  const DiffUnifiedOldLine = ({ index, diffLine, rawLine, syntaxLine, lineNumber, diffFile, setWidget, enableWrap, enableAddWidget, enableHighlight, onAddWidgetClick, }) => {
2670
4315
  return (React__namespace.createElement("tr", { "data-line": index, "data-state": "diff", className: "diff-line group" },
@@ -2792,16 +4437,20 @@ const onMouseDown = (e) => {
2792
4437
  }
2793
4438
  };
2794
4439
  const DiffUnifiedView = React.memo(({ diffFile }) => {
4440
+ const { useDiffContext } = useDiffViewContext();
2795
4441
  // performance optimization
2796
4442
  const useWidget = React.useMemo(() => reactivityStore.createStore(() => {
2797
4443
  const widgetSide = reactivityStore.ref(undefined);
2798
4444
  const widgetLineNumber = reactivityStore.ref(undefined);
2799
4445
  const setWidget = ({ side, lineNumber }) => {
4446
+ const { renderWidgetLine } = useDiffContext.getReadonlyState();
4447
+ if (typeof renderWidgetLine !== "function")
4448
+ return;
2800
4449
  widgetSide.value = side;
2801
4450
  widgetLineNumber.value = lineNumber;
2802
4451
  };
2803
4452
  return { widgetSide, widgetLineNumber, setWidget };
2804
- }), []);
4453
+ }), [useDiffContext]);
2805
4454
  const contextValue = React.useMemo(() => ({ useWidget }), [useWidget]);
2806
4455
  shim.useSyncExternalStore(diffFile.subscribe, diffFile.getUpdateCount);
2807
4456
  React.useEffect(() => {
@@ -2826,7 +4475,7 @@ const DiffUnifiedView = React.memo(({ diffFile }) => {
2826
4475
  React__namespace.createElement(DiffUnifiedLine, { index: item.index, lineNumber: item.lineNumber, diffFile: diffFile }),
2827
4476
  React__namespace.createElement(DiffUnifiedWidgetLine, { index: item.index, lineNumber: item.lineNumber, diffFile: diffFile }),
2828
4477
  React__namespace.createElement(DiffUnifiedExtendLine, { index: item.index, lineNumber: item.lineNumber, diffFile: diffFile })))),
2829
- React__namespace.createElement(DiffUnifiedLastHunkLine, { diffFile: diffFile })))))));
4478
+ React__namespace.createElement(DiffUnifiedHunkLine, { index: diffFile.unifiedLineLength, lineNumber: diffFile.unifiedLineLength, diffFile: diffFile })))))));
2830
4479
  });
2831
4480
  DiffUnifiedView.displayName = "DiffUnifiedView";
2832
4481
 
@@ -2939,7 +4588,7 @@ const _InternalDiffView = (props) => {
2939
4588
  ]);
2940
4589
  const value = React.useMemo(() => ({ useDiffContext }), [useDiffContext]);
2941
4590
  return (React__namespace.createElement(DiffViewContext.Provider, { value: value },
2942
- React__namespace.createElement("div", { className: "diff-tailwindcss-wrapper" },
4591
+ React__namespace.createElement("div", { className: "diff-tailwindcss-wrapper", "data-component": "git-diff-view", "data-version": `${"0.0.6"}` },
2943
4592
  React__namespace.createElement("div", { className: "diff-style-root", style: {
2944
4593
  // @ts-ignore
2945
4594
  [diffFontSizeName]: diffViewFontSize + "px",
@@ -2947,7 +4596,7 @@ const _InternalDiffView = (props) => {
2947
4596
  React__namespace.createElement("div", { id: `diff-root${diffFileId}`, className: "diff-view-wrapper" + (className ? ` ${className}` : ""), style: style }, diffViewMode === exports.DiffModeEnum.Split ? (React__namespace.createElement(DiffSplitView, { diffFile: diffFile })) : (React__namespace.createElement(DiffUnifiedView, { diffFile: diffFile })))))));
2948
4597
  };
2949
4598
  const InternalDiffView = React.memo(_InternalDiffView);
2950
- const DiffView = (props) => {
4599
+ const DiffViewWithRef = (props, ref) => {
2951
4600
  const { registerHighlighter, autoDetectLang, data, diffFile: _diffFile, ...restProps } = props;
2952
4601
  const diffFile = React.useMemo(() => {
2953
4602
  if (_diffFile) {
@@ -2984,11 +4633,14 @@ const DiffView = (props) => {
2984
4633
  }
2985
4634
  }, [diffFile, _diffFile]);
2986
4635
  useUnmount(() => diffFile._destroy(), [diffFile]);
4636
+ React.useImperativeHandle(ref, () => ({ getDiffFileInstance: () => diffFile }), [diffFile]);
2987
4637
  if (!diffFile)
2988
4638
  return null;
2989
4639
  return (React__namespace.createElement(InternalDiffView, { key: diffFile.getId(), ...restProps, diffFile: diffFile, diffViewFontSize: restProps.diffViewFontSize || 14 }));
2990
4640
  };
2991
- const version = "0.0.3";
4641
+ const DiffView = React.forwardRef(DiffViewWithRef);
4642
+ DiffView.displayName = "DiffView";
4643
+ const version = "0.0.6";
2992
4644
 
2993
4645
  exports.DiffView = DiffView;
2994
4646
  exports.DiffViewContext = DiffViewContext;