reslop 0.2.4 → 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 (67) hide show
  1. package/CHANGELOG.md +14 -1
  2. package/README.md +1 -2
  3. package/lib/capabilities.js +0 -4
  4. package/lib/clipboard.js +39 -1
  5. package/lib/deps-diff.js +0 -3
  6. package/lib/deps.js +144 -176
  7. package/lib/diff/inline.js +66 -1
  8. package/lib/diff/unit.js +11 -28
  9. package/lib/editor.js +69 -9
  10. package/lib/files.js +17 -16
  11. package/lib/git-deps.js +58 -95
  12. package/lib/github.js +7 -7
  13. package/lib/gitlab.js +3 -3
  14. package/lib/highlight/core.js +11 -0
  15. package/lib/highlight/dot.js +2 -12
  16. package/lib/highlight/log.js +4 -11
  17. package/lib/highlight/txt.js +2 -12
  18. package/lib/keys.js +22 -12
  19. package/lib/manifest.js +0 -4
  20. package/lib/npm-commands.js +1 -22
  21. package/lib/remote.js +1 -7
  22. package/lib/render/branches.js +4 -13
  23. package/lib/render/chrome.js +85 -31
  24. package/lib/render/commits.js +95 -207
  25. package/lib/render/diff.js +31 -1
  26. package/lib/render/files.js +7 -16
  27. package/lib/render/frame.js +95 -110
  28. package/lib/render/notes.js +71 -16
  29. package/lib/render/npm.js +104 -18
  30. package/lib/render/primitives.js +108 -45
  31. package/lib/render/unit.js +4 -1
  32. package/lib/report-parse.js +0 -1
  33. package/lib/report-run.js +0 -1
  34. package/lib/report-test.js +40 -47
  35. package/lib/report-tools.js +0 -7
  36. package/lib/review-merge.js +101 -131
  37. package/lib/review.js +64 -77
  38. package/lib/select.js +52 -0
  39. package/lib/session/actions.js +23 -22
  40. package/lib/session/branches.js +5 -3
  41. package/lib/session/change-actions.js +173 -146
  42. package/lib/session/commit.js +32 -13
  43. package/lib/session/compose.js +120 -77
  44. package/lib/session/dispatch.js +36 -21
  45. package/lib/session/edit-click.js +57 -0
  46. package/lib/session/files-pane.js +24 -24
  47. package/lib/session/items.js +69 -89
  48. package/lib/session/lifecycle.js +31 -96
  49. package/lib/session/load.js +51 -58
  50. package/lib/session/navigation.js +105 -96
  51. package/lib/session/npm.js +158 -77
  52. package/lib/session/ops.js +192 -51
  53. package/lib/session/pointer.js +11 -5
  54. package/lib/session/progress.js +9 -6
  55. package/lib/session/review.js +35 -54
  56. package/lib/session/tasks.js +292 -0
  57. package/lib/session/terminal.js +13 -17
  58. package/lib/session/unit.js +3 -4
  59. package/lib/session/update.js +76 -97
  60. package/lib/session/view.js +17 -14
  61. package/lib/session/watch.js +47 -27
  62. package/lib/session.js +34 -79
  63. package/lib/update.js +1 -5
  64. package/lib/utilities.js +21 -1
  65. package/lib/wrap.js +15 -0
  66. package/package.json +1 -1
  67. package/lib/session/todos.js +0 -293
@@ -11,7 +11,7 @@ const { listedCommits } = commit;
11
11
  const editor = require('../editor.js');
12
12
  const { Editor } = editor;
13
13
  const files = require('../files.js');
14
- const { itemPath, isTodoItem, listPath, isReadOnlyOrigin } = files;
14
+ const { itemPath, isTaskItem, listPath, isReadOnlyOrigin } = files;
15
15
  const diff = require('../diff/diff.js');
16
16
  const { blockAddText } = diff;
17
17
  const items = require('./items.js');
@@ -20,12 +20,13 @@ const review = require('../review.js');
20
20
  const { setFeedback, setCode, checkLabel, rememberTemplate } = review;
21
21
  const templates = require('./templates.js');
22
22
  const { createTemplatePick } = templates;
