reslop 0.2.6 → 0.3.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 (158) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/README.md +16 -14
  3. package/bin/reslop.js +8 -5
  4. package/lib/agents/agents.js +504 -0
  5. package/lib/agents/allow.js +71 -0
  6. package/lib/agents/allow.json +16 -0
  7. package/lib/agents/models.js +460 -0
  8. package/lib/api.js +15 -12
  9. package/lib/cli.js +61 -66
  10. package/lib/{files.js → common/files.js} +51 -52
  11. package/lib/common/find.js +61 -0
  12. package/lib/common/format.js +101 -0
  13. package/lib/{utilities.js → common/process.js} +7 -95
  14. package/lib/common/tasks.json +27 -0
  15. package/lib/common/utilities.js +112 -0
  16. package/lib/dashboard/git.js +9 -16
  17. package/lib/dashboard/model.js +117 -51
  18. package/lib/dashboard/npm.js +133 -32
  19. package/lib/dashboard/tree.js +27 -30
  20. package/lib/{session → dashboard}/watch.js +41 -31
  21. package/lib/dashboard/workspace.js +151 -0
  22. package/lib/{deps.js → deps/deps.js} +31 -45
  23. package/lib/{deps-diff.js → deps/diff.js} +35 -25
  24. package/lib/{manifest.js → deps/manifest.js} +47 -30
  25. package/lib/diff/align.js +1 -2
  26. package/lib/diff/diff.js +2 -2
  27. package/lib/diff/display.js +29 -5
  28. package/lib/diff/inline.js +10 -14
  29. package/lib/diff/items.js +6 -4
  30. package/lib/diff/patch.js +39 -3
  31. package/lib/diff/unit.js +45 -16
  32. package/lib/{git-branches.js → git/branches.js} +53 -48
  33. package/lib/{git-deps.js → git/deps.js} +123 -95
  34. package/lib/git/git.js +71 -0
  35. package/lib/git/worktree.js +475 -0
  36. package/lib/highlight/bash.js +1 -2
  37. package/lib/highlight/csv.js +1 -2
  38. package/lib/highlight/dot.js +1 -2
  39. package/lib/{highlight.js → highlight/highlight.js} +42 -11
  40. package/lib/highlight/html.js +20 -18
  41. package/lib/highlight/json.js +1 -2
  42. package/lib/highlight/log.js +1 -2
  43. package/lib/highlight/txt.js +1 -2
  44. package/lib/{session → input}/actions.js +240 -71
  45. package/lib/{editor.js → input/editor.js} +7 -8
  46. package/lib/{keys.js → input/keys.js} +36 -39
  47. package/lib/input/line-edit.js +77 -0
  48. package/lib/input/menu-pick.js +70 -0
  49. package/lib/render/agents.js +640 -0
  50. package/lib/render/branches.js +4 -5
  51. package/lib/render/commits.js +54 -64
  52. package/lib/render/{dash-activity.js → dashboard/activity.js} +25 -40
  53. package/lib/render/dashboard/blocks.js +490 -0
  54. package/lib/render/{dashboard.js → dashboard/dashboard.js} +25 -22
  55. package/lib/render/diff.js +166 -45
  56. package/lib/render/field.js +131 -0
  57. package/lib/render/files.js +22 -8
  58. package/lib/render/footer.js +383 -0
  59. package/lib/render/frame.js +127 -25
  60. package/lib/render/header.js +176 -0
  61. package/lib/render/log.js +319 -0
  62. package/lib/render/menu.js +60 -0
  63. package/lib/render/notes.js +29 -13
  64. package/lib/render/npm.js +322 -196
  65. package/lib/render/packages.js +89 -0
  66. package/lib/render/primitives.js +109 -16
  67. package/lib/render/render.js +7 -11
  68. package/lib/render/repos.js +214 -0
  69. package/lib/render/status.js +370 -0
  70. package/lib/render/status.json +85 -0
  71. package/lib/render/{dash-table.js → table.js} +19 -61
  72. package/lib/render/tiles.js +203 -47
  73. package/lib/render/unit.js +32 -13
  74. package/lib/{report-model.js → report/model.js} +4 -6
  75. package/lib/{report-parse.js → report/parse.js} +9 -14
  76. package/lib/{report-render.js → report/render.js} +46 -14
  77. package/lib/{report-run.js → report/run.js} +42 -30
  78. package/lib/{report-test.js → report/test.js} +56 -55
  79. package/lib/{report-tools.js → report/tools.js} +24 -33
  80. package/lib/review/agent-instructions.md +9 -0
  81. package/lib/review/format.js +426 -0
  82. package/lib/{review-merge.js → review/merge.js} +9 -8
  83. package/lib/review/review.js +190 -0
  84. package/lib/review/store.js +281 -0
  85. package/lib/review/templates.js +83 -0
  86. package/lib/runs/commands.js +14 -0
  87. package/lib/runs/logs.js +299 -0
  88. package/lib/runs/output.js +174 -0
  89. package/lib/runs/process.js +113 -0
  90. package/lib/{runs.js → runs/runs.js} +84 -36
  91. package/lib/runs/scripts.js +150 -0
  92. package/lib/session/agents/agents.js +958 -0
  93. package/lib/session/agents/job.js +320 -0
  94. package/lib/session/agents/jobs.js +355 -0
  95. package/lib/session/agents/options.js +153 -0
  96. package/lib/session/agents/plans.js +83 -0
  97. package/lib/session/branches.js +36 -44
  98. package/lib/session/change-actions.js +34 -100
  99. package/lib/session/commit.js +122 -56
  100. package/lib/session/compose.js +197 -146
  101. package/lib/session/dashboard.js +183 -37
  102. package/lib/session/dispatch.js +120 -22
  103. package/lib/session/files-pane.js +60 -24
  104. package/lib/session/find.js +81 -0
  105. package/lib/session/import.js +82 -0
  106. package/lib/session/items.js +8 -17
  107. package/lib/session/lifecycle.js +96 -66
  108. package/lib/session/load.js +2 -4
  109. package/lib/session/navigation.js +8 -3
  110. package/lib/session/npm/history.js +249 -0
  111. package/lib/session/npm/npm.js +815 -0
  112. package/lib/session/npm/packages.js +150 -0
  113. package/lib/session/ops.js +125 -34
  114. package/lib/session/pointer.js +216 -10
  115. package/lib/session/prefs.js +112 -0
  116. package/lib/session/reads.js +109 -0
  117. package/lib/session/review.js +28 -10
  118. package/lib/session/run-panel.js +40 -0
  119. package/lib/{session.js → session/session.js} +166 -59
  120. package/lib/session/tasks.js +321 -66
  121. package/lib/session/templates.js +1 -1
  122. package/lib/session/terminal.js +94 -12
  123. package/lib/session/unit.js +35 -30
  124. package/lib/session/update.js +194 -6
  125. package/lib/session/view.js +72 -59
  126. package/lib/session/workspace.js +416 -0
  127. package/lib/{capabilities.js → source/capabilities.js} +3 -12
  128. package/lib/{github.js → source/github.js} +102 -32
  129. package/lib/{gitlab.js → source/gitlab.js} +87 -10
  130. package/lib/{remote.js → source/remote.js} +144 -40
  131. package/lib/source/review-threads.graphql +20 -0
  132. package/lib/source/source.js +74 -0
  133. package/lib/term/ansi.js +396 -0
  134. package/lib/{clipboard.js → term/clipboard.js} +3 -6
  135. package/lib/{select.js → term/select.js} +2 -2
  136. package/lib/term/themes/dark.json +122 -0
  137. package/lib/term/themes/light.json +122 -0
  138. package/lib/term/wide.txt +21 -0
  139. package/lib/{wrap.js → term/wrap.js} +4 -4
  140. package/package.json +2 -2
  141. package/lib/ansi.js +0 -505
  142. package/lib/detect.js +0 -34
  143. package/lib/diff/edit.js +0 -33
  144. package/lib/git-worktree.js +0 -437
  145. package/lib/git.js +0 -79
  146. package/lib/npm-commands.js +0 -522
  147. package/lib/render/chrome.js +0 -628
  148. package/lib/render/dash-blocks.js +0 -245
  149. package/lib/render/present.js +0 -19
  150. package/lib/render/report.js +0 -43
  151. package/lib/review.js +0 -744
  152. package/lib/session/accessors.js +0 -26
  153. package/lib/session/edit-click.js +0 -57
  154. package/lib/session/npm.js +0 -452
  155. package/lib/session/progress.js +0 -41
  156. package/lib/source.js +0 -36
  157. package/lib/update.js +0 -192
  158. /package/lib/{git-reword-todo.js → git/reword-todo.js} +0 -0
