reslop 0.2.4 → 0.2.6

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 (109) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/LICENSE +1 -1
  3. package/README.md +12 -13
  4. package/bin/reslop.js +3 -8
  5. package/lib/ansi.js +32 -19
  6. package/lib/capabilities.js +8 -12
  7. package/lib/cli.js +40 -74
  8. package/lib/clipboard.js +48 -18
  9. package/lib/dashboard/git.js +128 -0
  10. package/lib/dashboard/model.js +332 -0
  11. package/lib/dashboard/npm.js +193 -0
  12. package/lib/dashboard/tree.js +278 -0
  13. package/lib/deps-diff.js +196 -404
  14. package/lib/deps.js +240 -399
  15. package/lib/detect.js +8 -18
  16. package/lib/diff/align.js +41 -113
  17. package/lib/diff/diff.js +12 -43
  18. package/lib/diff/display.js +25 -36
  19. package/lib/diff/edit.js +6 -20
  20. package/lib/diff/inline.js +138 -141
  21. package/lib/diff/items.js +33 -47
  22. package/lib/diff/parse.js +28 -56
  23. package/lib/diff/patch.js +12 -33
  24. package/lib/diff/unit.js +90 -206
  25. package/lib/editor.js +98 -55
  26. package/lib/files.js +86 -103
  27. package/lib/git-branches.js +254 -416
  28. package/lib/git-deps.js +182 -344
  29. package/lib/git-reword-todo.js +5 -13
  30. package/lib/git-worktree.js +141 -212
  31. package/lib/git.js +21 -49
  32. package/lib/github.js +67 -181
  33. package/lib/gitlab.js +40 -90
  34. package/lib/highlight/bash.js +55 -177
  35. package/lib/highlight/core.js +157 -132
  36. package/lib/highlight/css.js +25 -58
  37. package/lib/highlight/csv.js +19 -37
  38. package/lib/highlight/dart.js +72 -213
  39. package/lib/highlight/dot.js +16 -26
  40. package/lib/highlight/html.js +42 -63
  41. package/lib/highlight/js.js +177 -506
  42. package/lib/highlight/json.js +17 -37
  43. package/lib/highlight/log.js +22 -35
  44. package/lib/highlight/txt.js +9 -22
  45. package/lib/highlight.js +17 -46
  46. package/lib/keys.js +62 -85
  47. package/lib/manifest.js +67 -130
  48. package/lib/npm-commands.js +119 -189
  49. package/lib/remote.js +141 -197
  50. package/lib/render/branches.js +66 -73
  51. package/lib/render/chrome.js +264 -323
  52. package/lib/render/commits.js +187 -500
  53. package/lib/render/dash-activity.js +212 -0
  54. package/lib/render/dash-blocks.js +245 -0
  55. package/lib/render/dash-table.js +290 -0
  56. package/lib/render/dashboard.js +62 -0
  57. package/lib/render/diff.js +123 -228
  58. package/lib/render/files.js +45 -99
  59. package/lib/render/frame.js +129 -183
  60. package/lib/render/notes.js +144 -160
  61. package/lib/render/npm.js +234 -112
  62. package/lib/render/present.js +4 -11
  63. package/lib/render/primitives.js +131 -170
  64. package/lib/render/render.js +5 -14
  65. package/lib/render/report.js +43 -0
  66. package/lib/render/tiles.js +260 -0
  67. package/lib/render/unit.js +15 -28
  68. package/lib/report-model.js +110 -33
  69. package/lib/report-parse.js +56 -38
  70. package/lib/report-render.js +102 -59
  71. package/lib/report-run.js +87 -78
  72. package/lib/report-test.js +256 -141
  73. package/lib/report-tools.js +96 -161
  74. package/lib/review-merge.js +114 -152
  75. package/lib/review.js +115 -189
  76. package/lib/runs.js +225 -0
  77. package/lib/select.js +89 -75
  78. package/lib/session/accessors.js +14 -21
  79. package/lib/session/actions.js +82 -63
  80. package/lib/session/branches.js +193 -247
  81. package/lib/session/change-actions.js +171 -214
  82. package/lib/session/commit.js +280 -342
  83. package/lib/session/compose.js +559 -674
  84. package/lib/session/dashboard.js +385 -0
  85. package/lib/session/dispatch.js +164 -157
  86. package/lib/session/edit-click.js +57 -0
  87. package/lib/session/files-pane.js +85 -53
  88. package/lib/session/items.js +73 -104
  89. package/lib/session/lifecycle.js +403 -541
  90. package/lib/session/load.js +48 -68
  91. package/lib/session/navigation.js +96 -87
  92. package/lib/session/npm.js +409 -345
  93. package/lib/session/ops.js +196 -63
  94. package/lib/session/pointer.js +23 -15
  95. package/lib/session/progress.js +33 -21
  96. package/lib/session/review.js +33 -54
  97. package/lib/session/tasks.js +333 -0
  98. package/lib/session/templates.js +51 -59
  99. package/lib/session/terminal.js +85 -136
  100. package/lib/session/unit.js +3 -4
  101. package/lib/session/update.js +76 -97
  102. package/lib/session/view.js +52 -83
  103. package/lib/session/watch.js +208 -204
  104. package/lib/session.js +112 -237
  105. package/lib/update.js +45 -116
  106. package/lib/utilities.js +67 -89
  107. package/lib/wrap.js +33 -44
  108. package/package.json +1 -1
  109. package/lib/session/todos.js +0 -293
