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.
- package/CHANGELOG.md +14 -1
- package/README.md +1 -2
- package/lib/capabilities.js +0 -4
- package/lib/clipboard.js +39 -1
- package/lib/deps-diff.js +0 -3
- package/lib/deps.js +144 -176
- package/lib/diff/inline.js +66 -1
- package/lib/diff/unit.js +11 -28
- package/lib/editor.js +69 -9
- package/lib/files.js +17 -16
- package/lib/git-deps.js +58 -95
- package/lib/github.js +7 -7
- package/lib/gitlab.js +3 -3
- package/lib/highlight/core.js +11 -0
- package/lib/highlight/dot.js +2 -12
- package/lib/highlight/log.js +4 -11
- package/lib/highlight/txt.js +2 -12
- package/lib/keys.js +22 -12
- package/lib/manifest.js +0 -4
- package/lib/npm-commands.js +1 -22
- package/lib/remote.js +1 -7
- package/lib/render/branches.js +4 -13
- package/lib/render/chrome.js +85 -31
- package/lib/render/commits.js +95 -207
- package/lib/render/diff.js +31 -1
- package/lib/render/files.js +7 -16
- package/lib/render/frame.js +95 -110
- package/lib/render/notes.js +71 -16
- package/lib/render/npm.js +104 -18
- package/lib/render/primitives.js +108 -45
- package/lib/render/unit.js +4 -1
- package/lib/report-parse.js +0 -1
- package/lib/report-run.js +0 -1
- package/lib/report-test.js +40 -47
- package/lib/report-tools.js +0 -7
- package/lib/review-merge.js +101 -131
- package/lib/review.js +64 -77
- package/lib/select.js +52 -0
- package/lib/session/actions.js +23 -22
- package/lib/session/branches.js +5 -3
- package/lib/session/change-actions.js +173 -146
- package/lib/session/commit.js +32 -13
- package/lib/session/compose.js +120 -77
- package/lib/session/dispatch.js +36 -21
- package/lib/session/edit-click.js +57 -0
- package/lib/session/files-pane.js +24 -24
- package/lib/session/items.js +69 -89
- package/lib/session/lifecycle.js +31 -96
- package/lib/session/load.js +51 -58
- package/lib/session/navigation.js +105 -96
- package/lib/session/npm.js +158 -77
- package/lib/session/ops.js +192 -51
- package/lib/session/pointer.js +11 -5
- package/lib/session/progress.js +9 -6
- package/lib/session/review.js +35 -54
- package/lib/session/tasks.js +292 -0
- package/lib/session/terminal.js +13 -17
- package/lib/session/unit.js +3 -4
- package/lib/session/update.js +76 -97
- package/lib/session/view.js +17 -14
- package/lib/session/watch.js +47 -27
- package/lib/session.js +34 -79
- package/lib/update.js +1 -5
- package/lib/utilities.js +21 -1
- package/lib/wrap.js +15 -0
- package/package.json +1 -1
- package/lib/session/todos.js +0 -293
package/lib/session/compose.js
CHANGED
|
@@ -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,
|
|
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('./
|
|
24
|
-
const {
|
|
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', '
|
|
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
|
-
|
|
56
|
+
tasks: {
|
|
56
57
|
enter: 'edit-next',
|
|
57
58
|
tab: 'template',
|
|
58
59
|
escape: 'save',
|
|
59
|
-
move: '
|
|
60
|
+
move: 'tasks',
|
|
60
61
|
command: null,
|
|
61
|
-
view: '
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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 ||
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
303
|
-
api.ui.collection.keepOrigin(item, 'staged');
|
|
304
|
-
}
|
|
290
|
+
api.ui.collection.keepOrigin(item, 'staged');
|
|
305
291
|
staged += 1;
|
|
306
292
|
}
|
|
307
|
-
if (staged
|
|
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
|
|
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 === '
|
|
347
|
-
api.
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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 ||
|
|
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 === '
|
|
520
|
+
if (move === 'tasks') {
|
|
527
521
|
const size = viewSize(api);
|
|
528
522
|
const inner = todoBodyWidth(size.width);
|
|
529
|
-
return void api.
|
|
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 ||
|
|
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
|
-
|
|
699
|
-
|
|
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
|
|
797
|
+
const createComposer = ({ nav, review, ui }) => {
|
|
752
798
|
const state = initialState();
|
|
753
|
-
const api = { nav, review, ui,
|
|
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.
|
|
805
|
+
api.tasks = createTasksCompose(api);
|
|
761
806
|
const composer = {
|
|
762
807
|
state,
|
|
763
808
|
...composerMethods(api),
|
|
764
|
-
...api.
|
|
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
|
};
|
package/lib/session/dispatch.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
72
|
-
|
|
74
|
+
check: () => ui.composer.toggleFocusedTask(),
|
|
75
|
+
removeTask: () => {
|
|
73
76
|
if (ui.pane === 'npm') return void ui.npm.askDrop();
|
|
74
|
-
ui.composer.
|
|
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
|
-
|
|
157
|
-
|
|
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.
|
|
171
|
-
if (ui.composer.
|
|
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 };
|