reslop 0.1.9 → 0.2.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 (94) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/README.md +2 -1
  3. package/bin/reslop.js +2 -2
  4. package/lib/ansi.js +4 -2
  5. package/lib/api.js +2 -2
  6. package/lib/capabilities.js +7 -14
  7. package/lib/cli.js +28 -47
  8. package/lib/clipboard.js +1 -1
  9. package/lib/{deps/changes.js → deps-diff.js} +309 -19
  10. package/lib/deps.js +498 -29
  11. package/lib/detect.js +1 -7
  12. package/lib/diff/align.js +7 -10
  13. package/lib/diff/diff.js +53 -0
  14. package/lib/diff/display.js +5 -4
  15. package/lib/diff/inline.js +26 -21
  16. package/lib/diff/items.js +6 -4
  17. package/lib/diff/patch.js +1 -3
  18. package/lib/diff/unit.js +392 -0
  19. package/lib/files.js +5 -8
  20. package/lib/{git/branches.js → git-branches.js} +6 -5
  21. package/lib/{git/dependencies.js → git-deps.js} +111 -38
  22. package/lib/git.js +589 -68
  23. package/lib/github.js +28 -197
  24. package/lib/gitlab.js +24 -195
  25. package/lib/highlight/bash.js +1 -3
  26. package/lib/highlight/css.js +1 -3
  27. package/lib/highlight/js.js +11 -14
  28. package/lib/highlight.js +6 -9
  29. package/lib/keys.js +14 -8
  30. package/lib/manifest.js +330 -0
  31. package/lib/remote.js +374 -0
  32. package/lib/render/branches.js +14 -68
  33. package/lib/render/chrome.js +24 -9
  34. package/lib/render/commits.js +6 -59
  35. package/lib/render/diff.js +16 -16
  36. package/lib/render/files.js +6 -34
  37. package/lib/render/frame.js +23 -12
  38. package/lib/render/notes.js +4 -3
  39. package/lib/render/primitives.js +45 -0
  40. package/lib/{render.js → render/render.js} +5 -5
  41. package/lib/render/unit.js +41 -0
  42. package/lib/review.js +750 -14
  43. package/lib/select.js +5 -10
  44. package/lib/session/accessors.js +9 -1
  45. package/lib/session/actions.js +32 -16
  46. package/lib/session/branches.js +116 -128
  47. package/lib/session/change-actions.js +202 -123
  48. package/lib/session/commit.js +105 -112
  49. package/lib/session/compose.js +158 -13
  50. package/lib/session/dispatch.js +104 -60
  51. package/lib/session/files-pane.js +142 -148
  52. package/lib/session/items.js +22 -30
  53. package/lib/session/lifecycle.js +44 -44
  54. package/lib/session/load.js +19 -33
  55. package/lib/session/navigation.js +12 -1
  56. package/lib/session/ops.js +28 -41
  57. package/lib/session/pointer.js +54 -57
  58. package/lib/session/review.js +23 -37
  59. package/lib/session/terminal.js +6 -12
  60. package/lib/session/todos.js +8 -12
  61. package/lib/session/unit.js +208 -0
  62. package/lib/session/update.js +33 -46
  63. package/lib/session/view.js +78 -9
  64. package/lib/session/watch.js +11 -19
  65. package/lib/session.js +21 -246
  66. package/lib/source.js +3 -3
  67. package/lib/update.js +40 -75
  68. package/lib/{git/proc.js → utilities.js} +75 -11
  69. package/lib/wrap.js +1 -3
  70. package/package.json +1 -1
  71. package/lib/deps/items.js +0 -134
  72. package/lib/deps/manifest.js +0 -152
  73. package/lib/deps/merge.js +0 -120
  74. package/lib/deps/proposals.js +0 -164
  75. package/lib/deps/reports.js +0 -180
  76. package/lib/deps/rows.js +0 -176
  77. package/lib/deps/scan.js +0 -138
  78. package/lib/deps/usage.js +0 -68
  79. package/lib/deps/versions.js +0 -119
  80. package/lib/diff.js +0 -33
  81. package/lib/git/changes.js +0 -164
  82. package/lib/git/read.js +0 -253
  83. package/lib/git/run.js +0 -100
  84. package/lib/npm/commands.js +0 -100
  85. package/lib/remote/changes.js +0 -37
  86. package/lib/remote/headers.js +0 -36
  87. package/lib/remote/http.js +0 -122
  88. package/lib/remote/notes.js +0 -94
  89. package/lib/review/format.js +0 -434
  90. package/lib/review/storage.js +0 -150
  91. package/lib/review/store.js +0 -178
  92. package/lib/review/templates.js +0 -49
  93. package/lib/session/origins.js +0 -7
  94. package/lib/sys.js +0 -84
