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
@@ -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, parseScriptLine } = commands;
7
+ const { listCommands, reduceOutput, writeLog } = commands;
8
8
  const { staleLogFiles, removeStaleLogs, formatSize } = commands;
9
- const { scriptLine, saveScript, removeScript, reorderScript } = commands;
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.pane = 'npm';
53
+ const nav = api.ui.nav;
54
+ nav.pane = 'npm';
55
+ nav.resetListScroll('npm');
52
56
  api.ui.status = '';
53
- api.ui.nav.clearSelection();
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
- if (api.ui.progress) api.ui.progress.stop('npm');
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.output = text;
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.output, 'terminated')
118
+ ? withNotice(reduceOutput(api.state.raw, api.ui.top, ''), 'terminated')
105
119
  : reduceOutput(result.text, api.ui.top, result.status);
106
- api.state.output = reduced;
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
- if (api.ui.progress) api.ui.progress.start('npm');
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
- api.state.child = start(
149
+ const child = start(
132
150
  api.ui.top,
133
151
  entry,
134
- (text) => showOutput(api, reduceOutput(text, api.ui.top, '')),
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.output = withNotice(api.state.output, 'terminated');
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 editSelected = (api) => {
171
- if (api.state.viewing) return;
172
- if (!canEdit(api.ui)) {
173
- api.ui.status = 'read only';
174
- return;
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 (!entry || entry.kind !== 'script') {
178
- api.ui.status = 'not a script';
179
- return;
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.ui.composer.openCompose('npm', scriptLine(entry));
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.state.viewing) return;
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
- const text = editor ? editor.text : '';
198
- const parsed = parseScriptLine(text);
199
- if (!parsed) {
200
- api.ui.status = 'name: command';
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
- const at = api.state.commands.findIndex(
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
- if (api.state.viewing) return;
221
- if (!canEdit(api.ui)) {
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.state.viewing) return;
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
- if (api.state.viewing) return;
290
- if (!canEdit(api.ui)) {
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
- const at = api.state.commands.findIndex((item) => item.name === entry.name);
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),
@@ -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 createOpsRunner = (ui) => {
25
- const state = {
26
- gitBusy: false,
27
- busy: '',
28
- };
22
+ const showsBusy = (kind) => Boolean(kind) && !QUIET_KIND.has(kind);
29
23
 
30
- const reset = () => {
31
- state.gitBusy = false;
32
- state.busy = '';
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
- const setBusy = (kind) => {
36
- state.busy = kind;
37
- ui.startProgress();
38
- };
38
+ noteFree() {
39
+ const resume = this.resume;
40
+ this.resume = null;
41
+ if (resume) resume();
42
+ }
39
43
 
40
- const clearBusy = () => {
41
- state.busy = '';
42
- ui.stopProgress();
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
- const runRepo = (label, write) => {
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
- const finishBusy = (done, after) => {
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 && !state.busy) ui.status = done;
206
+ if (!ui.status && !this.busy) ui.status = done;
60
207
  ui.paint();
61
- };
208
+ }
62
209
 
63
- const runBusy = async (kind, done, write, after) => {
64
- if (state.gitBusy) return;
65
- state.gitBusy = true;
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
- state.gitBusy = false;
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
- const runWrite = (name, args, kind, done, after) => {
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
- return stateView(
98
- state,
99
- {
100
- reset,
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 = { ASYNC_WITHOUT_UI, pickRepoWrite, createOpsRunner };
252
+ module.exports = { pickRepoWrite, OpsRunner };