From 8ad21b07e1f8c52eb107abfd6b91fe1aac81ee9f Mon Sep 17 00:00:00 2001 From: Dorian Villet Date: Mon, 28 Sep 2026 15:33:23 +0200 Subject: [PATCH] Add option to show one-sided files as unified diffs A file with only additions or only deletions leaves one half of a split diff empty. The new `split-diffs.unify-one-sided-diffs` setting (off by default) renders such files as unified diffs, while files with both additions and deletions stay split, like Refined GitHub does. The decision is made per file, so when the setting is on, a file's hunks are buffered until the file ends. Closes #55 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017FPXTedgEC3D3TAaGpDdNe --- README.md | 10 + src/__snapshots__/index.test.ts.snap | 749 +++++++++++++++++++++++++++ src/getConfig.ts | 2 + src/getGitConfig.test.ts | 4 + src/getGitConfig.ts | 2 + src/index.test.ts | 54 ++ src/iterFormatHunk.ts | 6 +- src/iterSideBySideDiffs.ts | 39 +- src/previewTheme.ts | 1 + 9 files changed, 864 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0963aeb..3bbb894 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,16 @@ You can disable this with the `highlight-line-changes` setting: git config split-diffs.highlight-line-changes false ``` +### Unified diffs for one-sided files + +A file that only has additions, or only has deletions, leaves one half of a split diff empty. To show such files as unified diffs, and keep split diffs for files that have both, enable the `unify-one-sided-diffs` setting: + +``` +git config split-diffs.unify-one-sided-diffs true +``` + +This decides per file rather than per hunk, so each file's hunks are held back until the whole file has been read. + ### Enable scrolling in the terminal ```sh diff --git a/src/__snapshots__/index.test.ts.snap b/src/__snapshots__/index.test.ts.snap index 8049f4c..39cf995 100644 --- a/src/__snapshots__/index.test.ts.snap +++ b/src/__snapshots__/index.test.ts.snap @@ -215,6 +215,48 @@ Date: Sun Apr 4 19:04:26 2021 -0700 " `; +exports[`inlineChangesHighlighted commit with one-sided and two-sided files 1`] = ` +" +commit 1111111111111111111111111111111111111111 +Author: Shrey Banga +Date: Sun Apr 11 15:25:34 2021 -0700 + + Mix of changes + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ added.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,2 +1,3 @@ + 1 first 1 first + 2 + inserted + 2 second 3 second +@@ -10,2 +11,3 @@ + 10 tenth 11 tenth + 12 + another insert + 11 eleventh 13 eleventh +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ mixed.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,3 +1,3 @@ + 1 first 1 first + 2 + only inserted in this hunk + 2 second 3 second + 3 - third +@@ -10,3 +10,2 @@ + 10 tenth 10 tenth + 11 - only deleted in this hunk + 11 eleventh 11 eleventh +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ deleted.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,3 +1,2 @@ + 1 first 1 first + 2 - removed + 2 second 2 second + 3 3 +" +`; + exports[`inlineChangesHighlighted commit with tabs 1`] = ` " commit f735de7025c6d626c5ae1a291fe24f143dea0313 @@ -672,6 +714,48 @@ Date: Sun Apr 4 19:04:26 2021 -0700 " `; +exports[`splitWithWrapping commit with one-sided and two-sided files 1`] = ` +" +commit 1111111111111111111111111111111111111111 +Author: Shrey Banga +Date: Sun Apr 11 15:25:34 2021 -0700 + + Mix of changes + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ added.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,2 +1,3 @@ + 1 first 1 first + 2 + inserted + 2 second 3 second +@@ -10,2 +11,3 @@ + 10 tenth 11 tenth + 12 + another insert + 11 eleventh 13 eleventh +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ mixed.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,3 +1,3 @@ + 1 first 1 first + 2 + only inserted in this hunk + 2 second 3 second + 3 - third +@@ -10,3 +10,2 @@ + 10 tenth 10 tenth + 11 - only deleted in this hunk + 11 eleventh 11 eleventh +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ deleted.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,3 +1,2 @@ + 1 first 1 first + 2 - removed + 2 second 2 second + 3 3 +" +`; + exports[`splitWithWrapping commit with tabs 1`] = ` " commit f735de7025c6d626c5ae1a291fe24f143dea0313 @@ -1178,6 +1262,48 @@ Date: Sun Apr 4 19:04:26 2021 -0700 " `; +exports[`splitWithoutWrapping commit with one-sided and two-sided files 1`] = ` +" +commit 1111111111111111111111111111111111111111 +Author: Shrey Banga +Date: Sun Apr 11 15:25:34 2021 -0700 + + Mix of changes + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ added.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,2 +1,3 @@ + 1 first 1 first + 2 + inserted + 2 second 3 second +@@ -10,2 +11,3 @@ + 10 tenth 11 tenth + 12 + another insert + 11 eleventh 13 eleventh +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ mixed.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,3 +1,3 @@ + 1 first 1 first + 2 + only inserted in this hunk + 2 second 3 second + 3 - third +@@ -10,3 +10,2 @@ + 10 tenth 10 tenth + 11 - only deleted in this hunk + 11 eleventh 11 eleventh +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ deleted.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,3 +1,2 @@ + 1 first 1 first + 2 - removed + 2 second 2 second + 3 3 +" +`; + exports[`splitWithoutWrapping commit with tabs 1`] = ` " commit f735de7025c6d626c5ae1a291fe24f143dea0313 @@ -1633,6 +1759,48 @@ Date: Sun Apr 4 19:04:26 2021 -0700 " `; +exports[`syntaxHighlighted commit with one-sided and two-sided files 1`] = ` +" +commit 1111111111111111111111111111111111111111 +Author: Shrey Banga +Date: Sun Apr 11 15:25:34 2021 -0700 + + Mix of changes + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ added.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,2 +1,3 @@ + 1 ░░░░░ 1 ░░░░░ + 2 + ░░░░░░░░ + 2 ░░░░░░ 3 ░░░░░░ +@@ -10,2 +11,3 @@ + 10 ░░░░░ 11 ░░░░░ + 12 + ░░░░░░░░░░░░░░ + 11 ░░░░░░░░ 13 ░░░░░░░░ +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ mixed.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,3 +1,3 @@ + 1 ░░░░░ 1 ░░░░░ + 2 + ░░░░░░░░░░░░░░░░░░░░░░░░░░ + 2 ░░░░░░ 3 ░░░░░░ + 3 - ░░░░░ +@@ -10,3 +10,2 @@ + 10 ░░░░░ 10 ░░░░░ + 11 - ░░░░░░░░░░░░░░░░░░░░░░░░░ + 11 ░░░░░░░░ 11 ░░░░░░░░ +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ deleted.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,3 +1,2 @@ + 1 ░░░░░ 1 ░░░░░ + 2 - ░░░░░░░ + 2 ░░░░░░ 2 ░░░░░░ + 3 3 +" +`; + exports[`syntaxHighlighted commit with tabs 1`] = ` " commit f735de7025c6d626c5ae1a291fe24f143dea0313 @@ -2097,6 +2265,48 @@ Date: Sun Apr 4 19:04:26 2021 -0700 " `; +exports[`unifiedWithInlineChangesHighlighted commit with one-sided and two-sided files 1`] = ` +" +commit 1111111111111111111111111111111111111111 +Author: Shrey Banga +Date: Sun Apr 11 15:25:34 2021 -0700 + + Mix of changes + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ added.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,2 +1,3 @@ + 1 first + 2 + inserted + 2 second +@@ -10,2 +11,3 @@ + 10 tenth + 12 + another insert + 11 eleventh +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ mixed.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,3 +1,3 @@ + 1 first + 2 + only inserted in this hunk + 2 second + 3 - third +@@ -10,3 +10,2 @@ + 10 tenth + 11 - only deleted in this hunk + 12 eleventh +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ deleted.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,3 +1,2 @@ + 1 first + 2 - removed + 3 second + 4 +" +`; + exports[`unifiedWithInlineChangesHighlighted commit with tabs 1`] = ` " commit f735de7025c6d626c5ae1a291fe24f143dea0313 @@ -2567,6 +2777,48 @@ Date: Sun Apr 4 19:04:26 2021 -0700 " `; +exports[`unifiedWithWrapping commit with one-sided and two-sided files 1`] = ` +" +commit 1111111111111111111111111111111111111111 +Author: Shrey Banga +Date: Sun Apr 11 15:25:34 2021 -0700 + + Mix of changes + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ added.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,2 +1,3 @@ + 1 first + 2 + inserted + 2 second +@@ -10,2 +11,3 @@ + 10 tenth + 12 + another insert + 11 eleventh +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ mixed.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,3 +1,3 @@ + 1 first + 2 + only inserted in this hunk + 2 second + 3 - third +@@ -10,3 +10,2 @@ + 10 tenth + 11 - only deleted in this hunk + 12 eleventh +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ deleted.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,3 +1,2 @@ + 1 first + 2 - removed + 3 second + 4 +" +`; + exports[`unifiedWithWrapping commit with tabs 1`] = ` " commit f735de7025c6d626c5ae1a291fe24f143dea0313 @@ -2812,3 +3064,500 @@ Date: Sun Apr 11 15:25:34 2021 -0700 14 " `; + +exports[`unifyOneSidedDiffs commit with a small diff 1`] = ` +" +commit 26ca49fb83758bace20a473e231d576aa1bbe115 +Author: Shrey Banga +Date: Tue May 23 16:47:17 2017 -0700 + + sonos to brew + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ Brewfile +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -19,2 +19,3 @@ brew 'python3' + 19 brew 'socat' 19 brew 'socat' + 20 + brew 'sonos' + 20 brew 'terminal-notifier' 21 brew 'terminal-notifier' +@@ -42,3 +43,2 @@ cask 'rescuetime' + 42 cask 'slate' 43 cask 'slate' + 43 - cask 'sonos' + 43 cask 'spotify' 44 cask 'spotify' +" +`; + +exports[`unifyOneSidedDiffs commit with a small diff without a/ and b/ prefixs 1`] = ` +" +commit 26ca49fb83758bace20a473e231d576aa1bbe115 +Author: Shrey Banga +Date: Tue May 23 16:47:17 2017 -0700 + + sonos to brew + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ Brewfile +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -19,2 +19,3 @@ brew 'python3' + 19 brew 'socat' 19 brew 'socat' + 20 + brew 'sonos' + 20 brew 'terminal-notifier' 21 brew 'terminal-notifier' +@@ -42,3 +43,2 @@ cask 'rescuetime' + 42 cask 'slate' 43 cask 'slate' + 43 - cask 'sonos' + 43 cask 'spotify' 44 cask 'spotify' +" +`; + +exports[`unifyOneSidedDiffs commit with addition 1`] = ` +" +commit f735de7025c6d626c5ae1a291fe24f143dea0313 +Author: Shrey Banga +Date: Sun Apr 11 15:25:34 2021 -0700 + + Add theme support + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ todo.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -9,2 +9,3 @@ + 9 - [x] Organize code + 10 + - [x] Move visual config to theme + 10 - [ ] Handle empty diffs + 11 +" +`; + +exports[`unifyOneSidedDiffs commit with binary file addition 1`] = ` +" +commit c0ca4394fd55f1709430414f03db3d04cb9cc72c (HEAD -> main) +Author: Shrey Banga +Date: Wed Apr 14 18:24:29 2021 -0700 + + test binary file addition + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ screenshots/default.png +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ test dir a/default.png +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +" +`; + +exports[`unifyOneSidedDiffs commit with deletion 1`] = ` +"commit eccfb5a2b3d76ba53df315f977da74b18d50113e +Author: Shrey Banga +Date: Thu Aug 22 10:07:25 2019 -0700 + + Remove deprecated option + +diff --git a/Code/User/settings.json b/Code/User/settings.json +index a33d267..ae58a01 100644 +--- a/Code/User/settings.json ++++ b/Code/User/settings.json +@@ -26,5 +26,4 @@ + // search + "search.location": "panel", +- "search.usePCRE2": true, + + // telemetry +" +`; + +exports[`unifyOneSidedDiffs commit with double-spaced characters 1`] = ` +" +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ README.ja-JP.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -4 +4 @@ + 4 - このリポジトリは、MS-DOS v1.25 及び MS-DOS v2.0 のオリジナルのソースコード及びコンパイルされたバイナリを含んでいます。 4 + このリポジトリは、MS-DOS 及び MS-DOS v2.0 のオリジナルのソースコード及びコンパイルされたバイナリを含んでいます。 + 4 5 +" +`; + +exports[`unifyOneSidedDiffs commit with file addition 1`] = ` +" +commit e4951eee3b9a8fa471d01dd64075c5fd44879a26 +Author: Shrey Banga +Date: Sat Apr 10 14:35:42 2021 -0700 + + wip + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ .gitignore +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -0,0 +1,2 @@ + 1 + node_modules/** + 2 + build/** + +" +`; + +exports[`unifyOneSidedDiffs commit with file deletion 1`] = ` +" +commit 1e6ebaccc6fadf3390a749e7aa2cc6372b24325e +Author: Shrey Banga +Date: Sun Apr 18 03:17:05 2021 -0700 + + Update main.yml + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ scripts/screenshotTheme +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,7 +0,0 @@ + 1 - on run argv + 2 - with timeout of 5 seconds + 3 - tell app "Terminal" + 4 - open "screenshots.sh" + 5 - end tell + 6 - end timeout + 7 - end run + 8 \\ No newline at end of file +" +`; + +exports[`unifyOneSidedDiffs commit with file move 1`] = ` +" +commit 1c76ed4bb05429741fd4a48896bb84b11bc661f5 +Author: Shrey Banga +Date: Sat Apr 10 22:26:15 2021 -0700 + + Move sample diff files + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ colors.diff -> test-data/colors.diff +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +" +`; + +exports[`unifyOneSidedDiffs commit with inline line changes 1`] = ` +" +commit 9e424d329800e945e3003c9e275f80bdef69e591 +Author: Shrey Banga +Date: Sun Apr 4 19:04:26 2021 -0700 + + update lockfile + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ Brewfile.lock.json +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -5 +5 @@ + 5 - "revision": null 5 + "revision": "ee2f8d3ba2976e50ef577d517bf175c94fbbb0dd" +@@ -8 +8 @@ + 8 - "revision": null 8 + "revision": "c4cf96857c050dfa4f65f52109862dc68e55f60a" +@@ -11 +11 @@ + 11 - "revision": null 11 + "revision": "504d4b93aa6eaf4fce1bf17c19f55828ad7229a4" +@@ -16 +16 @@ + 16 - "version": "1.14.2_1", 16 + "version": "1.16.3", +@@ -17,0 +18 @@ + 18 + "rebuild": 0, +@@ -19,0 +21 @@ + 21 + "root_url": "https://homebrew.bintray.com/bottles", +@@ -20,0 +23,8 @@ + 23 + "arm64_big_sur": { + 24 + "url": "https://homebrew.bintray.com/bottles/go-1.16.3.arm64_big_sur.bottle.tar.gz", + 25 + "sha256": "e7c1efdd09e951eb46d01a3200b01e7fa55ce285b75470051be7fef34f4233ce" + 26 + }, + 27 + "big_sur": { + 28 + "url": "https://homebrew.bintray.com/bottles/go-1.16.3.big_sur.bottle.tar.gz", + 29 + "sha256": "ea37f33fd27369612a3e4e6db6adc46db0e8bdf6fac1332bf51bafaa66d43969" + 30 + }, +@@ -22,2 +32,2 @@ + 22 - "url": "https://homebrew.bintray.com/bottles/go-1.14.2_1.catalina.bottle.tar.gz", 32 + "url": "https://homebrew.bintray.com/bottles/go-1.16.3.catalina.bottle.tar.gz", + 23 - "sha256": "15b5623471330edcc681d7f9d57b449660e6d4b98c7f67af67f4991fc75d61fc" 33 + "sha256": "69c28f5e60612801c66e51e93d32068f822b245ab83246cb6cb374572eb59e15" +@@ -26,6 +36,2 @@ + 26 - "url": "https://homebrew.bintray.com/bottles/go-1.14.2_1.mojave.bottle.tar.gz", 36 + "url": "https://homebrew.bintray.com/bottles/go-1.16.3.mojave.bottle.tar.gz", + 27 - "sha256": "fa65e7dabe514e65ae625ed3c84a6bf58df01aceffc6e9aa99752ca8c320ce69" 37 + "sha256": "bf1e90ed1680b8ee1acb49f2f99426c8a8ac3e49efd63c7f3b41e57e7214dd19" + 28 - }, + 29 - "high_sierra": { + 30 - "url": "https://homebrew.bintray.com/bottles/go-1.14.2_1.high_sierra.bottle.tar.gz", + 31 - "sha256": "0997f6f5cda0e3bdb7789a80b53621cb588202ab37fd89bcd269f8dfafd23351" + 26 38 +" +`; + +exports[`unifyOneSidedDiffs commit with one-sided and two-sided files 1`] = ` +" +commit 1111111111111111111111111111111111111111 +Author: Shrey Banga +Date: Sun Apr 11 15:25:34 2021 -0700 + + Mix of changes + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ added.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,2 +1,3 @@ + 1 first + 2 + inserted + 2 second +@@ -10,2 +11,3 @@ + 10 tenth + 12 + another insert + 11 eleventh +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ mixed.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,3 +1,3 @@ + 1 first 1 first + 2 + only inserted in this hunk + 2 second 3 second + 3 - third +@@ -10,3 +10,2 @@ + 10 tenth 10 tenth + 11 - only deleted in this hunk + 11 eleventh 11 eleventh +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ deleted.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,3 +1,2 @@ + 1 first + 2 - removed + 3 second + 4 +" +`; + +exports[`unifyOneSidedDiffs commit with tabs 1`] = ` +" +commit f735de7025c6d626c5ae1a291fe24f143dea0313 +Author: Shrey Banga +Date: Sun Apr 11 15:25:34 2021 -0700 + + Add theme support + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ todo.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -9,2 +9,3 @@ + 9 - [x] Organize code + 10 + - [x] Move visual config to theme + 10 - [ ] Handle empty diffs + 11 +" +`; + +exports[`unifyOneSidedDiffs commits without diffs 1`] = ` +" +commit e5f896655402f8cf2d947c528d45e1d56bbf5717 (HEAD -> main) +Author: Shrey Banga +Date: Sun Apr 11 16:23:54 2021 -0700 + + Small refactor to allow testing end-to-end + +commit b637f38029f4a89c6a3b73b2b84a6a5b9e260730 +Author: Shrey Banga +Date: Sun Apr 11 11:53:02 2021 -0700 + + Organize code + +commit f323143e03af95fee5d38c21238a92ffd4461847 +Author: Shrey Banga +Date: Sun Apr 11 10:39:17 2021 -0700 + + more todos + +" +`; + +exports[`unifyOneSidedDiffs diff from issue #50 1`] = ` +" +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ file1 +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1 +1,2 @@ + 1 - This is file1 + 1 \\ No newline at end of file 1 \\ No newline at end of file + 2 + This is file1 + 3 + Experimental change +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ file2 +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1 +1,2 @@ + 1 + All good + 1 This is file2 +" +`; + +exports[`unifyOneSidedDiffs empty 1`] = ` +" +" +`; + +exports[`unifyOneSidedDiffs merge commit with 2 parents 1`] = ` +" +commit 3f504f4fbc1caf9c10814d48d8897a34f8a34dec +Merge: 2439767601 fbcdb8cf4f +Author: Gabriela Araujo Britto +Date: Thu Dec 21 17:57:42 2023 -0800 + + Merge branch 'main' into gabritto/d2 + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ src/compiler/binder.ts +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@@ -137,9 -136,9 +137,10 @@@ import + 137 isBlock, 136 isBlock, 137 isBlock, + 138 isBlockOrCatchScoped, 137 isBlockOrCatchScoped, 138 isBlockOrCatchScoped, + 139 IsBlockScopedContainer, 138 IsBlockScopedContainer, 139 IsBlockScopedContainer, + 139 + isBooleanLiteral, 140 + isBooleanLiteral, + 140 isCallExpression, 140 isCallExpression, 141 isCallExpression, + 141 isClassStaticBlockDeclaration, 141 isClassStaticBlockDeclaration, 142 isClassStaticBlockDeclaration, + 142 + isConditionalExpression, 143 + isConditionalExpression, + 143 isConditionalTypeNode, 142 isConditionalTypeNode, 144 isConditionalTypeNode, + 144 IsContainer, 143 IsContainer, 145 IsContainer, + 145 isDeclaration, 144 isDeclaration, 146 isDeclaration, +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ src/compiler/types.ts +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@@ -5985,8 -6063,7 +6063,8 @@@ export interface NodeLinks + 5985 decoratorSignature?: Signature; // Signature for decorator as i 6063 decoratorSignature?: Signature; // Signature for decorator as i 6063 decoratorSignature?: Signature; // Signature for decorator as i + 5986 spreadIndices?: { first: number | undefined, last: number | undefin 6064 spreadIndices?: { first: number | undefined, last: number | undefin 6064 spreadIndices?: { first: number | undefined, last: number | undefin + 5987 parameterInitializerContainsUndefined?: boolean; // True if this is 6065 parameterInitializerContainsUndefined?: boolean; // True if this is 6065 parameterInitializerContainsUndefined?: boolean; // True if this is + 5988 - fakeScopeForSignatureDeclaration?: boolean; // True if this is a fa 6066 - fakeScopeForSignatureDeclaration?: boolean; // True if this is a fa + 5988 + contextualReturnType?: Type; // If the node is a return stat 6066 + contextualReturnType?: Type; // If the node is a return stat + 6066 + fakeScopeForSignatureDeclaration?: "params" | "typeParams"; // If p 6067 + fakeScopeForSignatureDeclaration?: "params" | "typeParams"; // If p + 5989 assertionExpressionType?: Type; // Cached type of the expressio 6067 assertionExpressionType?: Type; // Cached type of the expressio 6068 assertionExpressionType?: Type; // Cached type of the expressio + 5990 } 6068 } 6069 } +" +`; + +exports[`unifyOneSidedDiffs merge commit with 3 parents 1`] = ` +" +commit d6d6a4aedfa78794c1b611c13d2ed1d3a66e1798 +Merge: 0dc976df1e 5f16a48236 3eadbf6c96 +Author: Andy Hanson +Date: Thu Sep 1 12:52:42 2016 -0700 + + Merge branch 'goto_definition_super', remote-tracking branch 'origin' into constructor_references + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ src/services/services.ts +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@@@ -2788,26 -2792,18 -2788,34 +2792,42 @@@@ namespace ts + 2788 return node && node.parent && node.parent.k 2792 return node && node.parent && node.parent.k 2788 return node && node.parent && node.parent.k 2792 return node && node.parent && node.parent.k + 2789 } 2793 } 2789 } 2793 } + 2790 2794 2790 2794 + 2791 + function climbPastPropertyAccess(node: Node) { 2791 + function climbPastPropertyAccess(node: Node) { 2795 + function climbPastPropertyAccess(node: Node) { + 2792 + return isRightSideOfPropertyAccess(node) ? 2792 + return isRightSideOfPropertyAccess(node) ? 2796 + return isRightSideOfPropertyAccess(node) ? + 2793 + } 2793 + } 2797 + } + 2794 + 2794 + 2798 + + 2795 - function climbPastManyPropertyAccesses(node: No 2799 - function climbPastManyPropertyAccesses(node: No + 2796 - return isRightSideOfPropertyAccess(node) ? 2800 - return isRightSideOfPropertyAccess(node) ? + 2799 + /** Get \`C\` given \`N\` if \`N\` is in the position + 2800 + function tryGetClassExtendingIdentifier(node: N + 2801 + return tryGetClassExtendingExpressionWithTy + 2795 + } 2802 + } + 2796 + 2803 + + 2795 function isCallExpressionTarget(node: Node): bo 2795 function isCallExpressionTarget(node: Node): bo 2797 function isCallExpressionTarget(node: Node): bo 2804 function isCallExpressionTarget(node: Node): bo + 2796 - if (isRightSideOfPropertyAccess(node)) { 2805 - if (isRightSideOfPropertyAccess(node)) { + 2797 - node = node.parent; 2806 - node = node.parent; + 2798 - } 2807 - } + 2798 - node = climbPastPropertyAccess(node); 2808 - node = climbPastPropertyAccess(node); + 2796 - return node && node.parent && node.parent.k 2799 - return node && node.parent && node.parent.k 2809 - return node && node.parent && node.parent.k + 2796 + return isCallOrNewExpressionTarget(node, Sy 2805 + return isCallOrNewExpressionTarget(node, Sy + 2797 } 2796 } 2798 } 2806 } + 2798 2797 2799 2807 + 2799 function isNewExpressionTarget(node: Node): boo 2798 function isNewExpressionTarget(node: Node): boo 2800 function isNewExpressionTarget(node: Node): boo 2808 function isNewExpressionTarget(node: Node): boo + 2799 - if (isRightSideOfPropertyAccess(node)) { 2809 - if (isRightSideOfPropertyAccess(node)) { + 2800 - node = node.parent; 2810 - node = node.parent; + 2801 - } 2811 - } + 2801 - node = climbPastPropertyAccess(node); 2812 - node = climbPastPropertyAccess(node); + 2799 - return node && node.parent && node.parent.k 2802 - return node && node.parent && node.parent.k 2813 - return node && node.parent && node.parent.k + 2800 + return isCallOrNewExpressionTarget(node, Sy 2809 + return isCallOrNewExpressionTarget(node, Sy + 2801 + } 2810 + } + 2802 + 2811 + + 2803 + function isCallOrNewExpressionTarget(node: Node 2812 + function isCallOrNewExpressionTarget(node: Node + 2804 + const target = climbPastPropertyAccess(node 2813 + const target = climbPastPropertyAccess(node + 2805 + return target && target.parent && target.pa 2814 + return target && target.parent && target.pa + 2806 + } 2815 + } + 2807 + 2816 + + 2808 - /** Get \`C\` given \`N\` if \`N\` is in the position 2817 - /** Get \`C\` given \`N\` if \`N\` is in the position + 2809 - function tryGetClassExtendingIdentifier(node: N 2818 - function tryGetClassExtendingIdentifier(node: N + 2810 - return tryGetClassExtendingExpressionWithTy 2819 - return tryGetClassExtendingExpressionWithTy + 2817 + function climbPastManyPropertyAccesses(node: No + 2818 + return isRightSideOfPropertyAccess(node) ? + 2801 + } 2819 + } + 2802 + 2820 + + 2803 + /** Returns a CallLikeExpression where \`node\` i 2821 + /** Returns a CallLikeExpression where \`node\` i + 2804 + function getAncestorCallLikeExpression(node: No 2822 + function getAncestorCallLikeExpression(node: No + 2805 + const target = climbPastManyPropertyAccesse 2823 + const target = climbPastManyPropertyAccesse + 2806 + const callLike = target.parent; 2824 + const callLike = target.parent; + 2807 + return callLike && isCallLikeExpression(cal 2825 + return callLike && isCallLikeExpression(cal + 2808 + } 2826 + } + 2809 + 2827 + + 2810 + function tryGetSignatureDeclaration(typeChecker 2828 + function tryGetSignatureDeclaration(typeChecker + 2811 + const callLike = getAncestorCallLikeExpress 2829 + const callLike = getAncestorCallLikeExpress + 2812 + return callLike && typeChecker.getResolvedS 2830 + return callLike && typeChecker.getResolvedS + 2808 } 2799 } 2813 } 2831 } + 2809 2800 2814 2832 + 2810 function isNameOfModuleDeclaration(node: Node) 2801 function isNameOfModuleDeclaration(node: Node) 2815 function isNameOfModuleDeclaration(node: Node) 2833 function isNameOfModuleDeclaration(node: Node) + 2811 2802 2816 2834 +" +`; + +exports[`unifyOneSidedDiffs multiple inserts and deletes in the same hunk 1`] = ` +" +commit e5f896655402f8cf2d947c528d45e1d56bbf5717 +Author: Shrey Banga +Date: Sun Apr 11 16:23:54 2021 -0700 + + Small refactor to allow testing end-to-end + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ src/index.ts +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,5 +1,6 @@ + 1 import chalk from 'chalk'; 1 import chalk from 'chalk'; + 2 import * as process from 'process'; 2 import * as process from 'process'; + 3 + import stream from 'stream'; + 3 import terminalSize from 'terminal-size'; 4 import terminalSize from 'terminal-size'; + 4 import { iterlinesFromReadableAsync } from './iterLinesFromReadable'; 5 import { iterlinesFromReadableAsync } from './iterLinesFromReadable'; + 5 import { iterLinesWithoutAnsiColors } from './iterLinesWithoutAnsiColors'; 6 import { iterLinesWithoutAnsiColors } from './iterLinesWithoutAnsiColors'; +@@ -12,15 +13,16 @@ function main() { + 12 const screenWidth = terminalSize().columns; 13 const screenWidth = terminalSize().columns; + 13 const theme = defaultTheme(chalk, screenWidth); 14 const theme = defaultTheme(chalk, screenWidth); + 14 15 + 15 - transformStreamWithIterables( 16 + stream.pipeline( + 16 - process.stdin, 17 + transformStreamWithIterables( + 17 - [ 18 + process.stdin, + 15 iterlinesFromReadableAsync, 19 iterlinesFromReadableAsync, + 16 iterLinesWithoutAnsiColors, 20 iterLinesWithoutAnsiColors, + 17 iterSideBySideDiff(theme), 21 iterSideBySideDiff(theme), + 18 - iterWithNewlines, 22 + iterWithNewlines + 19 - ], 23 + ), + 20 - process.stdout 24 + process.stdout, + 25 + console.error + 18 ); 26 ); + 19 } 27 } + 20 28 +" +`; + +exports[`unifyOneSidedDiffs with ANSI color codes 1`] = ` +" +commit f735de7025c6d626c5ae1a291fe24f143dea0313 +Author: Shrey Banga +Date: Sun Apr 11 15:25:34 2021 -0700 + + Add theme support + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ todo.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -7,6 +7,7 @@ + 7 - [x] Handle file addition/deletion properly + 8 - [x] Fix incorrect line positions when a hunk has discontinuous inserts and/or deletes + 9 - [x] Organize code + 10 + - [x] Move visual config to theme + 10 - [ ] Handle empty diffs + 11 - [ ] Handle moves and renames without diffs + 12 - [ ] Highlight changes in lines + 13 + 14 +" +`; diff --git a/src/getConfig.ts b/src/getConfig.ts index b63b5c6..15d6f4a 100644 --- a/src/getConfig.ts +++ b/src/getConfig.ts @@ -6,12 +6,14 @@ export type Config = Theme & { MIN_LINE_WIDTH: number; WRAP_LINES: boolean; HIGHLIGHT_LINE_CHANGES: boolean; + UNIFY_ONE_SIDED_DIFFS: boolean; }; export const CONFIG_DEFAULTS: Omit = { MIN_LINE_WIDTH: 80, WRAP_LINES: true, HIGHLIGHT_LINE_CHANGES: true, + UNIFY_ONE_SIDED_DIFFS: false, }; export function getConfig(gitConfig: GitConfig): Config { diff --git a/src/getGitConfig.test.ts b/src/getGitConfig.test.ts index 559a9f8..bc7d332 100644 --- a/src/getGitConfig.test.ts +++ b/src/getGitConfig.test.ts @@ -9,6 +9,7 @@ import { const DEFAULT_CONFIG: GitConfig = { WRAP_LINES: true, HIGHLIGHT_LINE_CHANGES: true, + UNIFY_ONE_SIDED_DIFFS: false, MIN_LINE_WIDTH: DEFAULT_MIN_LINE_WIDTH, THEME_NAME: DEFAULT_THEME_NAME, THEME_DIRECTORY: DEFAULT_THEME_DIRECTORY, @@ -24,6 +25,7 @@ describe('getGitConfig', () => { getGitConfig(` split-diffs.wrap-lines=false split-diffs.highlight-line-changes=false +split-diffs.unify-one-sided-diffs=true split-diffs.min-line-width=40 split-diffs.theme-name=arctic split-diffs.theme-directory=/tmp @@ -32,6 +34,7 @@ split-diffs.syntax-highlighting-theme=dark-plus ).toEqual({ WRAP_LINES: false, HIGHLIGHT_LINE_CHANGES: false, + UNIFY_ONE_SIDED_DIFFS: true, MIN_LINE_WIDTH: 40, THEME_NAME: 'arctic', THEME_DIRECTORY: '/tmp', @@ -57,6 +60,7 @@ split-diffs.syntax-highlighting-theme=nord getGitConfig(` split-diffs.wrap-lines=1 split-diffs.highlight-line-changes=1 +split-diffs.unify-one-sided-diffs=1 split-diffs.min-line-width=bar split-diffs.syntax-highlighting-theme=foo split-diffs.theme-name=baz diff --git a/src/getGitConfig.ts b/src/getGitConfig.ts index 5c0107c..06bc687 100644 --- a/src/getGitConfig.ts +++ b/src/getGitConfig.ts @@ -5,6 +5,7 @@ export type GitConfig = { MIN_LINE_WIDTH: number; WRAP_LINES: boolean; HIGHLIGHT_LINE_CHANGES: boolean; + UNIFY_ONE_SIDED_DIFFS: boolean; THEME_DIRECTORY: string; THEME_NAME: string; SYNTAX_HIGHLIGHTING_THEME?: string; @@ -53,6 +54,7 @@ export function getGitConfig(configString: string): GitConfig { MIN_LINE_WIDTH: minLineWidth, WRAP_LINES: rawConfig['wrap-lines'] !== 'false', HIGHLIGHT_LINE_CHANGES: rawConfig['highlight-line-changes'] !== 'false', + UNIFY_ONE_SIDED_DIFFS: rawConfig['unify-one-sided-diffs'] === 'true', THEME_DIRECTORY: rawConfig['theme-directory'] ?? DEFAULT_THEME_DIRECTORY, THEME_NAME: rawConfig['theme-name'] ?? DEFAULT_THEME_NAME, diff --git a/src/index.test.ts b/src/index.test.ts index 31025bf..880bf92 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -24,6 +24,7 @@ const TEST_CONFIG: Config = { MIN_LINE_WIDTH: 60, WRAP_LINES: false, HIGHLIGHT_LINE_CHANGES: false, + UNIFY_ONE_SIDED_DIFFS: false, ...TEST_THEME, }; @@ -56,6 +57,10 @@ const CONFIG_OVERRIDES: Record = { DELETED_WORD_COLOR: { color: { r: 255, g: 0, b: 0, a: 255 } }, INSERTED_WORD_COLOR: { color: { r: 0, g: 255, b: 0, a: 255 } }, }, + unifyOneSidedDiffs: { + MIN_LINE_WIDTH: 60, + UNIFY_ONE_SIDED_DIFFS: true, + }, syntaxHighlighted: { MIN_LINE_WIDTH: 60, WRAP_LINES: false, @@ -522,6 +527,55 @@ index 095ee29..439621e 100644 ).toMatchSnapshot(); }); + test('commit with one-sided and two-sided files', async function () { + expect( + await transform( + config, + ` +commit 1111111111111111111111111111111111111111 +Author: Shrey Banga +Date: Sun Apr 11 15:25:34 2021 -0700 + + Mix of changes + +diff --git a/added.md b/added.md +index 1111111..2222222 100644 +--- a/added.md ++++ b/added.md +@@ -1,2 +1,3 @@ + first ++inserted + second +@@ -10,2 +11,3 @@ + tenth ++another insert + eleventh +diff --git a/mixed.md b/mixed.md +index 3333333..4444444 100644 +--- a/mixed.md ++++ b/mixed.md +@@ -1,3 +1,3 @@ + first ++only inserted in this hunk + second +-third +@@ -10,3 +10,2 @@ + tenth +-only deleted in this hunk + eleventh +diff --git a/deleted.md b/deleted.md +index 5555555..6666666 100644 +--- a/deleted.md ++++ b/deleted.md +@@ -1,3 +1,2 @@ + first +-removed + second +` + ) + ).toMatchSnapshot(); + }); + test('merge commit with 2 parents', async function () { // Source: the TypeScript repo expect( diff --git a/src/iterFormatHunk.ts b/src/iterFormatHunk.ts index 276def8..97b714e 100644 --- a/src/iterFormatHunk.ts +++ b/src/iterFormatHunk.ts @@ -18,7 +18,8 @@ export async function* iterFormatHunk( context: Context, diffType: 'unified-diff' | 'combined-diff', hunkHeaderLine: string, - hunkParts: HunkPart[] + hunkParts: HunkPart[], + forceUnified: boolean = false ): AsyncIterable { const { HUNK_HEADER_COLOR, SCREEN_WIDTH, MIN_LINE_WIDTH } = context; @@ -37,7 +38,8 @@ export async function* iterFormatHunk( ); // Only split diffs if there's enough room - const splitDiffs = SCREEN_WIDTH >= MIN_LINE_WIDTH * hunkParts.length; + const splitDiffs = + !forceUnified && SCREEN_WIDTH >= MIN_LINE_WIDTH * hunkParts.length; if (splitDiffs) { yield* iterFormatHunkSplit(context, hunkParts, changes); diff --git a/src/iterSideBySideDiffs.ts b/src/iterSideBySideDiffs.ts index ae38fed..8299a72 100644 --- a/src/iterSideBySideDiffs.ts +++ b/src/iterSideBySideDiffs.ts @@ -38,7 +38,7 @@ async function* iterSideBySideDiffsFormatted( context: Context, lines: AsyncIterable ): AsyncIterable { - const { HORIZONTAL_SEPARATOR } = context; + const { HORIZONTAL_SEPARATOR, UNIFY_ONE_SIDED_DIFFS } = context; let state: State = 'unknown'; @@ -56,6 +56,12 @@ async function* iterSideBySideDiffsFormatted( let hunkParts: HunkPart[] = []; let hunkHeaderLine: string = ''; async function* yieldHunk(diffType: 'unified-diff' | 'combined-diff') { + if (diffType === 'unified-diff' && UNIFY_ONE_SIDED_DIFFS) { + // Whether a file is one-sided is only known once all its hunks + // have been read, so hold them until the file ends. + fileHunks.push({ hunkHeaderLine, hunkParts }); + return; + } yield* iterFormatHunk(context, diffType, hunkHeaderLine, hunkParts); for (const hunkPart of hunkParts) { hunkPart.startLineNo = -1; @@ -63,6 +69,30 @@ async function* iterSideBySideDiffsFormatted( } } + // Hunks of the current file, held back when UNIFY_ONE_SIDED_DIFFS is set + let fileHunks: { hunkHeaderLine: string; hunkParts: HunkPart[] }[] = []; + async function* yieldFileHunks() { + const hasPrefix = (lines: (string | null)[], prefix: string) => + lines.some((line) => line?.startsWith(prefix)); + const hasDeletions = fileHunks.some(({ hunkParts: [partA] }) => + hasPrefix(partA.lines, '-') + ); + const hasInsertions = fileHunks.some(({ hunkParts: [, partB] }) => + hasPrefix(partB.lines, '+') + ); + const isOneSided = !(hasDeletions && hasInsertions); + for (const { hunkHeaderLine, hunkParts } of fileHunks) { + yield* iterFormatHunk( + context, + 'unified-diff', + hunkHeaderLine, + hunkParts, + isOneSided + ); + } + fileHunks = []; + } + async function* flushPending() { if (state === 'unified-diff' || state === 'combined-diff') { yield* yieldFileName(); @@ -108,6 +138,12 @@ async function* iterSideBySideDiffsFormatted( // Handle state starts if (nextState) { yield* flushPending(); + if ( + nextState !== 'unified-diff-hunk-header' && + nextState !== 'unified-diff-hunk-body' + ) { + yield* yieldFileHunks(); + } switch (nextState) { case 'commit-header': @@ -309,6 +345,7 @@ async function* iterSideBySideDiffsFormatted( } yield* flushPending(); + yield* yieldFileHunks(); } export async function* iterSideBySideDiffs( diff --git a/src/previewTheme.ts b/src/previewTheme.ts index 298099f..10eaec9 100644 --- a/src/previewTheme.ts +++ b/src/previewTheme.ts @@ -12,6 +12,7 @@ const CONFIG = { MIN_LINE_WIDTH: 40, WRAP_LINES: true, HIGHLIGHT_LINE_CHANGES: true, + UNIFY_ONE_SIDED_DIFFS: false, }; async function previewTheme(