reslop 0.1.9 → 0.2.1
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 +25 -1
- package/README.md +3 -1
- package/bin/reslop.js +2 -2
- package/lib/ansi.js +148 -5
- package/lib/api.js +4 -2
- package/lib/capabilities.js +13 -14
- package/lib/cli.js +38 -49
- package/lib/clipboard.js +1 -1
- package/lib/{deps/changes.js → deps-diff.js} +309 -19
- package/lib/deps.js +498 -29
- package/lib/detect.js +1 -7
- package/lib/diff/align.js +7 -10
- package/lib/diff/diff.js +53 -0
- package/lib/diff/display.js +5 -4
- package/lib/diff/inline.js +26 -21
- package/lib/diff/items.js +6 -4
- package/lib/diff/patch.js +1 -3
- package/lib/diff/unit.js +392 -0
- package/lib/files.js +5 -8
- package/lib/{git/branches.js → git-branches.js} +191 -11
- package/lib/{git/dependencies.js → git-deps.js} +111 -38
- package/lib/git-reword-todo.js +20 -0
- package/lib/git.js +606 -68
- package/lib/github.js +28 -197
- package/lib/gitlab.js +24 -195
- package/lib/highlight/bash.js +1 -3
- package/lib/highlight/css.js +1 -3
- package/lib/highlight/js.js +11 -14
- package/lib/highlight.js +6 -9
- package/lib/keys.js +14 -8
- package/lib/manifest.js +330 -0
- package/lib/remote.js +374 -0
- package/lib/render/branches.js +18 -100
- package/lib/render/chrome.js +71 -14
- package/lib/render/commits.js +69 -60
- package/lib/render/diff.js +20 -20
- package/lib/render/files.js +6 -34
- package/lib/render/frame.js +36 -13
- package/lib/render/notes.js +26 -8
- package/lib/render/primitives.js +99 -11
- package/lib/{render.js → render/render.js} +8 -8
- package/lib/render/unit.js +41 -0
- package/lib/review-merge.js +270 -0
- package/lib/review.js +782 -14
- package/lib/select.js +5 -10
- package/lib/session/accessors.js +9 -1
- package/lib/session/actions.js +61 -18
- package/lib/session/branches.js +116 -128
- package/lib/session/change-actions.js +202 -123
- package/lib/session/commit.js +201 -115
- package/lib/session/compose.js +181 -15
- package/lib/session/dispatch.js +110 -60
- package/lib/session/files-pane.js +156 -148
- package/lib/session/items.js +22 -30
- package/lib/session/lifecycle.js +151 -54
- package/lib/session/load.js +19 -33
- package/lib/session/navigation.js +12 -1
- package/lib/session/ops.js +28 -41
- package/lib/session/pointer.js +57 -57
- package/lib/session/review.js +23 -37
- package/lib/session/terminal.js +6 -12
- package/lib/session/todos.js +37 -13
- package/lib/session/unit.js +208 -0
- package/lib/session/update.js +33 -46
- package/lib/session/view.js +78 -9
- package/lib/session/watch.js +128 -24
- package/lib/session.js +22 -246
- package/lib/source.js +3 -3
- package/lib/update.js +40 -75
- package/lib/{git/proc.js → utilities.js} +75 -11
- package/lib/wrap.js +1 -3
- package/package.json +2 -2
- package/lib/deps/items.js +0 -134
- package/lib/deps/manifest.js +0 -152
- package/lib/deps/merge.js +0 -120
- package/lib/deps/proposals.js +0 -164
- package/lib/deps/reports.js +0 -180
- package/lib/deps/rows.js +0 -176
- package/lib/deps/scan.js +0 -138
- package/lib/deps/usage.js +0 -68
- package/lib/deps/versions.js +0 -119
- package/lib/diff.js +0 -33
- package/lib/git/changes.js +0 -164
- package/lib/git/read.js +0 -253
- package/lib/git/run.js +0 -100
- package/lib/npm/commands.js +0 -100
- package/lib/remote/changes.js +0 -37
- package/lib/remote/headers.js +0 -36
- package/lib/remote/http.js +0 -122
- package/lib/remote/notes.js +0 -94
- package/lib/review/format.js +0 -434
- package/lib/review/storage.js +0 -150
- package/lib/review/store.js +0 -178
- package/lib/review/templates.js +0 -49
- package/lib/session/origins.js +0 -7
- package/lib/sys.js +0 -84
package/lib/session/lifecycle.js
CHANGED
|
@@ -4,12 +4,13 @@ const fs = require('node:fs');
|
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
|
|
6
6
|
const { isAbortError } = require('./load.js');
|
|
7
|
-
const { createDiskWatcher, POLL_MS } = require('./watch.js');
|
|
7
|
+
const { createDiskWatcher, POLL_MS, DEBOUNCE_MS } = require('./watch.js');
|
|
8
8
|
const files = require('../files.js');
|
|
9
|
-
const { itemPath, relativeAge } = files;
|
|
10
|
-
const { MANIFEST, LOCKFILE } = require('../deps
|
|
9
|
+
const { itemPath, relativeAge, TODO_FILE } = files;
|
|
10
|
+
const { MANIFEST, LOCKFILE, depFileMeta } = require('../deps.js');
|
|
11
11
|
const review = require('../review.js');
|
|
12
|
-
const { hasNotes } = review;
|
|
12
|
+
const { hasNotes, loadReview } = review;
|
|
13
|
+
const { mergeReview } = require('../review-merge.js');
|
|
13
14
|
|
|
14
15
|
const loadOptions = (api, extra = {}) => {
|
|
15
16
|
const { loadConfig, ui } = api;
|
|
@@ -30,8 +31,6 @@ const shouldDeferExtras = (api) => {
|
|
|
30
31
|
return audit && (waitingOutdated || waitingAudit);
|
|
31
32
|
};
|
|
32
33
|
|
|
33
|
-
const watchOpts = (api) => api.watch ?? {};
|
|
34
|
-
|
|
35
34
|
const endReadLoop = (api) => {
|
|
36
35
|
const finish = api.ui.finishReadLoop;
|
|
37
36
|
if (finish) finish();
|
|
@@ -50,8 +49,6 @@ const kindFromExtra = (extra = {}) => {
|
|
|
50
49
|
return 'reload';
|
|
51
50
|
};
|
|
52
51
|
|
|
53
|
-
const shouldResetNav = (kind) => kind !== 'open' && kind !== 'watch';
|
|
54
|
-
|
|
55
52
|
const finishIfEmpty = (api, kind) => {
|
|
56
53
|
const { collection, review: notes, ui, nav } = api;
|
|
57
54
|
if (collection.items.length) return;
|
|
@@ -125,9 +122,25 @@ const stampItemDates = (items, dates) => {
|
|
|
125
122
|
}
|
|
126
123
|
};
|
|
127
124
|
|
|
125
|
+
const syncUnitEditor = (api) => {
|
|
126
|
+
const composer = api.ui.composer;
|
|
127
|
+
if (!composer || typeof composer.applyDiskText !== 'function') return;
|
|
128
|
+
if (api.nav.pane !== 'unit') return;
|
|
129
|
+
const rel = api.nav.reviewPath;
|
|
130
|
+
if (!rel) return;
|
|
131
|
+
const repo = api.ui.repo;
|
|
132
|
+
if (!repo || typeof repo.fileText !== 'function') return;
|
|
133
|
+
const text = repo.fileText(api.ui.top || api.loadConfig.cwd, rel, api.ui.rev);
|
|
134
|
+
composer.applyDiskText(text);
|
|
135
|
+
};
|
|
136
|
+
|
|
128
137
|
const syncItemWatches = (api) => {
|
|
129
138
|
if (!api.diskWatcher) return;
|
|
130
|
-
|
|
139
|
+
const rels = itemRels(api.collection);
|
|
140
|
+
if (api.nav.pane === 'unit' && api.nav.reviewPath) {
|
|
141
|
+
if (!rels.includes(api.nav.reviewPath)) rels.push(api.nav.reviewPath);
|
|
142
|
+
}
|
|
143
|
+
api.diskWatcher.watchFiles(rels);
|
|
131
144
|
};
|
|
132
145
|
|
|
133
146
|
const applyLoaded = (api, loaded) => {
|
|
@@ -160,6 +173,7 @@ const applyLoaded = (api, loaded) => {
|
|
|
160
173
|
markLoaded(api);
|
|
161
174
|
loader.pendingExtras = loaded.pending === true;
|
|
162
175
|
syncItemWatches(api);
|
|
176
|
+
syncUnitEditor(api);
|
|
163
177
|
return true;
|
|
164
178
|
};
|
|
165
179
|
|
|
@@ -173,23 +187,42 @@ const load = (api) => {
|
|
|
173
187
|
applyLoaded(api, loaded);
|
|
174
188
|
};
|
|
175
189
|
|
|
176
|
-
const fileStamp = (top, rel
|
|
190
|
+
const fileStamp = (top, rel) => {
|
|
177
191
|
const full = path.join(top, rel);
|
|
178
192
|
try {
|
|
179
|
-
const stat = statSync(full);
|
|
193
|
+
const stat = fs.statSync(full);
|
|
180
194
|
return `${stat.mtimeMs}:${stat.size}`;
|
|
181
195
|
} catch {
|
|
182
196
|
return 'missing';
|
|
183
197
|
}
|
|
184
198
|
};
|
|
185
199
|
|
|
200
|
+
const npmRels = (items) => {
|
|
201
|
+
const rels = [MANIFEST, LOCKFILE];
|
|
202
|
+
for (const item of items) {
|
|
203
|
+
const rel = itemPath(item);
|
|
204
|
+
if (!depFileMeta(rel)) continue;
|
|
205
|
+
if (rels.includes(rel)) continue;
|
|
206
|
+
rels.push(rel);
|
|
207
|
+
}
|
|
208
|
+
rels.sort();
|
|
209
|
+
return rels;
|
|
210
|
+
};
|
|
211
|
+
|
|
186
212
|
const npmStamp = (api) => {
|
|
187
|
-
const extra = watchOpts(api);
|
|
188
|
-
const statSync = extra.statSync ?? fs.statSync;
|
|
189
213
|
const top = api.ui.top || api.loadConfig.cwd;
|
|
190
|
-
const
|
|
191
|
-
const
|
|
192
|
-
|
|
214
|
+
const rels = npmRels(api.collection.items);
|
|
215
|
+
const parts = new Array(rels.length);
|
|
216
|
+
for (let i = 0; i < rels.length; i++) {
|
|
217
|
+
parts[i] = `${rels[i]}:${fileStamp(top, rels[i])}`;
|
|
218
|
+
}
|
|
219
|
+
return parts.join('|');
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const npmExtrasStale = (api) => {
|
|
223
|
+
const cached = api.npmExtras;
|
|
224
|
+
if (!cached || !api.loadConfig.audit) return false;
|
|
225
|
+
return cached.stamp !== npmStamp(api);
|
|
193
226
|
};
|
|
194
227
|
|
|
195
228
|
const cacheNpmExtras = (api, loaded) => {
|
|
@@ -216,8 +249,7 @@ const followExtras = async (api, loaded, gen, kind) => {
|
|
|
216
249
|
next = await api.loader.fetchExtras(api.loadConfig.repo, loaded, extra);
|
|
217
250
|
} catch (error) {
|
|
218
251
|
if (!api.loader.isCurrent(gen)) return;
|
|
219
|
-
failOpen(api, error);
|
|
220
|
-
return;
|
|
252
|
+
return void failOpen(api, error);
|
|
221
253
|
}
|
|
222
254
|
if (!api.loader.isCurrent(gen)) return;
|
|
223
255
|
if (api.ui.done) {
|
|
@@ -241,7 +273,6 @@ const followExtras = async (api, loaded, gen, kind) => {
|
|
|
241
273
|
const npmReuseOpts = (api, extra = {}) => {
|
|
242
274
|
const opts = {};
|
|
243
275
|
if (extra.signal) opts.signal = extra.signal;
|
|
244
|
-
if (extra.keepView !== true) return opts;
|
|
245
276
|
const cached = api.npmExtras;
|
|
246
277
|
if (!cached) return opts;
|
|
247
278
|
if (cached.stamp !== npmStamp(api)) return opts;
|
|
@@ -267,9 +298,8 @@ const resetNavAfterLoad = (api, extra = {}) => {
|
|
|
267
298
|
};
|
|
268
299
|
|
|
269
300
|
const afterSnapshot = (api, kind, extra = {}) => {
|
|
270
|
-
if (
|
|
271
|
-
resetNavAfterLoad(api, extra);
|
|
272
|
-
return;
|
|
301
|
+
if (kind !== 'open' && kind !== 'watch') {
|
|
302
|
+
return void resetNavAfterLoad(api, extra);
|
|
273
303
|
}
|
|
274
304
|
if (extra.afterLoad) extra.afterLoad();
|
|
275
305
|
};
|
|
@@ -296,8 +326,7 @@ const runSnapshot = async (api, gen, kind, extra = {}) => {
|
|
|
296
326
|
);
|
|
297
327
|
} catch (error) {
|
|
298
328
|
if (!api.loader.isCurrent(gen)) return;
|
|
299
|
-
failOpen(api, error);
|
|
300
|
-
return;
|
|
329
|
+
return void failOpen(api, error);
|
|
301
330
|
}
|
|
302
331
|
if (!api.loader.isCurrent(gen)) return;
|
|
303
332
|
if (api.ui.done) {
|
|
@@ -306,7 +335,7 @@ const runSnapshot = async (api, gen, kind, extra = {}) => {
|
|
|
306
335
|
}
|
|
307
336
|
applyLoaded(api, api.loader.tag(loaded, gen));
|
|
308
337
|
afterSnapshot(api, kind, extra);
|
|
309
|
-
if (loaded.pending) {
|
|
338
|
+
if (loaded.pending === true || npmExtrasStale(api)) {
|
|
310
339
|
api.ops.setBusy('checking npm');
|
|
311
340
|
if (kind === 'open' || extra.quiet === true) api.ui.paint();
|
|
312
341
|
await followExtras(api, loaded, gen, kind);
|
|
@@ -352,10 +381,9 @@ const refreshFromRepo = (api, extra = {}) => {
|
|
|
352
381
|
);
|
|
353
382
|
applyLoaded(api, api.loader.tag(loaded, gen));
|
|
354
383
|
afterSnapshot(api, kind, extra);
|
|
355
|
-
if (loaded.pending) {
|
|
384
|
+
if (loaded.pending === true || npmExtrasStale(api)) {
|
|
356
385
|
api.ops.setBusy('checking npm');
|
|
357
|
-
followExtras(api, loaded, gen, kind);
|
|
358
|
-
return;
|
|
386
|
+
return void followExtras(api, loaded, gen, kind);
|
|
359
387
|
}
|
|
360
388
|
if (kind === 'keep') return;
|
|
361
389
|
finishIfEmpty(api, kind);
|
|
@@ -377,8 +405,7 @@ const reloadAfterChange = (api, afterLoad) => {
|
|
|
377
405
|
if (afterLoad) afterLoad();
|
|
378
406
|
};
|
|
379
407
|
if (api.ui.uiOpen && api.loadConfig.repo.loadAsync) {
|
|
380
|
-
refreshFromRepo(api, { afterLoad: restore });
|
|
381
|
-
return;
|
|
408
|
+
return void refreshFromRepo(api, { afterLoad: restore });
|
|
382
409
|
}
|
|
383
410
|
refreshFromRepo(api);
|
|
384
411
|
restore();
|
|
@@ -392,9 +419,7 @@ const onReload = (api) => {
|
|
|
392
419
|
|
|
393
420
|
const clearWatchRetry = (api) => {
|
|
394
421
|
if (api.watchRetry === null) return;
|
|
395
|
-
|
|
396
|
-
const clearTimeoutFn = extra.clearTimeout ?? clearTimeout;
|
|
397
|
-
clearTimeoutFn(api.watchRetry);
|
|
422
|
+
clearTimeout(api.watchRetry);
|
|
398
423
|
api.watchRetry = null;
|
|
399
424
|
};
|
|
400
425
|
|
|
@@ -404,6 +429,10 @@ const shouldDeferWatch = (api) => {
|
|
|
404
429
|
if (!ui.uiOpen) return true;
|
|
405
430
|
if (!api.loader.didLoad) return true;
|
|
406
431
|
if (ops.gitBusy) return true;
|
|
432
|
+
if (ui.mode === 'compose') {
|
|
433
|
+
const kind = ui.composer && ui.composer.state.composeKind;
|
|
434
|
+
if (kind === 'file') return false;
|
|
435
|
+
}
|
|
407
436
|
if (ui.mode !== 'review') return true;
|
|
408
437
|
return false;
|
|
409
438
|
};
|
|
@@ -411,44 +440,64 @@ const shouldDeferWatch = (api) => {
|
|
|
411
440
|
const WATCH_IGNORE_MS = 500;
|
|
412
441
|
|
|
413
442
|
const ignoreWatch = (api, ms = WATCH_IGNORE_MS) => {
|
|
414
|
-
|
|
415
|
-
const now = extra.now ?? Date.now;
|
|
416
|
-
api.ignoreWatchUntil = now() + ms;
|
|
443
|
+
api.ignoreWatchUntil = Date.now() + ms;
|
|
417
444
|
};
|
|
418
445
|
|
|
419
446
|
const onDiskChange = (api) => {
|
|
420
|
-
|
|
421
|
-
const now = extra.now ?? Date.now;
|
|
422
|
-
if (now() < (api.ignoreWatchUntil ?? 0)) return;
|
|
447
|
+
if (Date.now() < (api.ignoreWatchUntil ?? 0)) return;
|
|
423
448
|
if (shouldDeferWatch(api)) {
|
|
424
449
|
if (api.watchRetry !== null) return;
|
|
425
|
-
|
|
426
|
-
const delay = extra.debounceMs ?? 200;
|
|
427
|
-
api.watchRetry = setTimeoutFn(() => {
|
|
450
|
+
api.watchRetry = setTimeout(() => {
|
|
428
451
|
api.watchRetry = null;
|
|
429
452
|
onDiskChange(api);
|
|
430
|
-
},
|
|
453
|
+
}, DEBOUNCE_MS);
|
|
454
|
+
if (typeof api.watchRetry.unref === 'function') api.watchRetry.unref();
|
|
431
455
|
return;
|
|
432
456
|
}
|
|
433
457
|
refreshFromRepo(api, { keepView: true, quiet: true });
|
|
434
458
|
};
|
|
435
459
|
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
460
|
+
const catchUpWatch = (api) => {
|
|
461
|
+
clearWatchRetry(api);
|
|
462
|
+
if (shouldDeferWatch(api)) return void onDiskChange(api);
|
|
463
|
+
refreshFromRepo(api, { keepView: true, quiet: true });
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
const stampFile = (root, rel) => {
|
|
467
|
+
const full = path.join(root, rel);
|
|
443
468
|
let stat;
|
|
444
469
|
try {
|
|
445
|
-
stat = statSync(full);
|
|
470
|
+
stat = fs.statSync(full);
|
|
446
471
|
} catch {
|
|
447
472
|
return `${rel}:missing`;
|
|
448
473
|
}
|
|
449
474
|
return `${rel}:${stat.mtimeMs}:${stat.size}`;
|
|
450
475
|
};
|
|
451
476
|
|
|
477
|
+
const worktreeStamp = (api) => {
|
|
478
|
+
const root = api.ui.top || api.loadConfig.cwd;
|
|
479
|
+
const parts = [];
|
|
480
|
+
for (const rel of itemRels(api.collection)) {
|
|
481
|
+
if (!rel || rel === TODO_FILE) continue;
|
|
482
|
+
parts.push(stampFile(root, rel));
|
|
483
|
+
}
|
|
484
|
+
return parts.join('\n');
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
const watchesWorktree = (nav) =>
|
|
488
|
+
nav.todoOpen === true || nav.pane === 'branches' || nav.pane === 'commits';
|
|
489
|
+
|
|
490
|
+
const currentFileStamp = (api) => {
|
|
491
|
+
const { nav } = api;
|
|
492
|
+
const root = api.ui.top || api.loadConfig.cwd;
|
|
493
|
+
if (watchesWorktree(nav)) return worktreeStamp(api);
|
|
494
|
+
const pane = nav.pane;
|
|
495
|
+
if (pane !== 'diff' && pane !== 'unit') return '';
|
|
496
|
+
const rel = pane === 'unit' ? nav.reviewPath : itemPath(api.ui.current());
|
|
497
|
+
if (!rel) return '';
|
|
498
|
+
return stampFile(root, rel);
|
|
499
|
+
};
|
|
500
|
+
|
|
452
501
|
const pollCurrentFile = (api) => {
|
|
453
502
|
const stamp = currentFileStamp(api);
|
|
454
503
|
if (!stamp) {
|
|
@@ -464,9 +513,46 @@ const pollCurrentFile = (api) => {
|
|
|
464
513
|
onDiskChange(api);
|
|
465
514
|
};
|
|
466
515
|
|
|
516
|
+
const reviewFileStamp = (api) => {
|
|
517
|
+
const store = api.review && api.review.store;
|
|
518
|
+
if (!store || !store.reviewPath) return '';
|
|
519
|
+
try {
|
|
520
|
+
const stat = fs.statSync(store.reviewPath);
|
|
521
|
+
return `${stat.mtimeMs}:${stat.size}`;
|
|
522
|
+
} catch {
|
|
523
|
+
return 'missing';
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
const noteReviewStamp = (api) => {
|
|
528
|
+
api.reviewStamp = reviewFileStamp(api);
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
const applyReviewChange = (api) => {
|
|
532
|
+
if (!api.ui || api.ui.done) return;
|
|
533
|
+
const stamp = reviewFileStamp(api);
|
|
534
|
+
if (!stamp || stamp === api.reviewStamp) return;
|
|
535
|
+
if (stamp === 'missing') {
|
|
536
|
+
api.reviewStamp = stamp;
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
const store = api.review && api.review.store;
|
|
540
|
+
if (!store) return;
|
|
541
|
+
let disk;
|
|
542
|
+
try {
|
|
543
|
+
disk = loadReview(store.reviewPath, store.templates);
|
|
544
|
+
} catch {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
mergeReview(store, disk);
|
|
548
|
+
api.reviewStamp = stamp;
|
|
549
|
+
if (typeof api.ui.paint === 'function') api.ui.paint();
|
|
550
|
+
};
|
|
551
|
+
|
|
467
552
|
const stopWatch = (api) => {
|
|
468
553
|
clearWatchRetry(api);
|
|
469
554
|
api.watchStamp = '';
|
|
555
|
+
api.reviewStamp = '';
|
|
470
556
|
if (api.ui.term) api.ui.term.stopTimer('watch');
|
|
471
557
|
if (!api.diskWatcher) return;
|
|
472
558
|
api.diskWatcher.close();
|
|
@@ -478,22 +564,30 @@ const startWatch = (api) => {
|
|
|
478
564
|
if (!isWorktree(api.ui)) return;
|
|
479
565
|
const root = api.ui.top || api.loadConfig.cwd;
|
|
480
566
|
if (!root) return;
|
|
481
|
-
const extra = watchOpts(api);
|
|
482
567
|
api.diskWatcher = createDiskWatcher({
|
|
483
568
|
root,
|
|
484
569
|
paths: api.loadConfig.paths,
|
|
485
570
|
onChange: () => onDiskChange(api),
|
|
486
|
-
|
|
571
|
+
onReview: () => applyReviewChange(api),
|
|
487
572
|
});
|
|
488
573
|
syncItemWatches(api);
|
|
574
|
+
noteReviewStamp(api);
|
|
489
575
|
if (!api.ui.term) return;
|
|
490
|
-
api.ui.term.startTimer(
|
|
576
|
+
api.ui.term.startTimer(
|
|
577
|
+
'watch',
|
|
578
|
+
() => {
|
|
579
|
+
pollCurrentFile(api);
|
|
580
|
+
applyReviewChange(api);
|
|
581
|
+
},
|
|
582
|
+
POLL_MS,
|
|
583
|
+
);
|
|
491
584
|
};
|
|
492
585
|
|
|
493
586
|
const createLifecycle = (api) => {
|
|
494
587
|
api.diskWatcher = null;
|
|
495
588
|
api.watchRetry = null;
|
|
496
589
|
api.watchStamp = '';
|
|
590
|
+
api.reviewStamp = '';
|
|
497
591
|
api.ignoreWatchUntil = 0;
|
|
498
592
|
api.npmExtras = null;
|
|
499
593
|
return {
|
|
@@ -505,6 +599,9 @@ const createLifecycle = (api) => {
|
|
|
505
599
|
reloadAfterChange: (afterLoad) => reloadAfterChange(api, afterLoad),
|
|
506
600
|
onReload: () => onReload(api),
|
|
507
601
|
onDiskChange: () => onDiskChange(api),
|
|
602
|
+
applyReviewChange: () => applyReviewChange(api),
|
|
603
|
+
noteReviewStamp: () => noteReviewStamp(api),
|
|
604
|
+
catchUpWatch: () => catchUpWatch(api),
|
|
508
605
|
pollCurrentFile: () => pollCurrentFile(api),
|
|
509
606
|
startWatch: () => startWatch(api),
|
|
510
607
|
stopWatch: () => stopWatch(api),
|
package/lib/session/load.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { stateView } = require('./accessors.js');
|
|
4
|
+
|
|
3
5
|
const isAbortError = (error) => !!(error && error.code === 'ABORT');
|
|
4
6
|
|
|
5
7
|
const createLoadCoordinator = () => {
|
|
@@ -46,40 +48,24 @@ const createLoadCoordinator = () => {
|
|
|
46
48
|
return repo.loadExtras(loaded, extra);
|
|
47
49
|
};
|
|
48
50
|
|
|
49
|
-
return
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
state.pendingExtras = value;
|
|
51
|
+
return stateView(
|
|
52
|
+
state,
|
|
53
|
+
{
|
|
54
|
+
get signal() {
|
|
55
|
+
return state.abort?.signal;
|
|
56
|
+
},
|
|
57
|
+
bump,
|
|
58
|
+
isCurrent,
|
|
59
|
+
accept,
|
|
60
|
+
abort,
|
|
61
|
+
reset,
|
|
62
|
+
tag,
|
|
63
|
+
fetchSnapshot,
|
|
64
|
+
fetchExtras,
|
|
64
65
|
},
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
set didLoad(value) {
|
|
69
|
-
state.didLoad = value;
|
|
70
|
-
},
|
|
71
|
-
get signal() {
|
|
72
|
-
return state.abort?.signal;
|
|
73
|
-
},
|
|
74
|
-
bump,
|
|
75
|
-
isCurrent,
|
|
76
|
-
accept,
|
|
77
|
-
abort,
|
|
78
|
-
reset,
|
|
79
|
-
tag,
|
|
80
|
-
fetchSnapshot,
|
|
81
|
-
fetchExtras,
|
|
82
|
-
};
|
|
66
|
+
['promise', 'pendingExtras', 'didLoad'],
|
|
67
|
+
['gen'],
|
|
68
|
+
);
|
|
83
69
|
};
|
|
84
70
|
|
|
85
71
|
module.exports = { isAbortError, createLoadCoordinator };
|
|
@@ -12,6 +12,8 @@ const initialState = (options = {}) => ({
|
|
|
12
12
|
scroll: 0,
|
|
13
13
|
listScroll: 0,
|
|
14
14
|
reviewPath: null,
|
|
15
|
+
fileScope: 'diff',
|
|
16
|
+
unitLine: 0,
|
|
15
17
|
selection: null,
|
|
16
18
|
mouseAnchor: null,
|
|
17
19
|
pendingClick: null,
|
|
@@ -30,11 +32,20 @@ const clampIndex = (nav, length) => {
|
|
|
30
32
|
|
|
31
33
|
const current = (nav, items, todoItem) => {
|
|
32
34
|
if (nav.todoOpen) return todoItem;
|
|
35
|
+
if (nav.pane === 'unit' && nav.reviewPath) {
|
|
36
|
+
const item = items[nav.index];
|
|
37
|
+
if (item && listPath(item) === nav.reviewPath) return item;
|
|
38
|
+
for (const entry of items) {
|
|
39
|
+
if (listPath(entry) === nav.reviewPath) return entry;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
33
43
|
return items[nav.index] ?? null;
|
|
34
44
|
};
|
|
35
45
|
|
|
36
46
|
const syncReviewPath = (nav, item) => {
|
|
37
|
-
if (nav.pane !== 'diff') return;
|
|
47
|
+
if (nav.pane !== 'diff' && nav.pane !== 'unit') return;
|
|
48
|
+
if (nav.pane === 'unit' && nav.reviewPath) return;
|
|
38
49
|
nav.reviewPath = item ? listPath(item) : null;
|
|
39
50
|
};
|
|
40
51
|
|
package/lib/session/ops.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { stateView } = require('./accessors.js');
|
|
4
|
+
|
|
3
5
|
const ASYNC_WITHOUT_UI = ['pull', 'push'];
|
|
4
6
|
|
|
5
7
|
const pickRepoWrite = (repo, name, uiOpen) => {
|
|
@@ -18,7 +20,7 @@ const pickRepoWrite = (repo, name, uiOpen) => {
|
|
|
18
20
|
};
|
|
19
21
|
};
|
|
20
22
|
|
|
21
|
-
const createOpsRunner = (
|
|
23
|
+
const createOpsRunner = (ui) => {
|
|
22
24
|
const state = {
|
|
23
25
|
gitBusy: false,
|
|
24
26
|
busy: '',
|
|
@@ -31,53 +33,46 @@ const createOpsRunner = (options) => {
|
|
|
31
33
|
|
|
32
34
|
const setBusy = (kind) => {
|
|
33
35
|
state.busy = kind;
|
|
34
|
-
|
|
36
|
+
ui.startProgress();
|
|
35
37
|
};
|
|
36
38
|
|
|
37
39
|
const clearBusy = () => {
|
|
38
40
|
state.busy = '';
|
|
39
|
-
|
|
41
|
+
ui.stopProgress();
|
|
40
42
|
};
|
|
41
43
|
|
|
42
44
|
const runRepo = (label, write) => {
|
|
43
45
|
try {
|
|
44
|
-
write(
|
|
45
|
-
|
|
46
|
+
write(ui.top);
|
|
47
|
+
ui.status = label;
|
|
46
48
|
return true;
|
|
47
49
|
} catch (error) {
|
|
48
|
-
|
|
50
|
+
ui.status = error.message;
|
|
49
51
|
return false;
|
|
50
52
|
}
|
|
51
53
|
};
|
|
52
54
|
|
|
53
55
|
const finishBusy = (done, after) => {
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (options.getMode() === 'confirmPush') {
|
|
59
|
-
options.paint();
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
if (!options.getStatus() && after) after();
|
|
63
|
-
if (!options.getStatus() && !state.busy) options.setStatus(done);
|
|
64
|
-
options.paint();
|
|
56
|
+
if (ui.done || ui.mode === 'confirmPush') return void ui.paint();
|
|
57
|
+
if (!ui.status && after) after();
|
|
58
|
+
if (!ui.status && !state.busy) ui.status = done;
|
|
59
|
+
ui.paint();
|
|
65
60
|
};
|
|
66
61
|
|
|
67
62
|
const runBusy = async (kind, done, write, after) => {
|
|
68
63
|
if (state.gitBusy) return;
|
|
69
64
|
state.gitBusy = true;
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
ui.status = '';
|
|
66
|
+
ui.progressFrame = 0;
|
|
72
67
|
setBusy(kind);
|
|
73
|
-
|
|
68
|
+
ui.paint();
|
|
74
69
|
try {
|
|
75
|
-
await write(
|
|
70
|
+
await write(ui.top);
|
|
76
71
|
} catch (error) {
|
|
77
72
|
if (error.rejected && kind !== 'force pushing') {
|
|
78
|
-
|
|
73
|
+
ui.mode = 'confirmPush';
|
|
79
74
|
} else {
|
|
80
|
-
|
|
75
|
+
ui.status = error.message;
|
|
81
76
|
}
|
|
82
77
|
} finally {
|
|
83
78
|
clearBusy();
|
|
@@ -86,25 +81,17 @@ const createOpsRunner = (options) => {
|
|
|
86
81
|
finishBusy(done, after);
|
|
87
82
|
};
|
|
88
83
|
|
|
89
|
-
return
|
|
90
|
-
|
|
91
|
-
|
|
84
|
+
return stateView(
|
|
85
|
+
state,
|
|
86
|
+
{
|
|
87
|
+
reset,
|
|
88
|
+
setBusy,
|
|
89
|
+
clearBusy,
|
|
90
|
+
runRepo,
|
|
91
|
+
runBusy,
|
|
92
92
|
},
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
},
|
|
96
|
-
get busy() {
|
|
97
|
-
return state.busy;
|
|
98
|
-
},
|
|
99
|
-
set busy(value) {
|
|
100
|
-
state.busy = value;
|
|
101
|
-
},
|
|
102
|
-
reset,
|
|
103
|
-
setBusy,
|
|
104
|
-
clearBusy,
|
|
105
|
-
runRepo,
|
|
106
|
-
runBusy,
|
|
107
|
-
};
|
|
93
|
+
['gitBusy', 'busy'],
|
|
94
|
+
);
|
|
108
95
|
};
|
|
109
96
|
|
|
110
97
|
module.exports = { ASYNC_WITHOUT_UI, pickRepoWrite, createOpsRunner };
|