package/lib/detect.js CHANGED
@@ -16,29 +16,19 @@ const PROSE_BASENAMES = [
16
16
  'copyright',
17
17
  ];
18
18
 
19
- const isProseBasename = (base) => {
20
- const name = (base || '').toLowerCase();
21
- if (PROSE_BASENAMES.includes(name)) return true;
22
- const stem = name.replace(/\.(txt|text|md|markdown)$/i, '');
19
+ const isProse = (base) => {
20
+ const stem = base.replace(/\.(txt|text|md|markdown)$/, '');
23
21
  return PROSE_BASENAMES.includes(stem);
24
22
  };
25
23
 
26
- const LANG_RULES = [
27
- { test: (base) => base.endsWith('.d.ts'), lang: 'dts' },
28
- { test: isProseBasename, lang: 'md' },
29
- { test: (base) => base.endsWith('.log'), lang: 'log' },
30
- { test: (base) => base.startsWith('.'), lang: 'dot' },
31
- { test: (base) => !base.includes('.'), lang: 'dot' },
32
- ];
33
-
34
24
  const detectLang = (filePath) => {
35
25
  const base = path.basename(filePath || '').toLowerCase();
36
- for (const rule of LANG_RULES) {
37
- if (rule.test(base)) return rule.lang;
38
- }
26
+ if (base.endsWith('.d.ts')) return 'dts';
27
+ if (isProse(base)) return 'md';
28
+ if (base.endsWith('.log')) return 'log';
29
+ if (base.startsWith('.') || !base.includes('.')) return 'dot';
39
30
  const ext = fileExt(base);
40
- if (ext) return mapLang(ext);
41
- return 'dot';
31
+ return ext ? mapLang(ext) : 'dot';
42
32
  };
43
33
 
44
- module.exports = { mapLang, detectLang };
34
+ module.exports = { detectLang };
package/lib/diff/align.js CHANGED
@@ -18,9 +18,7 @@ const lineTokens = (text) => {
18
18
 
19
19
  const pairScore = (a, b) => {
20
20
  const token = tokenSimilarity(a, b);
21
- const headA = a[0];
22
- const headB = b[0];
23
- const head = headA && headA === headB ? 1 : 0;
21
+ const head = a[0] && a[0] === b[0] ? 1 : 0;
24
22
  return PAIR_HEAD_WEIGHT * head + PAIR_TOKEN_WEIGHT * token;
25
23
  };
26
24
 
@@ -44,15 +42,13 @@ const pairCandidates = (delTexts, addTexts) => {
44
42
  candidates.push({ i, j, score, dist });
45
43
  }
46
44
  }
47
- candidates.sort(comparePair);
48
- return candidates;
45
+ return candidates.sort(comparePair);
49
46
  };
50
47
 