23
- const todos = require('./todos.js');
24
- const { createTodoCompose } = todos;
23
+ const todos = require('./tasks.js');
24
+ const { createTasksCompose } = todos;
25
25
  const unit = require('./unit.js');
26
+ const clipboard = require('../clipboard.js');
26
27
 
27
28
  const COMPOSE_EDITS = ['backspace', 'delete', 'ctrl-z', 'ctrl-y'];
28
- const COMPOSE_LEAVE = ['next', 'prev', 'todo'];
29
+ const COMPOSE_LEAVE = ['next', 'prev', 'tasks'];
29
30
 
30
31
  const KIND = {
31
32
  feedback: {
@@ -52,13 +53,13 @@ const KIND = {
52
53
  command: null,
53
54
  view: 'code',
54
55
  },
55
- todo: {
56
+ tasks: {
56
57
  enter: 'edit-next',
57
58
  tab: 'template',
58
59
  escape: 'save',
59
- move: 'todo',
60
+ move: 'tasks',
60
61
  command: null,
61
- view: 'todo',
62
+ view: 'tasks',
62
63
  },
63
64
  commit: {
64
65
  enter: 'save',
@@ -91,11 +92,10 @@ const initialState = () => ({
91
92
  composeKind: null,
92
93
  commitKind: null,
93
94
  commitBody: '',
94
- composeTodoId: null,
95
+ composeTaskId: null,
95
96
  composeBaseline: '',
96
97
  templateIndex: -1,
97
98
  templateFocus: false,
98
- blinkOn: true,
99
99
  fileDirty: false,
100
100
  fileEditRows: null,
101
101
  fileOpened: '',
@@ -105,17 +105,6 @@ const reset = (state) => {
105
105
  Object.assign(state, initialState());
106
106
  };
107
107
 
108
- const resetBlink = (api) => {
109
- api.state.blinkOn = true;
110
- api.restartBlink();
111
- };
112
-
113
- const tickBlink = (api) => {
114
- if (!api.state.composeKind) return;
115
- api.state.blinkOn = !api.state.blinkOn;
116
- api.ui.draw();
117
- };
118
-
119
108
  const viewSize = (api) => api.ui.lastSize ?? api.ui.getSize();
120
109
 
121
110
  const syncCodeScroll = (api) => {
@@ -150,7 +139,7 @@ const openCompose = (api, kind, text, todoId) => {
150
139
  const { state, ui, nav } = api;
151
140
  ui.mode = 'compose';
152
141
  state.composeKind = kind;
153
- state.composeTodoId = todoId ?? null;
142
+ state.composeTaskId = todoId ?? null;
154
143
  state.composeBaseline = text;
155
144
  state.fileOpened = kind === 'file' ? text : '';
156
145
  state.editor = new Editor(text);
@@ -158,7 +147,6 @@ const openCompose = (api, kind, text, todoId) => {
158
147
  state.templateFocus = false;
159
148
  ui.status = '';
160
149
  nav.clearSelection();
161
- resetBlink(api);
162
150
  };
163
151
 
164
152
  const closeCompose = (api) => {
@@ -168,7 +156,7 @@ const closeCompose = (api) => {
168
156
  state.composeKind = null;
169
157
  state.commitKind = null;
170
158
  state.commitBody = '';
171
- state.composeTodoId = null;
159
+ state.composeTaskId = null;
172
160
  state.composeBaseline = '';
173
161
  state.fileOpened = '';
174
162
  state.templateIndex = -1;
@@ -201,7 +189,7 @@ const writesCodeFile = (api, item) => {
201
189
 
202
190
  const commitFeedback = (api, text) => {
203
191
  const item = api.ui.current();
204
- if (!item || isTodoItem(item)) return;
192
+ if (!item || isTaskItem(item)) return;
205
193
  const notes = api.review.store;
206
194
  setFeedback(notes, itemFeedKey(item), itemNote(item, text));
207
195
  };
@@ -265,7 +253,7 @@ const saveUnitFile = (api, text) => {
265
253
  api.ui.status = error.message;
266
254
  return false;
267
255
  }
268
- if (typeof api.ui.ignoreWatch === 'function') api.ui.ignoreWatch();
256
+ api.ui.ignoreWatch();
269
257
  api.state.composeBaseline = text;
270
258
  api.state.fileDirty = true;
271
259
  return true;
@@ -284,12 +272,12 @@ const stageEditedFile = (api, rel) => {
284
272
  api.ui.status = error.message;
285
273
  return false;
286
274
  }
287
- if (typeof api.ui.ignoreWatch === 'function') api.ui.ignoreWatch();
275
+ api.ui.ignoreWatch();
288
276
  return true;
289
277
  }
290
278
  if (typeof repo.add !== 'function') return false;
291
279
  let staged = 0;
292
- for (const item of api.ui.items ?? []) {
280
+ for (const item of api.ui.items) {
293
281
  if (listPath(item) !== rel) continue;
294
282
  if (isReadOnlyOrigin(item.origin)) continue;
295
283
  if (item.origin === 'staged') continue;
@@ -299,14 +287,10 @@ const stageEditedFile = (api, rel) => {
299
287
  api.ui.status = error.message;
300
288
  return staged > 0;
301
289
  }
302
- if (api.ui.collection && api.ui.collection.keepOrigin) {
303
- api.ui.collection.keepOrigin(item, 'staged');
304
- }
290
+ api.ui.collection.keepOrigin(item, 'staged');
305
291
  staged += 1;
306
292
  }
307
- if (staged && typeof api.ui.ignoreWatch === 'function') {
308
- api.ui.ignoreWatch();
309
- }
293
+ if (staged) api.ui.ignoreWatch();
310
294
  return staged > 0;
311
295
  };
312
296
 
@@ -325,7 +309,7 @@ const saveFileCompose = (api) => {
325
309
  api.ui.flushReview();
326
310
  closeCompose(api);
327
311
  const doneStatus = changed ? 'staged' : 'saved';
328
- if (!reload || typeof api.ui.reloadAfterChange !== 'function') {
312
+ if (!reload) {
329
313
  if (!api.ui.status) api.ui.status = doneStatus;
330
314
  return { kind: 'saved' };
331
315
  }
@@ -343,8 +327,8 @@ const commitCompose = (api) => {
343
327
  if (api.review.store) commitFeedback(api, text);
344
328
  return true;
345
329
  }
346
- if (state.composeKind === 'todo') {
347
- api.todos.commitTodo(text);
330
+ if (state.composeKind === 'tasks') {
331
+ api.tasks.commitTask(text);
348
332
  return true;
349
333
  }
350
334
  if (state.composeKind === 'code') return commitCode(api, text);
@@ -358,7 +342,7 @@ const saveCompose = (api, extra) => {
358
342
  if (spec && spec.command) return { kind: spec.command };
359
343
  const kind = api.state.composeKind;
360
344
  if (kind === 'file') return saveFileCompose(api);
361
- const item = api.ui.current ? api.ui.current() : null;
345
+ const item = api.ui.current();
362
346
  const shouldStage =
363
347
  kind === 'code' &&
364
348
  writesCodeFile(api, item) &&
@@ -379,14 +363,14 @@ const saveCompose = (api, extra) => {
379
363
  api.ui.flushReview();
380
364
  closeCompose(api);
381
365
  const doneStatus = shouldStage && reload ? 'staged' : 'saved';
382
- if (reload && typeof api.ui.reloadAfterChange === 'function') {
366
+ if (reload) {
383
367
  api.ui.reloadAfterChange(() => {
384
368
  if (!api.ui.status) api.ui.status = doneStatus;
385
369
  });
386
370
  } else {
387
371
  api.ui.status = doneStatus;
388
372
  }
389
- if (opts.editNext === true) api.todos.editNextTodo();
373
+ if (opts.editNext === true) api.tasks.editNextTask();
390
374
  return { kind: 'saved' };
391
375
  };
392
376
 
@@ -418,7 +402,16 @@ const composeView = (api) => {
418
402
  const kind = state.composeKind;
419
403
  const text = state.editor.text;
420
404
  const cursor = state.editor.cursor;
421
- return { kind, text, cursor, commitKind: state.commitKind };
405
+ const scrollCol = state.editor.scrollCol;
406
+ const anchor = state.editor.anchor;
407
+ return {
408
+ kind,
409
+ text,
410
+ cursor,
411
+ scrollCol,
412
+ anchor,
413
+ commitKind: state.commitKind,
414
+ };
422
415
  };
423
416
 
424
417
  const liveCodeText = (api) => {
@@ -444,7 +437,8 @@ const codeOverlayView = (api) => {
444
437
  const cursor = editing && editor ? editor.cursor : null;
445
438
  const picked = editing && editor ? editor.scrollCol : 0;
446
439
  const scrollCol = picked ?? 0;
447
- return { text, keepEmpty: editing, cursor, scrollCol };
440
+ const anchor = editing && editor ? editor.anchor : null;
441
+ return { text, keepEmpty: editing, cursor, scrollCol, anchor };
448
442
  };
449
443
 
450
444
  const idleNoteText = (api) => {
@@ -452,7 +446,7 @@ const idleNoteText = (api) => {
452
446
  if (!notes) return '';
453
447
  if (api.state.composeKind) return '';
454
448
  const item = api.ui.current();
455
- if (!item || isTodoItem(item)) return '';
449
+ if (!item || isTaskItem(item)) return '';
456
450
  const note = notes.feedback.get(itemFeedKey(item));
457
451
  if (!note || !note.text.trim()) return '';
458
452
  return checkLabel(note.text, note.done);
@@ -493,15 +487,15 @@ const moveTemplateLine = (api, delta, shown, width) => {
493
487
  return false;
494
488
  };
495
489
 
496
- const moveNoteLine = (api, delta) => {
490
+ const moveNoteLine = (api, delta, extend) => {
497
491
  const size = viewSize(api);
498
492
  const width = noteInnerWidth(size.width);
499
493
  const shown = api.templates.shownTemplates();
500
494
  const feedback = api.state.composeKind === 'feedback';
501
- if (shown.length && feedback) {
495
+ if (shown.length && feedback && !extend) {
502
496
  if (moveTemplateLine(api, delta, shown, width)) return;
503
497
  }
504
- api.state.editor.moveLine(delta, width);
498
+ api.state.editor.moveLine(delta, width, extend);
505
499
  };
506
500
 
507
501
  const fullCommitEdit = (api) =>
@@ -514,26 +508,26 @@ const editedCommitEntry = (api) => {
514
508
  return list[api.nav.commitCursor] ?? null;
515
509
  };
516
510
 
517
- const moveCommitLine = (api, delta) => {
511
+ const moveCommitLine = (api, delta, extend) => {
518
512
  const size = viewSize(api);
519
513
  const width = commitMessageWidth(editedCommitEntry(api), size.width);
520
- api.state.editor.moveLine(delta, width);
514
+ api.state.editor.moveLine(delta, width, extend);
521
515
  };
522
516
 
523
- const moveComposeLine = (api, delta) => {
517
+ const moveComposeLine = (api, delta, extend) => {
524
518
  const spec = KIND[api.state.composeKind];
525
519
  const move = spec ? spec.move : 'note';
526
- if (move === 'todo') {
520
+ if (move === 'tasks') {
527
521
  const size = viewSize(api);
528
522
  const inner = todoBodyWidth(size.width);
529
- return void api.todos.moveTodoCompose(delta, inner);
523
+ return void api.tasks.moveTaskCompose(delta, inner, extend);
530
524
  }
531
525
  if (move === 'code') {
532
- api.state.editor.moveLine(delta);
526
+ api.state.editor.moveLine(delta, undefined, extend);
533
527
  return void syncCodeScroll(api);
534
528
  }
535
- if (fullCommitEdit(api)) return void moveCommitLine(api, delta);
536
- moveNoteLine(api, delta);
529
+ if (fullCommitEdit(api)) return void moveCommitLine(api, delta, extend);
530
+ moveNoteLine(api, delta, extend);
537
531
  };
538
532
 
539
533
  const composePageStep = (api) => {
@@ -552,14 +546,14 @@ const jumpComposeTemplates = (api, toEnd) => {
552
546
  return true;
553
547
  };
554
548
 
555
- const composeHome = (api) => {
556
- if (jumpComposeTemplates(api, false)) return;
557
- api.state.editor.home();
549
+ const composeHome = (api, extend) => {
550
+ if (!extend && jumpComposeTemplates(api, false)) return;
551
+ api.state.editor.home(extend);
558
552
  };
559
553
 
560
- const composeEnd = (api) => {
561
- if (jumpComposeTemplates(api, true)) return;
562
- api.state.editor.end();
554
+ const composeEnd = (api, extend) => {
555
+ if (!extend && jumpComposeTemplates(api, true)) return;
556
+ api.state.editor.end(extend);
563
557
  };
564
558
 
565
559
  const moveComposePage = (api, dir) => {
@@ -573,7 +567,7 @@ const onFeedback = (api) => {
573
567
  return;
574
568
  }
575
569
  const item = api.ui.current();
576
- if (!item || isTodoItem(item)) {
570
+ if (!item || isTaskItem(item)) {
577
571
  api.ui.status = 'not a diff block';
578
572
  return;
579
573
  }
@@ -625,7 +619,6 @@ const onCode = (api) => {
625
619
 
626
620
  const insertNewline = (api) => {
627
621
  api.state.editor.insert('\n');
628
- resetBlink(api);
629
622
  followFileEdit(api);
630
623
  return null;
631
624
  };
@@ -638,6 +631,32 @@ const onEmptyLastLine = (editor) => {
638
631
  return text.slice(lastBreak + 1).trim() === '';
639
632
  };
640
633
 
634
+ const filledLines = (text) => {
635
+ let count = 0;
636
+ for (const line of `${text ?? ''}`.split('\n')) {
637
+ if (line.trim() !== '') count += 1;
638
+ }
639
+ return count;
640
+ };
641
+
642
+ const blankTail = (text) => {
643
+ const lines = `${text ?? ''}`.split('\n');
644
+ let count = 0;
645
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
646
+ if (lines[i].trim() !== '') break;
647
+ count += 1;
648
+ }
649
+ return count;
650
+ };
651
+
652
+ const enterFullCommit = (api) => {
653
+ const editor = api.state.editor;
654
+ const text = editor ? editor.text : '';
655
+ const shaped = filledLines(text) >= 2 || blankTail(text) >= 2;
656
+ if (onEmptyLastLine(editor) && shaped) return saveCompose(api);
657
+ return insertNewline(api);
658
+ };
659
+
641
660
  const enterCompose = (api) => {
642
661
  const { state } = api;
643
662
  if (state.templateFocus && state.composeKind === 'feedback') {
@@ -645,10 +664,7 @@ const enterCompose = (api) => {
645
664
  }
646
665
  const spec = KIND[state.composeKind];
647
666
  if (spec && spec.enter === 'newline') return insertNewline(api);
648
- if (fullCommitEdit(api)) {
649
- if (onEmptyLastLine(state.editor)) return saveCompose(api);
650
- return insertNewline(api);
651
- }
667
+ if (fullCommitEdit(api)) return enterFullCommit(api);
652
668
  if (spec && spec.enter === 'edit-next') {
653
669
  return saveCompose(api, { editNext: true });
654
670
  }
@@ -665,21 +681,53 @@ const leaveCompose = (api, key) => {
665
681
  return saveCompose(api);
666
682
  };
667
683
 
684
+ const copyCompose = (api) => {
685
+ const text = api.state.editor.selectedText();
686
+ if (!text) return;
687
+ const ok = clipboard.copyText(text, api.ui.stdout);
688
+ api.ui.status = ok ? 'copied' : 'copy failed';
689
+ };
690
+
691
+ const cutCompose = (api) => {
692
+ if (!api.state.editor.hasSelect()) return;
693
+ copyCompose(api);
694
+ api.state.editor.removeSpan();
695
+ };
696
+
697
+ const pasteCompose = (api) => {
698
+ const text = clipboard.pasteText();
699
+ if (!text) return;
700
+ api.state.editor.insert(text);
701
+ };
702
+
703
+ const COMPOSE_CLIP = ['ctrl-x', 'ctrl-v'];
704
+
668
705
  const COMPOSE_SPECIAL = {
669
706
  backspace: (api) => api.state.editor.backspace(),
670
707
  delete: (api) => api.state.editor.delete(),
671
708
  left: (api) => api.state.editor.move(-1),
672
709
  right: (api) => api.state.editor.move(1),
710
+ 'shift-left': (api) => api.state.editor.move(-1, true),
711
+ 'shift-right': (api) => api.state.editor.move(1, true),
673
712
  'ctrl-left': (api) => api.state.editor.moveWord(-1),
674
713
  'ctrl-right': (api) => api.state.editor.moveWord(1),
714
+ 'ctrl-shift-left': (api) => api.state.editor.moveWord(-1, true),
715
+ 'ctrl-shift-right': (api) => api.state.editor.moveWord(1, true),
675
716
  up: (api) => moveComposeLine(api, -1),
676
717
  down: (api) => moveComposeLine(api, 1),
718
+ 'shift-up': (api) => moveComposeLine(api, -1, true),
719
+ 'shift-down': (api) => moveComposeLine(api, 1, true),
677
720
  home: (api) => composeHome(api),
678
721
  end: (api) => composeEnd(api),
722
+ 'shift-home': (api) => composeHome(api, true),
723
+ 'shift-end': (api) => composeEnd(api, true),
679
724
  pageUp: (api) => moveComposePage(api, -1),
680
725
  pageDown: (api) => moveComposePage(api, 1),
681
726
  'ctrl-a': (api) => api.state.editor.home(),
682
727
  'ctrl-e': (api) => api.state.editor.end(),
728
+ 'ctrl-c': (api) => copyCompose(api),
729
+ 'ctrl-x': (api) => cutCompose(api),
730
+ 'ctrl-v': (api) => pasteCompose(api),
683
731
  'ctrl-z': (api) => api.state.editor.undoEdit(),
684
732
  'ctrl-y': (api) => api.state.editor.redoEdit(),
685
733
  tab: (api) => composeTab(api),
@@ -695,8 +743,8 @@ const handleKey = (api, key) => {
695
743
  const special = COMPOSE_SPECIAL[key];
696
744
  if (special) {
697
745
  const result = special(api);
698
- if (COMPOSE_EDITS.includes(key)) api.templates.clearTemplatePick();
699
- resetBlink(api);
746
+ const edits = COMPOSE_EDITS.includes(key) || COMPOSE_CLIP.includes(key);
747
+ if (edits) api.templates.clearTemplatePick();
700
748
  followFileEdit(api);
701
749
  syncCodeScroll(api);
702
750
  return result ?? null;
@@ -706,7 +754,6 @@ const handleKey = (api, key) => {
706
754
  if (key.charCodeAt(0) >= 32) {
707
755
  api.templates.clearTemplatePick();
708
756
  api.state.editor.insert(key);
709
- resetBlink(api);
710
757
  followFileEdit(api);
711
758
  syncCodeScroll(api);
712
759
  }
@@ -719,6 +766,7 @@ const applyDiskText = (api, text) => {
719
766
  const cursor = api.state.editor.cursor;
720
767
  api.state.editor.text = text;
721
768
  api.state.editor.cursor = Math.min(cursor, text.length);
769
+ api.state.editor.anchor = null;
722
770
  api.state.composeBaseline = text;
723
771
  api.state.fileEditRows = unit.linesFor(api.ui, api.nav.reviewPath, null);
724
772
  syncCodeScroll(api);
@@ -735,8 +783,6 @@ const composerMethods = (api) => ({
735
783
  composeView: () => composeView(api),
736
784
  codeOverlayView: () => codeOverlayView(api),
737
785
  idleNoteText: () => idleNoteText(api),
738
- resetBlink: () => resetBlink(api),
739
- tickBlink: () => tickBlink(api),
740
786
  onFeedback: () => onFeedback(api),
741
787
  onCode: () => onCode(api),
742
788
  handleKey: (key) => handleKey(api, key),
@@ -748,28 +794,25 @@ const composerMethods = (api) => ({
748
794
  applyDiskText: (text) => applyDiskText(api, text),
749
795
  });
750
796
 
751
- const createComposer = ({ nav, review, ui, restartBlink }) => {
797
+ const createComposer = ({ nav, review, ui }) => {
752
798
  const state = initialState();
753
- const api = { nav, review, ui, restartBlink, state };
799
+ const api = { nav, review, ui, state };
754
800
  api.open = (kind, text, todoId) => openCompose(api, kind, text, todoId);
755
801
  api.close = () => closeCompose(api);
756
802
  api.commit = () => commitCompose(api);
757
803
  api.save = (opts) => saveCompose(api, opts);
758
- api.resetBlink = () => resetBlink(api);
759
804
  api.templates = createTemplatePick(api);
760
- api.todos = createTodoCompose(api);
805
+ api.tasks = createTasksCompose(api);
761
806
  const composer = {
762
807
  state,
763
808
  ...composerMethods(api),
764
- ...api.todos,
809
+ ...api.tasks,
765
810
  ...api.templates,
766
811
  };
767
812
  return composer;
768
813
  };
769
814
 
770
815
  module.exports = {
771
- COMPOSE_EDITS,
772
816
  COMPOSE_LEAVE,
773
- KIND,
774
817
  createComposer,
775
818
  };
@@ -32,13 +32,14 @@ const createDispatcher = (ui) => {
32
32
  add: ui.changeActions.onAdd,
33
33
  unstage: ui.changeActions.onUnstage,
34
34
  revert: ui.changeActions.onRevert,
35
+ ignore: ui.changeActions.onIgnore,
35
36
  next: ui.filesPane.onNext,
36
37
  prev: ui.filesPane.onPrev,
37
38
  layout: ui.filesPane.onLayout,
38
39
  theme: ui.filesPane.onTheme,
39
40
  files: ui.filesPane.onFiles,
40
41
  feedback: ui.composer.onFeedback,
41
- todo: ui.composer.onTodo,
42
+ tasks: ui.composer.onTasks,
42
43
  code: ui.composer.onCode,
43
44
  reload: ui.lifecycle.onReload,
44
45
  commit: ui.commits.onCommit,
@@ -67,11 +68,13 @@ const createDispatcher = (ui) => {
67
68
  npmDrop: () => ui.npm.askDrop(),
68
69
  npmLogs: () => ui.npm.askLogs(),
69
70
  npmStop: () => ui.npm.closeView(),
71
+ back: () => ui.filesPane.onEscape(),
72
+ npmVerbose: () => ui.npm.toggleVerbose(),
70
73
  npmRerun: () => ui.npm.rerun(),
71
- check: () => ui.composer.toggleFocusedTodo(),
72
- removeTodo: () => {
74
+ check: () => ui.composer.toggleFocusedTask(),
75
+ removeTask: () => {
73
76
  if (ui.pane === 'npm') return void ui.npm.askDrop();
74
- ui.composer.removeFocusedTodo();
77
+ ui.composer.removeFocusedTask();
75
78
  },
76
79
  quit: () => ui.onQuit(),
77
80
  scrollUp: () => ui.filesPane.onScroll(-1),
@@ -114,8 +117,8 @@ const createInput = (ui) => {
114
117
  cancel: () => ui.cancelQuit(),
115
118
  },
116
119
  confirmUpdate: {
117
- accept: ui.updater.accept,
118
- decline: ui.updater.decline,
120
+ accept: () => ui.updater.accept(),
121
+ decline: () => ui.updater.decline(),
119
122
  },
120
123
  confirmDrop: {
121
124
  confirm: () => {
@@ -145,30 +148,42 @@ const createInput = (ui) => {
145
148
  applyConfirm(mode, key, confirmations[mode]);
146
149
  };
147
150
 
151
+ const npmViewKeys = {
152
+ escape: () => ui.npm.closeView(),
153
+ r: () => ui.npm.rerun(),
154
+ v: () => ui.npm.toggleVerbose(),
155
+ e: () => {},
156
+ n: () => {},
157
+ };
158
+
159
+ const npmListKeys = {
160
+ 'ctrl-up': () => ui.npm.reorder(-1),
161
+ 'ctrl-down': () => ui.npm.reorder(1),
162
+ up: () => ui.npm.move(-1),
163
+ down: () => ui.npm.move(1),
164
+ };
165
+
166
+ const npmKey = (key) => {
167
+ if (ui.pane !== 'npm') return null;
168
+ if (ui.npm.viewing && Object.hasOwn(npmViewKeys, key)) {
169
+ return npmViewKeys[key];
170
+ }
171
+ return Object.hasOwn(npmListKeys, key) ? npmListKeys[key] : null;
172
+ };
173
+
148
174
  const handleKey = (key) => {
149
175
  const mode = ui.mode;
150
176
  if (CONFIRM_MODE.includes(mode)) return void confirmChoice(key);
151
177
  if (mode === 'compose') {
152
- if (key === 'ctrl-c') return void ui.onQuit();
153
178
  const command = ui.composer.handleKey(key);
154
179
  return void ui.runComposeCommand(command);
155
180
  }
156
- if (ui.pane === 'npm' && ui.npm.viewing) {
157
- if (key === 'escape') return void ui.npm.closeView();
158
- if (key === 'r') return void ui.npm.rerun();
159
- if (key === 'e' || key === 'n') return;
160
- }
161
- if (ui.pane === 'npm' && key === 'ctrl-up') return void ui.npm.reorder(-1);
162
- if (ui.pane === 'npm' && key === 'ctrl-down') {
163
- return void ui.npm.reorder(1);
164
- }
165
- if (ui.pane === 'npm' && (key === 'up' || key === 'down')) {
166
- return void ui.npm.move(key === 'up' ? -1 : 1);
167
- }
181
+ const onNpmKey = npmKey(key);
182
+ if (onNpmKey) return void onNpmKey();
168
183
  if (key === 'escape') return void ui.filesPane.onEscape();
169
184
  const toggle = key === 'x' || key === ' ';
170
- if (toggle && ui.composer.toggleFocusedTodo()) return;
171
- if (ui.composer.typeIntoTodo(key)) return;
185
+ if (toggle && ui.composer.toggleFocusedTask()) return;
186
+ if (ui.composer.typeIntoTask(key)) return;
172
187
  const action = actionFromKey(key, ui.pane, ui.fileScope);
173
188
  if (action) ui.dispatch(action);
174
189
  };
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+
3
+ const wrap = require('../wrap.js');
4
+ const diff = require('../render/diff.js');
5
+
6
+ const { indexAtRow } = wrap;
7
+ const { codeInnerWidth } = diff;
8
+
9
+ const hitAt = (hits, cell) => {
10
+ for (const hit of hits) {
11
+ if (hit.y !== cell.y) continue;
12
+ const x0 = hit.x0 ?? hit.textX;
13
+ if (cell.x < x0) continue;
14
+ if (hit.x1 !== undefined && cell.x >= hit.x1) continue;
15
+ return hit;
16
+ }
17
+ return null;
18
+ };
19
+
20
+ const offsetOf = (hit, cellX) => {
21
+ const shown = cellX - hit.textX;
22
+ const col = Math.max(0, shown) + (hit.scroll || 0);
23
+ const at = indexAtRow(hit.start || 0, hit.text, col);
24
+ if (hit.limit !== undefined && at > hit.limit) return hit.limit;
25
+ return at;
26
+ };
27
+
28
+ const revealCode = (ui) => {
29
+ const kind = ui.composeKind;
30
+ if (kind !== 'code' && kind !== 'file') return;
31
+ const size = ui.lastSize ?? ui.getSize();
32
+ ui.editor.reveal(codeInnerWidth(size.width, ui.layout));
33
+ };
34
+
35
+ const focusField = (ui, hit) => {
36
+ if (!hit.field || !ui.npm) return true;
37
+ if (hit.field === ui.npm.editField) return true;
38
+ return ui.npm.focusField(hit.field);
39
+ };
40
+
41
+ const placeClick = (ui, cell) => {
42
+ if (ui.mode !== 'compose' || !ui.editor) return false;
43
+ const hits = ui.lastFrame && ui.lastFrame.editHits;
44
+ if (!hits || !hits.length) return false;
45
+ const hit = hitAt(hits, cell);
46
+ if (!hit) return false;
47
+ if (!focusField(ui, hit)) {
48
+ ui.paint();
49
+ return true;
50
+ }
51
+ ui.editor.place(offsetOf(hit, cell.x));
52
+ revealCode(ui);
53
+ ui.paint();
54
+ return true;
55
+ };
56
+
57
+ module.exports = { placeClick };