@adecore/editor-react 0.31.12 → 0.33.0

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 (137) hide show
  1. package/dist/AnchoredPopup.js +73 -19
  2. package/dist/AttributionCard.js +162 -4
  3. package/dist/BaselineStatus.js +62 -8
  4. package/dist/CallHierarchyPanel.js +977 -60
  5. package/dist/ChangeReview.js +76 -25
  6. package/dist/CodeAuthorsCard.js +314 -14
  7. package/dist/CompletionPopup.js +490 -83
  8. package/dist/DiffView.js +548 -181
  9. package/dist/EditorContextMenu.js +352 -34
  10. package/dist/EditorView.js +276 -99
  11. package/dist/FindReplace.js +525 -67
  12. package/dist/HoverCard.js +378 -38
  13. package/dist/HoverSections.js +367 -56
  14. package/dist/LanguagePopups.js +95 -34
  15. package/dist/Markdown.js +41 -6
  16. package/dist/PanelResizeHandle.js +199 -66
  17. package/dist/PathText.js +49 -7
  18. package/dist/PeekPanel.js +675 -72
  19. package/dist/PickPopup.js +304 -34
  20. package/dist/RenameCard.js +679 -85
  21. package/dist/RenderedDocs.js +329 -58
  22. package/dist/SignatureCard.js +91 -7
  23. package/dist/SplitDiffView.js +344 -254
  24. package/dist/SymbolPicker.js +440 -85
  25. package/dist/call-hierarchy-model.js +102 -68
  26. package/dist/call-hierarchy.js +458 -359
  27. package/dist/code-actions-model.js +88 -76
  28. package/dist/code-actions.js +372 -283
  29. package/dist/code-authors.js +64 -55
  30. package/dist/code-vision-model.js +134 -127
  31. package/dist/code-vision.js +224 -206
  32. package/dist/completion-call.js +94 -67
  33. package/dist/completion-model.js +334 -244
  34. package/dist/completion.js +840 -649
  35. package/dist/conflict-model.js +35 -29
  36. package/dist/context-menu-rows.js +42 -17
  37. package/dist/context-menu.js +64 -46
  38. package/dist/definition-link.js +88 -85
  39. package/dist/diagnostics-model.js +74 -55
  40. package/dist/diagnostics.js +121 -100
  41. package/dist/diff-expansion.js +38 -35
  42. package/dist/diff-overlays.js +144 -22
  43. package/dist/diff-syntax.js +65 -42
  44. package/dist/diff-wordmarks.js +26 -23
  45. package/dist/doc-comment-model.js +265 -216
  46. package/dist/editor-language.js +177 -152
  47. package/dist/error-message.js +2 -2
  48. package/dist/find-query.js +49 -36
  49. package/dist/folding.js +81 -62
  50. package/dist/format-on-type.js +86 -67
  51. package/dist/highlight-layers.js +19 -20
  52. package/dist/highlights.js +105 -83
  53. package/dist/history.js +44 -38
  54. package/dist/holder.js +18 -18
  55. package/dist/host-types.js +1 -1
  56. package/dist/hover-content.js +167 -129
  57. package/dist/hover.js +285 -245
  58. package/dist/index.js +10 -10
  59. package/dist/inlay-hints.js +369 -324
  60. package/dist/key-priority.js +34 -31
  61. package/dist/language-ids.js +45 -42
  62. package/dist/line-edits.js +74 -28
  63. package/dist/line-text.js +18 -3
  64. package/dist/locales/en.json +38 -38
  65. package/dist/locales/nl.json +41 -41
  66. package/dist/models.js +1 -1
  67. package/dist/navigation-history.js +40 -41
  68. package/dist/navigation.js +141 -123
  69. package/dist/panel-size.js +8 -8
  70. package/dist/patch-decorations.js +236 -160
  71. package/dist/patch-layout.js +331 -262
  72. package/dist/paths.js +3 -3
  73. package/dist/peek-model.js +86 -69
  74. package/dist/peek.js +254 -192
  75. package/dist/pick.js +164 -157
  76. package/dist/popup-focus.js +5 -5
  77. package/dist/popup-placement.js +36 -21
  78. package/dist/popups.js +13 -13
  79. package/dist/project-language.js +228 -211
  80. package/dist/project-problems.js +81 -69
  81. package/dist/proposal-model.js +163 -126
  82. package/dist/recent-choices.js +25 -25
  83. package/dist/refresher.js +44 -49
  84. package/dist/rename-model.js +113 -113
  85. package/dist/rename.js +321 -233
  86. package/dist/rendered-docs.d.ts +4 -2
  87. package/dist/rendered-docs.js +384 -318
  88. package/dist/rendering-context.js +5 -3
  89. package/dist/rendering.js +22 -4
  90. package/dist/review-group.js +42 -39
  91. package/dist/row-host.d.ts +4 -2
  92. package/dist/row-host.js +90 -78
  93. package/dist/selection-ranges.js +21 -17
  94. package/dist/semantic-delta.js +22 -19
  95. package/dist/semantic-model.js +86 -73
  96. package/dist/semantic-tokens.js +201 -162
  97. package/dist/shortcut-keys.js +16 -10
  98. package/dist/signature-model.js +34 -28
  99. package/dist/signature.js +177 -129
  100. package/dist/snippet-field-transforms.js +118 -106
  101. package/dist/snippet-indent.js +48 -43
  102. package/dist/snippet-range-order.js +11 -11
  103. package/dist/snippet-session.js +703 -667
  104. package/dist/snippet-transform-client.js +59 -59
  105. package/dist/snippet-transform-format.js +285 -263
  106. package/dist/snippet-transform-protocol.js +1 -1
  107. package/dist/snippet-transform-queue.js +163 -153
  108. package/dist/snippet-transform-worker.js +42 -35
  109. package/dist/snippet-variable-names.js +1 -36
  110. package/dist/snippet-variables.js +53 -63
  111. package/dist/snippet.js +477 -405
  112. package/dist/split-alignment.js +47 -35
  113. package/dist/split-scroll.js +42 -40
  114. package/dist/symbol-links.js +33 -25
  115. package/dist/symbol-picker-model.js +126 -116
  116. package/dist/symbol-picker.js +98 -59
  117. package/dist/symbols.js +117 -88
  118. package/dist/testing.js +248 -165
  119. package/dist/timers.js +35 -30
  120. package/dist/use-panel-body.js +49 -8
  121. package/dist/use-problem-counts.js +29 -15
  122. package/dist/view-state.js +54 -33
  123. package/dist/word-diff.js +18 -18
  124. package/dist/workspace-edit.js +300 -249
  125. package/package.json +6 -6
  126. package/src/CallHierarchyPanel.tsx +2 -2
  127. package/src/DiffView.tsx +3 -3
  128. package/src/FindReplace.tsx +2 -2
  129. package/src/LanguagePopups.tsx +2 -0
  130. package/src/PeekPanel.tsx +3 -3
  131. package/src/RenderedDocs.tsx +9 -10
  132. package/src/SymbolPicker.tsx +2 -2
  133. package/src/diff-overlays.tsx +6 -4
  134. package/src/locales/en.json +38 -38
  135. package/src/locales/nl.json +41 -41
  136. package/src/rendered-docs.ts +14 -3
  137. package/src/row-host.ts +14 -3
