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/npm.js
CHANGED
|
@@ -4,9 +4,11 @@ const commands = require('../npm-commands.js');
|
|
|
4
4
|
const npm = require('../render/npm.js');
|
|
5
5
|
const { logViewRows } = npm;
|
|
6
6
|
|
|
7
|
-
const { listCommands, reduceOutput, writeLog
|
|
7
|
+
const { listCommands, reduceOutput, writeLog } = commands;
|
|
8
8
|
const { staleLogFiles, removeStaleLogs, formatSize } = commands;
|
|
9
|
-
const {
|
|
9
|
+
const { saveScript, removeScript, reorderScript } = commands;
|
|
10
|
+
|
|
11
|
+
const NAME_RE = /^[\w:.-]+$/;
|
|
10
12
|
const { startNpm } = commands;
|
|
11
13
|
|
|
12
14
|
const selected = (api) => {
|
|
@@ -48,9 +50,11 @@ const open = (api) => {
|
|
|
48
50
|
refreshLogs(api);
|
|
49
51
|
api.state.viewing = false;
|
|
50
52
|
api.state.output = '';
|
|
51
|
-
api.ui.nav
|
|
53
|
+
const nav = api.ui.nav;
|
|
54
|
+
nav.pane = 'npm';
|
|
55
|
+
nav.resetListScroll('npm');
|
|
52
56
|
api.ui.status = '';
|
|
53
|
-
|
|
57
|
+
nav.clearSelection();
|
|
54
58
|
};
|
|
55
59
|
|
|
56
60
|
const scrollLog = (api, delta) => {
|
|
@@ -80,13 +84,22 @@ const move = (api, delta) => {
|
|
|
80
84
|
const stopRun = (api) => {
|
|
81
85
|
api.state.running = false;
|
|
82
86
|
api.state.child = null;
|
|
83
|
-
|
|
87
|
+
api.ui.progress.stop('npm');
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const shownOutput = (api, raw, status) => {
|
|
91
|
+
if (!api.state.verbose) return reduceOutput(raw, api.ui.top, status);
|
|
92
|
+
const text = `${raw ?? ''}`;
|
|
93
|
+
if (!text) return '';
|
|
94
|
+
if (text.endsWith('\n')) return text;
|
|
95
|
+
return `${text}\n`;
|
|
84
96
|
};
|
|
85
97
|
|
|
86
98
|
const showOutput = (api, text) => {
|
|
87
99
|
if (!api.state.viewing || !api.state.running) return;
|
|
88
100
|
if (api.state.stopping) return;
|
|
89
|
-
api.state.
|
|
101
|
+
api.state.raw = text;
|
|
102
|
+
api.state.output = shownOutput(api, text, '');
|
|
90
103
|
api.ui.paint();
|
|
91
104
|
};
|
|
92
105
|
|
|
@@ -100,10 +113,13 @@ const withNotice = (text, notice) => {
|
|
|
100
113
|
|
|
101
114
|
const finishRun = (api, entry, result) => {
|
|
102
115
|
const stopped = api.state.stopping === true;
|
|
116
|
+
const raw = stopped ? withNotice(api.state.raw, 'terminated') : result.text;
|
|
103
117
|
const reduced = stopped
|
|
104
|
-
? withNotice(api.state.
|
|
118
|
+
? withNotice(reduceOutput(api.state.raw, api.ui.top, ''), 'terminated')
|
|
105
119
|
: reduceOutput(result.text, api.ui.top, result.status);
|
|
106
|
-
api.state.
|
|
120
|
+
api.state.raw = raw;
|
|
121
|
+
api.state.exitStatus = stopped ? '' : result.status;
|
|
122
|
+
api.state.output = api.state.verbose ? shownOutput(api, raw, '') : reduced;
|
|
107
123
|
api.state.stopping = false;
|
|
108
124
|
stopRun(api);
|
|
109
125
|
try {
|
|
@@ -119,21 +135,24 @@ const finishRun = (api, entry, result) => {
|
|
|
119
135
|
|
|
120
136
|
const startRun = (api, entry) => {
|
|
121
137
|
api.state.output = '';
|
|
138
|
+
api.state.raw = '';
|
|
139
|
+
api.state.exitStatus = '';
|
|
122
140
|
api.state.viewing = true;
|
|
123
141
|
api.state.followEnd = true;
|
|
124
142
|
api.state.logScroll = 0;
|
|
125
143
|
api.state.running = true;
|
|
126
144
|
api.state.stopping = false;
|
|
127
145
|
api.ui.status = '';
|
|
128
|
-
|
|
146
|
+
api.ui.progress.start('npm');
|
|
129
147
|
const runner = api.ui.repo && api.ui.repo.runNpmCommand;
|
|
130
148
|
const start = typeof runner === 'function' ? runner : startNpm;
|
|
131
|
-
|
|
149
|
+
const child = start(
|
|
132
150
|
api.ui.top,
|
|
133
151
|
entry,
|
|
134
|
-
(text) => showOutput(api,
|
|
152
|
+
(text) => showOutput(api, text),
|
|
135
153
|
(result) => finishRun(api, entry, result),
|
|
136
154
|
);
|
|
155
|
+
if (api.state.running) api.state.child = child;
|
|
137
156
|
api.ui.paint();
|
|
138
157
|
};
|
|
139
158
|
|
|
@@ -141,6 +160,7 @@ const runSelected = (api) => {
|
|
|
141
160
|
if (api.state.viewing || api.state.child) return;
|
|
142
161
|
const entry = selected(api);
|
|
143
162
|
if (!entry) return;
|
|
163
|
+
api.state.verbose = false;
|
|
144
164
|
startRun(api, entry);
|
|
145
165
|
};
|
|
146
166
|
|
|
@@ -156,50 +176,106 @@ const closeView = (api) => {
|
|
|
156
176
|
api.state.stopping = true;
|
|
157
177
|
if (api.state.child) api.state.child.kill();
|
|
158
178
|
if (!api.state.running) return;
|
|
159
|
-
api.state.
|
|
179
|
+
api.state.raw = withNotice(api.state.raw, 'terminated');
|
|
180
|
+
api.state.output = api.state.verbose
|
|
181
|
+
? shownOutput(api, api.state.raw, '')
|
|
182
|
+
: withNotice(api.state.output, 'terminated');
|
|
160
183
|
stopRun(api);
|
|
161
184
|
api.ui.status = 'terminated';
|
|
162
185
|
api.ui.paint();
|
|
163
186
|
return;
|
|
164
187
|
}
|
|
165
188
|
api.state.viewing = false;
|
|
189
|
+
api.state.verbose = false;
|
|
166
190
|
api.ui.status = '';
|
|
167
191
|
api.ui.paint();
|
|
168
192
|
};
|
|
169
193
|
|
|
170
|
-
const
|
|
171
|
-
if (api.state.viewing) return;
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
194
|
+
const toggleVerbose = (api) => {
|
|
195
|
+
if (!api.state.viewing) return;
|
|
196
|
+
api.state.verbose = !api.state.verbose;
|
|
197
|
+
const status = api.state.running ? '' : api.state.exitStatus;
|
|
198
|
+
api.state.output = shownOutput(api, api.state.raw, status);
|
|
199
|
+
api.ui.status = api.state.verbose ? 'verbose' : 'filtered';
|
|
200
|
+
api.ui.paint();
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const editable = (api) => {
|
|
204
|
+
if (api.state.viewing) return false;
|
|
205
|
+
if (canEdit(api.ui)) return true;
|
|
206
|
+
api.ui.status = 'read only';
|
|
207
|
+
return false;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const selectedScript = (api) => {
|
|
211
|
+
if (!editable(api)) return null;
|
|
176
212
|
const entry = selected(api);
|
|
177
|
-
if (
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
213
|
+
if (entry && entry.kind === 'script') return entry;
|
|
214
|
+
api.ui.status = 'not a script';
|
|
215
|
+
return null;
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const focusScript = (api, name) => {
|
|
219
|
+
const at = api.state.commands.findIndex((entry) => entry.name === name);
|
|
220
|
+
if (at >= 0) api.ui.nav.npmCursor = at;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const editSelected = (api) => {
|
|
224
|
+
const entry = selectedScript(api);
|
|
225
|
+
if (!entry) return;
|
|
181
226
|
api.state.editName = entry.name;
|
|
182
|
-
api.
|
|
227
|
+
api.state.editField = 'name';
|
|
228
|
+
api.state.draftName = entry.name;
|
|
229
|
+
api.state.draftCommand = entry.command ?? '';
|
|
230
|
+
api.ui.composer.openCompose('npm', entry.name);
|
|
183
231
|
};
|
|
184
232
|
|
|
185
233
|
const newCommand = (api) => {
|
|
186
|
-
if (api
|
|
187
|
-
if (!canEdit(api.ui)) {
|
|
188
|
-
api.ui.status = 'read only';
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
234
|
+
if (!editable(api)) return;
|
|
191
235
|
api.state.editName = '';
|
|
236
|
+
api.state.editField = 'name';
|
|
237
|
+
api.state.draftName = '';
|
|
238
|
+
api.state.draftCommand = '';
|
|
192
239
|
api.ui.composer.openCompose('npm', '');
|
|
193
240
|
};
|
|
194
241
|
|
|
242
|
+
const switchField = (api, editor, field, text) => {
|
|
243
|
+
api.state.editField = field;
|
|
244
|
+
editor.replace(text);
|
|
245
|
+
api.ui.status = '';
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const finishName = (api, editor) => {
|
|
249
|
+
const name = editor.text.trim();
|
|
250
|
+
if (!name || !NAME_RE.test(name)) {
|
|
251
|
+
api.ui.status = 'name';
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
api.state.draftName = name;
|
|
255
|
+
switchField(api, editor, 'command', api.state.draftCommand);
|
|
256
|
+
return true;
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const focusField = (api, field) => {
|
|
260
|
+
const editor = api.ui.editor;
|
|
261
|
+
if (!editor) return false;
|
|
262
|
+
if (field === api.state.editField) return true;
|
|
263
|
+
if (field === 'command') return finishName(api, editor);
|
|
264
|
+
api.state.draftCommand = editor.text;
|
|
265
|
+
switchField(api, editor, 'name', api.state.draftName ?? '');
|
|
266
|
+
return true;
|
|
267
|
+
};
|
|
268
|
+
|
|
195
269
|
const finishEdit = (api) => {
|
|
196
270
|
const editor = api.ui.composer.state.editor;
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
271
|
+
if (!editor) return;
|
|
272
|
+
if (api.state.editField !== 'command') return void finishName(api, editor);
|
|
273
|
+
const command = editor.text.trim();
|
|
274
|
+
if (!command) {
|
|
275
|
+
api.ui.status = 'command';
|
|
201
276
|
return;
|
|
202
277
|
}
|
|
278
|
+
const parsed = { name: api.state.draftName, command };
|
|
203
279
|
try {
|
|
204
280
|
api.ui.ignoreWatch();
|
|
205
281
|
saveScript(api.ui.top, api.state.editName, parsed);
|
|
@@ -207,26 +283,16 @@ const finishEdit = (api) => {
|
|
|
207
283
|
api.ui.status = error.message;
|
|
208
284
|
return;
|
|
209
285
|
}
|
|
286
|
+
api.state.editField = '';
|
|
210
287
|
api.ui.composer.closeCompose();
|
|
211
288
|
refresh(api);
|
|
212
|
-
|
|
213
|
-
(entry) => entry.name === parsed.name,
|
|
214
|
-
);
|
|
215
|
-
if (at >= 0) api.ui.nav.npmCursor = at;
|
|
289
|
+
focusScript(api, parsed.name);
|
|
216
290
|
api.ui.status = 'saved';
|
|
217
291
|
};
|
|
218
292
|
|
|
219
293
|
const askDrop = (api) => {
|
|
220
|
-
|
|
221
|
-
if (!
|
|
222
|
-
api.ui.status = 'read only';
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
const entry = selected(api);
|
|
226
|
-
if (!entry || entry.kind !== 'script') {
|
|
227
|
-
api.ui.status = 'not a script';
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
294
|
+
const entry = selectedScript(api);
|
|
295
|
+
if (!entry) return;
|
|
230
296
|
api.state.dropKind = 'script';
|
|
231
297
|
api.state.dropName = entry.name;
|
|
232
298
|
api.ui.mode = 'confirmDrop';
|
|
@@ -234,11 +300,7 @@ const askDrop = (api) => {
|
|
|
234
300
|
};
|
|
235
301
|
|
|
236
302
|
const askLogs = (api) => {
|
|
237
|
-
if (api
|
|
238
|
-
if (!canEdit(api.ui)) {
|
|
239
|
-
api.ui.status = 'read only';
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
303
|
+
if (!editable(api)) return;
|
|
242
304
|
refreshLogs(api);
|
|
243
305
|
if (!api.state.logBytes) return;
|
|
244
306
|
api.state.dropKind = 'logs';
|
|
@@ -286,16 +348,8 @@ const cancelDrop = (api) => {
|
|
|
286
348
|
};
|
|
287
349
|
|
|
288
350
|
const reorder = (api, delta) => {
|
|
289
|
-
|
|
290
|
-
if (!
|
|
291
|
-
api.ui.status = 'read only';
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
const entry = selected(api);
|
|
295
|
-
if (!entry || entry.kind !== 'script') {
|
|
296
|
-
api.ui.status = 'not a script';
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
351
|
+
const entry = selectedScript(api);
|
|
352
|
+
if (!entry) return;
|
|
299
353
|
try {
|
|
300
354
|
api.ui.ignoreWatch();
|
|
301
355
|
if (!reorderScript(api.ui.top, entry.name, delta)) return;
|
|
@@ -304,26 +358,33 @@ const reorder = (api, delta) => {
|
|
|
304
358
|
return;
|
|
305
359
|
}
|
|
306
360
|
refresh(api);
|
|
307
|
-
|
|
308
|
-
if (at >= 0) api.ui.nav.npmCursor = at;
|
|
361
|
+
focusScript(api, entry.name);
|
|
309
362
|
api.ui.status = '';
|
|
310
363
|
};
|
|
311
364
|
|
|
365
|
+
const npmState = () => ({
|
|
366
|
+
commands: [],
|
|
367
|
+
viewing: false,
|
|
368
|
+
output: '',
|
|
369
|
+
followEnd: true,
|
|
370
|
+
logScroll: 0,
|
|
371
|
+
editName: '',
|
|
372
|
+
editField: '',
|
|
373
|
+
draftName: '',
|
|
374
|
+
draftCommand: '',
|
|
375
|
+
verbose: false,
|
|
376
|
+
raw: '',
|
|
377
|
+
exitStatus: '',
|
|
378
|
+
dropName: '',
|
|
379
|
+
dropKind: '',
|
|
380
|
+
logBytes: 0,
|
|
381
|
+
running: false,
|
|
382
|
+
stopping: false,
|
|
383
|
+
child: null,
|
|
384
|
+
});
|
|
385
|
+
|
|
312
386
|
const createNpmController = (ui) => {
|
|
313
|
-
const state =
|
|
314
|
-
commands: [],
|
|
315
|
-
viewing: false,
|
|
316
|
-
output: '',
|
|
317
|
-
followEnd: true,
|
|
318
|
-
logScroll: 0,
|
|
319
|
-
editName: '',
|
|
320
|
-
dropName: '',
|
|
321
|
-
dropKind: '',
|
|
322
|
-
logBytes: 0,
|
|
323
|
-
running: false,
|
|
324
|
-
stopping: false,
|
|
325
|
-
child: null,
|
|
326
|
-
};
|
|
387
|
+
const state = npmState();
|
|
327
388
|
const api = { ui, state };
|
|
328
389
|
return {
|
|
329
390
|
state,
|
|
@@ -352,6 +413,18 @@ const createNpmController = (ui) => {
|
|
|
352
413
|
get editName() {
|
|
353
414
|
return state.editName;
|
|
354
415
|
},
|
|
416
|
+
get editField() {
|
|
417
|
+
return state.editField;
|
|
418
|
+
},
|
|
419
|
+
get draftName() {
|
|
420
|
+
return state.draftName;
|
|
421
|
+
},
|
|
422
|
+
get draftCommand() {
|
|
423
|
+
return state.draftCommand;
|
|
424
|
+
},
|
|
425
|
+
get verbose() {
|
|
426
|
+
return state.verbose;
|
|
427
|
+
},
|
|
355
428
|
get running() {
|
|
356
429
|
return state.running;
|
|
357
430
|
},
|
|
@@ -364,6 +437,12 @@ const createNpmController = (ui) => {
|
|
|
364
437
|
state.followEnd = true;
|
|
365
438
|
state.logScroll = 0;
|
|
366
439
|
state.editName = '';
|
|
440
|
+
state.editField = '';
|
|
441
|
+
state.draftName = '';
|
|
442
|
+
state.draftCommand = '';
|
|
443
|
+
state.verbose = false;
|
|
444
|
+
state.raw = '';
|
|
445
|
+
state.exitStatus = '';
|
|
367
446
|
state.dropName = '';
|
|
368
447
|
state.dropKind = '';
|
|
369
448
|
state.logBytes = 0;
|
|
@@ -374,9 +453,11 @@ const createNpmController = (ui) => {
|
|
|
374
453
|
run: () => runSelected(api),
|
|
375
454
|
rerun: () => rerun(api),
|
|
376
455
|
closeView: () => closeView(api),
|
|
456
|
+
toggleVerbose: () => toggleVerbose(api),
|
|
377
457
|
edit: () => editSelected(api),
|
|
378
458
|
create: () => newCommand(api),
|
|
379
459
|
finishEdit: () => finishEdit(api),
|
|
460
|
+
focusField: (field) => focusField(api, field),
|
|
380
461
|
askDrop: () => askDrop(api),
|
|
381
462
|
askLogs: () => askLogs(api),
|
|
382
463
|
confirmDrop: () => confirmDrop(api),
|
package/lib/session/ops.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const accessors = require('./accessors.js');
|
|
4
|
-
const { stateView } = accessors;
|
|
5
|
-
|
|
6
3
|
const ASYNC_WITHOUT_UI = ['pull', 'push'];
|
|
4
|
+
const QUIET_KIND = new Set(['staged', 'unstaged']);
|
|
7
5
|
|
|
8
6
|
const pickRepoWrite = (repo, name, uiOpen) => {
|
|
9
7
|
const asyncFn = repo[`${name}Async`];
|
|
@@ -21,28 +19,176 @@ const pickRepoWrite = (repo, name, uiOpen) => {
|
|
|
21
19
|
};
|
|
22
20
|
};
|
|
23
21
|
|
|
24
|
-
const
|
|
25
|
-
const state = {
|
|
26
|
-
gitBusy: false,
|
|
27
|
-
busy: '',
|
|
28
|
-
};
|
|
22
|
+
const showsBusy = (kind) => Boolean(kind) && !QUIET_KIND.has(kind);
|
|
29
23
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
24
|
+
class ChangeQueue {
|
|
25
|
+
constructor(ui, runner) {
|
|
26
|
+
this.ui = ui;
|
|
27
|
+
this.runner = runner;
|
|
28
|
+
this.jobs = [];
|
|
29
|
+
this.head = 0;
|
|
30
|
+
this.epoch = 0;
|
|
31
|
+
this.draining = false;
|
|
32
|
+
this.pumpQueued = false;
|
|
33
|
+
this.holding = false;
|
|
34
|
+
this.resume = null;
|
|
35
|
+
this.reloading = false;
|
|
36
|
+
}
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
noteFree() {
|
|
39
|
+
const resume = this.resume;
|
|
40
|
+
this.resume = null;
|
|
41
|
+
if (resume) resume();
|
|
42
|
+
}
|
|
39
43
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
+
dropJobs() {
|
|
45
|
+
this.jobs = [];
|
|
46
|
+
this.head = 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
reset() {
|
|
50
|
+
this.epoch += 1;
|
|
51
|
+
this.dropJobs();
|
|
52
|
+
this.draining = false;
|
|
53
|
+
this.pumpQueued = false;
|
|
54
|
+
this.holding = false;
|
|
55
|
+
this.reloading = false;
|
|
56
|
+
this.noteFree();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
armBusy(kind) {
|
|
60
|
+
if (!showsBusy(kind)) return;
|
|
61
|
+
this.ui.status = '';
|
|
62
|
+
this.ui.progressFrame = 0;
|
|
63
|
+
this.runner.setBusy(kind);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
waitTurn() {
|
|
67
|
+
if (!this.runner.gitBusy || this.holding) return Promise.resolve();
|
|
68
|
+
return new Promise((resolve) => {
|
|
69
|
+
this.resume = resolve;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
44
72
|
|
|
45
|
-
|
|
73
|
+
hasPendingBusy() {
|
|
74
|
+
const { jobs, head } = this;
|
|
75
|
+
for (let i = head; i < jobs.length; i++) {
|
|
76
|
+
if (showsBusy(jobs[i].kind)) return true;
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
releaseHold() {
|
|
82
|
+
this.holding = false;
|
|
83
|
+
this.runner.gitBusy = false;
|
|
84
|
+
this.noteFree();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
startReload() {
|
|
88
|
+
const { ui } = this;
|
|
89
|
+
if (typeof ui.reloadAfterChange !== 'function') return void ui.paint();
|
|
90
|
+
this.reloading = true;
|
|
91
|
+
ui.reloadAfterChange(() => {
|
|
92
|
+
this.reloading = false;
|
|
93
|
+
ui.paint();
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async takeJob(stamp) {
|
|
98
|
+
const job = this.jobs[this.head];
|
|
99
|
+
this.head += 1;
|
|
100
|
+
this.armBusy(job.kind);
|
|
101
|
+
try {
|
|
102
|
+
await job.write(this.ui.top);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
if (stamp !== this.epoch) return 'stale';
|
|
105
|
+
this.ui.status = error.message;
|
|
106
|
+
return 'fail';
|
|
107
|
+
}
|
|
108
|
+
if (stamp !== this.epoch) return 'stale';
|
|
109
|
+
if (job.done) this.ui.status = job.done;
|
|
110
|
+
if (!this.hasPendingBusy()) this.runner.clearBusy();
|
|
111
|
+
return job.reload ? 'reload' : 'ok';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
schedule() {
|
|
115
|
+
if (this.draining || this.pumpQueued) return;
|
|
116
|
+
this.pumpQueued = true;
|
|
117
|
+
queueMicrotask(() => {
|
|
118
|
+
this.pumpQueued = false;
|
|
119
|
+
void this.run();
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
finish(stamp, needReload) {
|
|
124
|
+
if (stamp !== this.epoch) return;
|
|
125
|
+
this.draining = false;
|
|
126
|
+
this.releaseHold();
|
|
127
|
+
if (this.head < this.jobs.length) return void this.schedule();
|
|
128
|
+
this.dropJobs();
|
|
129
|
+
if (needReload) this.startReload();
|
|
130
|
+
else this.ui.paint();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async run() {
|
|
134
|
+
const stamp = this.epoch;
|
|
135
|
+
this.draining = true;
|
|
136
|
+
if (this.runner.gitBusy && !this.holding) await this.waitTurn();
|
|
137
|
+
if (stamp !== this.epoch) return;
|
|
138
|
+
this.holding = true;
|
|
139
|
+
this.runner.gitBusy = true;
|
|
140
|
+
let needReload = false;
|
|
141
|
+
while (this.head < this.jobs.length) {
|
|
142
|
+
const result = await this.takeJob(stamp);
|
|
143
|
+
if (result === 'stale') return;
|
|
144
|
+
if (result === 'fail') {
|
|
145
|
+
needReload = true;
|
|
146
|
+
this.dropJobs();
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
if (result === 'reload') needReload = true;
|
|
150
|
+
}
|
|
151
|
+
this.finish(stamp, needReload);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
enqueue(job) {
|
|
155
|
+
if (this.reloading) {
|
|
156
|
+
this.ui.loader.bump();
|
|
157
|
+
this.reloading = false;
|
|
158
|
+
job.reload = true;
|
|
159
|
+
}
|
|
160
|
+
this.jobs.push(job);
|
|
161
|
+
this.armBusy(job.kind);
|
|
162
|
+
this.schedule();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
class OpsRunner {
|
|
167
|
+
constructor(ui) {
|
|
168
|
+
this.ui = ui;
|
|
169
|
+
this.gitBusy = false;
|
|
170
|
+
this.busy = '';
|
|
171
|
+
this.queue = new ChangeQueue(ui, this);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
setBusy(kind) {
|
|
175
|
+
this.busy = kind;
|
|
176
|
+
this.ui.startProgress();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
clearBusy() {
|
|
180
|
+
this.busy = '';
|
|
181
|
+
this.ui.stopProgress();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
reset() {
|
|
185
|
+
this.gitBusy = false;
|
|
186
|
+
this.busy = '';
|
|
187
|
+
this.queue.reset();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
runRepo(label, write) {
|
|
191
|
+
const { ui } = this;
|
|
46
192
|
try {
|
|
47
193
|
write(ui.top);
|
|
48
194
|
ui.status = label;
|
|
@@ -51,21 +197,23 @@ const createOpsRunner = (ui) => {
|
|
|
51
197
|
ui.status = error.message;
|
|
52
198
|
return false;
|
|
53
199
|
}
|
|
54
|
-
}
|
|
200
|
+
}
|
|
55
201
|
|
|
56
|
-
|
|
202
|
+
finishBusy(done, after) {
|
|
203
|
+
const { ui } = this;
|
|
57
204
|
if (ui.done || ui.mode === 'confirmPush') return void ui.paint();
|
|
58
205
|
if (!ui.status && after) after();
|
|
59
|
-
if (!ui.status && !
|
|
206
|
+
if (!ui.status && !this.busy) ui.status = done;
|
|
60
207
|
ui.paint();
|
|
61
|
-
}
|
|
208
|
+
}
|
|
62
209
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
210
|
+
async runBusy(kind, done, write, after) {
|
|
211
|
+
const { ui } = this;
|
|
212
|
+
if (this.gitBusy) return;
|
|
213
|
+
this.gitBusy = true;
|
|
66
214
|
ui.status = '';
|
|
67
215
|
ui.progressFrame = 0;
|
|
68
|
-
setBusy(kind);
|
|
216
|
+
this.setBusy(kind);
|
|
69
217
|
ui.paint();
|
|
70
218
|
try {
|
|
71
219
|
await write(ui.top);
|
|
@@ -76,36 +224,29 @@ const createOpsRunner = (ui) => {
|
|
|
76
224
|
ui.status = error.message;
|
|
77
225
|
}
|
|
78
226
|
} finally {
|
|
79
|
-
clearBusy();
|
|
80
|
-
|
|
227
|
+
this.clearBusy();
|
|
228
|
+
this.gitBusy = false;
|
|
229
|
+
this.queue.noteFree();
|
|
81
230
|
}
|
|
82
|
-
finishBusy(done, after);
|
|
83
|
-
}
|
|
231
|
+
this.finishBusy(done, after);
|
|
232
|
+
}
|
|
84
233
|
|
|
85
|
-
|
|
234
|
+
runWrite(name, args, kind, done, after) {
|
|
235
|
+
const { ui } = this;
|
|
86
236
|
const picked = pickRepoWrite(ui.repo, name, ui.uiOpen);
|
|
87
237
|
const write = (top) => picked.write(top, ...args);
|
|
88
238
|
if (picked.async) {
|
|
89
239
|
const refresh = () => after({ doneStatus: done });
|
|
90
|
-
return void runBusy(kind, done, write, refresh);
|
|
240
|
+
return void this.runBusy(kind, done, write, refresh);
|
|
91
241
|
}
|
|
92
|
-
if (!runRepo(done, write)) return;
|
|
242
|
+
if (!this.runRepo(done, write)) return;
|
|
93
243
|
after({});
|
|
94
244
|
if (!ui.status) ui.status = done;
|
|
95
|
-
}
|
|
245
|
+
}
|
|
96
246
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
setBusy,
|
|
102
|
-
clearBusy,
|
|
103
|
-
runRepo,
|
|
104
|
-
runWrite,
|
|
105
|
-
runBusy,
|
|
106
|
-
},
|
|
107
|
-
['gitBusy', 'busy'],
|
|
108
|
-
);
|
|
109
|
-
};
|
|
247
|
+
enqueueLazy(job) {
|
|
248
|
+
this.queue.enqueue(job);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
110
251
|
|
|
111
|
-
module.exports = {
|
|
252
|
+
module.exports = { pickRepoWrite, OpsRunner };
|