reslop 0.1.9 → 0.2.1

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 (96) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/README.md +3 -1
  3. package/bin/reslop.js +2 -2
  4. package/lib/ansi.js +148 -5
  5. package/lib/api.js +4 -2
  6. package/lib/capabilities.js +13 -14
  7. package/lib/cli.js +38 -49
  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} +191 -11
  21. package/lib/{git/dependencies.js → git-deps.js} +111 -38
  22. package/lib/git-reword-todo.js +20 -0
  23. package/lib/git.js +606 -68
  24. package/lib/github.js +28 -197
  25. package/lib/gitlab.js +24 -195
  26. package/lib/highlight/bash.js +1 -3
  27. package/lib/highlight/css.js +1 -3
  28. package/lib/highlight/js.js +11 -14
  29. package/lib/highlight.js +6 -9
  30. package/lib/keys.js +14 -8
  31. package/lib/manifest.js +330 -0
  32. package/lib/remote.js +374 -0
  33. package/lib/render/branches.js +18 -100
  34. package/lib/render/chrome.js +71 -14
  35. package/lib/render/commits.js +69 -60
  36. package/lib/render/diff.js +20 -20
  37. package/lib/render/files.js +6 -34
  38. package/lib/render/frame.js +36 -13
  39. package/lib/render/notes.js +26 -8
  40. package/lib/render/primitives.js +99 -11
  41. package/lib/{render.js → render/render.js} +8 -8
  42. package/lib/render/unit.js +41 -0
  43. package/lib/review-merge.js +270 -0
  44. package/lib/review.js +782 -14
  45. package/lib/select.js +5 -10
  46. package/lib/session/accessors.js +9 -1
  47. package/lib/session/actions.js +61 -18
  48. package/lib/session/branches.js +116 -128
  49. package/lib/session/change-actions.js +202 -123
  50. package/lib/session/commit.js +201 -115
  51. package/lib/session/compose.js +181 -15
  52. package/lib/session/dispatch.js +110 -60
  53. package/lib/session/files-pane.js +156 -148
  54. package/lib/session/items.js +22 -30
  55. package/lib/session/lifecycle.js +151 -54
  56. package/lib/session/load.js +19 -33
  57. package/lib/session/navigation.js +12 -1
  58. package/lib/session/ops.js +28 -41
  59. package/lib/session/pointer.js +57 -57
  60. package/lib/session/review.js +23 -37
  61. package/lib/session/terminal.js +6 -12
  62. package/lib/session/todos.js +37 -13
  63. package/lib/session/unit.js +208 -0
  64. package/lib/session/update.js +33 -46
  65. package/lib/session/view.js +78 -9
  66. package/lib/session/watch.js +128 -24
  67. package/lib/session.js +22 -246
  68. package/lib/source.js +3 -3
  69. package/lib/update.js +40 -75
  70. package/lib/{git/proc.js → utilities.js} +75 -11
  71. package/lib/wrap.js +1 -3
  72. package/package.json +2 -2
  73. package/lib/deps/items.js +0 -134
  74. package/lib/deps/manifest.js +0 -152
  75. package/lib/deps/merge.js +0 -120
  76. package/lib/deps/proposals.js +0 -164
  77. package/lib/deps/reports.js +0 -180
  78. package/lib/deps/rows.js +0 -176
  79. package/lib/deps/scan.js +0 -138
  80. package/lib/deps/usage.js +0 -68
  81. package/lib/deps/versions.js +0 -119
  82. package/lib/diff.js +0 -33
  83. package/lib/git/changes.js +0 -164
  84. package/lib/git/read.js +0 -253
  85. package/lib/git/run.js +0 -100
  86. package/lib/npm/commands.js +0 -100
  87. package/lib/remote/changes.js +0 -37
  88. package/lib/remote/headers.js +0 -36
  89. package/lib/remote/http.js +0 -122
  90. package/lib/remote/notes.js +0 -94
  91. package/lib/review/format.js +0 -434
  92. package/lib/review/storage.js +0 -150
  93. package/lib/review/store.js +0 -178
  94. package/lib/review/templates.js +0 -49
  95. package/lib/session/origins.js +0 -7
  96. 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,