pi-diff-review 0.1.5 → 0.1.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.
- package/package.json +1 -1
- package/src/review-component.ts +83 -34
package/package.json
CHANGED
package/src/review-component.ts
CHANGED
|
@@ -38,6 +38,12 @@ export class ReviewComponent {
|
|
|
38
38
|
private layout: ReviewLayout = "side-by-side";
|
|
39
39
|
private diffRenderMode: DiffRenderMode = "unified";
|
|
40
40
|
private editor: Editor;
|
|
41
|
+
private splitRows?: SplitDiffRow[];
|
|
42
|
+
private splitRowByLineIndex?: number[];
|
|
43
|
+
private lineIndexById = new Map<string, number>();
|
|
44
|
+
private commentLineKeys = new Map<number, string[]>();
|
|
45
|
+
private commentsRevision = 0;
|
|
46
|
+
private commentLineKeysRevision = -1;
|
|
41
47
|
|
|
42
48
|
constructor(
|
|
43
49
|
private tui: ReviewTui,
|
|
@@ -49,6 +55,7 @@ export class ReviewComponent {
|
|
|
49
55
|
) {
|
|
50
56
|
const firstCommentable = this.lines.findIndex((line) => line.commentable);
|
|
51
57
|
this.selected = firstCommentable >= 0 ? firstCommentable : 0;
|
|
58
|
+
this.lines.forEach((line, index) => this.lineIndexById.set(line.id, index));
|
|
52
59
|
|
|
53
60
|
this.editor = new Editor(tui as never, {
|
|
54
61
|
borderColor: (s) => theme.fg("accent", s),
|
|
@@ -71,12 +78,13 @@ export class ReviewComponent {
|
|
|
71
78
|
const trimmed = value.trim();
|
|
72
79
|
const key = this.getSelectionKey(selection.start, selection.end);
|
|
73
80
|
if (!trimmed) {
|
|
74
|
-
this.comments.delete(key);
|
|
81
|
+
if (this.comments.delete(key)) this.markCommentsChanged();
|
|
75
82
|
} else {
|
|
76
83
|
this.comments.set(
|
|
77
84
|
key,
|
|
78
85
|
this.buildCommentFromSelection(selection, trimmed),
|
|
79
86
|
);
|
|
87
|
+
this.markCommentsChanged();
|
|
80
88
|
}
|
|
81
89
|
|
|
82
90
|
this.exitEditMode();
|
|
@@ -276,7 +284,7 @@ export class ReviewComponent {
|
|
|
276
284
|
}
|
|
277
285
|
|
|
278
286
|
private renderSplitDiffRows(width: number, height: number): string[] {
|
|
279
|
-
const rows = this.
|
|
287
|
+
const rows = this.getSplitDiffRows();
|
|
280
288
|
const output: string[] = [];
|
|
281
289
|
const separatorWidth = 3;
|
|
282
290
|
const leftWidth = Math.max(10, Math.floor((width - separatorWidth) / 2));
|
|
@@ -345,17 +353,25 @@ export class ReviewComponent {
|
|
|
345
353
|
invalidate(): void {}
|
|
346
354
|
|
|
347
355
|
private move(delta: number): void {
|
|
348
|
-
|
|
356
|
+
const next = Math.max(
|
|
349
357
|
0,
|
|
350
358
|
Math.min(this.lines.length - 1, this.selected + delta),
|
|
351
359
|
);
|
|
360
|
+
if (next === this.selected) return;
|
|
361
|
+
this.selected = next;
|
|
352
362
|
this.tui.requestRender();
|
|
353
363
|
}
|
|
354
364
|
|
|
355
365
|
private jumpToBoundary(boundary: "start" | "end"): void {
|
|
356
|
-
this.
|
|
357
|
-
|
|
358
|
-
this.
|
|
366
|
+
const next = boundary === "start" ? 0 : Math.max(0, this.lines.length - 1);
|
|
367
|
+
const hadSelection = this.selectionAnchor != null;
|
|
368
|
+
if (next === this.selected && !hadSelection) return;
|
|
369
|
+
this.selected = next;
|
|
370
|
+
if (hadSelection) {
|
|
371
|
+
this.clearSelection();
|
|
372
|
+
} else {
|
|
373
|
+
this.tui.requestRender();
|
|
374
|
+
}
|
|
359
375
|
}
|
|
360
376
|
|
|
361
377
|
private toggleLayout(): void {
|
|
@@ -383,14 +399,17 @@ export class ReviewComponent {
|
|
|
383
399
|
if (this.selectionAnchor == null) {
|
|
384
400
|
this.selectionAnchor = this.selected;
|
|
385
401
|
}
|
|
386
|
-
|
|
402
|
+
const next = Math.max(
|
|
387
403
|
0,
|
|
388
404
|
Math.min(this.lines.length - 1, this.selected + delta),
|
|
389
405
|
);
|
|
406
|
+
if (next === this.selected) return;
|
|
407
|
+
this.selected = next;
|
|
390
408
|
this.tui.requestRender();
|
|
391
409
|
}
|
|
392
410
|
|
|
393
411
|
private clearSelection(): void {
|
|
412
|
+
if (this.selectionAnchor == null) return;
|
|
394
413
|
this.selectionAnchor = undefined;
|
|
395
414
|
this.tui.requestRender();
|
|
396
415
|
}
|
|
@@ -431,19 +450,35 @@ export class ReviewComponent {
|
|
|
431
450
|
}
|
|
432
451
|
|
|
433
452
|
private getCommentKeysForLine(index: number): string[] {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
453
|
+
this.ensureCommentLineKeys();
|
|
454
|
+
return this.commentLineKeys.get(index) ?? [];
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
private markCommentsChanged(): void {
|
|
458
|
+
this.commentsRevision++;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
private ensureCommentLineKeys(): void {
|
|
462
|
+
if (this.commentLineKeysRevision === this.commentsRevision) return;
|
|
463
|
+
|
|
464
|
+
this.commentLineKeys = new Map<number, string[]>();
|
|
465
|
+
for (const [key, comment] of this.comments) {
|
|
466
|
+
const start = this.lineIndexById.get(comment.startLineId);
|
|
467
|
+
const end = this.lineIndexById.get(comment.endLineId);
|
|
468
|
+
if (start == null || end == null) continue;
|
|
469
|
+
const from = Math.min(start, end);
|
|
470
|
+
const to = Math.max(start, end);
|
|
471
|
+
for (let index = from; index <= to; index++) {
|
|
472
|
+
const keys = this.commentLineKeys.get(index);
|
|
473
|
+
if (keys) {
|
|
474
|
+
keys.push(key);
|
|
475
|
+
} else {
|
|
476
|
+
this.commentLineKeys.set(index, [key]);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
this.commentLineKeysRevision = this.commentsRevision;
|
|
447
482
|
}
|
|
448
483
|
|
|
449
484
|
private buildCommentFromSelection(
|
|
@@ -503,7 +538,11 @@ export class ReviewComponent {
|
|
|
503
538
|
private deleteComment(): void {
|
|
504
539
|
const selection = this.getActiveCommentSelection();
|
|
505
540
|
if (!selection) return;
|
|
506
|
-
|
|
541
|
+
if (
|
|
542
|
+
this.comments.delete(this.getSelectionKey(selection.start, selection.end))
|
|
543
|
+
) {
|
|
544
|
+
this.markCommentsChanged();
|
|
545
|
+
}
|
|
507
546
|
this.tui.requestRender();
|
|
508
547
|
}
|
|
509
548
|
|
|
@@ -532,10 +571,24 @@ export class ReviewComponent {
|
|
|
532
571
|
this.tui.requestRender(true);
|
|
533
572
|
}
|
|
534
573
|
|
|
535
|
-
private
|
|
574
|
+
private getSplitDiffRows(): SplitDiffRow[] {
|
|
575
|
+
if (this.splitRows) return this.splitRows;
|
|
576
|
+
|
|
536
577
|
const rows: SplitDiffRow[] = [];
|
|
578
|
+
const rowByLineIndex: number[] = [];
|
|
537
579
|
let index = 0;
|
|
538
580
|
|
|
581
|
+
const pushRow = (row: SplitDiffRow) => {
|
|
582
|
+
const displayRow = rows.length;
|
|
583
|
+
rows.push(row);
|
|
584
|
+
if (row.kind === "full") {
|
|
585
|
+
rowByLineIndex[row.cell.index] = displayRow;
|
|
586
|
+
} else {
|
|
587
|
+
if (row.left) rowByLineIndex[row.left.index] = displayRow;
|
|
588
|
+
if (row.right) rowByLineIndex[row.right.index] = displayRow;
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
|
|
539
592
|
while (index < this.lines.length) {
|
|
540
593
|
const line = this.lines[index]!;
|
|
541
594
|
|
|
@@ -554,7 +607,7 @@ export class ReviewComponent {
|
|
|
554
607
|
|
|
555
608
|
const count = Math.max(removals.length, additions.length);
|
|
556
609
|
for (let offset = 0; offset < count; offset++) {
|
|
557
|
-
|
|
610
|
+
pushRow({
|
|
558
611
|
kind: "split",
|
|
559
612
|
left: removals[offset],
|
|
560
613
|
right: additions[offset],
|
|
@@ -565,32 +618,28 @@ export class ReviewComponent {
|
|
|
565
618
|
|
|
566
619
|
if (line.kind === "context") {
|
|
567
620
|
const cell = { line, index };
|
|
568
|
-
|
|
621
|
+
pushRow({ kind: "split", left: cell, right: cell });
|
|
569
622
|
} else {
|
|
570
|
-
|
|
623
|
+
pushRow({ kind: "full", cell: { line, index } });
|
|
571
624
|
}
|
|
572
625
|
index++;
|
|
573
626
|
}
|
|
574
627
|
|
|
628
|
+
this.splitRows = rows;
|
|
629
|
+
this.splitRowByLineIndex = rowByLineIndex;
|
|
575
630
|
return rows;
|
|
576
631
|
}
|
|
577
632
|
|
|
578
633
|
private getSelectedDisplayRow(): number {
|
|
579
634
|
if (this.diffRenderMode === "unified") return this.selected;
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
item.kind === "full"
|
|
583
|
-
? item.cell.index === this.selected
|
|
584
|
-
: item.left?.index === this.selected ||
|
|
585
|
-
item.right?.index === this.selected,
|
|
586
|
-
);
|
|
587
|
-
return row === -1 ? 0 : row;
|
|
635
|
+
this.getSplitDiffRows();
|
|
636
|
+
return this.splitRowByLineIndex?.[this.selected] ?? 0;
|
|
588
637
|
}
|
|
589
638
|
|
|
590
639
|
private getDisplayRowCount(): number {
|
|
591
640
|
return this.diffRenderMode === "unified"
|
|
592
641
|
? this.lines.length
|
|
593
|
-
: this.
|
|
642
|
+
: this.getSplitDiffRows().length;
|
|
594
643
|
}
|
|
595
644
|
|
|
596
645
|
private renderSplitDiffCell(
|