@@ -46,14 +46,15 @@ const replaceAdds = (lines, overlay, blockId) => {
46
46
  return lines.filter((line) => line.type !== 'add');
47
47
  }
48
48
  const texts = text.split('\n');
49
- const tagged = [];
49
+ const { length } = texts;
50
+ const tagged = new Array(length);
50
51
  let offset = 0;
51
- for (let i = 0; i < texts.length; i++) {
52
+ for (let i = 0; i < length; i++) {
52
53
  const row = texts[i];
53
54
  const start = offset;
54
55
  const end = start + row.length;
55
- const isLast = i === texts.length - 1;
56
- tagged.push(taggedAdd(row, blockId, start, end));
56
+ const isLast = i === length - 1;
57
+ tagged[i] = taggedAdd(row, blockId, start, end);
57
58
  offset = isLast ? end : end + 1;
58
59
  }
59
60
  const out = [];
@@ -72,13 +72,11 @@ const tokenSimilarity = (a, b) => {
72
72
  return (2 * lcs) / (a.length + b.length);
73
73
  };
74
74
 
75
- const similarity = (left, right) =>
76
- tokenSimilarity(graphemes(left), graphemes(right));
77
-
78
75
  const fillLcsTable = (a, b) => {
79
76
  const n = a.length;
80
77
  const m = b.length;
81
- const dp = Array.from({ length: n + 1 }, () => new Uint32Array(m + 1));
78
+ const dp = new Array(n + 1);
79
+ for (let i = 0; i <= n; i++) dp[i] = new Uint32Array(m + 1);
82
80
  for (let i = 1; i <= n; i++) {
83
81
  const ai = a[i - 1];
84
82
  const row = dp[i];
@@ -92,30 +90,31 @@ const fillLcsTable = (a, b) => {
92
90
  };
93
91
 
94
92
  const tracebackLcs = (a, b, dp) => {
95
- const rev = [];
93
+ const rev = new Array(a.length + b.length - dp[a.length][b.length]);
94
+ let n = 0;
96
95
  let i = a.length;
97
96
  let j = b.length;
98
97
  while (i > 0 && j > 0) {
99
98
  if (a[i - 1] === b[j - 1]) {
100
- rev.push({ type: 'eq', text: a[i - 1] });
99
+ rev[n++] = { type: 'eq', text: a[i - 1] };
101
100
  i -= 1;
102
101
  j -= 1;
103
102
  continue;
104
103
  }
105
104
  if (dp[i - 1][j] >= dp[i][j - 1]) {
106
- rev.push({ type: 'del', text: a[i - 1] });
105
+ rev[n++] = { type: 'del', text: a[i - 1] };
107
106
  i -= 1;
108
107
  continue;
109
108
  }
110
- rev.push({ type: 'add', text: b[j - 1] });
109
+ rev[n++] = { type: 'add', text: b[j - 1] };
111
110
  j -= 1;
112
111
  }
113
112
  while (i > 0) {
114
- rev.push({ type: 'del', text: a[i - 1] });
113
+ rev[n++] = { type: 'del', text: a[i - 1] };
115
114
  i -= 1;
116
115
  }
117
116
  while (j > 0) {
118
- rev.push({ type: 'add', text: b[j - 1] });
117
+ rev[n++] = { type: 'add', text: b[j - 1] };
119
118
  j -= 1;
120
119
  }
121
120
  rev.reverse();
@@ -174,13 +173,17 @@ const diffTokenMiddle = (midA, midB) => {
174
173
  };
175
174
 
176
175
  const joinAffix = (prefix, mid, suffix) => {
177
- const parts = [];
178
- if (prefix.length) {
179
- parts.push({ text: prefix.join(''), changed: false });
176
+ const hasPrefix = prefix.length > 0;
177
+ const hasSuffix = suffix.length > 0;
178
+ const extra = (hasPrefix ? 1 : 0) + (hasSuffix ? 1 : 0);
179
+ const parts = new Array(mid.length + extra);
180
+ const at = hasPrefix ? 1 : 0;
181
+ if (hasPrefix) {
182
+ parts[0] = { text: prefix.join(''), changed: false };
180
183
  }
181
- for (const span of mid) parts.push(span);
182
- if (suffix.length) {
183
- parts.push({ text: suffix.join(''), changed: false });
184
+ for (let i = 0; i < mid.length; i++) parts[at + i] = mid[i];
185
+ if (hasSuffix) {
186
+ parts[at + mid.length] = { text: suffix.join(''), changed: false };
184
187
  }
185
188
  return collapseSpans(parts);
186
189
  };
@@ -253,7 +256,10 @@ const spansFromWordOps = (ops) => {
253
256
  i += 1;
254
257
  }
255
258
  const one = dels.length === 1 && adds.length === 1;
256
- const close = one && similarity(dels[0], adds[0]) >= WORD_REFINE_MIN;
259
+ const close =
260
+ one &&
261
+ tokenSimilarity(graphemes(dels[0]), graphemes(adds[0])) >=
262
+ WORD_REFINE_MIN;
257
263
  if (close) {
258
264
  const inner = diffTokens(graphemes(dels[0]), graphemes(adds[0]));
259
265
  for (const span of inner.oldSpans) oldParts.push(span);
@@ -291,10 +297,9 @@ const diffChars = (oldText, newText) => {
291
297
  } else {
292
298
  mid = spansFromWordOps(lcsOps(midA, midB));
293
299
  }
294
- return {
295
- oldSpans: joinAffix(prefix, mid.oldSpans, suffix),
296
- newSpans: joinAffix(prefix, mid.newSpans, suffix),
297
- };
300
+ const oldSpans = joinAffix(prefix, mid.oldSpans, suffix);
301
+ const newSpans = joinAffix(prefix, mid.newSpans, suffix);
302
+ return { oldSpans, newSpans };
298
303
  };
299
304
 
300
305
  module.exports = { tokenSimilarity, diffChars };
package/lib/diff/items.js CHANGED
@@ -82,14 +82,16 @@ const refreshIndexPatches = (items, item) => {
82
82
  if (other.hunk !== item.hunk || other.file !== item.file) continue;
83
83
  sides[other.blockId] = INDEX_SIDE[other.origin] ?? 'old';
84
84
  }
85
- const next = [];
86
- for (const other of items) {
85
+ const { length } = items;
86
+ const next = new Array(length);
87
+ for (let i = 0; i < length; i++) {
88
+ const other = items[i];
87
89
  if (other.hunk !== item.hunk || other.file !== item.file) {
88
- next.push(other);
90
+ next[i] = other;
89
91
  continue;
90
92
  }
91
93
  const patchAdd = formatPatch(other.file, other.hunk, other.blockId, sides);
92
- next.push({ ...other, patchAdd });
94
+ next[i] = { ...other, patchAdd };
93
95
  }
94
96
  return next;
95
97
  };
package/lib/diff/patch.js CHANGED
@@ -70,10 +70,8 @@ const countSides = (lines) => {
70
70
  const formatHunkHeader = (oldStart, oldCount, newStart, newCount) =>
71
71
  `@@ -${oldStart},${oldCount} +${newStart},${newCount} @@`;
72
72
 
73
- const lineMark = (type) => LINE_MARK[type] ?? ' ';
74
-
75
73
  const formatBodyLine = (line) => {
76
- const rows = [`${lineMark(line.type)}${line.text}`];
74
+ const rows = [`${LINE_MARK[line.type] ?? ' '}${line.text}`];
77
75
  if (line.noNl) rows.push('\');
78
76
  return rows;
79
77
  };
@@ -0,0 +1,392 @@
1
+ 'use strict';
2
+
3
+ const { attachInline } = require('./align.js');
4
+ const { listPath } = require('../files.js');
5
+
6
+ const splitEditor = (text) => {
7
+ const lines = `${text}`.split('\n');
8
+ if (!lines.length) return [''];
9
+ return lines;
10
+ };
11
+
12
+ const splitFile = (text) => {
13
+ if (!text) return [];
14
+ const lines = `${text}`.split('\n');
15
+ if (lines.length && lines[lines.length - 1] === '') lines.pop();
16
+ return lines;
17
+ };
18
+
19
+ const hunkKey = (item) => {
20
+ if (!item) return '';
21
+ const rel = listPath(item);
22
+ const hunk = item.hunk;
23
+ if (!hunk) return `${rel}\0${item.blockId ?? ''}`;
24
+ return `${rel}\0${hunk.oldStart}\0${hunk.newStart}\0${hunk.header}`;
25
+ };
26
+
27
+ const sameHunk = (left, right) => {
28
+ if (!left || !right) return false;
29
+ if (left.hunk && right.hunk && left.hunk === right.hunk) {
30
+ return listPath(left) === listPath(right);
31
+ }
32
+ const key = hunkKey(left);
33
+ if (!key) return false;
34
+ return key === hunkKey(right);
35
+ };
36
+
37
+ const emptyMarks = (last) => {
38
+ const size = last + 1;
39
+ const dels = new Array(size);
40
+ const adds = new Array(size);
41
+ for (let i = 0; i < size; i++) {
42
+ dels[i] = [];
43
+ adds[i] = null;
44
+ }
45
+ return { dels, adds };
46
+ };
47
+
48
+ const clipAt = (n, last) => Math.min(Math.max(1, n), last);
49
+
50
+ const applyHunk = (marks, item) => {
51
+ const hunk = item.hunk;
52
+ if (!hunk || !hunk.lines) return;
53
+ const last = marks.dels.length - 1;
54
+ let newLine = hunk.newStart > 0 ? hunk.newStart : 1;
55
+ for (const line of hunk.lines) {
56
+ const extra = {
57
+ text: line.text ?? '',
58
+ item,
59
+ blockId: line.blockId,
60
+ origin: item.origin,
61
+ };
62
+ if (line.type === 'del') {
63
+ marks.dels[clipAt(newLine, last)].push(extra);
64
+ continue;
65
+ }
66
+ if (line.type === 'add') {
67
+ marks.adds[clipAt(newLine, last)] = extra;
68
+ newLine += 1;
69
+ continue;
70
+ }
71
+ newLine += 1;
72
+ }
73
+ };
74
+
75
+ const tagged = (type, text, extra = {}) => ({
76
+ type,
77
+ text,
78
+ noNl: false,
79
+ blockId: extra.blockId ?? null,
80
+ origin: extra.origin ?? '',
81
+ item: extra.item ?? null,
82
+ });
83
+
84
+ const itemsForPath = (items, rel) => {
85
+ const out = [];
86
+ for (const item of items) {
87
+ if (listPath(item) === rel) out.push(item);
88
+ }
89
+ return out;
90
+ };
91
+
92
+ const rowsFromAdds = (items) => {
93
+ const rows = [];
94
+ for (const item of items) {
95
+ const lines = item.hunk && item.hunk.lines ? item.hunk.lines : [];
96
+ for (const line of lines) {
97
+ if (line.type === 'add') rows.push(line.text ?? '');
98
+ }
99
+ }
100
+ return rows;
101
+ };
102
+
103
+ const unitLines = (text, items) => {
104
+ let rows = splitFile(text);
105
+ if (!rows.length) rows = rowsFromAdds(items);
106
+ const marks = emptyMarks(rows.length + 1);
107
+ for (const item of items) applyHunk(marks, item);
108
+ const out = [];
109
+ for (let i = 0; i < rows.length; i++) {
110
+ const n = i + 1;
111
+ for (const del of marks.dels[n]) out.push(tagged('del', del.text, del));
112
+ const add = marks.adds[n];
113
+ if (add) out.push(tagged('add', rows[i], add));
114
+ else out.push(tagged('ctx', rows[i], {}));
115
+ }
116
+ for (const del of marks.dels[rows.length + 1]) {
117
+ out.push(tagged('del', del.text, del));
118
+ }
119
+ if (!out.length) out.push(tagged('ctx', '', {}));
120
+ return attachInline(out);
121
+ };
122
+
123
+ const sameBlock = (line, item) => {
124
+ if (!line || !item) return false;
125
+ if (line.item !== item) return false;
126
+ return line.blockId === item.blockId;
127
+ };
128
+
129
+ const blockLineRange = (lines, item) => {
130
+ let start = 0;
131
+ let end = 0;
132
+ let found = false;
133
+ if (!item) return { start, end };
134
+ for (let i = 0; i < lines.length; i++) {
135
+ const line = lines[i];
136
+ if (!sameBlock(line, item)) continue;
137
+ if (line.type === 'ctx') continue;
138
+ if (!found) {
139
+ start = i;
140
+ found = true;
141
+ }
142
+ end = i;
143
+ }
144
+ return { start, end };
145
+ };
146
+
147
+ const blockLineIndex = (lines, item) => blockLineRange(lines, item).start;
148
+
149
+ const hunkLineRange = (lines, item) => {
150
+ let start = 0;
151
+ let end = 0;
152
+ let found = false;
153
+ if (!item) return { start, end };
154
+ for (let i = 0; i < lines.length; i++) {
155
+ const line = lines[i];
156
+ if (!sameHunk(line.item, item)) continue;
157
+ if (line.type === 'ctx') continue;
158
+ if (!found) {
159
+ start = i;
160
+ found = true;
161
+ }
162
+ end = i;
163
+ }
164
+ return { start, end };
165
+ };
166
+
167
+ const fileOffset = (lines, at) => {
168
+ let offset = 0;
169
+ const target = Math.max(0, at);
170
+ for (let i = 0; i < lines.length; i++) {
171
+ const line = lines[i];
172
+ if (line.type === 'del') continue;
173
+ if (i >= target) return offset;
174
+ offset += (line.text ?? '').length + 1;
175
+ }
176
+ return offset;
177
+ };
178
+
179
+ const fileLineIndex = (lines, fileLine) => {
180
+ let n = 0;
181
+ let last = 0;
182
+ for (let i = 0; i < lines.length; i++) {
183
+ if (lines[i].type === 'del') continue;
184
+ last = i;
185
+ if (n === fileLine) return i;
186
+ n += 1;
187
+ }
188
+ return last;
189
+ };
190
+
191
+ const lastEditableIndex = (lines) => {
192
+ for (let i = lines.length - 1; i >= 0; i--) {
193
+ if (lines[i].type !== 'del') return i;
194
+ }
195
+ return -1;
196
+ };
197
+
198
+ const attachEditSpans = (lines) => {
199
+ let offset = 0;
200
+ const lastAt = lastEditableIndex(lines);
201
+ for (let i = 0; i < lines.length; i++) {
202
+ const line = lines[i];
203
+ if (line.type === 'del') continue;
204
+ const text = line.text ?? '';
205
+ const last = i === lastAt;
206
+ line.wrap = true;
207
+ line.editStart = offset;
208
+ line.editEnd = offset + text.length;
209
+ line.editLast = last;
210
+ offset = last ? offset + text.length : offset + text.length + 1;
211
+ }
212
+ return lines;
213
+ };
214
+
215
+ const LCS_CELLS = 4000000;
216
+
217
+ const lcsTable = (prev, next) => {
218
+ const n = prev.length;
219
+ const m = next.length;
220
+ const dp = new Array(n + 1);
221
+ for (let i = 0; i <= n; i++) dp[i] = new Array(m + 1).fill(0);
222
+ for (let i = n - 1; i >= 0; i--) {
223
+ for (let j = m - 1; j >= 0; j--) {
224
+ if (prev[i] === next[j]) dp[i][j] = dp[i + 1][j + 1] + 1;
225
+ else dp[i][j] = Math.max(dp[i + 1][j], dp[i][j + 1]);
226
+ }
227
+ }
228
+ return dp;
229
+ };
230
+
231
+ const zipOps = (prev, next) => {
232
+ const n = Math.min(prev.length, next.length);
233
+ const ops = new Array(Math.max(prev.length, next.length));
234
+ let at = 0;
235
+ for (let i = 0; i < n; i++) ops[at++] = { kind: 'rep', text: next[i] };
236
+ for (let i = n; i < prev.length; i++) ops[at++] = { kind: 'del' };
237
+ for (let i = n; i < next.length; i++) {
238
+ ops[at++] = { kind: 'ins', text: next[i] };
239
+ }
240
+ return ops;
241
+ };
242
+
243
+ const walkLcs = (prev, next, dp) => {
244
+ const ops = new Array(prev.length + next.length - dp[0][0]);
245
+ let n = 0;
246
+ let i = 0;
247
+ let j = 0;
248
+ while (i < prev.length && j < next.length) {
249
+ if (prev[i] === next[j]) {
250
+ ops[n++] = { kind: 'eq', text: next[j] };
251
+ i += 1;
252
+ j += 1;
253
+ continue;
254
+ }
255
+ if (dp[i + 1][j] >= dp[i][j + 1]) {
256
+ ops[n++] = { kind: 'del' };
257
+ i += 1;
258
+ continue;
259
+ }
260
+ ops[n++] = { kind: 'ins', text: next[j] };
261
+ j += 1;
262
+ }
263
+ while (i < prev.length) {
264
+ ops[n++] = { kind: 'del' };
265
+ i += 1;
266
+ }
267
+ while (j < next.length) {
268
+ ops[n++] = { kind: 'ins', text: next[j] };
269
+ j += 1;
270
+ }
271
+ return ops;
272
+ };
273
+
274
+ const collapseRep = (ops) => {
275
+ const out = [];
276
+ for (let i = 0; i < ops.length; i++) {
277
+ const op = ops[i];
278
+ const next = ops[i + 1];
279
+ if (op.kind === 'del' && next && next.kind === 'ins') {
280
+ out.push({ kind: 'rep', text: next.text });
281
+ i += 1;
282
+ continue;
283
+ }
284
+ if (op.kind === 'ins' && next && next.kind === 'del') {
285
+ out.push({ kind: 'rep', text: op.text });
286
+ i += 1;
287
+ continue;
288
+ }
289
+ out.push(op);
290
+ }
291
+ return out;
292
+ };
293
+
294
+ const lineOps = (prev, next) => {
295
+ if (prev.length === next.length) return zipOps(prev, next);
296
+ if (prev.length * next.length > LCS_CELLS) return zipOps(prev, next);
297
+ return collapseRep(walkLcs(prev, next, lcsTable(prev, next)));
298
+ };
299
+
300
+ const copyRow = (row, text) => ({
301
+ type: row.type,
302
+ text,
303
+ noNl: row.noNl === true,
304
+ blockId: row.blockId ?? null,
305
+ origin: row.origin ?? '',
306
+ item: row.item ?? null,
307
+ });
308
+
309
+ const pushDels = (out, base, at) => {
310
+ let i = at;
311
+ while (i < base.length && base[i].type === 'del') {
312
+ out.push(copyRow(base[i], base[i].text ?? ''));
313
+ i += 1;
314
+ }
315
+ return i;
316
+ };
317
+
318
+ const mergeEditRows = (baseRows, nextTexts) => {
319
+ const base = baseRows ?? [];
320
+ const texts = nextTexts && nextTexts.length ? nextTexts : [''];
321
+ const prev = [];
322
+ for (const row of base) {
323
+ if (row.type === 'del') continue;
324
+ prev.push(row.text ?? '');
325
+ }
326
+ const ops = lineOps(prev, texts);
327
+ const out = [];
328
+ let at = 0;
329
+ for (const op of ops) {
330
+ at = pushDels(out, base, at);
331
+ if (op.kind === 'ins') {
332
+ out.push(tagged('ctx', op.text, {}));
333
+ continue;
334
+ }
335
+ if (at >= base.length) continue;
336
+ if (op.kind === 'del') {
337
+ at += 1;
338
+ continue;
339
+ }
340
+ out.push(copyRow(base[at], op.text));
341
+ at += 1;
342
+ }
343
+ pushDels(out, base, at);
344
+ if (!out.length) out.push(tagged('ctx', '', {}));
345
+ return attachEditSpans(attachInline(out));
346
+ };
347
+
348
+ const fileLines = (text) => {
349
+ const rows = splitFile(text);
350
+ if (!rows.length) rows.push('');
351
+ const { length } = rows;
352
+ const out = new Array(length);
353
+ let offset = 0;
354
+ for (let i = 0; i < length; i++) {
355
+ const row = rows[i];
356
+ const start = offset;
357
+ const end = start + row.length;
358
+ const last = i === length - 1;
359
+ out[i] = {
360
+ type: 'ctx',
361
+ text: row,
362
+ noNl: false,
363
+ wrap: true,
364
+ editStart: start,
365
+ editEnd: end,
366
+ editLast: last,
367
+ blockId: null,
368
+ origin: '',
369
+ item: null,
370
+ };
371
+ offset = last ? end : end + 1;
372
+ }
373
+ return out;
374
+ };
375
+
376
+ module.exports = {
377
+ splitFile,
378
+ splitEditor,
379
+ itemsForPath,
380
+ unitLines,
381
+ sameBlock,
382
+ sameHunk,
383
+ hunkKey,
384
+ blockLineRange,
385
+ hunkLineRange,
386
+ blockLineIndex,
387
+ fileOffset,
388
+ fileLineIndex,
389
+ attachEditSpans,
390
+ mergeEditRows,
391
+ fileLines,
392
+ };
package/lib/files.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { listFilePath } = require('./deps/manifest.js');
3
+ const { listFilePath } = require('./manifest.js');
4
4
 
5
5
  const REVIEW_DIR = '.review';
6
6
  const ORIGIN_ORDER = ['staged', 'unstaged', 'untracked', 'commit', 'pr'];
@@ -8,13 +8,10 @@ const TODO_FILE = 'TODOs';
8
8
  const REPO_TODOS_LABEL = 'Repository TODOs and Issues';
9
9
  const TOTAL_LABEL = 'total';
10
10
 
11
- const normalizePathSpec = (spec) =>
12
- `${spec ?? ''}`.replaceAll('\\', '/').replace(/\/+$/, '');
13
-
14
11
  const isPathInScope = (rel, paths) => {
15
12
  if (!paths.length) return true;
16
13
  for (const spec of paths) {
17
- const norm = normalizePathSpec(spec);
14
+ const norm = `${spec ?? ''}`.replaceAll('\\', '/').replace(/\/+$/, '');
18
15
  if (!norm || norm === '.') return true;
19
16
  if (rel === norm || rel.startsWith(`${norm}/`)) return true;
20
17
  }
@@ -30,12 +27,12 @@ const listPath = (item) => listFilePath(itemPath(item));
30
27
 
31
28
  const isTodoItem = (item) => !!(item && item.origin === 'todo');
32
29
 
30
+ const isReadOnlyOrigin = (origin) => origin === 'commit' || origin === 'pr';
31
+
33
32
  const isTodosEntry = (entry) => !!(entry && entry.kind === 'todos');
34
33
 
35
34
  const isTotalEntry = (entry) => !!(entry && entry.kind === 'total');
36
35
 
37
- const repoTodosLabel = () => REPO_TODOS_LABEL;
38
-
39
36
  const makeTodoItem = () => ({
40
37
  origin: 'todo',
41
38
  file: {
@@ -226,9 +223,9 @@ module.exports = {
226
223
  itemPath,
227
224
  listPath,
228
225
  isTodoItem,
226
+ isReadOnlyOrigin,
229
227
  isTodosEntry,
230
228
  isTotalEntry,
231
- repoTodosLabel,
232
229
  makeTodoItem,
233
230
  fileStatus,
234
231
  fileEntries,
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { oneLine, requireOk, runGit, runGitAsync } = require('./run.js');
3
+ const { oneLine, requireOk, runGit, runGitAsync } = require('./git.js');
4
4
 
5
5
  const BRANCH_FORMAT = [
6
6
  '%(HEAD)',
@@ -183,10 +183,11 @@ const listBranches = (top) => {
183
183
  const names = new Set();
184
184
  for (const entry of branches) names.add(entry.name);
185
185
  const primary = defaultBranch(top, names);
186
- const listed = [];
187
- for (const entry of branches) {
188
- const isDefault = entry.name === primary;
189
- listed.push({ ...entry, isDefault });
186
+ const { length } = branches;
187
+ const listed = new Array(length);
188
+ for (let i = 0; i < length; i++) {
189
+ const entry = branches[i];
190
+ listed[i] = { ...entry, isDefault: entry.name === primary };
190
191
  }
191
192
  return listed;
192
193
  };