reslop 0.2.3 → 0.2.5

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 (89) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/README.md +8 -4
  3. package/bin/reslop.js +4 -2
  4. package/lib/api.js +2 -1
  5. package/lib/capabilities.js +0 -4
  6. package/lib/cli.js +19 -10
  7. package/lib/clipboard.js +45 -4
  8. package/lib/deps-diff.js +61 -118
  9. package/lib/deps.js +166 -235
  10. package/lib/detect.js +4 -2
  11. package/lib/diff/align.js +2 -1
  12. package/lib/diff/diff.js +6 -3
  13. package/lib/diff/display.js +2 -1
  14. package/lib/diff/inline.js +68 -2
  15. package/lib/diff/patch.js +2 -1
  16. package/lib/diff/unit.js +15 -30
  17. package/lib/editor.js +91 -13
  18. package/lib/files.js +54 -27
  19. package/lib/git-branches.js +107 -154
  20. package/lib/git-deps.js +78 -155
  21. package/lib/git-worktree.js +508 -0
  22. package/lib/git.js +23 -580
  23. package/lib/github.js +9 -8
  24. package/lib/gitlab.js +5 -4
  25. package/lib/highlight/bash.js +2 -1
  26. package/lib/highlight/core.js +11 -0
  27. package/lib/highlight/css.js +2 -1
  28. package/lib/highlight/csv.js +2 -1
  29. package/lib/highlight/dot.js +3 -12
  30. package/lib/highlight/html.js +2 -1
  31. package/lib/highlight/json.js +2 -1
  32. package/lib/highlight/log.js +5 -11
  33. package/lib/highlight/txt.js +3 -12
  34. package/lib/highlight.js +2 -1
  35. package/lib/keys.js +22 -12
  36. package/lib/manifest.js +4 -6
  37. package/lib/npm-commands.js +82 -24
  38. package/lib/remote.js +21 -20
  39. package/lib/render/branches.js +9 -27
  40. package/lib/render/chrome.js +109 -35
  41. package/lib/render/commits.js +216 -216
  42. package/lib/render/diff.js +160 -47
  43. package/lib/render/files.js +9 -26
  44. package/lib/render/frame.js +97 -118
  45. package/lib/render/notes.js +74 -26
  46. package/lib/render/npm.js +104 -30
  47. package/lib/render/present.js +2 -1
  48. package/lib/render/primitives.js +128 -52
  49. package/lib/render/render.js +2 -1
  50. package/lib/render/unit.js +17 -11
  51. package/lib/report-model.js +6 -4
  52. package/lib/report-parse.js +8 -14
  53. package/lib/report-render.js +38 -21
  54. package/lib/report-run.js +12 -11
  55. package/lib/report-test.js +45 -51
  56. package/lib/report-tools.js +44 -25
  57. package/lib/review-merge.js +102 -131
  58. package/lib/review.js +69 -78
  59. package/lib/select.js +52 -0
  60. package/lib/session/actions.js +72 -87
  61. package/lib/session/branches.js +18 -52
  62. package/lib/session/change-actions.js +175 -147
  63. package/lib/session/commit.js +65 -61
  64. package/lib/session/compose.js +152 -83
  65. package/lib/session/dispatch.js +53 -46
  66. package/lib/session/edit-click.js +57 -0
  67. package/lib/session/files-pane.js +30 -51
  68. package/lib/session/items.js +73 -90
  69. package/lib/session/lifecycle.js +37 -98
  70. package/lib/session/load.js +52 -57
  71. package/lib/session/navigation.js +105 -95
  72. package/lib/session/npm.js +205 -71
  73. package/lib/session/ops.js +200 -45
  74. package/lib/session/pointer.js +33 -8
  75. package/lib/session/progress.js +9 -6
  76. package/lib/session/review.js +35 -53
  77. package/lib/session/tasks.js +292 -0
  78. package/lib/session/terminal.js +17 -19
  79. package/lib/session/unit.js +3 -4
  80. package/lib/session/update.js +76 -96
  81. package/lib/session/view.js +45 -16
  82. package/lib/session/watch.js +47 -27
  83. package/lib/session.js +66 -97
  84. package/lib/source.js +6 -3
  85. package/lib/update.js +5 -13
  86. package/lib/utilities.js +25 -3
  87. package/lib/wrap.js +18 -1
  88. package/package.json +1 -1
  89. package/lib/session/todos.js +0 -293