@@ -1,24 +1,32 @@
1
1
  'use strict';
2
2
 
3
- const ansi = require('../ansi.js');
4
- const { THEME, paint, visibleWidth, truncateVisible } = ansi;
3
+ const { clamp } = require('../common/utilities.js');
4
+
5
+ const {
6
+ THEME,
7
+ paint,
8
+ visibleWidth,
9
+ truncateVisible,
10
+ } = require('../term/ansi.js');
5
11
 
6
12
  const GAP_X = 2;
7
13
  const GAP_Y = 1;
8
14
  const MIN_W = 34;
9
15
  const MIN_H = 6;
10
16
  const MAX_COLS = 4;
17
+ const MAX_NORMAL = 3;
18
+ const MAX_WIDE = 2;
11
19
  const RULE = '─';
12
20
  const ELLIPSIS = '…';
13
21
  const BAR_ON = '█';
14
22
  const BAR_OFF = '░';
15
- const SPARK = '▁▂▃▄▅▆▇█';
16
23
  const SPINNER = '⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏';
17
24
 
18
25
  const seg = (text, tone = 'text', bold = false) => ({
19
26
  text: `${text}`,
20
27
  tone,
21
28
  bold,
29
+ bg: null,
22
30
  });
23
31
 
24
32
  const toneColors = () => ({
@@ -30,8 +38,18 @@ const toneColors = () => ({
30
38
  warn: THEME.warnFg,
31
39
  error: THEME.errorFg,
32
40
  sha: THEME.shaFg,
33
- trend: THEME.dashTrendFg,
41
+ dep: THEME.pkgDepFg,
42
+ dev: THEME.pkgDevFg,
43
+ pkg: THEME.pkgFg,
44
+ chain: THEME.pkgChainFg,
45
+ heat: THEME.heatFg,
46
+ heat1: THEME.heat1,
47
+ heat2: THEME.heat2,
48
+ heat3: THEME.heat3,
49
+ heat4: THEME.heat4,
50
+ blink: THEME.heatBlinkFg,
34
51
  key: THEME.dashKeyFg,
52
+ caption: THEME.dashCaptionFg,
35
53
  });
36
54
 
37
55
  const headColors = () => ({
@@ -78,13 +96,14 @@ const paintSegments = (segs, width, bg, color, fill = ' ', colors = null) => {
78
96
  }
79
97
  let out = '';
80
98
  for (const item of clipped) {
81
- out += paint(item.text, ink[item.tone], bg, color, item.bold);
99
+ const pieceBg = item.bg ?? bg;
100
+ out += paint(item.text, ink[item.tone], pieceBg, color, item.bold);
82
101
  }
83
102
  return out + paint(fill.repeat(pad), ink.muted, bg, color);
84
103
  };
85
104
 
86
105
  const barSegments = (ratio, width, tone = 'add') => {
87
- const clamped = Math.min(1, Math.max(0, Number.isFinite(ratio) ? ratio : 0));
106
+ const clamped = clamp(Number.isFinite(ratio) ? ratio : 0, 0, 1);
88
107
  const on = Math.round(clamped * width);
89
108
  return [
90
109
  seg(BAR_ON.repeat(on), tone),
@@ -92,52 +111,67 @@ const barSegments = (ratio, width, tone = 'add') => {
92
111
  ];
93
112
  };
94
113
 
95
- const sparkline = (values, width) => {
96
- const count = values.length;
97
- if (!count || width < 1) return '';
98
- const max = Math.max(...values);
99
- const top = SPARK.length - 1;
100
- let out = '';
101
- for (let i = 0; i < width; i++) {
102
- const span = width <= 1 ? count - 1 : (i * (count - 1)) / (width - 1);
103
- const at = Math.round(span);
104
- const level = max > 0 ? Math.round((values[at] / max) * top) : 0;
105
- out += SPARK[level];
106
- }
107
- return out;
114
+ const RUN_ICON = {
115
+ passed: ['✔', 'add'],
116
+ failed: ['✖', 'error'],
117
+ aborted: ['⊘', 'muted'],
108
118
  };
109
119
 
110
120
  const spinner = (frame) => SPINNER[Math.abs(frame ?? 0) % SPINNER.length];
111
121
 
122
+ const blinkTone = (frame) => (Math.abs(frame) % 2 ? 'hot' : 'warn');
123
+
112
124
  const spread = (total, parts) => {
113
125
  const base = Math.floor(total / parts);
114
126
  const extra = total % parts;
115
127
  return Array.from({ length: parts }, (_, i) => base + (i < extra ? 1 : 0));
116
128
  };
117
129
 
118
- const placeBand = (shown, from, size, width) => {
130
+ const isWide = (tile) => tile.size === 'wide';
131
+
132
+ const rowCap = (row, tile, maxCols) => {
133
+ const wide = isWide(tile) || row.some(isWide);
134
+ const cap = wide ? MAX_WIDE : MAX_NORMAL;
135
+ return Math.min(maxCols, cap);
136
+ };
137
+
138
+ const canFit = (row, tile, maxCols) => row.length < rowCap(row, tile, maxCols);
139
+
140
+ const packRows = (shown, maxCols) => {
141
+ const rows = [];
142
+ let current = [];
143
+ for (const tile of shown) {
144
+ if (current.length && !canFit(current, tile, maxCols)) {
145
+ rows.push(current);
146
+ current = [];
147
+ }
148
+ current.push(tile);
149
+ }
150
+ if (current.length) rows.push(current);
151
+ return rows;
152
+ };
153
+
154
+ const placeBand = (row, width) => {
155
+ const size = row.length;
119
156
  const room = Math.max(0, width - (size - 1) * GAP_X);
120
157
  const widths = spread(room, size);
121
158
  const tiles = [];
122
159
  let x = 0;
123
160
  for (let i = 0; i < size; i++) {
124
- tiles.push({ id: shown[from + i].id, x, w: widths[i] });
161
+ tiles.push({ id: row[i].id, x, w: widths[i] });
125
162
  x += widths[i] + GAP_X;
126
163
  }
127
164
  return tiles;
128
165
  };
129
166
 
130
- const placeTiles = (shown, cols, width, height) => {
131
- const rows = Math.ceil(shown.length / cols);
132
- const sizes = spread(shown.length, rows);
133
- const heights = spread(height - (rows - 1) * GAP_Y, rows);
167
+ const placePacked = (rows, width, height) => {
168
+ const count = Math.max(1, rows.length);
169
+ const heights = spread(height - (count - 1) * GAP_Y, count);
134
170
  const bands = [];
135
- let from = 0;
136
171
  let y = 0;
137
- for (let row = 0; row < rows; row++) {
138
- const tiles = placeBand(shown, from, sizes[row], width);
172
+ for (let row = 0; row < rows.length; row++) {
173
+ const tiles = placeBand(rows[row], width);
139
174
  bands.push({ y, h: Math.max(1, heights[row]), tiles });
140
- from += sizes[row];
141
175
  y += heights[row] + GAP_Y;
142
176
  }
143
177
  return bands;
@@ -145,16 +179,16 @@ const placeTiles = (shown, cols, width, height) => {
145
179
 
146
180
  const layoutTiles = (width, height, tiles) => {
147
181
  const cap = Math.floor((width + GAP_X) / (MIN_W + GAP_X));
148
- const maxCols = Math.max(1, Math.min(MAX_COLS, cap));
182
+ const maxCols = clamp(cap, 1, MAX_COLS);
149
183
  const ranked = [...tiles].sort((left, right) => left.rank - right.rank);
150
184
  for (let count = tiles.length; count >= 1; count--) {
151
185
  const keep = new Set(ranked.slice(0, count).map((tile) => tile.id));
152
186
  const shown = tiles.filter((tile) => keep.has(tile.id));
153
- const cols = Math.min(maxCols, count);
154
- const rows = Math.ceil(count / cols);
155
- const rowH = Math.floor((height - (rows - 1) * GAP_Y) / rows);
187
+ const rows = packRows(shown, maxCols);
188
+ const n = rows.length;
189
+ const rowH = Math.floor((height - (n - 1) * GAP_Y) / n);
156
190
  if (rowH >= MIN_H || count === 1) {
157
- return placeTiles(shown, cols, width, height);
191
+ return placePacked(rows, width, height);
158
192
  }
159
193
  }
160
194
  return [];
@@ -173,9 +207,15 @@ const paintFramed = (line, width, bg, color, fill = ' ', colors = null) => {
173
207
  return edge + paintSegments(line, inner, bg, color, fill, colors) + edge;
174
208
  };
175
209
 
176
- const paintHead = (tile, content, width, color) => {
210
+ const paintFlushRight = (line, width, bg, color) => {
211
+ const edge = paintSegments([seg(' ')], 1, bg, color);
212
+ const inner = Math.max(0, width - 1);
213
+ return edge + paintSegments(line, inner, bg, color);
214
+ };
215
+
216
+ const paintHead = (tile, content, width, color, headBg) => {
177
217
  const fill = color ? ' ' : RULE;
178
- const bg = THEME.dashHeadBg;
218
+ const bg = headBg;
179
219
  const colors = headColors();
180
220
  if (content.titleLine) {
181
221
  return paintFramed(content.titleLine, width, bg, color, fill, colors);
@@ -184,22 +224,58 @@ const paintHead = (tile, content, width, color) => {
184
224
  return paintSegments(segs, width, bg, color, fill, colors);
185
225
  };
186
226
 
187
- const rowBackground = (index, filled) => {
188
- if (filled && index % 2 === 1) return THEME.dashRowBg;
189
- return THEME.dashTileBg;
227
+ const rowBackground = (index, filled, backs) => {
228
+ if (filled && index % 2 === 1) return backs.row;
229
+ return backs.tile;
230
+ };
231
+
232
+ const centeredSegments = (line, room) => {
233
+ const clipped = clipSegments(line, room);
234
+ const pad = Math.floor((room - segmentsWidth(clipped)) / 2);
235
+ if (pad < 1) return clipped;
236
+ return [seg(' '.repeat(pad)), ...clipped];
237
+ };
238
+
239
+ const centeredBody = (lines, room, inner) => {
240
+ const count = Math.min(lines.length, room);
241
+ const start = Math.floor((room - count) / 2);
242
+ const rows = [];
243
+ for (let i = 0; i < room; i++) {
244
+ const at = i - start;
245
+ const line = at >= 0 && at < count ? lines[at] : null;
246
+ if (!line || !line.length) {
247
+ rows.push([]);
248
+ continue;
249
+ }
250
+ rows.push(centeredSegments(line, inner));
251
+ }
252
+ return rows;
190
253
  };
191
254
 
192
- const paintTile = (tile, content, width, height, color) => {
193
- const { lines, footer } = content;
194
- const rows = [paintHead(tile, content, width, color)];
255
+ const paintTile = (tile, content, width, height, color, backs = null) => {
256
+ const shade = backs ?? {
257
+ head: THEME.dashHeadBg,
258
+ tile: THEME.dashTileBg,
259
+ row: THEME.dashRowBg,
260
+ };
261
+ const lines = content.lines ?? [];
262
+ const { footer } = content;
263
+ const rows = [paintHead(tile, content, width, color, shade.head)];
195
264
  const bodyRoom = footer ? height - 2 : height - 1;
265
+ const inner = Math.max(0, width - 2);
266
+ const centered = content.center ? centeredBody(lines, bodyRoom, inner) : null;
196
267
  for (let i = 0; i < bodyRoom; i++) {
197
- const bg = rowBackground(i, i < lines.length);
198
- rows.push(paintFramed(lines[i] ?? [], width, bg, color));
268
+ let line = [];
269
+ if (centered) line = centered[i];
270
+ else if (lines[i]) line = lines[i];
271
+ const filled = !centered && i < lines.length;
272
+ const bg = rowBackground(i, filled, shade);
273
+ rows.push(paintFramed(line, width, bg, color));
199
274
  }
200
275
  if (footer && height > 1) {
201
- const bg = rowBackground(bodyRoom, true);
202
- rows.push(paintFramed(footer, width, bg, color));
276
+ const bg = rowBackground(bodyRoom, true, shade);
277
+ const paintRow = content.footerFlush ? paintFlushRight : paintFramed;
278
+ rows.push(paintRow(footer, width, bg, color));
203
279
  }
204
280
  return rows;
205
281
  };
@@ -229,6 +305,81 @@ const composeBands = (bands, painted, width, height, color) => {
229
305
  return rows.slice(0, height);
230
306
  };
231
307
 
308
+ const gridSize = (width, height, cols, rows) => ({
309
+ tileW: Math.floor((width - (cols - 1) * GAP_X) / cols),
310
+ tileH: Math.floor((height - (rows - 1) * GAP_Y) / rows),
311
+ });
312
+
313
+ const equalGrid = (width, height, count, minW = MIN_W, minH = MIN_H) => {
314
+ const safe = Math.max(1, count);
315
+ const colCap = Math.max(
316
+ 1,
317
+ Math.min(safe, Math.floor((width + GAP_X) / (minW + GAP_X)) || 1),
318
+ );
319
+ const rowCap = Math.max(
320
+ 1,
321
+ Math.min(safe, Math.floor((height + GAP_Y) / (minH + GAP_Y)) || 1),
322
+ );
323
+ let best = null;
324
+ for (let cols = 1; cols <= colCap; cols++) {
325
+ for (let rows = 1; rows <= rowCap; rows++) {
326
+ const size = gridSize(width, height, cols, rows);
327
+ if (size.tileW < minW || size.tileH < minH) continue;
328
+ const cap = cols * rows;
329
+ const shown = Math.min(safe, cap);
330
+ const score = shown * 1000000 + size.tileW * size.tileH;
331
+ if (!best || score > best.score) {
332
+ best = { width, cols, rows, cap, ...size, score };
333
+ }
334
+ }
335
+ }
336
+ if (best) return best;
337
+ return {
338
+ width,
339
+ cols: 1,
340
+ rows: 1,
341
+ cap: 1,
342
+ tileW: Math.max(1, width),
343
+ tileH: Math.max(1, height),
344
+ score: 0,
345
+ };
346
+ };
347
+
348
+ const pageWindow = (count, cursor, cap) => {
349
+ const room = Math.max(1, cap);
350
+ if (count <= room) return { start: 0, end: count };
351
+ const at = clamp(cursor, 0, count - 1);
352
+ let start = Math.floor(at / room) * room;
353
+ if (start + room > count) start = count - room;
354
+ return { start, end: start + room };
355
+ };
356
+
357
+ const placeEqualTiles = (tiles, grid) => {
358
+ const bands = [];
359
+ const cols = Math.max(1, grid.cols);
360
+ const rows = Math.ceil(tiles.length / cols);
361
+ const gaps = Math.max(0, cols - 1) * GAP_X;
362
+ const full = grid.width ?? cols * grid.tileW + gaps;
363
+ const room = Math.max(0, full - gaps);
364
+ const widths = spread(room, cols);
365
+ for (let row = 0; row < rows; row++) {
366
+ const slice = tiles.slice(row * cols, (row + 1) * cols);
367
+ if (!slice.length) break;
368
+ const placed = [];
369
+ let x = 0;
370
+ for (let i = 0; i < slice.length; i++) {
371
+ const w = widths[i];
372
+ placed.push({ id: slice[i].id, x, w });
373
+ x += w + GAP_X;
374
+ }
375
+ const y = row * (grid.tileH + GAP_Y);
376
+ const h = grid.tileH;
377
+ const band = { y, h, tiles: placed };
378
+ bands.push(band);
379
+ }
380
+ return bands;
381
+ };
382
+
232
383
  const tileHits = (bands, headerLines) => {
233
384
  const hits = [];
234
385
  for (const band of bands) {
@@ -249,10 +400,15 @@ module.exports = {
249
400
  segmentsWidth,
250
401
  clipSegments,
251
402
  barSegments,
252
- sparkline,
403
+ RUN_ICON,
253
404
  spinner,
405
+ blinkTone,
254
406
  titleSegments,
255
407
  layoutTiles,
408
+ equalGrid,
409
+ pageWindow,
410
+ placeEqualTiles,
411
+ paintSegments,
256
412
  paintTile,
257
413
  composeBands,
258
414
  tileHits,
@@ -1,14 +1,14 @@
1
1
  'use strict';
2
2
 
3
- const detect = require('../detect.js');
4
- const diffLib = require('../diff/diff.js');
5
- const primitives = require('./primitives.js');
6
- const diff = require('./diff.js');
7
-
8
- const { detectLang } = detect;
9
- const { sameHunk } = diffLib;
10
- const { paneResult } = primitives;
11
- const { paintDiffLine, expandSoftRows, codeHits } = diff;
3
+ const { detectLang } = require('../highlight/highlight.js');
4
+ const { sameHunk } = require('../diff/diff.js');
5
+ const { paneResult, lazyRows } = require('./primitives.js');
6
+ const {
7
+ paintDiffLine,
8
+ expandSoftRows,
9
+ codeHits,
10
+ viewDigits,
11
+ } = require('./diff.js');
12
12
 
13
13
  const paintBodyUnit = (view, width, color) => {
14
14
  const { item, codeOverlay: overlay } = view;
@@ -17,14 +17,33 @@ const paintBodyUnit = (view, width, color) => {
17
17
  view.mode === 'compose' && typeof overlay?.cursor === 'number';
18
18
  const scrollCol = (editing ? overlay.scrollCol : 0) ?? 0;
19
19
  const lines = view.unitLines ?? [];
20
- const source = editing ? lines : expandSoftRows(lines, width, false, true);
21
- const body = source.map((line) => {
20
+ const digits = viewDigits(view);
21
+ let source = lines;
22
+ if (!editing) source = expandSoftRows(lines, width, false, true, digits);
23
+ const body = lazyRows(source.length, (index) => {
24
+ const line = source[index];
22
25
  const changed = line.type === 'add' || line.type === 'del';
23
26
  const outside = changed && !(item && sameHunk(line.item, item));
24
27
  const row = editing || outside ? { ...line, mark: ' ' } : line;
25
- return paintDiffLine(row, width, color, row.origin, lang, scrollCol);
28
+ return paintDiffLine(
29
+ row,
30
+ width,
31
+ color,
32
+ row.origin,
33
+ lang,
34
+ scrollCol,
35
+ digits,
36
+ );
26
37
  });
27
- const hits = codeHits(source, false, width, overlay, editing, scrollCol);
38
+ const hits = codeHits(
39
+ source,
40
+ false,
41
+ width,
42
+ overlay,
43
+ editing,
44
+ scrollCol,
45
+ digits,
46
+ );
28
47
  return paneResult({ body, ...hits });
29
48
  };
30
49
 
@@ -1,10 +1,8 @@
1
1
  'use strict';
2
2
 
3
- const ansi = require('./ansi.js');
4
- const { stripAnsi } = ansi;
5
- const commands = require('./npm-commands.js');
6
-
7
- const { traceLine, relativize } = commands;
3
+ const { stripAnsi } = require('../term/ansi.js');
4
+ const { traceLine, relativize } = require('../runs/output.js');
5
+ const { trimText } = require('../common/utilities.js');
8
6
 
9
7
  const summary = () => ({
10
8
  tests: null,
@@ -59,7 +57,7 @@ const userTrace = (text, root) => {
59
57
  };
60
58
 
61
59
  const normalizeMessage = (message) => {
62
- const text = `${message ?? ''}`.trim().replace(/\s+/g, ' ');
60
+ const text = trimText(message).replace(/\s+/g, ' ');
63
61
  return text.replace(/\s+\S+:\d+:\d+$/, '');
64
62
  };
65
63
 
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
- const ansi = require('./ansi.js');
4
- const { stripAnsi } = ansi;
5
- const model = require('./report-model.js');
6
- const testParse = require('./report-test.js');
7
- const toolParse = require('./report-tools.js');
3
+ const { jsonParse } = require('metautil');
4
+ const { stripAnsi } = require('../term/ansi.js');
5
+ const model = require('./model.js');
6
+ const testParse = require('./test.js');
7
+ const toolParse = require('./tools.js');
8
8
 
9
9
  const { problem, report, userTrace, groupReport, envelope } = model;
10
10
  const { parseTap, parseNodeJsonl, parseTestJson, parseSpec } = testParse;
@@ -27,11 +27,7 @@ const toolHint = (program, args) => {
27
27
  const parseJson = (text) => {
28
28
  const trimmed = text.trim();
29
29
  if (!trimmed || (trimmed[0] !== '{' && trimmed[0] !== '[')) return null;
30
- try {
31
- return JSON.parse(trimmed);
32
- } catch {
33
- return null;
34
- }
30
+ return jsonParse(trimmed);
35
31
  };
36
32
 
37
33
  const parseJsonReports = (text, root) => {
@@ -91,10 +87,9 @@ const mergeReports = (left, right) => {
91
87
  const buildDocument = (stdout, stderr, root, exit, hint) => {
92
88
  const out = stripAnsi(stdout);
93
89
  const err = stripAnsi(stderr);
94
- const reports = mergeReports(
95
- parseReports(out, root),
96
- parseReports(err, root),
97
- );
90
+ const fromOut = parseReports(out, root);
91
+ const fromErr = parseReports(err, root);
92
+ const reports = mergeReports(fromOut, fromErr);
98
93
  const text = out.trim() ? out : err;
99
94
  const grouped = withHint(reports, text, exit, hint).map(groupReport);
100
95
  const rest = stderrRest(err, grouped);
@@ -1,16 +1,15 @@
1
1
  'use strict';
2
2
 
3
- const utilities = require('./utilities.js');
4
- const { unquote } = utilities;
3
+ const { unquote } = require('../common/utilities.js');
4
+ const { formatTable } = require('../runs/output.js');
5
5
 
6
6
  const NOTICE_HEAD =
7
7
  'reslop: reduced output; passing results are omitted ' +
8
8
  'and similar problems are grouped';
9
9
 
10
10
  const notice = (rawFile) => {
11
- const target = rawFile
12
- ? `the raw log \`.log/${rawFile}\``
13
- : 'the matching raw log under `.log/`';
11
+ let target = 'the matching raw log under `.log/`';
12
+ if (rawFile) target = `the raw log \`.log/${rawFile}\``;
14
13
  const hint = `Agent: if this report hides a needed detail, read ${target}.`;
15
14
  return `${NOTICE_HEAD}\n${hint}`;
16
15
  };
@@ -38,12 +37,12 @@ const sameValue = (line, value) => {
38
37
  return text === value || unquote(text) === value;
39
38
  };
40
39
 
41
- const startsDiff = (line, item) =>
42
- isComparison(line) ||
43
- isDiffHeader(line) ||
44
- isMarkedDiff(line) ||
45
- sameValue(line, item.expected) ||
46
- sameValue(line, item.actual);
40
+ const startsDiff = (line, item) => {
41
+ if (isComparison(line) || isDiffHeader(line)) return true;
42
+ if (isMarkedDiff(line)) return true;
43
+ if (sameValue(line, item.expected)) return true;
44
+ return sameValue(line, item.actual);
45
+ };
47
46
 
48
47
  const displayMessage = (item) => {
49
48
  const values = item.expected !== '' || item.actual !== '';
@@ -135,13 +134,46 @@ const loggedText = (text, rawFile) => {
135
134
  return `${head}\n\n${body}`;
136
135
  };
137
136
 
137
+ const reportProblem = (item) => {
138
+ const title = item.name || item.message || item.code || 'problem';
139
+ const mark = item.severity === 'warning' ? '△' : '✖';
140
+ const count = item.count > 1 ? ` × ${item.count}` : '';
141
+ const lines = ['', `${mark} ${title.split('\n')[0]}${count}`, ''];
142
+ lines.push(...formatTable(problemRows(item)));
143
+ const message = displayMessage(item);
144
+ if (message) lines.push('', ...quoteLines(message));
145
+ if (item.trace) {
146
+ lines.push('', 'Stack');
147
+ for (const row of item.trace.split(/\r?\n/)) {
148
+ const frame = row.trim().replace(/^at\s+/, '');
149
+ if (frame) lines.push(` at ${frame}`);
150
+ }
151
+ }
152
+ if (item.related.length) {
153
+ lines.push('', 'Related failures');
154
+ for (const row of item.related) lines.push(` ↳ ${row}`);
155
+ }
156
+ return lines;
157
+ };
158
+
159
+ const renderReport = (doc) => {
160
+ const lines = [];
161
+ for (const item of doc.reports) {
162
+ if (lines.length) lines.push('');
163
+ const mark = item.ok ? '✔' : '✖';
164
+ lines.push(`${mark} ${item.tool} · exit ${doc.exit}`, '');
165
+ lines.push(...formatTable(summaryRows(item.summary)));
166
+ for (const entry of item.problems) lines.push(...reportProblem(entry));
167
+ }
168
+ if (doc.stderr) lines.push('', 'Stderr', doc.stderr);
169
+ return `${lines.join('\n')}\n`;
170
+ };
171
+
138
172
  module.exports = {
139
173
  NOTICE_HEAD,
140
174
  notice,
141
- summaryRows,
142
- problemRows,
143
175
  displayMessage,
144
- quoteLines,
145
176
  loggedText,
146
177
  renderDocument,
178
+ renderReport,
147
179
  };