@@ -1,125 +1,184 @@
1
1
  import { parseHunkHeader } from '@adecore/merge';
2
- export const DEFAULT_PATCH_LIMITS = { maxLines: 50_000, maxCharacters: 4_000_000 };
3
- const OTHER = { role: 'other', old: null, new: null };
4
- const FILE_HEADER = { role: 'file-header', old: null, new: null };
5
- const HUNK_HEADER = { role: 'hunk-header', old: null, new: null };
6
- const NO_NEWLINE = { role: 'no-newline', old: null, new: null };
7
- const BINARY = { role: 'binary', old: null, new: null };
2
+ export const DEFAULT_PATCH_LIMITS = {
3
+ maxLines: 50_000,
4
+ maxCharacters: 4_000_000
5
+ };
6
+ const OTHER = {
7
+ role: 'other',
8
+ old: null,
9
+ new: null
10
+ };
11
+ const FILE_HEADER = {
12
+ role: 'file-header',
13
+ old: null,
14
+ new: null
15
+ };
16
+ const HUNK_HEADER = {
17
+ role: 'hunk-header',
18
+ old: null,
19
+ new: null
20
+ };
21
+ const NO_NEWLINE = {
22
+ role: 'no-newline',
23
+ old: null,
24
+ new: null
25
+ };
26
+ const BINARY = {
27
+ role: 'binary',
28
+ old: null,
29
+ new: null
30
+ };
8
31
  const GIT_HEADERS = /^(?:index |old mode |new mode |deleted file mode |new file mode |similarity index |dissimilarity index |rename from |rename to |copy from |copy to )/;