@@ -1,10 +1,14 @@
1
1
  'use strict';
2
2
 
3
3
  const commands = require('../npm-commands.js');
4
- const { logViewRows } = require('../render/npm.js');
4
+ const npm = require('../render/npm.js');
5
+ const { logViewRows } = npm;
5
6
 
6
- const { listCommands, reduceOutput, writeLog, parseScriptLine } = commands;
7
- const { scriptLine, saveScript, removeScript, reorderScript } = commands;
7
+ const { listCommands, reduceOutput, writeLog } = commands;
8
+ const { staleLogFiles, removeStaleLogs, formatSize } = commands;
9
+ const { saveScript, removeScript, reorderScript } = commands;
10
+
11
+ const NAME_RE = /^[\w:.-]+$/;
8
12
  const { startNpm } = commands;
9
13
 
10
14
  const selected = (api) => {
@@ -30,14 +34,27 @@ const canEdit = (ui) => {
30
34
  return true;
31
35
  };
32
36
 
37
+ const refreshLogs = (api) => {
38
+ try {
39
+ const found = staleLogFiles(api.ui.top);
40
+ api.state.logBytes = found.bytes;
41
+ } catch (error) {
42
+ api.state.logBytes = 0;
43
+ api.ui.status = error.message;
44
+ }
45
+ };
46
+
33
47
  const open = (api) => {
34
48
  if (api.ui.mode === 'compose') return;
35
49
  refresh(api);
50
+ refreshLogs(api);
36
51
  api.state.viewing = false;
37
52
  api.state.output = '';
38
- api.ui.nav.pane = 'npm';
53
+ const nav = api.ui.nav;
54
+ nav.pane = 'npm';
55
+ nav.resetListScroll('npm');
39
56
  api.ui.status = '';
40
- api.ui.nav.clearSelection();
57
+ nav.clearSelection();
41
58
  };
42
59
 
43
60
  const scrollLog = (api, delta) => {
@@ -67,13 +84,22 @@ const move = (api, delta) => {
67
84
  const stopRun = (api) => {
68
85
  api.state.running = false;
69
86
  api.state.child = null;
70
- if (api.ui.progress) api.ui.progress.stop('npm');
87
+ api.ui.progress.stop('npm');
88
+ };
89
+
90
+ const shownOutput = (api, raw, status) => {
91
+ if (!api.state.verbose) return reduceOutput(raw, api.ui.top, status);
92
+ const text = `${raw ?? ''}`;
93
+ if (!text) return '';
94
+ if (text.endsWith('\n')) return text;
95
+ return `${text}\n`;
71
96
  };
72
97
 
73
98
  const showOutput = (api, text) => {
74
99
  if (!api.state.viewing || !api.state.running) return;
75
100
  if (api.state.stopping) return;
76
- api.state.output = text;
101
+ api.state.raw = text;
102
+ api.state.output = shownOutput(api, text, '');
77
103
  api.ui.paint();
78
104
  };
79
105
 
@@ -87,10 +113,13 @@ const withNotice = (text, notice) => {
87
113
 
88
114
  const finishRun = (api, entry, result) => {
89
115
  const stopped = api.state.stopping === true;
116
+ const raw = stopped ? withNotice(api.state.raw, 'terminated') : result.text;
90
117
  const reduced = stopped
91
- ? withNotice(api.state.output, 'terminated')
118
+ ? withNotice(reduceOutput(api.state.raw, api.ui.top, ''), 'terminated')
92
119
  : reduceOutput(result.text, api.ui.top, result.status);
93
- api.state.output = reduced;
120
+ api.state.raw = raw;
121
+ api.state.exitStatus = stopped ? '' : result.status;
122
+ api.state.output = api.state.verbose ? shownOutput(api, raw, '') : reduced;
94
123
  api.state.stopping = false;
95
124
  stopRun(api);
96
125
  try {
@@ -106,21 +135,24 @@ const finishRun = (api, entry, result) => {
106
135
 
107
136
  const startRun = (api, entry) => {
108
137
  api.state.output = '';
138
+ api.state.raw = '';
139
+ api.state.exitStatus = '';
109
140
  api.state.viewing = true;
110
141
  api.state.followEnd = true;
111
142
  api.state.logScroll = 0;
112
143
  api.state.running = true;
113
144
  api.state.stopping = false;
114
145
  api.ui.status = '';
115
- if (api.ui.progress) api.ui.progress.start('npm');
146
+ api.ui.progress.start('npm');
116
147
  const runner = api.ui.repo && api.ui.repo.runNpmCommand;
117
148
  const start = typeof runner === 'function' ? runner : startNpm;
118
- api.state.child = start(
149
+ const child = start(
119
150
  api.ui.top,
120
151
  entry,
121
- (text) => showOutput(api, reduceOutput(text, api.ui.top, '')),
152
+ (text) => showOutput(api, text),
122
153
  (result) => finishRun(api, entry, result),
123
154
  );
155
+ if (api.state.running) api.state.child = child;
124
156
  api.ui.paint();
125
157
  };
126
158
 
@@ -128,6 +160,7 @@ const runSelected = (api) => {
128
160
  if (api.state.viewing || api.state.child) return;
129
161
  const entry = selected(api);
130
162
  if (!entry) return;
163
+ api.state.verbose = false;
131
164
  startRun(api, entry);
132
165
  };
133
166
 
@@ -143,50 +176,106 @@ const closeView = (api) => {
143
176
  api.state.stopping = true;
144
177
  if (api.state.child) api.state.child.kill();
145
178
  if (!api.state.running) return;
146
- api.state.output = withNotice(api.state.output, 'terminated');
179
+ api.state.raw = withNotice(api.state.raw, 'terminated');
180
+ api.state.output = api.state.verbose
181
+ ? shownOutput(api, api.state.raw, '')
182
+ : withNotice(api.state.output, 'terminated');
147
183
  stopRun(api);
148
184
  api.ui.status = 'terminated';
149
185
  api.ui.paint();
150
186
  return;
151
187
  }
152
188
  api.state.viewing = false;
189
+ api.state.verbose = false;
153
190
  api.ui.status = '';
154
191
  api.ui.paint();
155
192
  };
156
193
 
157
- const editSelected = (api) => {
158
- if (api.state.viewing) return;
159
- if (!canEdit(api.ui)) {
160
- api.ui.status = 'read only';
161
- return;
162
- }
194
+ const toggleVerbose = (api) => {
195
+ if (!api.state.viewing) return;
196
+ api.state.verbose = !api.state.verbose;
197
+ const status = api.state.running ? '' : api.state.exitStatus;
198
+ api.state.output = shownOutput(api, api.state.raw, status);
199
+ api.ui.status = api.state.verbose ? 'verbose' : 'filtered';
200
+ api.ui.paint();
201
+ };
202
+
203
+ const editable = (api) => {
204
+ if (api.state.viewing) return false;
205
+ if (canEdit(api.ui)) return true;
206
+ api.ui.status = 'read only';
207
+ return false;
208
+ };
209
+
210
+ const selectedScript = (api) => {
211
+ if (!editable(api)) return null;
163
212
  const entry = selected(api);
164
- if (!entry || entry.kind !== 'script') {
165
- api.ui.status = 'not a script';
166
- return;
167
- }
213
+ if (entry && entry.kind === 'script') return entry;
214
+ api.ui.status = 'not a script';
215
+ return null;
216
+ };
217
+
218
+ const focusScript = (api, name) => {
219
+ const at = api.state.commands.findIndex((entry) => entry.name === name);
220
+ if (at >= 0) api.ui.nav.npmCursor = at;
221
+ };
222
+
223
+ const editSelected = (api) => {
224
+ const entry = selectedScript(api);
225
+ if (!entry) return;
168
226
  api.state.editName = entry.name;
169
- api.ui.composer.openCompose('npm', scriptLine(entry));
227
+ api.state.editField = 'name';
228
+ api.state.draftName = entry.name;
229
+ api.state.draftCommand = entry.command ?? '';
230
+ api.ui.composer.openCompose('npm', entry.name);
170
231
  };
171
232
 
172
233
  const newCommand = (api) => {
173
- if (api.state.viewing) return;
174
- if (!canEdit(api.ui)) {
175
- api.ui.status = 'read only';
176
- return;
177
- }
234
+ if (!editable(api)) return;
178
235
  api.state.editName = '';
236
+ api.state.editField = 'name';
237
+ api.state.draftName = '';
238
+ api.state.draftCommand = '';
179
239
  api.ui.composer.openCompose('npm', '');
180
240
  };
181
241
 
242
+ const switchField = (api, editor, field, text) => {
243
+ api.state.editField = field;
244
+ editor.replace(text);
245
+ api.ui.status = '';
246
+ };
247
+
248
+ const finishName = (api, editor) => {
249
+ const name = editor.text.trim();
250
+ if (!name || !NAME_RE.test(name)) {
251
+ api.ui.status = 'name';
252
+ return false;
253
+ }
254
+ api.state.draftName = name;
255
+ switchField(api, editor, 'command', api.state.draftCommand);
256
+ return true;
257
+ };
258
+
259
+ const focusField = (api, field) => {
260
+ const editor = api.ui.editor;
261
+ if (!editor) return false;
262
+ if (field === api.state.editField) return true;
263
+ if (field === 'command') return finishName(api, editor);
264
+ api.state.draftCommand = editor.text;
265
+ switchField(api, editor, 'name', api.state.draftName ?? '');
266
+ return true;
267
+ };
268
+
182
269
  const finishEdit = (api) => {
183
270
  const editor = api.ui.composer.state.editor;
184
- const text = editor ? editor.text : '';
185
- const parsed = parseScriptLine(text);
186
- if (!parsed) {
187
- api.ui.status = 'name: command';
271
+ if (!editor) return;
272
+ if (api.state.editField !== 'command') return void finishName(api, editor);
273
+ const command = editor.text.trim();
274
+ if (!command) {
275
+ api.ui.status = 'command';
188
276
  return;
189
277
  }
278
+ const parsed = { name: api.state.draftName, command };
190
279
  try {
191
280
  api.ui.ignoreWatch();
192
281
  saveScript(api.ui.top, api.state.editName, parsed);
@@ -194,35 +283,51 @@ const finishEdit = (api) => {
194
283
  api.ui.status = error.message;
195
284
  return;
196
285
  }
286
+ api.state.editField = '';
197
287
  api.ui.composer.closeCompose();
198
288
  refresh(api);
199
- const at = api.state.commands.findIndex(
200
- (entry) => entry.name === parsed.name,
201
- );
202
- if (at >= 0) api.ui.nav.npmCursor = at;
289
+ focusScript(api, parsed.name);
203
290
  api.ui.status = 'saved';
204
291
  };
205
292
 
206
293
  const askDrop = (api) => {
207
- if (api.state.viewing) return;
208
- if (!canEdit(api.ui)) {
209
- api.ui.status = 'read only';
210
- return;
211
- }
212
- const entry = selected(api);
213
- if (!entry || entry.kind !== 'script') {
214
- api.ui.status = 'not a script';
215
- return;
216
- }
294
+ const entry = selectedScript(api);
295
+ if (!entry) return;
296
+ api.state.dropKind = 'script';
217
297
  api.state.dropName = entry.name;
218
298
  api.ui.mode = 'confirmDrop';
219
299
  api.ui.status = '';
220
300
  };
221
301
 
302
+ const askLogs = (api) => {
303
+ if (!editable(api)) return;
304
+ refreshLogs(api);
305
+ if (!api.state.logBytes) return;
306
+ api.state.dropKind = 'logs';
307
+ const size = formatSize(api.state.logBytes);
308
+ api.state.dropName = `logs older than 5 days (${size})`;
309
+ api.ui.mode = 'confirmDrop';
310
+ api.ui.status = '';
311
+ };
312
+
222
313
  const confirmDrop = (api) => {
314
+ const kind = api.state.dropKind;
223
315
  const name = api.state.dropName;
224
316
  api.state.dropName = '';
317
+ api.state.dropKind = '';
225
318
  api.ui.mode = 'review';
319
+ if (kind === 'logs') {
320
+ try {
321
+ removeStaleLogs(api.ui.top);
322
+ } catch (error) {
323
+ api.ui.status = error.message;
324
+ refreshLogs(api);
325
+ return;
326
+ }
327
+ refreshLogs(api);
328
+ api.ui.status = 'dropped logs';
329
+ return;
330
+ }
226
331
  if (!name) return;
227
332
  try {
228
333
  api.ui.ignoreWatch();
@@ -237,21 +342,14 @@ const confirmDrop = (api) => {
237
342
 
238
343
  const cancelDrop = (api) => {
239
344
  api.state.dropName = '';
345
+ api.state.dropKind = '';
240
346
  api.ui.mode = 'review';
241
347
  api.ui.status = '';
242
348
  };
243
349
 
244
350
  const reorder = (api, delta) => {
245
- if (api.state.viewing) return;
246
- if (!canEdit(api.ui)) {
247
- api.ui.status = 'read only';
248
- return;
249
- }
250
- const entry = selected(api);
251
- if (!entry || entry.kind !== 'script') {
252
- api.ui.status = 'not a script';
253
- return;
254
- }
351
+ const entry = selectedScript(api);
352
+ if (!entry) return;
255
353
  try {
256
354
  api.ui.ignoreWatch();
257
355
  if (!reorderScript(api.ui.top, entry.name, delta)) return;
@@ -260,24 +358,33 @@ const reorder = (api, delta) => {
260
358
  return;
261
359
  }
262
360
  refresh(api);
263
- const at = api.state.commands.findIndex((item) => item.name === entry.name);
264
- if (at >= 0) api.ui.nav.npmCursor = at;
361
+ focusScript(api, entry.name);
265
362
  api.ui.status = '';
266
363
  };
267
364
 
365
+ const npmState = () => ({
366
+ commands: [],
367
+ viewing: false,
368
+ output: '',
369
+ followEnd: true,
370
+ logScroll: 0,
371
+ editName: '',
372
+ editField: '',
373
+ draftName: '',
374
+ draftCommand: '',
375
+ verbose: false,
376
+ raw: '',
377
+ exitStatus: '',
378
+ dropName: '',
379
+ dropKind: '',
380
+ logBytes: 0,
381
+ running: false,
382
+ stopping: false,
383
+ child: null,
384
+ });
385
+
268
386
  const createNpmController = (ui) => {
269
- const state = {
270
- commands: [],
271
- viewing: false,
272
- output: '',
273
- followEnd: true,
274
- logScroll: 0,
275
- editName: '',
276
- dropName: '',
277
- running: false,
278
- stopping: false,
279
- child: null,
280
- };
387
+ const state = npmState();
281
388
  const api = { ui, state };
282
389
  return {
283
390
  state,
@@ -293,6 +400,10 @@ const createNpmController = (ui) => {
293
400
  get dropName() {
294
401
  return state.dropName;
295
402
  },
403
+ get logLabel() {
404
+ if (!state.logBytes) return '';
405
+ return `old logs ${formatSize(state.logBytes)}`;
406
+ },
296
407
  get followEnd() {
297
408
  return state.followEnd;
298
409
  },
@@ -302,6 +413,18 @@ const createNpmController = (ui) => {
302
413
  get editName() {
303
414
  return state.editName;
304
415
  },
416
+ get editField() {
417
+ return state.editField;
418
+ },
419
+ get draftName() {
420
+ return state.draftName;
421
+ },
422
+ get draftCommand() {
423
+ return state.draftCommand;
424
+ },
425
+ get verbose() {
426
+ return state.verbose;
427
+ },
305
428
  get running() {
306
429
  return state.running;
307
430
  },
@@ -314,7 +437,15 @@ const createNpmController = (ui) => {
314
437
  state.followEnd = true;
315
438
  state.logScroll = 0;
316
439
  state.editName = '';
440
+ state.editField = '';
441
+ state.draftName = '';
442
+ state.draftCommand = '';
443
+ state.verbose = false;
444
+ state.raw = '';
445
+ state.exitStatus = '';
317
446
  state.dropName = '';
447
+ state.dropKind = '';
448
+ state.logBytes = 0;
318
449
  state.stopping = false;
319
450
  },
320
451
  open: () => open(api),
@@ -322,10 +453,13 @@ const createNpmController = (ui) => {
322
453
  run: () => runSelected(api),
323
454
  rerun: () => rerun(api),
324
455
  closeView: () => closeView(api),
456
+ toggleVerbose: () => toggleVerbose(api),
325
457
  edit: () => editSelected(api),
326
458
  create: () => newCommand(api),
327
459
  finishEdit: () => finishEdit(api),
460
+ focusField: (field) => focusField(api, field),
328
461
  askDrop: () => askDrop(api),
462
+ askLogs: () => askLogs(api),
329
463
  confirmDrop: () => confirmDrop(api),
330
464
  cancelDrop: () => cancelDrop(api),
331
465
  reorder: (delta) => reorder(api, delta),