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
@@ -5,11 +5,11 @@ const keys = require('../keys.js');
5
5
  const files = require('../files.js');
6
6
  const primitives = require('./primitives.js');
7
7
 
8
- const { itemPath, TODO_FILE, isTodosEntry, fileTotals } = files;
8
+ const { itemPath, REPO_TASKS_LABEL, isTasksEntry, fileTotals } = files;
9
9
  const { ACTION_IDS, BRANCH_ACTIONS, COMMIT_ACTIONS, actionLetter } = keys;
10
10
  const { buttonWord } = keys;
11
11
  const { FILES_HIDDEN, FILES_GIT_DISABLED, BRANCHES_DISABLED } = keys;
12
- const { COMMITS_DISABLED, TODO_DISABLED, DIFF_DISABLED, UNIT_DISABLED } = keys;
12
+ const { COMMITS_DISABLED, TASKS_DISABLED, DIFF_DISABLED, UNIT_DISABLED } = keys;
13
13
  const { NPM_DISABLED } = keys;
14
14
  const { diffGitDisabled, branchGitDisabled, commitGitDisabled } = keys;
15
15
  const { CONFIRM, confirmPrefix, promptFromChoices } = keys;
@@ -122,6 +122,9 @@ const BUTTON_LEAD = ' ';
122
122
  const BUTTON_GAP = ' ';
123
123
  const BUTTON_EDGE = 1;
124
124
  const buttonGap = (index) => (index ? BUTTON_GAP : BUTTON_LEAD);
125
+ const BACK_ARROW = '🢐';
126
+ const STOP_MARK = '⊗';
127
+ const BACK_KEY = 'esc';
125
128
  const LOGO = '👁️';
126
129
 