9
- const ESCAPES = { a: 7, b: 8, f: 12, n: 10, r: 13, t: 9, v: 11, '"': 34, '\\': 92 };
32
+ const ESCAPES = {
33
+ a: 7,
34
+ b: 8,
35
+ f: 12,
36
+ n: 10,
37
+ r: 13,
38
+ t: 9,
39
+ v: 11,
40
+ '"': 34,
41
+ '\\': 92
42
+ };
10
43
  /* Reads a path as git writes it: bare, or in quotes with C escapes and octal bytes. */
11
44
  function unquote(raw) {
12
- if (raw.length < 2 || !raw.startsWith('"') || !raw.endsWith('"')) {
13
- return raw;
45
+ if (raw.length < 2 || !raw.startsWith('"') || !raw.endsWith('"')) {
46
+ return raw;
47
+ }
48
+ const bytes = [];
49
+ const encoder = new TextEncoder();
50
+ const body = raw.slice(1, -1);
51
+ for (let i = 0; i < body.length; i++) {
52
+ if (body[i] !== '\\') {
53
+ const point = String.fromCodePoint(body.codePointAt(i));
54
+ bytes.push(...encoder.encode(point));
55
+ i += point.length - 1;
56
+ continue;
14
57
  }
15
- const bytes = [];
16
- const encoder = new TextEncoder();
17
- const body = raw.slice(1, -1);
18
- for (let i = 0; i < body.length; i++) {
19
- if (body[i] !== '\\') {
20
- const point = String.fromCodePoint(body.codePointAt(i));
21
- bytes.push(...encoder.encode(point));
22
- i += point.length - 1;
23
- continue;
24
- }
25
- const octal = /^[0-3][0-7]{2}/.exec(body.slice(i + 1, i + 4));
26
- const escaped = ESCAPES[body[i + 1] ?? ''];
27
- if (octal !== null) {
28
- bytes.push(Number.parseInt(octal[0], 8));
29
- i += 3;
30
- }
31
- else if (escaped !== undefined) {
32
- bytes.push(escaped);
33
- i++;
34
- }
35
- else {
36
- bytes.push(92);
37
- }
58
+ const octal = /^[0-3][0-7]{2}/.exec(body.slice(i + 1, i + 4));
59
+ const escaped = ESCAPES[body[i + 1] ?? ''];
60
+ if (octal !== null) {
61
+ bytes.push(Number.parseInt(octal[0], 8));
62
+ i += 3;
63
+ } else if (escaped !== undefined) {
64
+ bytes.push(escaped);
65
+ i++;
66
+ } else {
67
+ bytes.push(92);
38
68
  }
39
- return new TextDecoder().decode(new Uint8Array(bytes));
69
+ }
70
+ return new TextDecoder().decode(new Uint8Array(bytes));
40
71
  }
41
72
  /* The name on a `---`, `+++`, `rename` or `copy` line: a timestamp after a tab goes, and `/dev/null` is no file. */
42
73
  function pathOf(raw) {
43
- const tab = raw.indexOf('\t');
44
- const name = unquote(tab === -1 ? raw : raw.slice(0, tab));
45
- return name === '/dev/null' ? null : name;
74
+ const tab = raw.indexOf('\t');
75
+ const name = unquote(tab === -1 ? raw : raw.slice(0, tab));
76
+ return name === '/dev/null' ? null : name;
46
77
  }
47
78
  function stripPrefix(path, prefix) {
48
- return path !== null && path.startsWith(prefix) ? path.slice(2) : path;
79
+ return path !== null && path.startsWith(prefix) ? path.slice(2) : path;
49
80
  }
50
81
  /* The two names of a `diff --git a/x b/y` line; with spaces in an unquoted name, the split that gives both halves the same name wins. */
51
82
  function gitPaths(rest) {
52
- if (rest.startsWith('"')) {
53
- const end = rest.indexOf('" ', 1);
54
- if (end !== -1) {
55
- const oldName = unquote(rest.slice(0, end + 1));
56
- const newName = unquote(rest.slice(end + 2));
57
- const prefixed = oldName.startsWith('a/') && newName.startsWith('b/');
58
- return prefixed ? { oldPath: oldName.slice(2), newPath: newName.slice(2), prefixed } : { oldPath: oldName, newPath: newName, prefixed };
59
- }
83
+ if (rest.startsWith('"')) {
84
+ const end = rest.indexOf('" ', 1);
85
+ if (end !== -1) {
86
+ const oldName = unquote(rest.slice(0, end + 1));
87
+ const newName = unquote(rest.slice(end + 2));
88
+ const prefixed = oldName.startsWith('a/') && newName.startsWith('b/');
89
+ return prefixed ? {
90
+ oldPath: oldName.slice(2),
91
+ newPath: newName.slice(2),
92
+ prefixed
93
+ } : {
94
+ oldPath: oldName,
95
+ newPath: newName,
96
+ prefixed
97
+ };
60
98
  }
61
- const middle = rest.indexOf(' b/');
62
- if (rest.startsWith('a/') && middle !== -1) {
63
- for (let at = middle; at !== -1; at = rest.indexOf(' b/', at + 1)) {
64
- if (rest.slice(2, at) === rest.slice(at + 3)) {
65
- return { oldPath: rest.slice(2, at), newPath: rest.slice(at + 3), prefixed: true };
66
- }
67
- }
68
- return { oldPath: rest.slice(2, middle), newPath: unquote(rest.slice(middle + 3)), prefixed: true };
99
+ }
100
+ const middle = rest.indexOf(' b/');
101
+ if (rest.startsWith('a/') && middle !== -1) {
102
+ for (let at = middle; at !== -1; at = rest.indexOf(' b/', at + 1)) {
103
+ if (rest.slice(2, at) === rest.slice(at + 3)) {
104
+ return {
105
+ oldPath: rest.slice(2, at),
106
+ newPath: rest.slice(at + 3),
107
+ prefixed: true
108
+ };
109
+ }
69
110
  }
70
- const split = rest.indexOf(' ');
71
- return split === -1
72
- ? { oldPath: unquote(rest), newPath: null, prefixed: false }
73
- : { oldPath: unquote(rest.slice(0, split)), newPath: unquote(rest.slice(split + 1)), prefixed: false };
74
- }
75
- function emptySection(firstLine, git) {
76
111
  return {
77
- oldPath: null,
78
- newPath: null,
79
- git,
80
- created: false,
81
- deleted: false,
82
- renamed: false,
83
- copied: false,
84
- binary: false,
85
- firstLine,
86
- lastLine: firstLine,
87
- added: 0,
88
- removed: 0,
89
- hunks: [],
90
- named: false,
91
- prefixed: false
112
+ oldPath: rest.slice(2, middle),
113
+ newPath: unquote(rest.slice(middle + 3)),
114
+ prefixed: true
92
115
  };
116
+ }
117
+ const split = rest.indexOf(' ');
118
+ return split === -1 ? {
119
+ oldPath: unquote(rest),
120
+ newPath: null,
121
+ prefixed: false
122
+ } : {
123
+ oldPath: unquote(rest.slice(0, split)),
124
+ newPath: unquote(rest.slice(split + 1)),
125
+ prefixed: false
126
+ };
127
+ }
128
+ function emptySection(firstLine, git) {
129
+ return {
130
+ oldPath: null,
131
+ newPath: null,
132
+ git,
133
+ created: false,
134
+ deleted: false,
135
+ renamed: false,
136
+ copied: false,
137
+ binary: false,
138
+ firstLine,
139
+ lastLine: firstLine,
140
+ added: 0,
141
+ removed: 0,
142
+ hunks: [],
143
+ named: false,
144
+ prefixed: false
145
+ };
93
146
  }
94
147
  function statusOf(section) {
95
- if (section.binary) {
96
- return 'binary';
97
- }
98
- if (section.created || (!section.git && section.oldPath === null && section.newPath !== null)) {
99
- return 'added';
100
- }
101
- if (section.deleted || (!section.git && section.newPath === null && section.oldPath !== null)) {
102
- return 'deleted';
103
- }
104
- if (section.renamed) {
105
- return 'renamed';
106
- }
107
- return section.copied ? 'copied' : 'modified';
148
+ if (section.binary) {
149
+ return 'binary';
150
+ }
151
+ if (section.created || !section.git && section.oldPath === null && section.newPath !== null) {
152
+ return 'added';
153
+ }
154
+ if (section.deleted || !section.git && section.newPath === null && section.oldPath !== null) {
155
+ return 'deleted';
156
+ }
157
+ if (section.renamed) {
158
+ return 'renamed';
159
+ }
160
+ return section.copied ? 'copied' : 'modified';
108
161
  }
109
162
  function linesOf(text, limits) {
110
- const lines = [];
111
- let start = 0;
112
- while (lines.length < limits.maxLines && start <= limits.maxCharacters) {
113
- const end = text.indexOf('\n', start);
114
- if (end === -1) {
115
- lines.push(text.slice(start).replace(/\r$/, ''));
116
- return { lines, truncated: false };
117
- }
118
- lines.push(text.slice(start, end).replace(/\r$/, ''));
119
- start = end + 1;
163
+ const lines = [];
164
+ let start = 0;
165
+ while (lines.length < limits.maxLines && start <= limits.maxCharacters) {
166
+ const end = text.indexOf('\n', start);
167
+ if (end === -1) {
168
+ lines.push(text.slice(start).replace(/\r$/, ''));
169
+ return {
170
+ lines,
171
+ truncated: false
172
+ };
120
173
  }
121
- // Stopping right after a final newline leaves nothing unread.
122
- return { lines, truncated: start < text.length };
174
+ lines.push(text.slice(start, end).replace(/\r$/, ''));
175
+ start = end + 1;
176
+ }
177
+ // Stopping right after a final newline leaves nothing unread.
178
+ return {
179
+ lines,
180
+ truncated: start < text.length
181
+ };
123
182
  }
124
183
  /*
125
184
  * What every line of a patch is, with the file numbers of its lines and the files it touches. It reads
@@ -128,177 +187,187 @@ function linesOf(text, limits) {
128
187
  * and says so in `truncated`.
129
188
  */
130
189
  export function patchLayoutOf(text, limits = {}) {
131
- const { lines: source, truncated } = linesOf(text, { ...DEFAULT_PATCH_LIMITS, ...limits });
132
- const lines = [];
133
- const sections = [];
134
- let section;
135
- let hunk;
136
- let binaryData = false;
137
- const seal = () => {
138
- if (hunk !== undefined && (hunk.oldLeft > 0 || hunk.newLeft > 0)) {
139
- hunk.layout.consistent = false;
140
- }
141
- hunk = undefined;
142
- };
143
- const open = (line, git) => {
144
- seal();
145
- binaryData = false;
146
- section = emptySection(line, git);
147
- sections.push(section);
148
- return section;
149
- };
150
- // A removed line can read `-- x`, so `---` starts a file only with `+++` under it.
151
- const startsFile = (index) => source[index].startsWith('--- ') && (source[index + 1]?.startsWith('+++ ') ?? false);
152
- const push = (line, index) => {
153
- lines.push(line);
154
- if (section !== undefined) {
155
- section.lastLine = index;
156
- }
157
- };
158
- for (let index = 0; index < source.length; index++) {
159
- const line = source[index];
160
- const remaining = hunk !== undefined && (hunk.oldLeft > 0 || hunk.newLeft > 0);
161
- const gitStart = line.startsWith('diff --git ');
162
- const header = line.startsWith('@@') ? parseHunkHeader(line) : undefined;
163
- const fileStart = startsFile(index) && (!remaining || source[index + 2]?.startsWith('@@') === true);
164
- if (remaining && !gitStart && header === undefined && !fileStart) {
165
- if (isContent(line) || line === '' || line.startsWith('\\')) {
166
- push(contentLine(line, hunk, section), index);
167
- }
168
- else {
169
- hunk.layout.consistent = false;
170
- push(OTHER, index);
171
- }
172
- continue;
173
- }
174
- if (gitStart) {
175
- const created = open(index, true);
176
- ({ oldPath: created.oldPath, newPath: created.newPath, prefixed: created.prefixed } = gitPaths(line.slice('diff --git '.length)));
177
- push(FILE_HEADER, index);
178
- continue;
179
- }
180
- if (fileStart) {
181
- const reuse = section !== undefined && section.git && !section.named && section.hunks.length === 0;
182
- const target = reuse ? section : open(index, false);
183
- seal();
184
- nameSides(target, line, source[index + 1]);
185
- push(FILE_HEADER, index);
186
- push(FILE_HEADER, index + 1);
187
- index++;
188
- continue;
189
- }
190
- if (header !== undefined) {
191
- seal();
192
- const target = section ?? open(index, false);
193
- const layout = { line: index, ...header, consistent: true };
194
- target.hunks.push(layout);
195
- hunk = { layout, oldLeft: header.oldCount, newLeft: header.newCount, oldNumber: header.oldStart, newNumber: header.newStart };
196
- push(HUNK_HEADER, index);
197
- continue;
198
- }
199
- if (section === undefined) {
200
- push(OTHER, index);
201
- }
202
- else if (hunk !== undefined && line.startsWith('\\')) {
203
- push(NO_NEWLINE, index);
204
- }
205
- else if (hunk !== undefined && isContent(line) && line !== '-- ') {
206
- // The counts ran out, so a line a person typed below the hunk extends it. The `-- ` of a mail signature does not.
207
- hunk.layout.consistent = false;
208
- push(contentLine(line, hunk, section), index);
209
- }
210
- else if (hunk === undefined && section.git && GIT_HEADERS.test(line)) {
211
- readGitHeader(section, line);
212
- push(FILE_HEADER, index);
213
- }
214
- else if (/^Binary files .* differ$/.test(line) || line === 'GIT binary patch') {
215
- section.binary = true;
216
- binaryData = line === 'GIT binary patch';
217
- push(BINARY, index);
218
- }
219
- else if (binaryData && line !== '') {
220
- push(BINARY, index);
221
- }
222
- else if (line === '') {
223
- lines.push(OTHER);
224
- }
225
- else {
226
- push(OTHER, index);
227
- }
190
+ const {
191
+ lines: source,
192
+ truncated
193
+ } = linesOf(text, {
194
+ ...DEFAULT_PATCH_LIMITS,
195
+ ...limits
196
+ });
197
+ const lines = [];
198
+ const sections = [];
199
+ let section;
200
+ let hunk;
201
+ let binaryData = false;
202
+ const seal = () => {
203
+ if (hunk !== undefined && (hunk.oldLeft > 0 || hunk.newLeft > 0)) {
204
+ hunk.layout.consistent = false;
228
205
  }
206
+ hunk = undefined;
207
+ };
208
+ const open = (line, git) => {
229
209
  seal();
230
- return {
231
- lines,
232
- truncated,
233
- files: sections.map((entry) => ({
234
- oldPath: entry.oldPath,
235
- newPath: entry.newPath,
236
- status: statusOf(entry),
237
- firstLine: entry.firstLine,
238
- lastLine: entry.lastLine,
239
- added: entry.added,
240
- removed: entry.removed,
241
- hunks: entry.hunks
242
- }))
243
- };
244
- }
245
- function isContent(line) {
246
- return line.startsWith('+') || line.startsWith('-') || line.startsWith(' ');
247
- }
248
- /* A line under a hunk header; an empty line is a context line whose space an editor trimmed. */
249
- function contentLine(line, hunk, section) {
250
- if (line.startsWith('\\')) {
251
- return NO_NEWLINE;
210
+ binaryData = false;
211
+ section = emptySection(line, git);
212
+ sections.push(section);
213
+ return section;
214
+ };
215
+ // A removed line can read `-- x`, so `---` starts a file only with `+++` under it.
216
+ const startsFile = index => source[index].startsWith('--- ') && (source[index + 1]?.startsWith('+++ ') ?? false);
217
+ const push = (line, index) => {
218
+ lines.push(line);
219
+ if (section !== undefined) {
220
+ section.lastLine = index;
252
221
  }
253
- const kind = line.startsWith('+') ? 'added' : line.startsWith('-') ? 'removed' : 'context';
254
- const takesOld = kind !== 'added';
255
- const takesNew = kind !== 'removed';
256
- if ((takesOld && hunk.oldLeft === 0) || (takesNew && hunk.newLeft === 0)) {
222
+ };
223
+ for (let index = 0; index < source.length; index++) {
224
+ const line = source[index];
225
+ const remaining = hunk !== undefined && (hunk.oldLeft > 0 || hunk.newLeft > 0);
226
+ const gitStart = line.startsWith('diff --git ');
227
+ const header = line.startsWith('@@') ? parseHunkHeader(line) : undefined;
228
+ const fileStart = startsFile(index) && (!remaining || source[index + 2]?.startsWith('@@') === true);
229
+ if (remaining && !gitStart && header === undefined && !fileStart) {
230
+ if (isContent(line) || line === '' || line.startsWith('\\')) {
231
+ push(contentLine(line, hunk, section), index);
232
+ } else {
257
233
  hunk.layout.consistent = false;
234
+ push(OTHER, index);
235
+ }
236
+ continue;
237
+ }
238
+ if (gitStart) {
239
+ const created = open(index, true);
240
+ ({
241
+ oldPath: created.oldPath,
242
+ newPath: created.newPath,
243
+ prefixed: created.prefixed
244
+ } = gitPaths(line.slice('diff --git '.length)));
245
+ push(FILE_HEADER, index);
246
+ continue;
247
+ }
248
+ if (fileStart) {
249
+ const reuse = section !== undefined && section.git && !section.named && section.hunks.length === 0;
250
+ const target = reuse ? section : open(index, false);
251
+ seal();
252
+ nameSides(target, line, source[index + 1]);
253
+ push(FILE_HEADER, index);
254
+ push(FILE_HEADER, index + 1);
255
+ index++;
256
+ continue;
258
257
  }
259
- if (kind === 'added') {
260
- section.added++;
258
+ if (header !== undefined) {
259
+ seal();
260
+ const target = section ?? open(index, false);
261
+ const layout = {
262
+ line: index,
263
+ ...header,
264
+ consistent: true
265
+ };
266
+ target.hunks.push(layout);
267
+ hunk = {
268
+ layout,
269
+ oldLeft: header.oldCount,
270
+ newLeft: header.newCount,
271
+ oldNumber: header.oldStart,
272
+ newNumber: header.newStart
273
+ };
274
+ push(HUNK_HEADER, index);
275
+ continue;
261
276
  }
262
- else if (kind === 'removed') {
263
- section.removed++;
277
+ if (section === undefined) {
278
+ push(OTHER, index);
279
+ } else if (hunk !== undefined && line.startsWith('\\')) {
280
+ push(NO_NEWLINE, index);
281
+ } else if (hunk !== undefined && isContent(line) && line !== '-- ') {
282
+ // The counts ran out, so a line a person typed below the hunk extends it. The `-- ` of a mail signature does not.
283
+ hunk.layout.consistent = false;
284
+ push(contentLine(line, hunk, section), index);
285
+ } else if (hunk === undefined && section.git && GIT_HEADERS.test(line)) {
286
+ readGitHeader(section, line);
287
+ push(FILE_HEADER, index);
288
+ } else if (/^Binary files .* differ$/.test(line) || line === 'GIT binary patch') {
289
+ section.binary = true;
290
+ binaryData = line === 'GIT binary patch';
291
+ push(BINARY, index);
292
+ } else if (binaryData && line !== '') {
293
+ push(BINARY, index);
294
+ } else if (line === '') {
295
+ lines.push(OTHER);
296
+ } else {
297
+ push(OTHER, index);
264
298
  }
265
- const oldNumber = takesOld ? hunk.oldNumber++ : null;
266
- const newNumber = takesNew ? hunk.newNumber++ : null;
267
- hunk.oldLeft = takesOld ? Math.max(0, hunk.oldLeft - 1) : hunk.oldLeft;
268
- hunk.newLeft = takesNew ? Math.max(0, hunk.newLeft - 1) : hunk.newLeft;
269
- return { role: kind, old: oldNumber, new: newNumber };
299
+ }
300
+ seal();
301
+ return {
302
+ lines,
303
+ truncated,
304
+ files: sections.map(entry => ({
305
+ oldPath: entry.oldPath,
306
+ newPath: entry.newPath,
307
+ status: statusOf(entry),
308
+ firstLine: entry.firstLine,
309
+ lastLine: entry.lastLine,
310
+ added: entry.added,
311
+ removed: entry.removed,
312
+ hunks: entry.hunks
313
+ }))
314
+ };
315
+ }
316
+ function isContent(line) {
317
+ return line.startsWith('+') || line.startsWith('-') || line.startsWith(' ');
318
+ }
319
+ /* A line under a hunk header; an empty line is a context line whose space an editor trimmed. */
320
+ function contentLine(line, hunk, section) {
321
+ if (line.startsWith('\\')) {
322
+ return NO_NEWLINE;
323
+ }
324
+ const kind = line.startsWith('+') ? 'added' : line.startsWith('-') ? 'removed' : 'context';
325
+ const takesOld = kind !== 'added';
326
+ const takesNew = kind !== 'removed';
327
+ if (takesOld && hunk.oldLeft === 0 || takesNew && hunk.newLeft === 0) {
328
+ hunk.layout.consistent = false;
329
+ }
330
+ if (kind === 'added') {
331
+ section.added++;
332
+ } else if (kind === 'removed') {
333
+ section.removed++;
334
+ }
335
+ const oldNumber = takesOld ? hunk.oldNumber++ : null;
336
+ const newNumber = takesNew ? hunk.newNumber++ : null;
337
+ hunk.oldLeft = takesOld ? Math.max(0, hunk.oldLeft - 1) : hunk.oldLeft;
338
+ hunk.newLeft = takesNew ? Math.max(0, hunk.newLeft - 1) : hunk.newLeft;
339
+ return {
340
+ role: kind,
341
+ old: oldNumber,
342
+ new: newNumber
343
+ };
270
344
  }
271
345
  function nameSides(section, oldLine, newLine) {
272
- const oldName = pathOf(oldLine.slice(4));
273
- const newName = pathOf(newLine.slice(4));
274
- const prefixed = section.git ? section.prefixed : (oldName?.startsWith('a/') ?? false) && (newName?.startsWith('b/') ?? false);
275
- section.oldPath = prefixed ? stripPrefix(oldName, 'a/') : oldName;
276
- section.newPath = prefixed ? stripPrefix(newName, 'b/') : newName;
277
- section.created ||= oldName === null;
278
- section.deleted ||= newName === null;
279
- section.named = true;
346
+ const oldName = pathOf(oldLine.slice(4));
347
+ const newName = pathOf(newLine.slice(4));
348
+ const prefixed = section.git ? section.prefixed : (oldName?.startsWith('a/') ?? false) && (newName?.startsWith('b/') ?? false);
349
+ section.oldPath = prefixed ? stripPrefix(oldName, 'a/') : oldName;
350
+ section.newPath = prefixed ? stripPrefix(newName, 'b/') : newName;
351
+ section.created ||= oldName === null;
352
+ section.deleted ||= newName === null;
353
+ section.named = true;
280
354
  }
281
355
  function readGitHeader(section, line) {
282
- if (line.startsWith('new file mode ')) {
283
- section.created = true;
284
- }
285
- else if (line.startsWith('deleted file mode ')) {
286
- section.deleted = true;
287
- }
288
- else if (line.startsWith('rename from ')) {
289
- section.renamed = true;
290
- section.oldPath = pathOf(line.slice('rename from '.length));
291
- }
292
- else if (line.startsWith('rename to ')) {
293
- section.renamed = true;
294
- section.newPath = pathOf(line.slice('rename to '.length));
295
- }
296
- else if (line.startsWith('copy from ')) {
297
- section.copied = true;
298
- section.oldPath = pathOf(line.slice('copy from '.length));
299
- }
300
- else if (line.startsWith('copy to ')) {
301
- section.copied = true;
302
- section.newPath = pathOf(line.slice('copy to '.length));
303
- }
304
- }
356
+ if (line.startsWith('new file mode ')) {
357
+ section.created = true;
358
+ } else if (line.startsWith('deleted file mode ')) {
359
+ section.deleted = true;
360
+ } else if (line.startsWith('rename from ')) {
361
+ section.renamed = true;
362
+ section.oldPath = pathOf(line.slice('rename from '.length));
363
+ } else if (line.startsWith('rename to ')) {
364
+ section.renamed = true;
365
+ section.newPath = pathOf(line.slice('rename to '.length));
366
+ } else if (line.startsWith('copy from ')) {
367
+ section.copied = true;
368
+ section.oldPath = pathOf(line.slice('copy from '.length));
369
+ } else if (line.startsWith('copy to ')) {
370
+ section.copied = true;
371
+ section.newPath = pathOf(line.slice('copy to '.length));
372
+ }
373
+ }
package/dist/paths.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /* A path inside `folder` relative to it; any other path as it is. */
2
2
  export function relativeTo(path, folder) {
3
- return path.startsWith(`${folder}/`) ? path.slice(folder.length + 1) : path;
3
+ return path.startsWith(`${folder}/`) ? path.slice(folder.length + 1) : path;
4
4
  }
5
5
  export function basenameOf(path) {
6
- return path.replaceAll('\\', '/').split('/').at(-1) ?? path;
7
- }
6
+ return path.replaceAll('\\', '/').split('/').at(-1) ?? path;
7
+ }