reslop 0.1.9 → 0.2.0
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 +12 -1
- package/README.md +2 -1
- package/bin/reslop.js +2 -2
- package/lib/ansi.js +4 -2
- package/lib/api.js +2 -2
- package/lib/capabilities.js +7 -14
- package/lib/cli.js +28 -47
- 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} +6 -5
- package/lib/{git/dependencies.js → git-deps.js} +111 -38
- package/lib/git.js +589 -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 +14 -68
- package/lib/render/chrome.js +24 -9
- package/lib/render/commits.js +6 -59
- package/lib/render/diff.js +16 -16
- package/lib/render/files.js +6 -34
- package/lib/render/frame.js +23 -12
- package/lib/render/notes.js +4 -3
- package/lib/render/primitives.js +45 -0
- package/lib/{render.js → render/render.js} +5 -5
- package/lib/render/unit.js +41 -0
- package/lib/review.js +750 -14
- package/lib/select.js +5 -10
- package/lib/session/accessors.js +9 -1
- package/lib/session/actions.js +32 -16
- package/lib/session/branches.js +116 -128
- package/lib/session/change-actions.js +202 -123
- package/lib/session/commit.js +105 -112
- package/lib/session/compose.js +158 -13
- package/lib/session/dispatch.js +104 -60
- package/lib/session/files-pane.js +142 -148
- package/lib/session/items.js +22 -30
- package/lib/session/lifecycle.js +44 -44
- 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 +54 -57
- package/lib/session/review.js +23 -37
- package/lib/session/terminal.js +6 -12
- package/lib/session/todos.js +8 -12
- 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 +11 -19
- package/lib/session.js +21 -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 +1 -1
- 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,10 +4,10 @@ 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
9
|
const { itemPath, relativeAge } = files;
|
|
10
|
-
const { MANIFEST, LOCKFILE } = require('../deps
|
|
10
|
+
const { MANIFEST, LOCKFILE } = require('../deps.js');
|
|
11
11
|
const review = require('../review.js');
|
|
12
12
|
const { hasNotes } = review;
|
|
13
13
|
|
|
@@ -30,8 +30,6 @@ const shouldDeferExtras = (api) => {
|
|
|
30
30
|
return audit && (waitingOutdated || waitingAudit);
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
const watchOpts = (api) => api.watch ?? {};
|
|
34
|
-
|
|
35
33
|
const endReadLoop = (api) => {
|
|
36
34
|
const finish = api.ui.finishReadLoop;
|
|
37
35
|
if (finish) finish();
|
|
@@ -50,8 +48,6 @@ const kindFromExtra = (extra = {}) => {
|
|
|
50
48
|
return 'reload';
|
|
51
49
|
};
|
|
52
50
|
|
|
53
|
-
const shouldResetNav = (kind) => kind !== 'open' && kind !== 'watch';
|
|
54
|
-
|
|
55
51
|
const finishIfEmpty = (api, kind) => {
|
|
56
52
|
const { collection, review: notes, ui, nav } = api;
|
|
57
53
|
if (collection.items.length) return;
|
|
@@ -125,9 +121,25 @@ const stampItemDates = (items, dates) => {
|
|
|
125
121
|
}
|
|
126
122
|
};
|
|
127
123
|
|
|
124
|
+
const syncUnitEditor = (api) => {
|
|
125
|
+
const composer = api.ui.composer;
|
|
126
|
+
if (!composer || typeof composer.applyDiskText !== 'function') return;
|
|
127
|
+
if (api.nav.pane !== 'unit') return;
|
|
128
|
+
const rel = api.nav.reviewPath;
|
|
129
|
+
if (!rel) return;
|
|
130
|
+
const repo = api.ui.repo;
|
|
131
|
+
if (!repo || typeof repo.fileText !== 'function') return;
|
|
132
|
+
const text = repo.fileText(api.ui.top || api.loadConfig.cwd, rel, api.ui.rev);
|
|
133
|
+
composer.applyDiskText(text);
|
|
134
|
+
};
|
|
135
|
+
|
|
128
136
|
const syncItemWatches = (api) => {
|
|
129
137
|
if (!api.diskWatcher) return;
|
|
130
|
-
|
|
138
|
+
const rels = itemRels(api.collection);
|
|
139
|
+
if (api.nav.pane === 'unit' && api.nav.reviewPath) {
|
|
140
|
+
if (!rels.includes(api.nav.reviewPath)) rels.push(api.nav.reviewPath);
|
|
141
|
+
}
|
|
142
|
+
api.diskWatcher.watchFiles(rels);
|
|
131
143
|
};
|
|
132
144
|
|
|
133
145
|
const applyLoaded = (api, loaded) => {
|
|
@@ -160,6 +172,7 @@ const applyLoaded = (api, loaded) => {
|
|
|
160
172
|
markLoaded(api);
|
|
161
173
|
loader.pendingExtras = loaded.pending === true;
|
|
162
174
|
syncItemWatches(api);
|
|
175
|
+
syncUnitEditor(api);
|
|
163
176
|
return true;
|
|
164
177
|
};
|
|
165
178
|
|
|
@@ -173,10 +186,10 @@ const load = (api) => {
|
|
|
173
186
|
applyLoaded(api, loaded);
|
|
174
187
|
};
|
|
175
188
|
|
|
176
|
-
const fileStamp = (top, rel
|
|
189
|
+
const fileStamp = (top, rel) => {
|
|
177
190
|
const full = path.join(top, rel);
|
|
178
191
|
try {
|
|
179
|
-
const stat = statSync(full);
|
|
192
|
+
const stat = fs.statSync(full);
|
|
180
193
|
return `${stat.mtimeMs}:${stat.size}`;
|
|
181
194
|
} catch {
|
|
182
195
|
return 'missing';
|
|
@@ -184,11 +197,9 @@ const fileStamp = (top, rel, statSync) => {
|
|
|
184
197
|
};
|
|
185
198
|
|
|
186
199
|
const npmStamp = (api) => {
|
|
187
|
-
const extra = watchOpts(api);
|
|
188
|
-
const statSync = extra.statSync ?? fs.statSync;
|
|
189
200
|
const top = api.ui.top || api.loadConfig.cwd;
|
|
190
|
-
const pkg = fileStamp(top, MANIFEST
|
|
191
|
-
const lock = fileStamp(top, LOCKFILE
|
|
201
|
+
const pkg = fileStamp(top, MANIFEST);
|
|
202
|
+
const lock = fileStamp(top, LOCKFILE);
|
|
192
203
|
return `${pkg}|${lock}`;
|
|
193
204
|
};
|
|
194
205
|
|
|
@@ -216,8 +227,7 @@ const followExtras = async (api, loaded, gen, kind) => {
|
|
|
216
227
|
next = await api.loader.fetchExtras(api.loadConfig.repo, loaded, extra);
|
|
217
228
|
} catch (error) {
|
|
218
229
|
if (!api.loader.isCurrent(gen)) return;
|
|
219
|
-
failOpen(api, error);
|
|
220
|
-
return;
|
|
230
|
+
return void failOpen(api, error);
|
|
221
231
|
}
|
|
222
232
|
if (!api.loader.isCurrent(gen)) return;
|
|
223
233
|
if (api.ui.done) {
|
|
@@ -267,9 +277,8 @@ const resetNavAfterLoad = (api, extra = {}) => {
|
|
|
267
277
|
};
|
|
268
278
|
|
|
269
279
|
const afterSnapshot = (api, kind, extra = {}) => {
|
|
270
|
-
if (
|
|
271
|
-
resetNavAfterLoad(api, extra);
|
|
272
|
-
return;
|
|
280
|
+
if (kind !== 'open' && kind !== 'watch') {
|
|
281
|
+
return void resetNavAfterLoad(api, extra);
|
|
273
282
|
}
|
|
274
283
|
if (extra.afterLoad) extra.afterLoad();
|
|
275
284
|
};
|
|
@@ -296,8 +305,7 @@ const runSnapshot = async (api, gen, kind, extra = {}) => {
|
|
|
296
305
|
);
|
|
297
306
|
} catch (error) {
|
|
298
307
|
if (!api.loader.isCurrent(gen)) return;
|
|
299
|
-
failOpen(api, error);
|
|
300
|
-
return;
|
|
308
|
+
return void failOpen(api, error);
|
|
301
309
|
}
|
|
302
310
|
if (!api.loader.isCurrent(gen)) return;
|
|
303
311
|
if (api.ui.done) {
|
|
@@ -354,8 +362,7 @@ const refreshFromRepo = (api, extra = {}) => {
|
|
|
354
362
|
afterSnapshot(api, kind, extra);
|
|
355
363
|
if (loaded.pending) {
|
|
356
364
|
api.ops.setBusy('checking npm');
|
|
357
|
-
followExtras(api, loaded, gen, kind);
|
|
358
|
-
return;
|
|
365
|
+
return void followExtras(api, loaded, gen, kind);
|
|
359
366
|
}
|
|
360
367
|
if (kind === 'keep') return;
|
|
361
368
|
finishIfEmpty(api, kind);
|
|
@@ -377,8 +384,7 @@ const reloadAfterChange = (api, afterLoad) => {
|
|
|
377
384
|
if (afterLoad) afterLoad();
|
|
378
385
|
};
|
|
379
386
|
if (api.ui.uiOpen && api.loadConfig.repo.loadAsync) {
|
|
380
|
-
refreshFromRepo(api, { afterLoad: restore });
|
|
381
|
-
return;
|
|
387
|
+
return void refreshFromRepo(api, { afterLoad: restore });
|
|
382
388
|
}
|
|
383
389
|
refreshFromRepo(api);
|
|
384
390
|
restore();
|
|
@@ -392,9 +398,7 @@ const onReload = (api) => {
|
|
|
392
398
|
|
|
393
399
|
const clearWatchRetry = (api) => {
|
|
394
400
|
if (api.watchRetry === null) return;
|
|
395
|
-
|
|
396
|
-
const clearTimeoutFn = extra.clearTimeout ?? clearTimeout;
|
|
397
|
-
clearTimeoutFn(api.watchRetry);
|
|
401
|
+
clearTimeout(api.watchRetry);
|
|
398
402
|
api.watchRetry = null;
|
|
399
403
|
};
|
|
400
404
|
|
|
@@ -404,6 +408,10 @@ const shouldDeferWatch = (api) => {
|
|
|
404
408
|
if (!ui.uiOpen) return true;
|
|
405
409
|
if (!api.loader.didLoad) return true;
|
|
406
410
|
if (ops.gitBusy) return true;
|
|
411
|
+
if (ui.mode === 'compose') {
|
|
412
|
+
const kind = ui.composer && ui.composer.state.composeKind;
|
|
413
|
+
if (kind === 'file') return false;
|
|
414
|
+
}
|
|
407
415
|
if (ui.mode !== 'review') return true;
|
|
408
416
|
return false;
|
|
409
417
|
};
|
|
@@ -411,38 +419,32 @@ const shouldDeferWatch = (api) => {
|
|
|
411
419
|
const WATCH_IGNORE_MS = 500;
|
|
412
420
|
|
|
413
421
|
const ignoreWatch = (api, ms = WATCH_IGNORE_MS) => {
|
|
414
|
-
|
|
415
|
-
const now = extra.now ?? Date.now;
|
|
416
|
-
api.ignoreWatchUntil = now() + ms;
|
|
422
|
+
api.ignoreWatchUntil = Date.now() + ms;
|
|
417
423
|
};
|
|
418
424
|
|
|
419
425
|
const onDiskChange = (api) => {
|
|
420
|
-
|
|
421
|
-
const now = extra.now ?? Date.now;
|
|
422
|
-
if (now() < (api.ignoreWatchUntil ?? 0)) return;
|
|
426
|
+
if (Date.now() < (api.ignoreWatchUntil ?? 0)) return;
|
|
423
427
|
if (shouldDeferWatch(api)) {
|
|
424
428
|
if (api.watchRetry !== null) return;
|
|
425
|
-
|
|
426
|
-
const delay = extra.debounceMs ?? 200;
|
|
427
|
-
api.watchRetry = setTimeoutFn(() => {
|
|
429
|
+
api.watchRetry = setTimeout(() => {
|
|
428
430
|
api.watchRetry = null;
|
|
429
431
|
onDiskChange(api);
|
|
430
|
-
},
|
|
432
|
+
}, DEBOUNCE_MS);
|
|
433
|
+
if (typeof api.watchRetry.unref === 'function') api.watchRetry.unref();
|
|
431
434
|
return;
|
|
432
435
|
}
|
|
433
436
|
refreshFromRepo(api, { keepView: true, quiet: true });
|
|
434
437
|
};
|
|
435
438
|
|
|
436
439
|
const currentFileStamp = (api) => {
|
|
437
|
-
|
|
438
|
-
|
|
440
|
+
const pane = api.nav.pane;
|
|
441
|
+
if (pane !== 'diff' && pane !== 'unit') return '';
|
|
442
|
+
const rel = pane === 'unit' ? api.nav.reviewPath : itemPath(api.ui.current());
|
|
439
443
|
if (!rel) return '';
|
|
440
|
-
const extra = watchOpts(api);
|
|
441
|
-
const statSync = extra.statSync ?? fs.statSync;
|
|
442
444
|
const full = path.join(api.ui.top || api.loadConfig.cwd, rel);
|
|
443
445
|
let stat;
|
|
444
446
|
try {
|
|
445
|
-
stat = statSync(full);
|
|
447
|
+
stat = fs.statSync(full);
|
|
446
448
|
} catch {
|
|
447
449
|
return `${rel}:missing`;
|
|
448
450
|
}
|
|
@@ -478,12 +480,10 @@ const startWatch = (api) => {
|
|
|
478
480
|
if (!isWorktree(api.ui)) return;
|
|
479
481
|
const root = api.ui.top || api.loadConfig.cwd;
|
|
480
482
|
if (!root) return;
|
|
481
|
-
const extra = watchOpts(api);
|
|
482
483
|
api.diskWatcher = createDiskWatcher({
|
|
483
484
|
root,
|
|
484
485
|
paths: api.loadConfig.paths,
|
|
485
486
|
onChange: () => onDiskChange(api),
|
|
486
|
-
...extra,
|
|
487
487
|
});
|
|
488
488
|
syncItemWatches(api);
|
|
489
489
|
if (!api.ui.term) return;
|
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 };
|
package/lib/session/pointer.js
CHANGED
|
@@ -2,50 +2,51 @@
|
|
|
2
2
|
|
|
3
3
|
const { extractText, isRange } = require('../select.js');
|
|
4
4
|
const { hitAction } = require('../keys.js');
|
|
5
|
+
const { copyText } = require('../clipboard.js');
|
|
5
6
|
|
|
6
|
-
const mouseCell = (
|
|
7
|
-
const size =
|
|
7
|
+
const mouseCell = (ui, event) => {
|
|
8
|
+
const size = ui.lastSize ?? ui.getSize();
|
|
8
9
|
const x = Math.min(Math.max(1, event.x), size.width);
|
|
9
10
|
const y = Math.min(Math.max(1, event.y), size.height);
|
|
10
11
|
return { x, y };
|
|
11
12
|
};
|
|
12
13
|
|
|
13
|
-
const onPress = (
|
|
14
|
-
const nav =
|
|
15
|
-
const cell = mouseCell(
|
|
14
|
+
const onPress = (ui, event) => {
|
|
15
|
+
const nav = ui.nav;
|
|
16
|
+
const cell = mouseCell(ui, event);
|
|
16
17
|
nav.mouseAnchor = cell;
|
|
17
18
|
nav.pendingClick = null;
|
|
18
19
|
nav.selection = null;
|
|
19
|
-
const frame =
|
|
20
|
+
const frame = ui.lastFrame;
|
|
20
21
|
if (frame && cell.y === frame.height) {
|
|
21
22
|
nav.pendingClick = hitAction(frame.buttons, cell.x - 1);
|
|
22
23
|
}
|
|
23
24
|
};
|
|
24
25
|
|
|
25
|
-
const onDrag = (
|
|
26
|
-
const nav =
|
|
26
|
+
const onDrag = (ui, event) => {
|
|
27
|
+
const nav = ui.nav;
|
|
27
28
|
if (!nav.mouseAnchor) return;
|
|
28
29
|
nav.pendingClick = null;
|
|
29
30
|
nav.selection = {
|
|
30
31
|
start: nav.mouseAnchor,
|
|
31
|
-
end: mouseCell(
|
|
32
|
+
end: mouseCell(ui, event),
|
|
32
33
|
};
|
|
33
34
|
};
|
|
34
35
|
|
|
35
|
-
const clickButton = (
|
|
36
|
-
const nav =
|
|
37
|
-
const frame =
|
|
36
|
+
const clickButton = (ui, cell, clickId) => {
|
|
37
|
+
const nav = ui.nav;
|
|
38
|
+
const frame = ui.lastFrame;
|
|
38
39
|
if (!clickId || !frame || isRange(nav.selection)) return false;
|
|
39
40
|
const id = hitAction(frame.buttons, cell.x - 1);
|
|
40
41
|
if (id === clickId && cell.y === frame.height) {
|
|
41
|
-
|
|
42
|
+
ui.dispatch(id);
|
|
42
43
|
}
|
|
43
44
|
nav.selection = null;
|
|
44
45
|
return true;
|
|
45
46
|
};
|
|
46
47
|
|
|
47
|
-
const clickHit = (
|
|
48
|
-
const nav =
|
|
48
|
+
const clickHit = (ui, hits, cell, onHit) => {
|
|
49
|
+
const nav = ui.nav;
|
|
49
50
|
if (!hits || isRange(nav.selection)) return false;
|
|
50
51
|
const x = cell.x - 1;
|
|
51
52
|
const hit = hits.find((row) => {
|
|
@@ -54,108 +55,104 @@ const clickHit = (options, hits, cell, onHit) => {
|
|
|
54
55
|
return x >= row.x0 && x < row.x1;
|
|
55
56
|
});
|
|
56
57
|
if (!hit) return false;
|
|
57
|
-
onHit(
|
|
58
|
+
onHit(ui, hit);
|
|
58
59
|
nav.selection = null;
|
|
59
60
|
return true;
|
|
60
61
|
};
|
|
61
62
|
|
|
62
|
-
const copySelection = (
|
|
63
|
-
const nav =
|
|
64
|
-
const frame =
|
|
63
|
+
const copySelection = (ui) => {
|
|
64
|
+
const nav = ui.nav;
|
|
65
|
+
const frame = ui.lastFrame;
|
|
65
66
|
if (!isRange(nav.selection) || !frame) return;
|
|
66
67
|
const text = extractText(frame.rows, nav.selection);
|
|
67
68
|
if (!text) return;
|
|
68
|
-
const ok =
|
|
69
|
-
|
|
69
|
+
const ok = copyText(text, ui.stdout);
|
|
70
|
+
ui.status = ok ? 'copied' : 'copy failed';
|
|
70
71
|
};
|
|
71
72
|
|
|
72
73
|
const RELEASE_TARGETS = [
|
|
73
74
|
{
|
|
74
75
|
match: () => true,
|
|
75
76
|
hits: 'statusHits',
|
|
76
|
-
onHit: (
|
|
77
|
-
if (hit.id === 'branch')
|
|
78
|
-
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
options.confirmChoice(hit.id);
|
|
77
|
+
onHit: (ui, hit) => {
|
|
78
|
+
if (hit.id === 'branch') return void ui.gitBranches.onBranch();
|
|
79
|
+
ui.input.confirmChoice(hit.id);
|
|
82
80
|
},
|
|
83
81
|
},
|
|
84
82
|
{
|
|
85
|
-
match: (
|
|
83
|
+
match: (ui) => ui.pane === 'files',
|
|
86
84
|
hits: 'fileHits',
|
|
87
|
-
onHit: (
|
|
88
|
-
|
|
89
|
-
|
|
85
|
+
onHit: (ui, hit) => {
|
|
86
|
+
ui.nav.fileCursor = hit.cursor;
|
|
87
|
+
ui.filesPane.onOpenFile();
|
|
90
88
|
},
|
|
91
89
|
},
|
|
92
90
|
{
|
|
93
|
-
match: (
|
|
91
|
+
match: (ui) => ui.pane === 'branches',
|
|
94
92
|
hits: 'fileHits',
|
|
95
|
-
onHit: (
|
|
96
|
-
|
|
97
|
-
|
|
93
|
+
onHit: (ui, hit) => {
|
|
94
|
+
ui.nav.branchCursor = hit.cursor;
|
|
95
|
+
ui.gitBranches.onCheckoutBranch();
|
|
98
96
|
},
|
|
99
97
|
},
|
|
100
98
|
{
|
|
101
|
-
match: (
|
|
99
|
+
match: (ui) => ui.pane === 'commits',
|
|
102
100
|
hits: 'fileHits',
|
|
103
|
-
onHit: (
|
|
104
|
-
|
|
101
|
+
onHit: (ui, hit) => {
|
|
102
|
+
ui.nav.commitCursor = hit.cursor;
|
|
105
103
|
},
|
|
106
104
|
},
|
|
107
105
|
{
|
|
108
|
-
match: (
|
|
106
|
+
match: (ui) => ui.pane === 'diff',
|
|
109
107
|
hits: 'todoHits',
|
|
110
|
-
onHit: (
|
|
108
|
+
onHit: (ui, hit) => ui.composer.onTodoHit(hit.cursor),
|
|
111
109
|
},
|
|
112
110
|
{
|
|
113
|
-
match: (
|
|
111
|
+
match: (ui) => ui.mode === 'compose',
|
|
114
112
|
hits: 'templateHits',
|
|
115
|
-
onHit: (
|
|
113
|
+
onHit: (ui, hit) => ui.selectTemplate(hit.cursor),
|
|
116
114
|
},
|
|
117
115
|
];
|
|
118
116
|
|
|
119
|
-
const onRelease = (
|
|
120
|
-
const nav =
|
|
121
|
-
const cell = mouseCell(
|
|
117
|
+
const onRelease = (ui, event) => {
|
|
118
|
+
const nav = ui.nav;
|
|
119
|
+
const cell = mouseCell(ui, event);
|
|
122
120
|
if (nav.mouseAnchor) {
|
|
123
121
|
nav.selection = { start: nav.mouseAnchor, end: cell };
|
|
124
122
|
}
|
|
125
123
|
const clickId = nav.pendingClick;
|
|
126
124
|
nav.pendingClick = null;
|
|
127
125
|
nav.mouseAnchor = null;
|
|
128
|
-
if (clickButton(
|
|
129
|
-
const frame =
|
|
126
|
+
if (clickButton(ui, cell, clickId)) return;
|
|
127
|
+
const frame = ui.lastFrame;
|
|
130
128
|
for (const target of RELEASE_TARGETS) {
|
|
131
|
-
if (!target.match(
|
|
129
|
+
if (!target.match(ui)) continue;
|
|
132
130
|
const hits = frame?.[target.hits];
|
|
133
|
-
const opened = clickHit(
|
|
131
|
+
const opened = clickHit(ui, hits, cell, target.onHit);
|
|
134
132
|
if (opened) return;
|
|
135
133
|
}
|
|
136
|
-
copySelection(
|
|
134
|
+
copySelection(ui);
|
|
137
135
|
};
|
|
138
136
|
|
|
139
137
|
const POINTER_HANDLERS = {
|
|
140
|
-
wheelUp: (
|
|
141
|
-
wheelDown: (
|
|
138
|
+
wheelUp: (ui) => ui.filesPane.onScroll(-1),
|
|
139
|
+
wheelDown: (ui) => ui.filesPane.onScroll(1),
|
|
142
140
|
press: onPress,
|
|
143
141
|
drag: onDrag,
|
|
144
142
|
release: onRelease,
|
|
145
143
|
};
|
|
146
144
|
|
|
147
|
-
const handle = (
|
|
145
|
+
const handle = (ui, event) => {
|
|
148
146
|
const kind = event.kind;
|
|
149
147
|
const handler = POINTER_HANDLERS[kind];
|
|
150
148
|
if (!handler) return;
|
|
151
149
|
const isWheel = kind === 'wheelUp' || kind === 'wheelDown';
|
|
152
150
|
if (!isWheel && event.btn !== 0 && event.btn !== undefined) return;
|
|
153
|
-
handler(
|
|
151
|
+
handler(ui, event);
|
|
154
152
|
};
|
|
155
153
|
|
|
156
|
-
const createPointer = (
|
|
157
|
-
handle: (event) => handle(
|
|
158
|
-
mouseCell: (event) => mouseCell(options, event),
|
|
154
|
+
const createPointer = (ui) => ({
|
|
155
|
+
handle: (event) => handle(ui, event),
|
|
159
156
|
});
|
|
160
157
|
|
|
161
158
|
module.exports = { createPointer };
|