51
48
  const pairIndices = (delTexts, addTexts) => {
52
49
  const delToAdd = new Array(delTexts.length).fill(-1);
53
50
  const addUsed = new Array(addTexts.length).fill(false);
54
- const candidates = pairCandidates(delTexts, addTexts);
55
- for (const pair of candidates) {
51
+ for (const pair of pairCandidates(delTexts, addTexts)) {
56
52
  if (delToAdd[pair.i] >= 0 || addUsed[pair.j]) continue;
57
53
  delToAdd[pair.i] = pair.j;
58
54
  addUsed[pair.j] = true;
@@ -66,155 +62,87 @@ const pairIndices = (delTexts, addTexts) => {
66
62
  return delToAdd;
67
63
  };
68
64
 
69
- const changeSides = (block) => {
70
- const dels = [];
71
- const adds = [];
72
- for (const line of block) {
73
- if (line.type === 'del') dels.push(line);
74
- if (line.type === 'add') adds.push(line);
75
- }
76
- return { dels, adds };
65
+ const pairBlock = (block) => {
66
+ const dels = block.filter((line) => line.type === 'del');
67
+ const adds = block.filter((line) => line.type === 'add');
68
+ const delToAdd = pairIndices(
69
+ dels.map((line) => line.text),
70
+ adds.map((line) => line.text),
71
+ );
72
+ return { dels, adds, delToAdd };
77
73
  };
78
74
 
79
- const applyInlineSpans = (dels, adds, delToAdd) => {
80
- const addSeen = new Set();
75
+ const wholeSpans = (text, changed) => [{ text, changed }];
76
+
77
+ const attachInline = (lines) => {
78
+ const painted = lines.map((line) => ({ ...line }));
79
+ const { dels, adds, delToAdd } = pairBlock(painted);
80
+ for (const add of adds) add.spans = wholeSpans(add.text, true);
81
81
  for (let i = 0; i < dels.length; i++) {
82
82
  const j = delToAdd[i];
83
83
  if (j < 0) {
84
- dels[i].spans = [{ text: dels[i].text, changed: true }];
84
+ dels[i].spans = wholeSpans(dels[i].text, true);
85
85
  continue;
86
86
  }
87
- addSeen.add(j);
88
87
  const diff = diffChars(dels[i].text, adds[j].text);
89
88
  dels[i].spans = diff.oldSpans;
90
89
  adds[j].spans = diff.newSpans;
91
90
  }
92
- for (let j = 0; j < adds.length; j++) {
93
- if (addSeen.has(j)) continue;
94
- adds[j].spans = [{ text: adds[j].text, changed: true }];
95
- }
96
- };
97
-
98
- const attachInline = (lines) => {
99
- const painted = lines.map((line) => ({ ...line }));
100
- const { dels, adds } = changeSides(painted);
101
- const delToAdd = pairIndices(
102
- dels.map((line) => line.text),
103
- adds.map((line) => line.text),
104
- );
105
- applyInlineSpans(dels, adds, delToAdd);
106
91
  for (const line of painted) {
107
- if (isCtxType(line.type)) {
108
- line.spans = [{ text: line.text, changed: false }];
109
- }
92
+ if (isCtxType(line.type)) line.spans = wholeSpans(line.text, false);
110
93
  }
111
94
  return painted;
112
95
  };
113
96
 
114
- const addPartners = (delToAdd, addCount) => {
115
- const addToDel = new Array(addCount).fill(-1);
116
- for (let i = 0; i < delToAdd.length; i++) {
117
- const j = delToAdd[i];
118
- if (j >= 0) addToDel[j] = i;
119
- }
120
- return addToDel;
121
- };
122
-
123
- const walkAligned = (dels, adds, delToAdd, emit) => {
124
- const addToDel = addPartners(delToAdd, adds.length);
125
- const usedAdd = new Array(adds.length).fill(false);
97
+ const pushAligned = (rows, dels, adds, delToAdd) => {
98
+ const paired = new Array(adds.length).fill(false);
99
+ for (const j of delToAdd) if (j >= 0) paired[j] = true;
126
100
  let addPos = 0;
127
- const takeUnpairedAdds = (limit) => {
128
- while (addPos < limit) {
129
- const free = addToDel[addPos] < 0 && !usedAdd[addPos];
130
- if (free) {
131
- emit.unpairedAdd(adds[addPos]);
132
- usedAdd[addPos] = true;
133
- }
134
- addPos += 1;
101
+ const takeAdds = (limit) => {
102
+ for (; addPos < limit; addPos++) {
103
+ if (!paired[addPos]) rows.push({ left: null, right: adds[addPos] });
135
104
  }
136
105
  };
137
106
  for (let i = 0; i < dels.length; i++) {
138
107
  const j = delToAdd[i];
139
108
  if (j < 0) {
140
- emit.unpairedDel(dels[i]);
109
+ rows.push({ left: dels[i], right: null });
141
110
  continue;
142
111
  }
143
- takeUnpairedAdds(j);
144
- emit.pair(dels[i], adds[j]);
145
- usedAdd[j] = true;
112
+ takeAdds(j);
113
+ rows.push({ left: dels[i], right: adds[j] });
146
114
  if (addPos <= j) addPos = j + 1;
147
115
  }
148
- takeUnpairedAdds(adds.length);
149
- for (let j = 0; j < adds.length; j++) {
150
- if (!usedAdd[j]) emit.unpairedAdd(adds[j]);
151
- }
116
+ takeAdds(adds.length);
152
117
  };
153
118
 
154
- const alignChangeBlock = (dels, adds, delToAdd) => {
155
- const out = [];
156
- walkAligned(dels, adds, delToAdd, {
157
- unpairedDel: (del) => {
158
- out.push(del);
159
- },
160
- unpairedAdd: (add) => {
161
- out.push(add);
162
- },
163
- pair: (del, add) => {
164
- out.push(del);
165
- out.push(add);
166
- },
167
- });
168
- return out;
169
- };
170
-
171
- const mapHunkBlocks = (lines, onCtx, onChange) => {
172
- const out = [];
119
+ const sideRows = (lines) => {
120
+ const rows = [];
173
121
  let i = 0;
174
122
  while (i < lines.length) {
175
123
  if (isCtxType(lines[i].type)) {
176
- out.push(onCtx(lines[i]));
124
+ rows.push({ left: lines[i], right: lines[i] });
177
125
  i += 1;
178
126
  continue;
179
127
  }
180
128
  const start = i;
181
129
  while (i < lines.length && !isCtxType(lines[i].type)) i += 1;
182
- const block = lines.slice(start, i);
183
- const { dels, adds } = changeSides(block);
184
- const delToAdd = pairIndices(
185
- dels.map((line) => line.text),
186
- adds.map((line) => line.text),
187
- );
188
- for (const row of onChange(dels, adds, delToAdd)) out.push(row);
130
+ const { dels, adds, delToAdd } = pairBlock(lines.slice(start, i));
131
+ pushAligned(rows, dels, adds, delToAdd);
189
132
  }
190
- return out;
133
+ return rows;
191
134
  };
192
135
 
193
- const alignSplitBlock = (dels, adds, delToAdd) => {
136
+ const mixedLines = (lines) => {
194
137
  const out = [];
195
- walkAligned(dels, adds, delToAdd, {
196
- unpairedDel: (del) => {
197
- out.push({ left: del, right: null });
198
- },
199
- unpairedAdd: (add) => {
200
- out.push({ left: null, right: add });
201
- },
202
- pair: (del, add) => {
203
- out.push({ left: del, right: add });
204
- },
205
- });
138
+ for (const { left, right } of sideRows(lines)) {
139
+ if (left) out.push(left);
140
+ if (right && right !== left) out.push(right);
141
+ }
206
142
  return out;
207
143
  };
208
144
 
209
- const LAYOUT_ALIGN = {
210
- mixed: (lines) => mapHunkBlocks(lines, (line) => line, alignChangeBlock),
211
- side: (lines) =>
212
- mapHunkBlocks(
213
- lines,
214
- (line) => ({ left: line, right: line }),
215
- alignSplitBlock,
216
- ),
217
- };
145
+ const LAYOUT_ALIGN = { mixed: mixedLines, side: sideRows };
218
146
 
219
147
  module.exports = {
220
148
  isCtxType,
package/lib/diff/diff.js CHANGED
@@ -1,56 +1,25 @@
1
1
  'use strict';
2
2
 
3
3
  const inline = require('./inline.js');
4
- const { diffChars } = inline;
5
4
  const align = require('./align.js');
6
- const { isCtxType, pairIndices, attachInline } = align;
7
5
  const parse = require('./parse.js');
8
- const { parseDiff } = parse;
9
6
  const patch = require('./patch.js');
10
- const { splitHunk, flattenBlock, countSides, formatPatch } = patch;
11
7
  const display = require('./display.js');
12
- const { DISPLAY_CONTEXT, displayLines, blockAddText } = display;
13
8
  const edit = require('./edit.js');
14
- const { addBlockRange, replaceBlockAdds } = edit;
15
9
  const items = require('./items.js');
16
- const { synthesizeNewFile, itemsFromFiles, refreshIndexPatches } = items;
17
10
  const unit = require('./unit.js');
18
- const { unitLines, itemsForPath, blockLineRange, blockLineIndex } = unit;
19
- const { hunkLineRange, hunkKey, sameHunk, splitEditor } = unit;
20
- const { fileOffset, fileLines, sameBlock, splitFile } = unit;
21
- const { fileLineIndex, attachEditSpans, mergeEditRows } = unit;
22
11
 
23
12
  module.exports = {
24
- DISPLAY_CONTEXT,
25
- isCtxType,
26
- diffChars,
27
- pairIndices,
28
- attachInline,
29
- parseDiff,
30
- splitHunk,
31
- flattenBlock,
32
- countSides,
33
- formatPatch,
34
- displayLines,
35
- blockAddText,
36
- addBlockRange,
37
- replaceBlockAdds,
38
- synthesizeNewFile,
39
- itemsFromFiles,
40
- refreshIndexPatches,
41
- splitFile,
42
- splitEditor,
43
- itemsForPath,
44
- unitLines,
45
- sameBlock,
46
- sameHunk,
47
- hunkKey,
48
- blockLineRange,
49
- hunkLineRange,
50
- blockLineIndex,
51
- fileOffset,
52
- fileLines,
53
- fileLineIndex,
54
- attachEditSpans,
55
- mergeEditRows,
13
+ diffChars: inline.diffChars,
14
+ isCtxType: align.isCtxType,
15
+ pairIndices: align.pairIndices,
16
+ attachInline: align.attachInline,
17
+ parseDiff: parse.parseDiff,
18
+ splitHunk: patch.splitHunk,
19
+ flattenBlock: patch.flattenBlock,
20
+ formatPatch: patch.formatPatch,
21
+ ...display,
22
+ ...edit,
23
+ ...items,
24
+ ...unit,
56
25
  };
@@ -23,41 +23,35 @@ const windowAroundBlock = (lines, blockId, radius) => {
23
23
 
24
24
  const insertAddIndex = (lines) => {
25
25
  for (let i = lines.length - 1; i >= 0; i--) {
26
- if (isCtxType(lines[i].type)) continue;
27
- return i + 1;
26
+ if (!isCtxType(lines[i].type)) return i + 1;
28
27
  }
29
28
  return lines.length;
30
29
  };
31
30
 
32
- const taggedAdd = (text, blockId, start, end) => ({
33
- type: 'add',
34
- text,
35
- noNl: false,
36
- blockId,
37
- wrap: true,
38
- editStart: start,
39
- editEnd: end,
40
- editLast: true,
41
- });
31
+ const taggedAdds = (text, blockId) => {
32
+ const tagged = [];
33
+ let offset = 0;
34
+ for (const row of text.split('\n')) {
35
+ tagged.push({
36
+ type: 'add',
37
+ text: row,
38
+ noNl: false,
39
+ blockId,
40
+ wrap: true,
41
+ editStart: offset,
42
+ editEnd: offset + row.length,
43
+ editLast: true,
44
+ });
45
+ offset += row.length + 1;
46
+ }
47
+ return tagged;
48
+ };
42
49
 
43
50
  const replaceAdds = (lines, overlay, blockId) => {
44
- const text = overlay.text ?? '';
45
- const keepEmpty = overlay.keepEmpty === true;
46
- if (text === '' && !keepEmpty) {
51
+ if (overlay.text === '' && !overlay.keepEmpty) {
47
52
  return lines.filter((line) => line.type !== 'add');
48
53
  }
49
- const texts = text.split('\n');
50
- const { length } = texts;
51
- const tagged = new Array(length);
52
- let offset = 0;
53
- for (let i = 0; i < length; i++) {
54
- const row = texts[i];
55
- const start = offset;
56
- const end = start + row.length;
57
- const isLast = i === length - 1;
58
- tagged[i] = taggedAdd(row, blockId, start, end);
59
- offset = isLast ? end : end + 1;
60
- }
54
+ const tagged = taggedAdds(overlay.text, blockId);
61
55
  const out = [];
62
56
  let placed = 0;
63
57
  let lastAdd = -1;
@@ -67,9 +61,8 @@ const replaceAdds = (lines, overlay, blockId) => {
67
61
  continue;
68
62
  }
69
63
  if (placed >= tagged.length) continue;
70
- out.push(tagged[placed]);
64
+ out.push(tagged[placed++]);
71
65
  lastAdd = out.length - 1;
72
- placed += 1;
73
66
  }
74
67
  const extras = tagged.slice(placed);
75
68
  if (!extras.length) return out;
@@ -82,9 +75,7 @@ const blockAddText = (hunk, blockId) => {
82
75
  if (!hunk) return '';
83
76
  const parts = [];
84
77
  for (const line of hunk.lines) {
85
- if (line.type !== 'add') continue;
86
- if (line.blockId !== blockId) continue;
87
- parts.push(line.text);
78
+ if (line.type === 'add' && line.blockId === blockId) parts.push(line.text);
88
79
  }
89
80
  return parts.join('\n');
90
81
  };
@@ -93,11 +84,9 @@ const displayLines = (hunk, blockId, layout = 'unified', radius, overlay) => {
93
84
  const flat = flattenBlock(hunk, blockId, 'new');
94
85
  const source = overlay ? replaceAdds(flat, overlay, blockId) : flat;
95
86
  const around = radius ?? DISPLAY_CONTEXT;
96
- const windowed = windowAroundBlock(source, blockId, around);
97
- const painted = attachInline(windowed);
87
+ const painted = attachInline(windowAroundBlock(source, blockId, around));
98
88
  const alignRows = LAYOUT_ALIGN[layout];
99
- if (alignRows) return alignRows(painted);
100
- return painted;
89
+ return alignRows ? alignRows(painted) : painted;
101
90
  };
102
91
 
103
92
  module.exports = { DISPLAY_CONTEXT, displayLines, blockAddText };
package/lib/diff/edit.js CHANGED
@@ -1,19 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const contentLines = (content) => {
4
- if (content === '') return { lines: [], ended: false };
5
- const ended = content.endsWith('\n');
6
- const body = ended ? content.slice(0, -1) : content;
7
- return { lines: body.split('\n'), ended };
8
- };
9
-
10
- const joinLines = (lines, ended) => {
11
- const body = lines.join('\n');
12
- if (!ended) return body;
13
- if (!lines.length) return '\n';
14
- return `${body}\n`;
15
- };
16
-
17
3
  const addBlockRange = (hunk, blockId) => {
18
4
  let newLine = hunk.newStart;
19
5
  let start = 0;
@@ -35,13 +21,13 @@ const addBlockRange = (hunk, blockId) => {
35
21
 
36
22
  const replaceBlockAdds = (content, hunk, blockId, text) => {
37
23
  const range = addBlockRange(hunk, blockId);
38
- const parsed = contentLines(content);
39
- const last = parsed.lines.length;
40
- const from = Math.min(last, Math.max(0, range.start - 1));
24
+ const ended = content.endsWith('\n');
25
+ const body = ended ? content.slice(0, -1) : content;
26
+ const lines = content === '' ? [] : body.split('\n');
27
+ const from = Math.min(lines.length, Math.max(0, range.start - 1));
41
28
  const added = text === '' ? [] : text.split('\n');
42
- const next = parsed.lines.slice();
43
- next.splice(from, range.count, ...added);
44
- return joinLines(next, parsed.ended);
29
+ lines.splice(from, range.count, ...added);
30
+ return lines.join('\n') + (ended ? '\n' : '');
45
31
  };
46
32
 
47
33
  module.exports = { addBlockRange, replaceBlockAdds };