reslop 0.2.3 → 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 +27 -1
- package/README.md +8 -4
- package/bin/reslop.js +4 -2
- package/lib/api.js +2 -1
- package/lib/capabilities.js +0 -4
- package/lib/cli.js +19 -10
- package/lib/clipboard.js +45 -4
- package/lib/deps-diff.js +61 -118
- package/lib/deps.js +166 -235
- package/lib/detect.js +4 -2
- package/lib/diff/align.js +2 -1
- package/lib/diff/diff.js +6 -3
- package/lib/diff/display.js +2 -1
- package/lib/diff/inline.js +68 -2
- package/lib/diff/patch.js +2 -1
- package/lib/diff/unit.js +15 -30
- package/lib/editor.js +91 -13
- package/lib/files.js +54 -27
- package/lib/git-branches.js +107 -154
- package/lib/git-deps.js +78 -155
- package/lib/git-worktree.js +508 -0
- package/lib/git.js +23 -580
- package/lib/github.js +9 -8
- package/lib/gitlab.js +5 -4
- package/lib/highlight/bash.js +2 -1
- package/lib/highlight/core.js +11 -0
- package/lib/highlight/css.js +2 -1
- package/lib/highlight/csv.js +2 -1
- package/lib/highlight/dot.js +3 -12
- package/lib/highlight/html.js +2 -1
- package/lib/highlight/json.js +2 -1
- package/lib/highlight/log.js +5 -11
- package/lib/highlight/txt.js +3 -12
- package/lib/highlight.js +2 -1
- package/lib/keys.js +22 -12
- package/lib/manifest.js +4 -6
- package/lib/npm-commands.js +82 -24
- package/lib/remote.js +21 -20
- package/lib/render/branches.js +9 -27
- package/lib/render/chrome.js +109 -35
- package/lib/render/commits.js +216 -216
- package/lib/render/diff.js +160 -47
- package/lib/render/files.js +9 -26
- package/lib/render/frame.js +97 -118
- package/lib/render/notes.js +74 -26
- package/lib/render/npm.js +104 -30
- package/lib/render/present.js +2 -1
- package/lib/render/primitives.js +128 -52
- package/lib/render/render.js +2 -1
- package/lib/render/unit.js +17 -11
- package/lib/report-model.js +6 -4
- package/lib/report-parse.js +8 -14
- package/lib/report-render.js +38 -21
- package/lib/report-run.js +12 -11
- package/lib/report-test.js +45 -51
- package/lib/report-tools.js +44 -25
- package/lib/review-merge.js +102 -131
- package/lib/review.js +69 -78
- package/lib/select.js +52 -0
- package/lib/session/actions.js +72 -87
- package/lib/session/branches.js +18 -52
- package/lib/session/change-actions.js +175 -147
- package/lib/session/commit.js +65 -61
- package/lib/session/compose.js +152 -83
- package/lib/session/dispatch.js +53 -46
- package/lib/session/edit-click.js +57 -0
- package/lib/session/files-pane.js +30 -51
- package/lib/session/items.js +73 -90
- package/lib/session/lifecycle.js +37 -98
- package/lib/session/load.js +52 -57
- package/lib/session/navigation.js +105 -95
- package/lib/session/npm.js +205 -71
- package/lib/session/ops.js +200 -45
- package/lib/session/pointer.js +33 -8
- package/lib/session/progress.js +9 -6
- package/lib/session/review.js +35 -53
- package/lib/session/tasks.js +292 -0
- package/lib/session/terminal.js +17 -19
- package/lib/session/unit.js +3 -4
- package/lib/session/update.js +76 -96
- package/lib/session/view.js +45 -16
- package/lib/session/watch.js +47 -27
- package/lib/session.js +66 -97
- package/lib/source.js +6 -3
- package/lib/update.js +5 -13
- package/lib/utilities.js +25 -3
- package/lib/wrap.js +18 -1
- package/package.json +1 -1
- package/lib/session/todos.js +0 -293
package/lib/session/lifecycle.js
CHANGED
|
@@ -3,14 +3,18 @@
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
const {
|
|
6
|
+
const sessionLoad = require('./load.js');
|
|
7
|
+
const { isAbortError, tagLoaded, fetchSnapshot, fetchExtras } = sessionLoad;
|
|
8
|
+
const watch = require('./watch.js');
|
|
9
|
+
const { createDiskWatcher, DEBOUNCE_MS } = watch;
|
|
8
10
|
const files = require('../files.js');
|
|
9
|
-
const { itemPath, relativeAge
|
|
10
|
-
const
|
|
11
|
+
const { itemPath, relativeAge } = files;
|
|
12
|
+
const deps = require('../deps.js');
|
|
13
|
+
const { MANIFEST, LOCKFILE, depFileMeta } = deps;
|
|
11
14
|
const review = require('../review.js');
|
|
12
15
|
const { hasNotes, loadReview } = review;
|
|
13
|
-
const
|
|
16
|
+
const reviewMerge = require('../review-merge.js');
|
|
17
|
+
const { mergeReview } = reviewMerge;
|
|
14
18
|
|
|
15
19
|
const loadOptions = (api, extra = {}) => {
|
|
16
20
|
const { loadConfig, ui } = api;
|
|
@@ -78,7 +82,7 @@ const failOpen = (api, error) => {
|
|
|
78
82
|
|
|
79
83
|
const restoreAfterLoad = (api, here, wasTodo) => {
|
|
80
84
|
const { collection, nav, ui } = api;
|
|
81
|
-
nav.
|
|
85
|
+
nav.tasksOpen = wasTodo;
|
|
82
86
|
if (here && !wasTodo) {
|
|
83
87
|
const idx = collection.findRestoredIndex(here);
|
|
84
88
|
if (idx >= 0) nav.index = idx;
|
|
@@ -148,7 +152,7 @@ const applyLoaded = (api, loaded) => {
|
|
|
148
152
|
if (loaded.generation !== undefined) {
|
|
149
153
|
if (!loader.accept(loaded.generation, ui.done)) return false;
|
|
150
154
|
}
|
|
151
|
-
const wasTodo = nav.
|
|
155
|
+
const wasTodo = nav.tasksOpen === true;
|
|
152
156
|
const here = ui.current();
|
|
153
157
|
ui.top = loaded.top ?? api.loadConfig.cwd;
|
|
154
158
|
collection.replace(loaded.items);
|
|
@@ -239,6 +243,13 @@ const clearNpmExtras = (api) => {
|
|
|
239
243
|
api.npmExtras = null;
|
|
240
244
|
};
|
|
241
245
|
|
|
246
|
+
const isWanted = (api, gen) => {
|
|
247
|
+
if (!api.loader.isCurrent(gen)) return false;
|
|
248
|
+
if (!api.ui.done) return true;
|
|
249
|
+
api.ops.clearBusy();
|
|
250
|
+
return false;
|
|
251
|
+
};
|
|
252
|
+
|
|
242
253
|
const followExtras = async (api, loaded, gen, kind) => {
|
|
243
254
|
const extra = loadOptions(api, {
|
|
244
255
|
paths: api.loadConfig.paths,
|
|
@@ -246,25 +257,18 @@ const followExtras = async (api, loaded, gen, kind) => {
|
|
|
246
257
|
});
|
|
247
258
|
let next;
|
|
248
259
|
try {
|
|
249
|
-
next = await
|
|
260
|
+
next = await fetchExtras(api.loadConfig.repo, loaded, extra);
|
|
250
261
|
} catch (error) {
|
|
251
|
-
if (
|
|
252
|
-
return void failOpen(api, error);
|
|
253
|
-
}
|
|
254
|
-
if (!api.loader.isCurrent(gen)) return;
|
|
255
|
-
if (api.ui.done) {
|
|
256
|
-
api.ops.clearBusy();
|
|
262
|
+
if (api.loader.isCurrent(gen)) failOpen(api, error);
|
|
257
263
|
return;
|
|
258
264
|
}
|
|
259
|
-
if (!
|
|
260
|
-
|
|
265
|
+
if (!isWanted(api, gen)) return;
|
|
266
|
+
if (next) {
|
|
267
|
+
applyLoaded(api, tagLoaded(next, gen));
|
|
268
|
+
cacheNpmExtras(api, next);
|
|
269
|
+
} else {
|
|
261
270
|
api.loader.pendingExtras = false;
|
|
262
|
-
api.ui.paint();
|
|
263
|
-
finishIfEmpty(api, kind);
|
|
264
|
-
return;
|
|
265
271
|
}
|
|
266
|
-
applyLoaded(api, api.loader.tag(next, gen));
|
|
267
|
-
cacheNpmExtras(api, next);
|
|
268
272
|
api.ops.clearBusy();
|
|
269
273
|
api.ui.paint();
|
|
270
274
|
finishIfEmpty(api, kind);
|
|
@@ -297,7 +301,13 @@ const resetNavAfterLoad = (api, extra = {}) => {
|
|
|
297
301
|
if (extra.afterLoad) extra.afterLoad();
|
|
298
302
|
};
|
|
299
303
|
|
|
304
|
+
const refreshListsForPane = (api) => {
|
|
305
|
+
const pane = api.ui.nav.pane;
|
|
306
|
+
if (pane === 'commits' && api.ui.commits) api.ui.commits.refresh();
|
|
307
|
+
};
|
|
308
|
+
|
|
300
309
|
const afterSnapshot = (api, kind, extra = {}) => {
|
|
310
|
+
refreshListsForPane(api);
|
|
301
311
|
if (kind !== 'open' && kind !== 'watch') {
|
|
302
312
|
return void resetNavAfterLoad(api, extra);
|
|
303
313
|
}
|
|
@@ -315,7 +325,7 @@ const runSnapshot = async (api, gen, kind, extra = {}) => {
|
|
|
315
325
|
beginSnapshot(api, extra);
|
|
316
326
|
let loaded;
|
|
317
327
|
try {
|
|
318
|
-
loaded = await
|
|
328
|
+
loaded = await fetchSnapshot(
|
|
319
329
|
api.loadConfig.repo,
|
|
320
330
|
api.loadConfig.cwd,
|
|
321
331
|
api.loadConfig.paths,
|
|
@@ -325,15 +335,11 @@ const runSnapshot = async (api, gen, kind, extra = {}) => {
|
|
|
325
335
|
}),
|
|
326
336
|
);
|
|
327
337
|
} catch (error) {
|
|
328
|
-
if (
|
|
329
|
-
return void failOpen(api, error);
|
|
330
|
-
}
|
|
331
|
-
if (!api.loader.isCurrent(gen)) return;
|
|
332
|
-
if (api.ui.done) {
|
|
333
|
-
api.ops.clearBusy();
|
|
338
|
+
if (api.loader.isCurrent(gen)) failOpen(api, error);
|
|
334
339
|
return;
|
|
335
340
|
}
|
|
336
|
-
|
|
341
|
+
if (!isWanted(api, gen)) return;
|
|
342
|
+
applyLoaded(api, tagLoaded(loaded, gen));
|
|
337
343
|
afterSnapshot(api, kind, extra);
|
|
338
344
|
if (loaded.pending === true || npmExtrasStale(api)) {
|
|
339
345
|
api.ops.setBusy('checking npm');
|
|
@@ -379,7 +385,7 @@ const refreshFromRepo = (api, extra = {}) => {
|
|
|
379
385
|
api.loadConfig.paths,
|
|
380
386
|
snapshotOpts(api, extra),
|
|
381
387
|
);
|
|
382
|
-
applyLoaded(api,
|
|
388
|
+
applyLoaded(api, tagLoaded(loaded, gen));
|
|
383
389
|
afterSnapshot(api, kind, extra);
|
|
384
390
|
if (loaded.pending === true || npmExtrasStale(api)) {
|
|
385
391
|
api.ops.setBusy('checking npm');
|
|
@@ -463,59 +469,6 @@ const catchUpWatch = (api) => {
|
|
|
463
469
|
refreshFromRepo(api, { keepView: true, quiet: true });
|
|
464
470
|
};
|
|
465
471
|
|
|
466
|
-
const stampFile = (root, rel) => {
|
|
467
|
-
const full = path.join(root, rel);
|
|
468
|
-
let stat;
|
|
469
|
-
try {
|
|
470
|
-
stat = fs.statSync(full);
|
|
471
|
-
} catch {
|
|
472
|
-
return `${rel}:missing`;
|
|
473
|
-
}
|
|
474
|
-
return `${rel}:${stat.mtimeMs}:${stat.size}`;
|
|
475
|
-
};
|
|
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 ||
|
|
489
|
-
nav.pane === 'branches' ||
|
|
490
|
-
nav.pane === 'commits' ||
|
|
491
|
-
nav.pane === 'npm';
|
|
492
|
-
|
|
493
|
-
const currentFileStamp = (api) => {
|
|
494
|
-
const { nav } = api;
|
|
495
|
-
const root = api.ui.top || api.loadConfig.cwd;
|
|
496
|
-
if (watchesWorktree(nav)) return worktreeStamp(api);
|
|
497
|
-
const pane = nav.pane;
|
|
498
|
-
if (pane !== 'diff' && pane !== 'unit') return '';
|
|
499
|
-
const rel = pane === 'unit' ? nav.reviewPath : itemPath(api.ui.current());
|
|
500
|
-
if (!rel) return '';
|
|
501
|
-
return stampFile(root, rel);
|
|
502
|
-
};
|
|
503
|
-
|
|
504
|
-
const pollCurrentFile = (api) => {
|
|
505
|
-
const stamp = currentFileStamp(api);
|
|
506
|
-
if (!stamp) {
|
|
507
|
-
api.watchStamp = '';
|
|
508
|
-
return;
|
|
509
|
-
}
|
|
510
|
-
if (!api.watchStamp) {
|
|
511
|
-
api.watchStamp = stamp;
|
|
512
|
-
return;
|
|
513
|
-
}
|
|
514
|
-
if (stamp === api.watchStamp) return;
|
|
515
|
-
api.watchStamp = stamp;
|
|
516
|
-
onDiskChange(api);
|
|
517
|
-
};
|
|
518
|
-
|
|
519
472
|
const reviewFileStamp = (api) => {
|
|
520
473
|
const store = api.review && api.review.store;
|
|
521
474
|
if (!store || !store.reviewPath) return '';
|
|
@@ -549,14 +502,12 @@ const applyReviewChange = (api) => {
|
|
|
549
502
|
}
|
|
550
503
|
mergeReview(store, disk);
|
|
551
504
|
api.reviewStamp = stamp;
|
|
552
|
-
|
|
505
|
+
api.ui.paint();
|
|
553
506
|
};
|
|
554
507
|
|
|
555
508
|
const stopWatch = (api) => {
|
|
556
509
|
clearWatchRetry(api);
|
|
557
|
-
api.watchStamp = '';
|
|
558
510
|
api.reviewStamp = '';
|
|
559
|
-
if (api.ui.term) api.ui.term.stopTimer('watch');
|
|
560
511
|
if (!api.diskWatcher) return;
|
|
561
512
|
api.diskWatcher.close();
|
|
562
513
|
api.diskWatcher = null;
|
|
@@ -575,26 +526,15 @@ const startWatch = (api) => {
|
|
|
575
526
|
});
|
|
576
527
|
syncItemWatches(api);
|
|
577
528
|
noteReviewStamp(api);
|
|
578
|
-
if (!api.ui.term) return;
|
|
579
|
-
api.ui.term.startTimer(
|
|
580
|
-
'watch',
|
|
581
|
-
() => {
|
|
582
|
-
pollCurrentFile(api);
|
|
583
|
-
applyReviewChange(api);
|
|
584
|
-
},
|
|
585
|
-
POLL_MS,
|
|
586
|
-
);
|
|
587
529
|
};
|
|
588
530
|
|
|
589
531
|
const createLifecycle = (api) => {
|
|
590
532
|
api.diskWatcher = null;
|
|
591
533
|
api.watchRetry = null;
|
|
592
|
-
api.watchStamp = '';
|
|
593
534
|
api.reviewStamp = '';
|
|
594
535
|
api.ignoreWatchUntil = 0;
|
|
595
536
|
api.npmExtras = null;
|
|
596
537
|
return {
|
|
597
|
-
applyLoaded: (loaded) => applyLoaded(api, loaded),
|
|
598
538
|
load: () => load(api),
|
|
599
539
|
openLoad: () => openLoad(api),
|
|
600
540
|
loadReady: () => loadReady(api),
|
|
@@ -605,7 +545,6 @@ const createLifecycle = (api) => {
|
|
|
605
545
|
applyReviewChange: () => applyReviewChange(api),
|
|
606
546
|
noteReviewStamp: () => noteReviewStamp(api),
|
|
607
547
|
catchUpWatch: () => catchUpWatch(api),
|
|
608
|
-
pollCurrentFile: () => pollCurrentFile(api),
|
|
609
548
|
startWatch: () => startWatch(api),
|
|
610
549
|
stopWatch: () => stopWatch(api),
|
|
611
550
|
ignoreWatch: (ms) => ignoreWatch(api, ms),
|
package/lib/session/load.js
CHANGED
|
@@ -1,71 +1,66 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const utilities = require('../utilities.js');
|
|
4
|
+
const { isAbort: isAbortError } = utilities;
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
class LoadCoordinator {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.gen = 0;
|
|
9
|
+
this.abortController = null;
|
|
10
|
+
this.promise = null;
|
|
11
|
+
this.pendingExtras = false;
|
|
12
|
+
this.didLoad = false;
|
|
13
|
+
}
|
|
6
14
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
abort: null,
|
|
11
|
-
promise: null,
|
|
12
|
-
pendingExtras: false,
|
|
13
|
-
didLoad: false,
|
|
14
|
-
};
|
|
15
|
+
get signal() {
|
|
16
|
+
return this.abortController?.signal;
|
|
17
|
+
}
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return
|
|
21
|
-
}
|
|
19
|
+
bump() {
|
|
20
|
+
this.gen += 1;
|
|
21
|
+
this.abortController?.abort();
|
|
22
|
+
this.abortController = new AbortController();
|
|
23
|
+
return this.gen;
|
|
24
|
+
}
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
isCurrent(gen, done = false) {
|
|
27
|
+
return gen === this.gen && !done;
|
|
28
|
+
}
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
30
|
+
accept(gen, done = false) {
|
|
31
|
+
return this.isCurrent(gen, done);
|
|
32
|
+
}
|
|
29
33
|
|
|
30
|
-
|
|
31
|
-
abort();
|
|
32
|
-
|
|
33
|
-
state.abort = null;
|
|
34
|
-
state.promise = null;
|
|
35
|
-
state.pendingExtras = false;
|
|
36
|
-
state.didLoad = false;
|
|
37
|
-
};
|
|
34
|
+
abort() {
|
|
35
|
+
this.abortController?.abort();
|
|
36
|
+
}
|
|
38
37
|
|
|
39
|
-
|
|
38
|
+
reset() {
|
|
39
|
+
this.abort();
|
|
40
|
+
this.gen += 1;
|
|
41
|
+
this.abortController = null;
|
|
42
|
+
this.promise = null;
|
|
43
|
+
this.pendingExtras = false;
|
|
44
|
+
this.didLoad = false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
40
47
|
|
|
41
|
-
|
|
42
|
-
if (repo.loadAsync) return repo.loadAsync(cwd, paths, options);
|
|
43
|
-
return repo.load(cwd, paths, options);
|
|
44
|
-
};
|
|
48
|
+
const tagLoaded = (loaded, gen) => ({ ...loaded, generation: gen });
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
+
const fetchSnapshot = async (repo, cwd, paths, options) => {
|
|
51
|
+
if (repo.loadAsync) return repo.loadAsync(cwd, paths, options);
|
|
52
|
+
return repo.load(cwd, paths, options);
|
|
53
|
+
};
|
|
50
54
|
|
|
51
|
-
|
|
52
|
-
|
|
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,
|
|
65
|
-
},
|
|
66
|
-
['promise', 'pendingExtras', 'didLoad'],
|
|
67
|
-
['gen'],
|
|
68
|
-
);
|
|
55
|
+
const fetchExtras = async (repo, loaded, extra) => {
|
|
56
|
+
if (!repo.loadExtras) return null;
|
|
57
|
+
return repo.loadExtras(loaded, extra);
|
|
69
58
|
};
|
|
70
59
|
|
|
71
|
-
module.exports = {
|
|
60
|
+
module.exports = {
|
|
61
|
+
isAbortError,
|
|
62
|
+
LoadCoordinator,
|
|
63
|
+
tagLoaded,
|
|
64
|
+
fetchSnapshot,
|
|
65
|
+
fetchExtras,
|
|
66
|
+
};
|
|
@@ -1,106 +1,116 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const files = require('../files.js');
|
|
4
|
-
const { listPath,
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
listScroll: 0,
|
|
15
|
-
reviewPath: null,
|
|
16
|
-
fileScope: 'diff',
|
|
17
|
-
unitLine: 0,
|
|
18
|
-
selection: null,
|
|
19
|
-
mouseAnchor: null,
|
|
20
|
-
pendingClick: null,
|
|
21
|
-
todoOpen: false,
|
|
22
|
-
todoFocus: 0,
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
const clampIndex = (nav, length) => {
|
|
26
|
-
if (!length) {
|
|
27
|
-
nav.index = 0;
|
|
28
|
-
return;
|
|
4
|
+
const { listPath, isTaskItem, isTasksEntry, TASKS_FILE } = files;
|
|
5
|
+
|
|
6
|
+
const LIST_PANES = ['files', 'branches', 'commits', 'npm'];
|
|
7
|
+
|
|
8
|
+
const freshListScroll = () =>
|
|
9
|
+
Object.fromEntries(LIST_PANES.map((pane) => [pane, 0]));
|
|
10
|
+
|
|
11
|
+
class Navigation {
|
|
12
|
+
constructor(options = {}) {
|
|
13
|
+
this.reset(options.startPane ?? 'files');
|
|
29
14
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
15
|
+
|
|
16
|
+
reset(startPane = this.pane) {
|
|
17
|
+
this.pane = startPane;
|
|
18
|
+
this.index = 0;
|
|
19
|
+
this.fileCursor = 0;
|
|
20
|
+
this.branchCursor = 0;
|
|
21
|
+
this.commitCursor = 0;
|
|
22
|
+
this.npmCursor = 0;
|
|
23
|
+
this.scroll = 0;
|
|
24
|
+
this.listScrollByPane = freshListScroll();
|
|
25
|
+
this.reviewPath = null;
|
|
26
|
+
this.fileScope = 'diff';
|
|
27
|
+
this.unitLine = 0;
|
|
28
|
+
this.selection = null;
|
|
29
|
+
this.mouseAnchor = null;
|
|
30
|
+
this.pendingClick = null;
|
|
31
|
+
this.lastClick = null;
|
|
32
|
+
this.tasksOpen = false;
|
|
33
|
+
this.tasksFocus = 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
clampIndex(length) {
|
|
37
|
+
if (!length) {
|
|
38
|
+
this.index = 0;
|
|
39
|
+
return;
|
|
41
40
|
}
|
|
42
|
-
|
|
41
|
+
if (this.index < 0) this.index = 0;
|
|
42
|
+
if (this.index >= length) this.index = length - 1;
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return;
|
|
44
|
+
|
|
45
|
+
current(items, tasksItem) {
|
|
46
|
+
if (this.tasksOpen) return tasksItem;
|
|
47
|
+
if (this.pane === 'unit' && this.reviewPath) {
|
|
48
|
+
const item = items[this.index];
|
|
49
|
+
if (item && listPath(item) === this.reviewPath) return item;
|
|
50
|
+
for (const entry of items) {
|
|
51
|
+
if (listPath(entry) === this.reviewPath) return entry;
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
return items[this.index] ?? null;
|
|
57
56
|
}
|
|
58
|
-
if (nav.fileCursor >= length) nav.fileCursor = length - 1;
|
|
59
|
-
};
|
|
60
57
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
58
|
+
syncReviewPath(item) {
|
|
59
|
+
if (this.pane !== 'diff' && this.pane !== 'unit') return;
|
|
60
|
+
if (this.pane === 'unit' && this.reviewPath) return;
|
|
61
|
+
this.reviewPath = item ? listPath(item) : null;
|
|
65
62
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
63
|
+
|
|
64
|
+
clampFileCursor(length) {
|
|
65
|
+
if (!length) {
|
|
66
|
+
this.fileCursor = 0;
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (this.fileCursor >= length) this.fileCursor = length - 1;
|
|
71
70
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
|
|
72
|
+
followReviewPath(fileList, item) {
|
|
73
|
+
if (!fileList.length) {
|
|
74
|
+
this.fileCursor = 0;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
let idx = fileList.findIndex(
|
|
78
|
+
(entry) => !isTasksEntry(entry) && entry.path === this.reviewPath,
|
|
79
|
+
);
|
|
80
|
+
if (this.reviewPath === TASKS_FILE || isTaskItem(item)) {
|
|
81
|
+
idx = fileList.findIndex((entry) => isTasksEntry(entry));
|
|
82
|
+
}
|
|
83
|
+
if (idx >= 0) {
|
|
84
|
+
this.fileCursor = idx;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
this.clampFileCursor(fileList.length);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
restoreFileCursor(fileList) {
|
|
91
|
+
if (this.pane !== 'files') return;
|
|
92
|
+
this.followReviewPath(fileList, null);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
clearSelection() {
|
|
96
|
+
this.selection = null;
|
|
97
|
+
this.mouseAnchor = null;
|
|
98
|
+
this.pendingClick = null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
get listScroll() {
|
|
102
|
+
return this.listScrollByPane[this.pane] ?? 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
set listScroll(value) {
|
|
106
|
+
if (!LIST_PANES.includes(this.pane)) return;
|
|
107
|
+
this.listScrollByPane[this.pane] = value ?? 0;
|
|
75
108
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const clearSelection = (nav) => {
|
|
85
|
-
nav.selection = null;
|
|
86
|
-
nav.mouseAnchor = null;
|
|
87
|
-
nav.pendingClick = null;
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const createNavigation = (options = {}) => {
|
|
91
|
-
const nav = initialState(options);
|
|
92
|
-
nav.reset = (startPane) => {
|
|
93
|
-
const pane = startPane ?? nav.pane;
|
|
94
|
-
Object.assign(nav, initialState({ startPane: pane }));
|
|
95
|
-
};
|
|
96
|
-
nav.clampIndex = (length) => clampIndex(nav, length);
|
|
97
|
-
nav.current = (items, todoItem) => current(nav, items, todoItem);
|
|
98
|
-
nav.syncReviewPath = (item) => syncReviewPath(nav, item);
|
|
99
|
-
nav.clampFileCursor = (length) => clampFileCursor(nav, length);
|
|
100
|
-
nav.followReviewPath = (list, item) => followReviewPath(nav, list, item);
|
|
101
|
-
nav.restoreFileCursor = (list) => restoreFileCursor(nav, list);
|
|
102
|
-
nav.clearSelection = () => clearSelection(nav);
|
|
103
|
-
return nav;
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
module.exports = { createNavigation };
|
|
109
|
+
|
|
110
|
+
resetListScroll(pane = this.pane) {
|
|
111
|
+
if (!LIST_PANES.includes(pane)) return;
|
|
112
|
+
this.listScrollByPane[pane] = 0;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
module.exports = { Navigation };
|