127
130
  const pathSegments = (rel) => {
@@ -152,10 +155,10 @@ const headerScreen = (view) => {
152
155
  if (pane === 'files') {
153
156
  const files = view.files ?? [];
154
157
  const entry = files[view.fileCursor ?? 0];
155
- if (isTodosEntry(entry)) return TODO_FILE;
158
+ if (isTasksEntry(entry)) return REPO_TASKS_LABEL;
156
159
  return '';
157
160
  }
158
- if (view.item && view.item.origin === 'todo') return TODO_FILE;
161
+ if (view.item && view.item.origin === 'task') return REPO_TASKS_LABEL;
159
162
  return '';
160
163
  };
161
164
 
@@ -163,11 +166,11 @@ const headerTarget = (view) => {
163
166
  const pane = view.pane ?? 'diff';
164
167
  if (pane === 'files') {
165
168
  const entry = (view.files ?? [])[view.fileCursor ?? 0];
166
- if (!entry || isTodosEntry(entry)) return '';
169
+ if (!entry || isTasksEntry(entry)) return '';
167
170
  return entry.path;
168
171
  }
169
172
  if (pane === 'unit') return view.reviewPath ?? '';
170
- if (!view.item || view.item.origin === 'todo') return '';
173
+ if (!view.item || view.item.origin === 'task') return '';
171
174
  return itemPath(view.item);
172
175
  };
173
176
 
@@ -251,6 +254,38 @@ const QUIT_MARK = {
251
254
  missBold: false,
252
255
  };
253
256
 
257
+ const backFooterId = (view) => {
258
+ const pane = view.pane ?? 'diff';
259
+ if (pane === 'branches' || pane === 'commits') return 'back';
260
+ if (pane === 'npm') return view.npmView ? 'npmStop' : 'back';
261
+ if (view.item && view.item.origin === 'task') return 'back';
262
+ return '';
263
+ };
264
+
265
+ const backHintMark = (view) => {
266
+ if (view.pane === 'npm' && view.npmView && view.npmRunning) return STOP_MARK;
267
+ return BACK_ARROW;
268
+ };
269
+
270
+ const hintBody = (mark) => {
271
+ const gap = mark === STOP_MARK ? ' ' : '';
272
+ return `${mark}${gap}${BACK_KEY}`;
273
+ };
274
+
275
+ const buttonLabel = (id, short, backId, mark) => {
276
+ if (id === backId) return hintBody(mark);
277
+ if (short) return actionLetter(id);
278
+ return buttonWord(id);
279
+ };
280
+
281
+ const paintBackHint = (color, mark) => {
282
+ const bgRgb = THEME.buttonBg;
283
+ const body = hintBody(mark);
284
+ const arrow = paint(mark, THEME.buttonHotFg, bgRgb, color, true);
285
+ const rest = paint(body.slice(mark.length), THEME.buttonFg, bgRgb, color);
286
+ return arrow + rest;
287
+ };
288
+
254
289
  const paintMarkedWord = (label, letter, color, mark) => {
255
290
  const at = label.indexOf(letter);
256
291
  if (at < 0) {
@@ -266,37 +301,49 @@ const paintMarkedWord = (label, letter, color, mark) => {
266
301
  return out;
267
302
  };
268
303
 
269
- const layoutButtons = (width, compact, hidden, extra, disabled) => {
270
- const hiddenIds = hidden ?? [];
271
- const extraIds = extra ?? [];
272
- const disabledIds = disabled ?? [];
273
- const ids = [];
304
+ const footerIds = (hidden, extra, backId) => {
305
+ const ids = backId ? [backId] : [];
274
306
  for (const id of ACTION_IDS) {
275
- if (id === 'quit') {
276
- for (const add of extraIds) {
277
- if (!ids.includes(add)) ids.push(add);
278
- }
307
+ if (id !== 'quit') {
308
+ if (!hidden.includes(id)) ids.push(id);
279
309
  continue;
280
310
  }
281
- if (hiddenIds.includes(id)) continue;
282
- ids.push(id);
311
+ for (const add of extra) {
312
+ if (!ids.includes(add)) ids.push(add);
313
+ }
283
314
  }
315
+ return ids;
316
+ };
317
+
318
+ const layoutButtons = (width, options = {}) => {
319
+ const { compact = false, hidden = [], extra = [] } = options;
320
+ const { disabled: disabledIds = [], backId = '', mark = '' } = options;
321
+ const hint = mark || BACK_ARROW;
322
+ const ids = footerIds(hidden, extra, backId);
284
323
  const build = (short) => {
285
324
  const hits = [];
286
325
  const parts = [];
287
326
  let x = 0;
288
327
  const limit = Math.max(0, width - BUTTON_EDGE);
289
328
  for (const id of ids) {
290
- const letter = actionLetter(id);
291
- const word = buttonWord(id);
292
- const label = short ? letter : word;
329
+ const back = id === backId;
330
+ const letter = back ? BACK_KEY : actionLetter(id);
331
+ const label = buttonLabel(id, short, backId, hint);
293
332
  const gap = buttonGap(parts.length);
294
333
  const piece = `${gap}${label}`;
295
334
  const w = visibleWidth(piece);
296
335
  if (x + w > limit && parts.length) break;
297
336
  const off = disabledIds.includes(id);
298
337
  if (!off) hits.push({ id, x0: x, x1: x + w });
299
- parts.push({ action: { id }, piece, letter, label, disabled: off });
338
+ parts.push({
339
+ action: { id },
340
+ piece,
341
+ letter,
342
+ label,
343
+ disabled: off,
344
+ back,
345
+ mark: back ? hint : '',
346
+ });
300
347
  x += w;
301
348
  }
302
349
  return { hits, parts, width: x };
@@ -316,7 +363,8 @@ const paintButtons = (layout, width, color) => {
316
363
  const part = layout.parts[i];
317
364
  const gap = buttonGap(i);
318
365
  out += paint(gap, fgRgb, bgRgb, color);
319
- if (part.disabled) out += paint(part.label, fgRgb, bgRgb, color);
366
+ if (part.back) out += paintBackHint(color, part.mark);
367
+ else if (part.disabled) out += paint(part.label, fgRgb, bgRgb, color);
320
368
  else out += paintMarkedWord(part.label, part.letter, color, BUTTON_MARK);
321
369
  used += visibleWidth(part.piece);
322
370
  }
@@ -351,9 +399,9 @@ const countsKind = (sourceKind, revShort) => {
351
399
 
352
400
  const notesLine = (counts) => {
353
401
  const feedback = counts.feedback ?? 0;
354
- const todo = counts.todo ?? 0;
402
+ const tasks = counts.tasks ?? 0;
355
403
  const code = counts.code ?? 0;
356
- return `feedback ${feedback} todo ${todo} code ${code}`;
404
+ return `feedback ${feedback} tasks ${tasks} code ${code}`;
357
405
  };
358
406
 
359
407
  const statusLead = (view) => {
@@ -397,7 +445,9 @@ const statusStats = (view) => {
397
445
  const fallbackN = counts.staged ?? 0;
398
446
  const n = hasGit ? total.staged : fallbackN;
399
447
  const m = hasGit ? total.remaining : counted;
400
- return joinStatus([statusDelta(total), `${n}/${m}`]);
448
+ const git = joinStatus([statusDelta(total), `${n}/${m}`]);
449
+ if (!view.npmLogs) return git;
450
+ return joinStatus([view.npmLogs, git]);
401
451
  };
402
452
 
403
453
  const statusLayout = (view, status, width) => {
@@ -454,6 +504,7 @@ const paintChoicePrompt = (choices, width, color, prefix = '') => {
454
504
  };
455
505
 
456
506
  const STATUS_MARK = /(\+[0-9]+(?:\/[0-9]+)?|-[0-9]+(?:\/[0-9]+)?)/g;
507
+ const OLD_LOGS = /old logs \S+/;
457
508
 
458
509
  const paintStatusMark = (token, color) => {
459
510
  const bgRgb = THEME.chromeBg;
@@ -467,6 +518,18 @@ const paintStatusLeft = (text, color) => {
467
518
  const bgRgb = THEME.chromeBg;
468
519
  const muted = THEME.mutedFg;
469
520
  if (!color) return text;
521
+ const found = text.match(OLD_LOGS);
522
+ if (found) {
523
+ const at = found.index;
524
+ const token = found[0];
525
+ const before = text.slice(0, at);
526
+ const after = text.slice(at + token.length);
527
+ return (
528
+ paintStatusLeft(before, color) +
529
+ paint(token, THEME.shaFg, bgRgb, color) +
530
+ paintStatusLeft(after, color)
531
+ );
532
+ }
470
533
  let out = '';
471
534
  let last = 0;
472
535
  for (const match of text.matchAll(STATUS_MARK)) {
@@ -604,7 +667,7 @@ const hiddenActions = (view) => {
604
667
  if (pane === 'branches') return BRANCHES_DISABLED;
605
668
  if (pane === 'commits') return COMMITS_DISABLED;
606
669
  if (pane === 'npm') return NPM_DISABLED;
607
- if (view.item && view.item.origin === 'todo') return TODO_DISABLED;
670
+ if (view.item && view.item.origin === 'task') return TASKS_DISABLED;
608
671
  return DIFF_DISABLED;
609
672
  };
610
673
 
@@ -613,7 +676,7 @@ const dimFooterActions = (view) => {
613
676
  if (pane === 'files') {
614
677
  const files = view.files ?? [];
615
678
  const entry = files[view.fileCursor ?? 0];
616
- if (!entry || entry.kind !== 'todos') return [];
679
+ if (!entry || entry.kind !== 'tasks') return [];
617
680
  return FILES_GIT_DISABLED;
618
681
  }
619
682
  if (pane === 'diff' || pane === 'unit') return diffGitDisabled(view.item);
@@ -621,13 +684,18 @@ const dimFooterActions = (view) => {
621
684
  if (pane === 'commits') {
622
685
  const entry = selectedCommit(view);
623
686
  const staged = (view.counts?.staged ?? 0) > 0;
624
- if (!entry) return commitGitDisabled({ canCommit: staged });
625
- return commitGitDisabled({ ...entry, canCommit: staged });
687
+ const unstagedCurrent = !view.rev && !view.revShort;
688
+ return commitGitDisabled({
689
+ ...entry,
690
+ canCommit: staged,
691
+ unstagedCurrent,
692
+ });
626
693
  }
627
694
  if (pane === 'npm' && view.npmView) {
628
695
  if (view.npmRunning) return ['npmRerun'];
629
696
  return [];
630
697
  }
698
+ if (pane === 'npm' && !view.npmLogs) return ['npmLogs'];
631
699
  return [];
632
700
  };
633
701
 
@@ -640,12 +708,21 @@ const extraFooterActions = (view) => {
640
708
  if (pane === 'branches') return Object.keys(BRANCH_ACTIONS);
641
709
  if (pane === 'commits') return Object.keys(COMMIT_ACTIONS);
642
710
  if (pane === 'npm') {
643
- if (view.npmView) return ['npmStop', 'npmRerun'];
644
- return ['npmEdit', 'npmNew'];
711
+ if (view.npmView) return ['npmVerbose', 'npmRerun'];
712
+ return ['npmEdit', 'npmNew', 'npmDrop', 'npmLogs'];
645
713
  }
646
714
  return [];
647
715
  };
648
716
 
717
+ const footerLayout = (view, width) =>
718
+ layoutButtons(width, {
719
+ hidden: hiddenActions(view),
720
+ extra: extraFooterActions(view),
721
+ disabled: dimFooterActions(view),
722
+ backId: backFooterId(view),
723
+ mark: backHintMark(view),
724
+ });
725
+
649
726
  module.exports = {
650
727
  QUIT_PROMPT,
651
728
  PUSH_PROMPT,
@@ -655,13 +732,10 @@ module.exports = {
655
732
  headerText,
656
733
  paintHeader,
657
734
  layoutButtons,
658
- paintButtons,
735
+ footerLayout,
659
736
  paintFooter,
660
737
  footerHits,
661
738
  paintStatusLine,
662
739
  statusChoiceHits,
663
740
  statusBranchHit,
664
- hiddenActions,
665
- dimFooterActions,
666
- extraFooterActions,
667
741
  };