reslop 0.2.1 → 0.2.3
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 +11 -1
- package/lib/ansi.js +120 -4
- package/lib/cli.js +3 -0
- package/lib/deps.js +43 -3
- package/lib/git-branches.js +47 -15
- package/lib/keys.js +10 -4
- package/lib/npm-commands.js +513 -0
- package/lib/render/branches.js +21 -28
- package/lib/render/chrome.js +137 -122
- package/lib/render/commits.js +604 -54
- package/lib/render/frame.js +38 -4
- package/lib/render/npm.js +228 -0
- package/lib/render/primitives.js +7 -36
- package/lib/report-model.js +127 -0
- package/lib/report-parse.js +94 -0
- package/lib/report-render.js +87 -0
- package/lib/report-run.js +164 -0
- package/lib/report-test.js +506 -0
- package/lib/report-tools.js +311 -0
- package/lib/session/actions.js +51 -2
- package/lib/session/commit.js +94 -19
- package/lib/session/compose.js +48 -5
- package/lib/session/dispatch.js +30 -2
- package/lib/session/files-pane.js +20 -4
- package/lib/session/lifecycle.js +4 -1
- package/lib/session/navigation.js +1 -0
- package/lib/session/npm.js +335 -0
- package/lib/session/pointer.js +8 -0
- package/lib/session/view.js +14 -3
- package/lib/session.js +10 -2
- package/package.json +6 -6
package/lib/session/compose.js
CHANGED
|
@@ -4,6 +4,8 @@ const wrap = require('../wrap.js');
|
|
|
4
4
|
const { cursorInWrap, wrapDoc } = wrap;
|
|
5
5
|
const render = require('../render/render.js');
|
|
6
6
|
const { noteInnerWidth, codeInnerWidth, todoBodyWidth } = render;
|
|
7
|
+
const { commitMessageWidth } = require('../render/commits.js');
|
|
8
|
+
const { listedCommits } = require('./commit.js');
|
|
7
9
|
const { Editor } = require('../editor.js');
|
|
8
10
|
const files = require('../files.js');
|
|
9
11
|
const { itemPath, isTodoItem, listPath, isReadOnlyOrigin } = files;
|
|
@@ -68,12 +70,21 @@ const KIND = {
|
|
|
68
70
|
command: 'branch',
|
|
69
71
|
view: 'note',
|
|
70
72
|
},
|
|
73
|
+
npm: {
|
|
74
|
+
enter: 'save',
|
|
75
|
+
tab: 'insert-tab',
|
|
76
|
+
escape: 'cancel',
|
|
77
|
+
move: 'note',
|
|
78
|
+
command: 'npm',
|
|
79
|
+
view: 'note',
|
|
80
|
+
},
|
|
71
81
|
};
|
|
72
82
|
|
|
73
83
|
const initialState = () => ({
|
|
74
84
|
editor: null,
|
|
75
85
|
composeKind: null,
|
|
76
86
|
commitKind: null,
|
|
87
|
+
commitBody: '',
|
|
77
88
|
composeTodoId: null,
|
|
78
89
|
composeBaseline: '',
|
|
79
90
|
templateIndex: -1,
|
|
@@ -140,6 +151,7 @@ const closeCompose = (api) => {
|
|
|
140
151
|
state.editor = null;
|
|
141
152
|
state.composeKind = null;
|
|
142
153
|
state.commitKind = null;
|
|
154
|
+
state.commitBody = '';
|
|
143
155
|
state.composeTodoId = null;
|
|
144
156
|
state.composeBaseline = '';
|
|
145
157
|
state.fileOpened = '';
|
|
@@ -474,6 +486,22 @@ const moveNoteLine = (api, delta) => {
|
|
|
474
486
|
api.state.editor.moveLine(delta, width);
|
|
475
487
|
};
|
|
476
488
|
|
|
489
|
+
const fullCommitEdit = (api) =>
|
|
490
|
+
api.state.composeKind === 'commit' && api.ui.commits.commitView === 'full';
|
|
491
|
+
|
|
492
|
+
const editedCommitEntry = (api) => {
|
|
493
|
+
const kind = api.state.commitKind;
|
|
494
|
+
if (kind !== 'amend' && kind !== 'reword') return null;
|
|
495
|
+
const list = listedCommits(api.ui.commits.commits);
|
|
496
|
+
return list[api.nav.commitCursor] ?? null;
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
const moveCommitLine = (api, delta) => {
|
|
500
|
+
const size = viewSize(api);
|
|
501
|
+
const width = commitMessageWidth(editedCommitEntry(api), size.width);
|
|
502
|
+
api.state.editor.moveLine(delta, width);
|
|
503
|
+
};
|
|
504
|
+
|
|
477
505
|
const moveComposeLine = (api, delta) => {
|
|
478
506
|
const spec = KIND[api.state.composeKind];
|
|
479
507
|
const move = spec ? spec.move : 'note';
|
|
@@ -487,6 +515,7 @@ const moveComposeLine = (api, delta) => {
|
|
|
487
515
|
const inner = codeInnerWidth(size.width, api.ui.layout);
|
|
488
516
|
return void api.state.editor.moveLine(delta, inner);
|
|
489
517
|
}
|
|
518
|
+
if (fullCommitEdit(api)) return void moveCommitLine(api, delta);
|
|
490
519
|
moveNoteLine(api, delta);
|
|
491
520
|
};
|
|
492
521
|
|
|
@@ -575,17 +604,31 @@ const onCode = (api) => {
|
|
|
575
604
|
openCompose(api, 'code', text);
|
|
576
605
|
};
|
|
577
606
|
|
|
607
|
+
const insertNewline = (api) => {
|
|
608
|
+
api.state.editor.insert('\n');
|
|
609
|
+
resetBlink(api);
|
|
610
|
+
followFileEdit(api);
|
|
611
|
+
return null;
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
const onEmptyLastLine = (editor) => {
|
|
615
|
+
if (!editor) return false;
|
|
616
|
+
const text = editor.text;
|
|
617
|
+
const lastBreak = text.lastIndexOf('\n');
|
|
618
|
+
if (editor.cursor < lastBreak + 1) return false;
|
|
619
|
+
return text.slice(lastBreak + 1).trim() === '';
|
|
620
|
+
};
|
|
621
|
+
|
|
578
622
|
const enterCompose = (api) => {
|
|
579
623
|
const { state } = api;
|
|
580
624
|
if (state.templateFocus && state.composeKind === 'feedback') {
|
|
581
625
|
return api.templates.selectTemplate(state.templateIndex);
|
|
582
626
|
}
|
|
583
627
|
const spec = KIND[state.composeKind];
|
|
584
|
-
if (spec && spec.enter === 'newline')
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
return null;
|
|
628
|
+
if (spec && spec.enter === 'newline') return insertNewline(api);
|
|
629
|
+
if (fullCommitEdit(api)) {
|
|
630
|
+
if (onEmptyLastLine(state.editor)) return saveCompose(api);
|
|
631
|
+
return insertNewline(api);
|
|
589
632
|
}
|
|
590
633
|
if (spec && spec.enter === 'edit-next') {
|
|
591
634
|
return saveCompose(api, { editNext: true });
|
package/lib/session/dispatch.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const compose = require('./compose.js');
|
|
4
|
+
const { listedCommits } = require('./commit.js');
|
|
4
5
|
const { COMPOSE_LEAVE } = compose;
|
|
5
6
|
const actions = require('./actions.js');
|
|
6
7
|
const { CONFIRM_MODE, disabledActions, actionFromKey, applyConfirm } = actions;
|
|
@@ -16,7 +17,7 @@ const currentTarget = (ui) => {
|
|
|
16
17
|
return ui.branches[ui.branchCursor];
|
|
17
18
|
}
|
|
18
19
|
if (ui.pane === 'commits') {
|
|
19
|
-
const entry = ui.commits.commits[ui.commitCursor];
|
|
20
|
+
const entry = listedCommits(ui.commits.commits)[ui.commitCursor];
|
|
20
21
|
const staged = targetHasStaged(ui);
|
|
21
22
|
if (!entry) return { canCommit: staged };
|
|
22
23
|
return { ...entry, canCommit: staged };
|
|
@@ -43,6 +44,7 @@ const createDispatcher = (ui) => {
|
|
|
43
44
|
reword: ui.commits.onReword,
|
|
44
45
|
apply: ui.commits.onApply,
|
|
45
46
|
fixup: ui.commits.onFixup,
|
|
47
|
+
view: () => ui.commits.onView(),
|
|
46
48
|
branch: ui.gitBranches.onBranch,
|
|
47
49
|
pull: ui.gitBranches.onPull,
|
|
48
50
|
push: ui.gitBranches.onPush,
|
|
@@ -57,8 +59,16 @@ const createDispatcher = (ui) => {
|
|
|
57
59
|
open: ui.filesPane.onOpenFile,
|
|
58
60
|
file: ui.filesPane.onFileScope,
|
|
59
61
|
diff: ui.filesPane.onDiffScope,
|
|
62
|
+
npm: ui.npm.open,
|
|
63
|
+
npmEdit: ui.npm.edit,
|
|
64
|
+
npmNew: ui.npm.create,
|
|
65
|
+
npmStop: () => ui.npm.closeView(),
|
|
66
|
+
npmRerun: () => ui.npm.rerun(),
|
|
60
67
|
check: () => ui.composer.toggleFocusedTodo(),
|
|
61
|
-
removeTodo:
|
|
68
|
+
removeTodo: () => {
|
|
69
|
+
if (ui.pane === 'npm') return void ui.npm.askDrop();
|
|
70
|
+
ui.composer.removeFocusedTodo();
|
|
71
|
+
},
|
|
62
72
|
quit: () => ui.onQuit(),
|
|
63
73
|
scrollUp: () => ui.filesPane.onScroll(-1),
|
|
64
74
|
scrollDown: () => ui.filesPane.onScroll(1),
|
|
@@ -90,6 +100,10 @@ const createDispatcher = (ui) => {
|
|
|
90
100
|
if (action === 'next') return void ui.commits.onCommitMove(1);
|
|
91
101
|
if (action === 'prev') return void ui.commits.onCommitMove(-1);
|
|
92
102
|
}
|
|
103
|
+
if (pane === 'npm') {
|
|
104
|
+
if (action === 'next') return void ui.npm.move(1);
|
|
105
|
+
if (action === 'prev') return void ui.npm.move(-1);
|
|
106
|
+
}
|
|
93
107
|
const hidden = disabledActions(pane, currentTarget(ui));
|
|
94
108
|
if (hidden.includes(action)) return;
|
|
95
109
|
const handler = handlers[action];
|
|
@@ -113,12 +127,14 @@ const createInput = (ui) => {
|
|
|
113
127
|
},
|
|
114
128
|
confirmDrop: {
|
|
115
129
|
confirm: () => {
|
|
130
|
+
if (ui.npm.dropName) return void ui.npm.confirmDrop();
|
|
116
131
|
if (ui.commits.dropName) {
|
|
117
132
|
return void ui.commits.confirmDropCommit();
|
|
118
133
|
}
|
|
119
134
|
ui.gitBranches.confirmDropBranch();
|
|
120
135
|
},
|
|
121
136
|
cancel: () => {
|
|
137
|
+
if (ui.npm.dropName) return void ui.npm.cancelDrop();
|
|
122
138
|
if (ui.commits.dropName) {
|
|
123
139
|
return void ui.commits.cancelDropCommit();
|
|
124
140
|
}
|
|
@@ -145,6 +161,18 @@ const createInput = (ui) => {
|
|
|
145
161
|
const command = ui.composer.handleKey(key);
|
|
146
162
|
return void ui.runComposeCommand(command);
|
|
147
163
|
}
|
|
164
|
+
if (ui.pane === 'npm' && ui.npm.viewing) {
|
|
165
|
+
if (key === 'escape') return void ui.npm.closeView();
|
|
166
|
+
if (key === 'r') return void ui.npm.rerun();
|
|
167
|
+
if (key === 'e' || key === 'n') return;
|
|
168
|
+
}
|
|
169
|
+
if (ui.pane === 'npm' && key === 'ctrl-up') return void ui.npm.reorder(-1);
|
|
170
|
+
if (ui.pane === 'npm' && key === 'ctrl-down') {
|
|
171
|
+
return void ui.npm.reorder(1);
|
|
172
|
+
}
|
|
173
|
+
if (ui.pane === 'npm' && (key === 'up' || key === 'down')) {
|
|
174
|
+
return void ui.npm.move(key === 'up' ? -1 : 1);
|
|
175
|
+
}
|
|
148
176
|
if (key === 'escape') return void ui.filesPane.onEscape();
|
|
149
177
|
const toggle = key === 'x' || key === ' ';
|
|
150
178
|
if (toggle && ui.composer.toggleFocusedTodo()) return;
|
|
@@ -4,6 +4,7 @@ const ansi = require('../ansi.js');
|
|
|
4
4
|
const files = require('../files.js');
|
|
5
5
|
const { THEME_NAMES, setTheme, themeName } = ansi;
|
|
6
6
|
const { isTodosEntry, TODO_FILE } = files;
|
|
7
|
+
const { logViewRows } = require('../render/npm.js');
|
|
7
8
|
const unit = require('./unit.js');
|
|
8
9
|
const { setFileScope, openUnitFile, moveUnitBlock } = unit;
|
|
9
10
|
|
|
@@ -28,7 +29,10 @@ const syncFileCursor = (ui) => {
|
|
|
28
29
|
const showFiles = (ui) => {
|
|
29
30
|
const nav = ui.nav;
|
|
30
31
|
const held =
|
|
31
|
-
nav.todoOpen === true ||
|
|
32
|
+
nav.todoOpen === true ||
|
|
33
|
+
nav.pane === 'branches' ||
|
|
34
|
+
nav.pane === 'commits' ||
|
|
35
|
+
nav.pane === 'npm';
|
|
32
36
|
nav.pane = 'files';
|
|
33
37
|
nav.clearSelection();
|
|
34
38
|
followReviewPath(ui);
|
|
@@ -37,7 +41,11 @@ const showFiles = (ui) => {
|
|
|
37
41
|
};
|
|
38
42
|
|
|
39
43
|
const onEscape = (ui) => {
|
|
40
|
-
if (
|
|
44
|
+
if (
|
|
45
|
+
ui.nav.pane === 'branches' ||
|
|
46
|
+
ui.nav.pane === 'commits' ||
|
|
47
|
+
ui.nav.pane === 'npm'
|
|
48
|
+
) {
|
|
41
49
|
return void showFiles(ui);
|
|
42
50
|
}
|
|
43
51
|
if (ui.nav.pane === 'files') return void ui.onQuit();
|
|
@@ -65,7 +73,8 @@ const onOpenFile = (ui) => {
|
|
|
65
73
|
if (ui.nav.pane === 'branches') {
|
|
66
74
|
return void ui.gitBranches.onCheckoutBranch();
|
|
67
75
|
}
|
|
68
|
-
if (ui.nav.pane === 'commits') return;
|
|
76
|
+
if (ui.nav.pane === 'commits') return void ui.commits.onShowDiff();
|
|
77
|
+
if (ui.nav.pane === 'npm') return void ui.npm.run();
|
|
69
78
|
if (ui.nav.pane !== 'files') {
|
|
70
79
|
if (ui.nav.todoOpen) ui.composer.editFocusedTodo();
|
|
71
80
|
return;
|
|
@@ -152,7 +161,9 @@ const scrollStep = (ui, fraction) => {
|
|
|
152
161
|
const rows = size.height ?? 24;
|
|
153
162
|
const fallback = Math.max(1, rows - 4);
|
|
154
163
|
const bodyH = frame?.bodyH ?? fallback;
|
|
155
|
-
const
|
|
164
|
+
const viewing = ui.nav.pane === 'npm' && ui.npm && ui.npm.viewing;
|
|
165
|
+
const viewRows = viewing ? logViewRows(bodyH) : bodyH;
|
|
166
|
+
const height = Math.max(1, viewRows);
|
|
156
167
|
const span = Math.floor(height * Math.abs(fraction));
|
|
157
168
|
const n = Math.max(1, span);
|
|
158
169
|
if (fraction < 0) return -n;
|
|
@@ -167,6 +178,7 @@ const onScroll = (ui, delta) => {
|
|
|
167
178
|
if (ui.nav.pane === 'commits') {
|
|
168
179
|
return void ui.commits.onCommitMove(delta);
|
|
169
180
|
}
|
|
181
|
+
if (ui.nav.pane === 'npm') return void ui.npm.move(delta);
|
|
170
182
|
if (ui.nav.todoOpen && ui.composer.todoRowCount() > 1) {
|
|
171
183
|
return void ui.composer.moveTodoFocus(delta);
|
|
172
184
|
}
|
|
@@ -198,6 +210,10 @@ const onJump = (ui, toEnd) => {
|
|
|
198
210
|
const delta = toEnd ? Number.MAX_SAFE_INTEGER : -Number.MAX_SAFE_INTEGER;
|
|
199
211
|
return void ui.commits.onCommitMove(delta);
|
|
200
212
|
}
|
|
213
|
+
if (ui.nav.pane === 'npm') {
|
|
214
|
+
const delta = toEnd ? Number.MAX_SAFE_INTEGER : -Number.MAX_SAFE_INTEGER;
|
|
215
|
+
return void ui.npm.move(delta);
|
|
216
|
+
}
|
|
201
217
|
if (ui.nav.todoOpen && ui.composer.todoRowCount() > 0) {
|
|
202
218
|
const last = ui.composer.todoRowCount() - 1;
|
|
203
219
|
const target = toEnd ? last : 0;
|
package/lib/session/lifecycle.js
CHANGED
|
@@ -485,7 +485,10 @@ const worktreeStamp = (api) => {
|
|
|
485
485
|
};
|
|
486
486
|
|
|
487
487
|
const watchesWorktree = (nav) =>
|
|
488
|
-
nav.todoOpen === true ||
|
|
488
|
+
nav.todoOpen === true ||
|
|
489
|
+
nav.pane === 'branches' ||
|
|
490
|
+
nav.pane === 'commits' ||
|
|
491
|
+
nav.pane === 'npm';
|
|
489
492
|
|
|
490
493
|
const currentFileStamp = (api) => {
|
|
491
494
|
const { nav } = api;
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const commands = require('../npm-commands.js');
|
|
4
|
+
const { logViewRows } = require('../render/npm.js');
|
|
5
|
+
|
|
6
|
+
const { listCommands, reduceOutput, writeLog, parseScriptLine } = commands;
|
|
7
|
+
const { scriptLine, saveScript, removeScript, reorderScript } = commands;
|
|
8
|
+
const { startNpm } = commands;
|
|
9
|
+
|
|
10
|
+
const selected = (api) => {
|
|
11
|
+
const cursor = api.ui.nav.npmCursor;
|
|
12
|
+
return api.state.commands[cursor];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const clampCursor = (api) => {
|
|
16
|
+
const last = api.state.commands.length - 1;
|
|
17
|
+
const nav = api.ui.nav;
|
|
18
|
+
if (nav.npmCursor > last) nav.npmCursor = Math.max(0, last);
|
|
19
|
+
if (nav.npmCursor < 0) nav.npmCursor = 0;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const refresh = (api) => {
|
|
23
|
+
api.state.commands = listCommands(api.ui.top);
|
|
24
|
+
clampCursor(api);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const canEdit = (ui) => {
|
|
28
|
+
if (ui.capabilities && ui.capabilities.changes === false) return false;
|
|
29
|
+
if (ui.readOnly) return false;
|
|
30
|
+
return true;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const open = (api) => {
|
|
34
|
+
if (api.ui.mode === 'compose') return;
|
|
35
|
+
refresh(api);
|
|
36
|
+
api.state.viewing = false;
|
|
37
|
+
api.state.output = '';
|
|
38
|
+
api.ui.nav.pane = 'npm';
|
|
39
|
+
api.ui.status = '';
|
|
40
|
+
api.ui.nav.clearSelection();
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const scrollLog = (api, delta) => {
|
|
44
|
+
const lines = api.state.output.split('\n');
|
|
45
|
+
let count = lines.at(-1) === '' ? lines.length - 1 : lines.length;
|
|
46
|
+
if (api.state.running) count += 1;
|
|
47
|
+
const frame = api.ui.lastFrame;
|
|
48
|
+
const bodyH = frame && frame.bodyH ? frame.bodyH : 1;
|
|
49
|
+
const max = Math.max(0, count - logViewRows(bodyH));
|
|
50
|
+
let start = api.state.followEnd ? max : api.state.logScroll;
|
|
51
|
+
start = Math.min(max, Math.max(0, start + delta));
|
|
52
|
+
api.state.followEnd = start === max;
|
|
53
|
+
api.state.logScroll = start;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const move = (api, delta) => {
|
|
57
|
+
if (api.state.viewing) return void scrollLog(api, delta);
|
|
58
|
+
if (!api.state.commands.length) return;
|
|
59
|
+
const nav = api.ui.nav;
|
|
60
|
+
const last = api.state.commands.length - 1;
|
|
61
|
+
const next = Math.min(last, Math.max(0, nav.npmCursor + delta));
|
|
62
|
+
if (next === nav.npmCursor) return;
|
|
63
|
+
nav.npmCursor = next;
|
|
64
|
+
api.ui.status = '';
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const stopRun = (api) => {
|
|
68
|
+
api.state.running = false;
|
|
69
|
+
api.state.child = null;
|
|
70
|
+
if (api.ui.progress) api.ui.progress.stop('npm');
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const showOutput = (api, text) => {
|
|
74
|
+
if (!api.state.viewing || !api.state.running) return;
|
|
75
|
+
if (api.state.stopping) return;
|
|
76
|
+
api.state.output = text;
|
|
77
|
+
api.ui.paint();
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const withNotice = (text, notice) => {
|
|
81
|
+
const body = `${text ?? ''}`;
|
|
82
|
+
if (body.endsWith(`${notice}\n`)) return body;
|
|
83
|
+
if (!body) return `${notice}\n`;
|
|
84
|
+
if (body.endsWith('\n')) return `${body}${notice}\n`;
|
|
85
|
+
return `${body}\n${notice}\n`;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const finishRun = (api, entry, result) => {
|
|
89
|
+
const stopped = api.state.stopping === true;
|
|
90
|
+
const reduced = stopped
|
|
91
|
+
? withNotice(api.state.output, 'terminated')
|
|
92
|
+
: reduceOutput(result.text, api.ui.top, result.status);
|
|
93
|
+
api.state.output = reduced;
|
|
94
|
+
api.state.stopping = false;
|
|
95
|
+
stopRun(api);
|
|
96
|
+
try {
|
|
97
|
+
writeLog(api.ui.top, entry.name, reduced);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
api.ui.status = error.message;
|
|
100
|
+
api.ui.paint();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (stopped && api.state.viewing) api.ui.status = 'terminated';
|
|
104
|
+
api.ui.paint();
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const startRun = (api, entry) => {
|
|
108
|
+
api.state.output = '';
|
|
109
|
+
api.state.viewing = true;
|
|
110
|
+
api.state.followEnd = true;
|
|
111
|
+
api.state.logScroll = 0;
|
|
112
|
+
api.state.running = true;
|
|
113
|
+
api.state.stopping = false;
|
|
114
|
+
api.ui.status = '';
|
|
115
|
+
if (api.ui.progress) api.ui.progress.start('npm');
|
|
116
|
+
const runner = api.ui.repo && api.ui.repo.runNpmCommand;
|
|
117
|
+
const start = typeof runner === 'function' ? runner : startNpm;
|
|
118
|
+
api.state.child = start(
|
|
119
|
+
api.ui.top,
|
|
120
|
+
entry,
|
|
121
|
+
(text) => showOutput(api, reduceOutput(text, api.ui.top, '')),
|
|
122
|
+
(result) => finishRun(api, entry, result),
|
|
123
|
+
);
|
|
124
|
+
api.ui.paint();
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const runSelected = (api) => {
|
|
128
|
+
if (api.state.viewing || api.state.child) return;
|
|
129
|
+
const entry = selected(api);
|
|
130
|
+
if (!entry) return;
|
|
131
|
+
startRun(api, entry);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const rerun = (api) => {
|
|
135
|
+
if (!api.state.viewing || api.state.running) return;
|
|
136
|
+
const entry = selected(api);
|
|
137
|
+
if (!entry) return;
|
|
138
|
+
startRun(api, entry);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const closeView = (api) => {
|
|
142
|
+
if (api.state.running) {
|
|
143
|
+
api.state.stopping = true;
|
|
144
|
+
if (api.state.child) api.state.child.kill();
|
|
145
|
+
if (!api.state.running) return;
|
|
146
|
+
api.state.output = withNotice(api.state.output, 'terminated');
|
|
147
|
+
stopRun(api);
|
|
148
|
+
api.ui.status = 'terminated';
|
|
149
|
+
api.ui.paint();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
api.state.viewing = false;
|
|
153
|
+
api.ui.status = '';
|
|
154
|
+
api.ui.paint();
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const editSelected = (api) => {
|
|
158
|
+
if (api.state.viewing) return;
|
|
159
|
+
if (!canEdit(api.ui)) {
|
|
160
|
+
api.ui.status = 'read only';
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const entry = selected(api);
|
|
164
|
+
if (!entry || entry.kind !== 'script') {
|
|
165
|
+
api.ui.status = 'not a script';
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
api.state.editName = entry.name;
|
|
169
|
+
api.ui.composer.openCompose('npm', scriptLine(entry));
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const newCommand = (api) => {
|
|
173
|
+
if (api.state.viewing) return;
|
|
174
|
+
if (!canEdit(api.ui)) {
|
|
175
|
+
api.ui.status = 'read only';
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
api.state.editName = '';
|
|
179
|
+
api.ui.composer.openCompose('npm', '');
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const finishEdit = (api) => {
|
|
183
|
+
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';
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
api.ui.ignoreWatch();
|
|
192
|
+
saveScript(api.ui.top, api.state.editName, parsed);
|
|
193
|
+
} catch (error) {
|
|
194
|
+
api.ui.status = error.message;
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
api.ui.composer.closeCompose();
|
|
198
|
+
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;
|
|
203
|
+
api.ui.status = 'saved';
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
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
|
+
}
|
|
217
|
+
api.state.dropName = entry.name;
|
|
218
|
+
api.ui.mode = 'confirmDrop';
|
|
219
|
+
api.ui.status = '';
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const confirmDrop = (api) => {
|
|
223
|
+
const name = api.state.dropName;
|
|
224
|
+
api.state.dropName = '';
|
|
225
|
+
api.ui.mode = 'review';
|
|
226
|
+
if (!name) return;
|
|
227
|
+
try {
|
|
228
|
+
api.ui.ignoreWatch();
|
|
229
|
+
removeScript(api.ui.top, name);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
api.ui.status = error.message;
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
refresh(api);
|
|
235
|
+
api.ui.status = `dropped ${name}`;
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const cancelDrop = (api) => {
|
|
239
|
+
api.state.dropName = '';
|
|
240
|
+
api.ui.mode = 'review';
|
|
241
|
+
api.ui.status = '';
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
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
|
+
}
|
|
255
|
+
try {
|
|
256
|
+
api.ui.ignoreWatch();
|
|
257
|
+
if (!reorderScript(api.ui.top, entry.name, delta)) return;
|
|
258
|
+
} catch (error) {
|
|
259
|
+
api.ui.status = error.message;
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
refresh(api);
|
|
263
|
+
const at = api.state.commands.findIndex((item) => item.name === entry.name);
|
|
264
|
+
if (at >= 0) api.ui.nav.npmCursor = at;
|
|
265
|
+
api.ui.status = '';
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
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
|
+
};
|
|
281
|
+
const api = { ui, state };
|
|
282
|
+
return {
|
|
283
|
+
state,
|
|
284
|
+
get commands() {
|
|
285
|
+
return state.commands;
|
|
286
|
+
},
|
|
287
|
+
get viewing() {
|
|
288
|
+
return state.viewing;
|
|
289
|
+
},
|
|
290
|
+
get output() {
|
|
291
|
+
return state.output;
|
|
292
|
+
},
|
|
293
|
+
get dropName() {
|
|
294
|
+
return state.dropName;
|
|
295
|
+
},
|
|
296
|
+
get followEnd() {
|
|
297
|
+
return state.followEnd;
|
|
298
|
+
},
|
|
299
|
+
get logScroll() {
|
|
300
|
+
return state.logScroll;
|
|
301
|
+
},
|
|
302
|
+
get editName() {
|
|
303
|
+
return state.editName;
|
|
304
|
+
},
|
|
305
|
+
get running() {
|
|
306
|
+
return state.running;
|
|
307
|
+
},
|
|
308
|
+
reset() {
|
|
309
|
+
if (state.child) state.child.kill();
|
|
310
|
+
stopRun(api);
|
|
311
|
+
state.commands = [];
|
|
312
|
+
state.viewing = false;
|
|
313
|
+
state.output = '';
|
|
314
|
+
state.followEnd = true;
|
|
315
|
+
state.logScroll = 0;
|
|
316
|
+
state.editName = '';
|
|
317
|
+
state.dropName = '';
|
|
318
|
+
state.stopping = false;
|
|
319
|
+
},
|
|
320
|
+
open: () => open(api),
|
|
321
|
+
move: (delta) => move(api, delta),
|
|
322
|
+
run: () => runSelected(api),
|
|
323
|
+
rerun: () => rerun(api),
|
|
324
|
+
closeView: () => closeView(api),
|
|
325
|
+
edit: () => editSelected(api),
|
|
326
|
+
create: () => newCommand(api),
|
|
327
|
+
finishEdit: () => finishEdit(api),
|
|
328
|
+
askDrop: () => askDrop(api),
|
|
329
|
+
confirmDrop: () => confirmDrop(api),
|
|
330
|
+
cancelDrop: () => cancelDrop(api),
|
|
331
|
+
reorder: (delta) => reorder(api, delta),
|
|
332
|
+
};
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
module.exports = { createNpmController };
|
package/lib/session/pointer.js
CHANGED
|
@@ -99,9 +99,17 @@ const RELEASE_TARGETS = [
|
|
|
99
99
|
match: (ui) => ui.pane === 'commits',
|
|
100
100
|
hits: 'fileHits',
|
|
101
101
|
onHit: (ui, hit) => {
|
|
102
|
+
if (ui.mode === 'compose') return;
|
|
102
103
|
ui.nav.commitCursor = hit.cursor;
|
|
103
104
|
},
|
|
104
105
|
},
|
|
106
|
+
{
|
|
107
|
+
match: (ui) => ui.pane === 'npm',
|
|
108
|
+
hits: 'fileHits',
|
|
109
|
+
onHit: (ui, hit) => {
|
|
110
|
+
ui.nav.npmCursor = hit.cursor;
|
|
111
|
+
},
|
|
112
|
+
},
|
|
105
113
|
{
|
|
106
114
|
match: (ui) => ui.pane === 'diff',
|
|
107
115
|
hits: 'todoHits',
|
package/lib/session/view.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const path = require('node:path');
|
|
4
4
|
|
|
5
5
|
const files = require('../files.js');
|
|
6
|
+
const { listedCommits } = require('./commit.js');
|
|
6
7
|
const { fileEntries, REPO_TODOS_LABEL } = files;
|
|
7
8
|
const review = require('../review.js');
|
|
8
9
|
const { noteCounts } = review;
|
|
@@ -143,7 +144,7 @@ const unitBodyLines = (api) => {
|
|
|
143
144
|
};
|
|
144
145
|
|
|
145
146
|
const view = (api) => {
|
|
146
|
-
const { composer, nav, ui, gitBranches, commits, updater } = api;
|
|
147
|
+
const { composer, nav, ui, gitBranches, commits, npm, updater } = api;
|
|
147
148
|
const templates = composer.shownTemplates();
|
|
148
149
|
const change = ui.change;
|
|
149
150
|
const sourceKind = (change && change.source) || '';
|
|
@@ -169,9 +170,19 @@ const view = (api) => {
|
|
|
169
170
|
branch: ui.branch,
|
|
170
171
|
branches: gitBranches.branches,
|
|
171
172
|
branchCursor: nav.branchCursor,
|
|
172
|
-
commits: commits.commits,
|
|
173
|
+
commits: listedCommits(commits.commits),
|
|
173
174
|
commitCursor: nav.commitCursor,
|
|
175
|
+
commitView: commits.commitView,
|
|
176
|
+
npmCommands: npm.commands,
|
|
177
|
+
npmCursor: nav.npmCursor,
|
|
178
|
+
npmView: npm.viewing,
|
|
179
|
+
npmOutput: npm.output,
|
|
180
|
+
npmFollow: npm.followEnd,
|
|
181
|
+
npmScroll: npm.logScroll,
|
|
182
|
+
npmEditName: npm.editName,
|
|
183
|
+
npmRunning: npm.running,
|
|
174
184
|
repoName: repoNameOf(api),
|
|
185
|
+
rev: ui.rev || '',
|
|
175
186
|
revShort: ui.revShort || '',
|
|
176
187
|
sourceLabel: ui.sourceLabel || '',
|
|
177
188
|
sourceKind,
|
|
@@ -186,7 +197,7 @@ const view = (api) => {
|
|
|
186
197
|
templateIndex: composer.clampedTemplateIndex(templates),
|
|
187
198
|
updateFrom: updater.from,
|
|
188
199
|
updateTo: updater.to,
|
|
189
|
-
dropName: gitBranches.dropName || commits.dropName,
|
|
200
|
+
dropName: gitBranches.dropName || commits.dropName || npm.dropName,
|
|
190
201
|
};
|
|
191
202
|
};
|
|
192
203
|
|