From 75508a82b39099ae1a9160bdadf46d5ad22dbf38 Mon Sep 17 00:00:00 2001 From: Dorian Villet Date: Mon, 28 Sep 2026 15:40:57 +0200 Subject: [PATCH] End hunks once the line counts in their header are used up Hunks used to end only when a line started another state, such as a `commit ` header. Output from `git log -p` with a custom --pretty format (e.g. one starting with "Commit:") never produced such a line, so every following commit was drawn as lines of the previous hunk. The blank line git prints between commits was also drawn as a phantom numbered line at the end of each hunk. Now each hunk tracks the line counts from its header (also for combined diffs) and ends when they reach zero, letting a trailing "\ No newline at end of file" through. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017FPXTedgEC3D3TAaGpDdNe --- src/__snapshots__/index.test.ts.snap | 236 ++++++++++++++++++++++----- src/index.test.ts | 32 ++++ src/iterSideBySideDiffs.ts | 57 ++++++- 3 files changed, 282 insertions(+), 43 deletions(-) diff --git a/src/__snapshots__/index.test.ts.snap b/src/__snapshots__/index.test.ts.snap index 8049f4c..c385b53 100644 --- a/src/__snapshots__/index.test.ts.snap +++ b/src/__snapshots__/index.test.ts.snap @@ -59,7 +59,7 @@ Date: Sun Apr 11 15:25:34 2021 -0700 9 - [x] Organize code 9 - [x] Organize code 10 + - [x] Move visual config to theme 10 - [ ] Handle empty diffs 11 - [ ] Handle empty diffs - 11 12 + " `; @@ -107,7 +107,7 @@ exports[`inlineChangesHighlighted commit with double-spaced characters 1`] = ` ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── @@ -4 +4 @@ 4 - このリポジトリは、MS-DOS ░░░░░░及び MS-DOS v2.0 のオリジナルのソースコード及びコンパイルされたバイナリを含んでいます。 4 + このリポジトリは、MS-DOS 及び MS-DOS v2.0 のオリジナルのソースコード及びコンパイルされたバイナリを含んでいます。 - 4 5 + " `; @@ -211,7 +211,7 @@ Date: Sun Apr 4 19:04:26 2021 -0700 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 + " `; @@ -230,7 +230,32 @@ Date: Sun Apr 11 15:25:34 2021 -0700 9 - [x] Organize code 9 - [x] Organize code 10 + - [x] Move visual config to theme 10 - [ ] Handle empty diffs 11 - [ ] Handle empty diffs - 11 12 + +" +`; + +exports[`inlineChangesHighlighted commits with a custom pretty format 1`] = ` +" +Commit: 2222222222222222222222222222222222222222 +Subject: Second commit + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ file.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,2 +1,2 @@ + 1 first 1 first + 2 - second 2 + changed second +Commit: 1111111111111111111111111111111111111111 +Subject: First commit + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ file.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1 +1,2 @@ + 1 first 1 first + 2 + second + 2 \\ No newline at end of file 3 \\ No newline at end of file + " `; @@ -389,7 +414,7 @@ Date: Thu Sep 1 12:52:42 2016 -0700 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 + " `; @@ -450,8 +475,8 @@ Date: Sun Apr 11 15:25:34 2021 -0700 10 - [ ] Handle empty diffs 11 - [ ] Handle empty diffs 11 - [ ] Handle moves and renames without diffs 12 - [ ] Handle moves and renames without diffs 12 - [ ] Highlight changes in lines 13 - [ ] Highlight changes in lines - 13 14 - 14 15 + + " `; @@ -514,7 +539,7 @@ Date: Sun Apr 11 15:25:34 2021 -0700 9 - [x] Organize code 9 - [x] Organize code 10 + - [x] Move visual config to theme 10 - [ ] Handle empty diffs 11 - [ ] Handle empty diffs - 11 12 + " `; @@ -564,7 +589,7 @@ exports[`splitWithWrapping commit with double-spaced characters 1`] = ` 4 - このリポジトリは、MS-DOS v1.25 及び MS-DOS v2.0 4 + このリポジトリは、MS-DOS 及び MS-DOS v2.0 のオリジナルのソースコード及びコンパイルされたバイナリを含んでいます。 のオリジナルのソースコード及びコンパイルされたバイナリを含んでいます。 - 4 5 + " `; @@ -668,7 +693,7 @@ Date: Sun Apr 4 19:04:26 2021 -0700 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 + " `; @@ -687,7 +712,32 @@ Date: Sun Apr 11 15:25:34 2021 -0700 9 - [x] Organize code 9 - [x] Organize code 10 + - [x] Move visual config to theme 10 - [ ] Handle empty diffs 11 - [ ] Handle empty diffs - 11 12 + +" +`; + +exports[`splitWithWrapping commits with a custom pretty format 1`] = ` +" +Commit: 2222222222222222222222222222222222222222 +Subject: Second commit + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ file.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,2 +1,2 @@ + 1 first 1 first + 2 - second 2 + changed second +Commit: 1111111111111111111111111111111111111111 +Subject: First commit + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ file.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1 +1,2 @@ + 1 first 1 first + 2 + second + 2 \\ No newline at end of file 3 \\ No newline at end of file + " `; @@ -897,7 +947,7 @@ Date: Thu Sep 1 12:52:42 2016 -0700 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 + " `; @@ -958,8 +1008,8 @@ Date: Sun Apr 11 15:25:34 2021 -0700 10 - [ ] Handle empty diffs 11 - [ ] Handle empty diffs 11 - [ ] Handle moves and renames without diffs 12 - [ ] Handle moves and renames without diffs 12 - [ ] Highlight changes in lines 13 - [ ] Highlight changes in lines - 13 14 - 14 15 + + " `; @@ -1022,7 +1072,7 @@ Date: Sun Apr 11 15:25:34 2021 -0700 9 - [x] Organize code 9 - [x] Organize code 10 + - [x] Move visual config to theme 10 - [ ] Handle empty diffs 11 - [ ] Handle empty diffs - 11 12 + " `; @@ -1070,7 +1120,7 @@ exports[`splitWithoutWrapping commit with double-spaced characters 1`] = ` ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── @@ -4 +4 @@ 4 - このリポジトリは、MS-DOS v1.25 及び MS-DOS v2.0 のオリジナルのソースコード及びコンパイルされたバイナリを含んでいます。 4 + このリポジトリは、MS-DOS 及び MS-DOS v2.0 のオリジナルのソースコード及びコンパイルされたバイナリを含んでいます。 - 4 5 + " `; @@ -1174,7 +1224,7 @@ Date: Sun Apr 4 19:04:26 2021 -0700 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 + " `; @@ -1193,7 +1243,32 @@ Date: Sun Apr 11 15:25:34 2021 -0700 9 - [x] Organize code 9 - [x] Organize code 10 + - [x] Move visual config to theme 10 - [ ] Handle empty diffs 11 - [ ] Handle empty diffs - 11 12 + +" +`; + +exports[`splitWithoutWrapping commits with a custom pretty format 1`] = ` +" +Commit: 2222222222222222222222222222222222222222 +Subject: Second commit + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ file.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,2 +1,2 @@ + 1 first 1 first + 2 - second 2 + changed second +Commit: 1111111111111111111111111111111111111111 +Subject: First commit + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ file.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1 +1,2 @@ + 1 first 1 first + 2 + second + 2 \\ No newline at end of file 3 \\ No newline at end of file + " `; @@ -1352,7 +1427,7 @@ Date: Thu Sep 1 12:52:42 2016 -0700 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 + " `; @@ -1413,8 +1488,8 @@ Date: Sun Apr 11 15:25:34 2021 -0700 10 - [ ] Handle empty diffs 11 - [ ] Handle empty diffs 11 - [ ] Handle moves and renames without diffs 12 - [ ] Handle moves and renames without diffs 12 - [ ] Highlight changes in lines 13 - [ ] Highlight changes in lines - 13 14 - 14 15 + + " `; @@ -1477,7 +1552,7 @@ Date: Sun Apr 11 15:25:34 2021 -0700 9 ░░░░░░░░░░░░░░░░░░░░░ 9 ░░░░░░░░░░░░░░░░░░░░░ 10 + ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 10 ░░░░░░░░░░░░░░░░░░░░░░░░░░ 11 ░░░░░░░░░░░░░░░░░░░░░░░░░░ - 11 12 + " `; @@ -1525,7 +1600,7 @@ exports[`syntaxHighlighted commit with double-spaced characters 1`] = ` ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── @@ -4 +4 @@ 4 - ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 4 + ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ - 4 5 + " `; @@ -1629,7 +1704,7 @@ Date: Sun Apr 4 19:04:26 2021 -0700 29 - ░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 30 - ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 31 - ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ - 26 38 + " `; @@ -1648,7 +1723,32 @@ Date: Sun Apr 11 15:25:34 2021 -0700 9 ░░░░░░░░░░░░░░░░░░░░░░░░░░ 9 ░░░░░░░░░░░░░░░░░░░░░░░░░░ 10 + ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 10 ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 11 ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ - 11 12 + +" +`; + +exports[`syntaxHighlighted commits with a custom pretty format 1`] = ` +" +Commit: 2222222222222222222222222222222222222222 +Subject: Second commit + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ file.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,2 +1,2 @@ + 1 ░░░░░ 1 ░░░░░ + 2 - ░░░░░░ 2 + ░░░░░░░░░░░░░░ +Commit: 1111111111111111111111111111111111111111 +Subject: First commit + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ file.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1 +1,2 @@ + 1 ░░░░░ 1 ░░░░░ + 2 + ░░░░░░ + 2 \\ ░░░░░░░░░░░░░░░░░░░░░░░░░░ 3 \\ ░░░░░░░░░░░░░░░░░░░░░░░░░░ + " `; @@ -1807,7 +1907,7 @@ Date: Thu Sep 1 12:52:42 2016 -0700 2808 ░░░░░ 2799 ░░░░░ 2813 ░░░░░ 2831 ░░░░░ 2809 2800 2814 2832 2810 ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2801 ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2815 ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2833 ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ - 2811 2802 2816 2834 + " `; @@ -1868,8 +1968,8 @@ Date: Sun Apr 11 15:25:34 2021 -0700 10 ░░░░░░░░░░░░░░░░░░░░░░░░░░ 11 ░░░░░░░░░░░░░░░░░░░░░░░░░░ 11 ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 12 ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 12 ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 13 ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ - 13 14 - 14 15 + + " `; @@ -1932,7 +2032,7 @@ Date: Sun Apr 11 15:25:34 2021 -0700 9 - [x] Organize code 10 + - [x] Move visual config to theme 10 - [ ] Handle empty diffs - 11 + " `; @@ -1981,7 +2081,7 @@ exports[`unifiedWithInlineChangesHighlighted commit with double-spaced character @@ -4 +4 @@ 4 - このリポジトリは、MS-DOS ░░░░░░及び MS-DOS v2.0 のオリジナルのソースコード及びコンパイルされたバイナリを含んでいます。 4 + このリポジトリは、MS-DOS 及び MS-DOS v2.0 のオリジナルのソースコード及びコンパイルされたバイナリを含んでいます。 - 5 + " `; @@ -2093,7 +2193,7 @@ Date: Sun Apr 4 19:04:26 2021 -0700 31 - "sha256": "0997f6f5cda0e3bdb7789a80b53621cb588202ab37fd89bcd269f8dfafd23351" 36 + "url": "https://homebrew.bintray.com/bottles/go-1.░░.░.mojave.bottle.tar.gz", 37 + "sha256": "░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░" - 32 + " `; @@ -2112,7 +2212,33 @@ Date: Sun Apr 11 15:25:34 2021 -0700 9 - [x] Organize code 10 + - [x] Move visual config to theme 10 - [ ] Handle empty diffs - 11 + +" +`; + +exports[`unifiedWithInlineChangesHighlighted commits with a custom pretty format 1`] = ` +" +Commit: 2222222222222222222222222222222222222222 +Subject: Second commit + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ file.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,2 +1,2 @@ + 1 first + 2 - second + 2 + changed second +Commit: 1111111111111111111111111111111111111111 +Subject: First commit + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ file.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1 +1,2 @@ + 1 first + 2 + second + 2 \\ No newline at end of file + " `; @@ -2271,7 +2397,7 @@ Date: Thu Sep 1 12:52:42 2016 -0700 2831 } 2832 2833 function isNameOfModuleDeclaration(node: Node) { - 2834 + " `; @@ -2338,8 +2464,8 @@ Date: Sun Apr 11 15:25:34 2021 -0700 10 - [ ] Handle empty diffs 11 - [ ] Handle moves and renames without diffs 12 - [ ] Highlight changes in lines - 13 - 14 + + " `; @@ -2402,7 +2528,7 @@ Date: Sun Apr 11 15:25:34 2021 -0700 9 - [x] Organize code 10 + - [x] Move visual config to theme 10 - [ ] Handle empty diffs - 11 + " `; @@ -2451,7 +2577,7 @@ exports[`unifiedWithWrapping commit with double-spaced characters 1`] = ` @@ -4 +4 @@ 4 - このリポジトリは、MS-DOS v1.25 及び MS-DOS v2.0 のオリジナルのソースコード及びコンパイルされたバイナリを含んでいます。 4 + このリポジトリは、MS-DOS 及び MS-DOS v2.0 のオリジナルのソースコード及びコンパイルされたバイナリを含んでいます。 - 5 + " `; @@ -2563,7 +2689,7 @@ Date: Sun Apr 4 19:04:26 2021 -0700 31 - "sha256": "0997f6f5cda0e3bdb7789a80b53621cb588202ab37fd89bcd269f8dfafd23351" 36 + "url": "https://homebrew.bintray.com/bottles/go-1.16.3.mojave.bottle.tar.gz", 37 + "sha256": "bf1e90ed1680b8ee1acb49f2f99426c8a8ac3e49efd63c7f3b41e57e7214dd19" - 32 + " `; @@ -2582,7 +2708,33 @@ Date: Sun Apr 11 15:25:34 2021 -0700 9 - [x] Organize code 10 + - [x] Move visual config to theme 10 - [ ] Handle empty diffs - 11 + +" +`; + +exports[`unifiedWithWrapping commits with a custom pretty format 1`] = ` +" +Commit: 2222222222222222222222222222222222222222 +Subject: Second commit + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ file.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1,2 +1,2 @@ + 1 first + 2 - second + 2 + changed second +Commit: 1111111111111111111111111111111111111111 +Subject: First commit + +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + ■■ file.md +──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +@@ -1 +1,2 @@ + 1 first + 2 + second + 2 \\ No newline at end of file + " `; @@ -2741,7 +2893,7 @@ Date: Thu Sep 1 12:52:42 2016 -0700 2831 } 2832 2833 function isNameOfModuleDeclaration(node: Node) { - 2834 + " `; @@ -2808,7 +2960,7 @@ Date: Sun Apr 11 15:25:34 2021 -0700 10 - [ ] Handle empty diffs 11 - [ ] Handle moves and renames without diffs 12 - [ ] Highlight changes in lines - 13 - 14 + + " `; diff --git a/src/index.test.ts b/src/index.test.ts index 31025bf..99e1b1d 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -522,6 +522,38 @@ index 095ee29..439621e 100644 ).toMatchSnapshot(); }); + test('commits with a custom pretty format', async function () { + expect( + await transform( + config, + ` +Commit: 2222222222222222222222222222222222222222 +Subject: Second commit + +diff --git a/file.md b/file.md +index 1111111..2222222 100644 +--- a/file.md ++++ b/file.md +@@ -1,2 +1,2 @@ + first +-second ++changed second +Commit: 1111111111111111111111111111111111111111 +Subject: First commit + +diff --git a/file.md b/file.md +index 0000000..1111111 100644 +--- a/file.md ++++ b/file.md +@@ -1 +1,2 @@ + first ++second +\\ No newline at end of file +` + ) + ).toMatchSnapshot(); + }); + test('merge commit with 2 parents', async function () { // Source: the TypeScript repo expect( diff --git a/src/iterSideBySideDiffs.ts b/src/iterSideBySideDiffs.ts index ae38fed..31284aa 100644 --- a/src/iterSideBySideDiffs.ts +++ b/src/iterSideBySideDiffs.ts @@ -55,6 +55,39 @@ async function* iterSideBySideDiffsFormatted( // Hunk metadata let hunkParts: HunkPart[] = []; let hunkHeaderLine: string = ''; + // Lines each hunk part still expects, from the counts in the hunk header. + // The hunk ends once they are all used up, so that whatever follows it + // (e.g. a commit printed with a custom --pretty format) isn't mistaken + // for hunk lines. + let hunkLinesRemaining: number[] = []; + // Whether the last hunk ended by running out of lines, rather than being + // interrupted by another state + let isAfterHunk = false; + function isHunkComplete() { + return hunkLinesRemaining.every((count) => count <= 0); + } + function parseHunkLineCount(fileRange: string) { + // A range is "start,count", or just "start" when the count is 1 + const [, countString] = fileRange.slice(1).split(','); + return countString === undefined ? 1 : parseInt(countString, 10); + } + function consumeHunkLine(linePrefix: string) { + // "\ No newline at end of file" doesn't count as a line + if (linePrefix.startsWith('\\')) { + return; + } + // With N parents, the prefix has one column per parent. A line exists + // in a parent unless that parent's column is "+", and in the result + // unless any column is "-". A unified diff is the N = 1 case. + for (let i = 0; i < linePrefix.length; i++) { + if (linePrefix[i] !== '+') { + hunkLinesRemaining[i]--; + } + } + if (!linePrefix.includes('-')) { + hunkLinesRemaining[linePrefix.length]--; + } + } async function* yieldHunk(diffType: 'unified-diff' | 'combined-diff') { yield* iterFormatHunk(context, diffType, hunkHeaderLine, hunkParts); for (const hunkPart of hunkParts) { @@ -103,6 +136,13 @@ async function* iterSideBySideDiffsFormatted( !line.startsWith(' ') ) { nextState = 'unknown'; + } else if ( + (state === 'unified-diff-hunk-body' || + state === 'combined-diff-hunk-body') && + isHunkComplete() && + !line.startsWith('\\') + ) { + nextState = 'unknown'; } // Handle state starts @@ -113,7 +153,8 @@ async function* iterSideBySideDiffsFormatted( case 'commit-header': if ( state === 'unified-diff-hunk-header' || - state === 'unified-diff-hunk-body' + state === 'unified-diff-hunk-body' || + (state === 'unknown' && isAfterHunk) ) { yield HORIZONTAL_SEPARATOR; } @@ -133,6 +174,13 @@ async function* iterSideBySideDiffsFormatted( break; } + if (nextState !== 'unknown') { + isAfterHunk = false; + } else if (state !== 'unknown') { + isAfterHunk = + state === 'unified-diff-hunk-body' || + state === 'combined-diff-hunk-body'; + } state = nextState; } @@ -206,9 +254,14 @@ async function* iterSideBySideDiffsFormatted( assert.ok(startBString.startsWith('+')); hunkParts[1].startLineNo = parseInt(startBString.slice(1), 10); + + hunkLinesRemaining = [aHeader, bHeader].map( + parseHunkLineCount + ); break; } case 'unified-diff-hunk-body': { + consumeHunkLine(line.slice(0, 1)); const [{ lines: hunkLinesA }, { lines: hunkLinesB }] = hunkParts; if (line.startsWith('-')) { @@ -238,6 +291,7 @@ async function* iterSideBySideDiffsFormatted( hunkHeaderLine = line; const fileRanges = hunkHeader.split(' '); + hunkLinesRemaining = fileRanges.map(parseHunkLineCount); hunkParts = []; for (let i = 0; i < fileRanges.length; i++) { const fileRange = fileRanges[i]; @@ -261,6 +315,7 @@ async function* iterSideBySideDiffsFormatted( // and the rest show changes made in the corresponding parent. const linePrefix = line.slice(0, hunkParts.length - 1); const lineSuffix = line.slice(hunkParts.length - 1); + consumeHunkLine(linePrefix); const isLineAdded = linePrefix.includes('+'); const isLineRemoved = linePrefix.includes('-');