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/watch.js
CHANGED
|
@@ -74,12 +74,13 @@ const closeWatcher = (watcher) => {
|
|
|
74
74
|
|
|
75
75
|
const schedule = (state) => {
|
|
76
76
|
if (state.closed) return;
|
|
77
|
-
if (state.timer !== null)
|
|
78
|
-
state.timer =
|
|
77
|
+
if (state.timer !== null) clearTimeout(state.timer);
|
|
78
|
+
state.timer = setTimeout(() => {
|
|
79
79
|
state.timer = null;
|
|
80
80
|
if (state.closed) return;
|
|
81
81
|
state.onChange();
|
|
82
82
|
}, state.debounceMs);
|
|
83
|
+
if (typeof state.timer.unref === 'function') state.timer.unref();
|
|
83
84
|
};
|
|
84
85
|
|
|
85
86
|
const handleWatchEvent = (state, dir, eventType, filename) => {
|
|
@@ -100,9 +101,9 @@ const watchOne = (state, target, extra = {}) => {
|
|
|
100
101
|
const onEvent = (eventType, filename) => {
|
|
101
102
|
handleWatchEvent(state, target, eventType, filename);
|
|
102
103
|
};
|
|
103
|
-
const watcher =
|
|
104
|
+
const watcher = fs.watch(
|
|
104
105
|
target,
|
|
105
|
-
{ persistent:
|
|
106
|
+
{ persistent: false, encoding: 'utf8', ...extra },
|
|
106
107
|
onEvent,
|
|
107
108
|
);
|
|
108
109
|
if (typeof watcher.on === 'function') {
|
|
@@ -120,7 +121,7 @@ const watchTree = (state, dir) => {
|
|
|
120
121
|
if (state.recursive) return;
|
|
121
122
|
let entries;
|
|
122
123
|
try {
|
|
123
|
-
entries =
|
|
124
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
124
125
|
} catch {
|
|
125
126
|
return;
|
|
126
127
|
}
|
|
@@ -135,7 +136,7 @@ const maybeWatchNewDir = (state, full) => {
|
|
|
135
136
|
if (state.recursive) return;
|
|
136
137
|
let stat;
|
|
137
138
|
try {
|
|
138
|
-
stat =
|
|
139
|
+
stat = fs.statSync(full);
|
|
139
140
|
} catch {
|
|
140
141
|
return;
|
|
141
142
|
}
|
|
@@ -148,15 +149,11 @@ const watchScope = (state, spec) => {
|
|
|
148
149
|
const target = path.resolve(state.root, spec);
|
|
149
150
|
let stat;
|
|
150
151
|
try {
|
|
151
|
-
stat =
|
|
152
|
+
stat = fs.statSync(target);
|
|
152
153
|
} catch {
|
|
153
|
-
watchOne(state, path.dirname(target));
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
if (stat.isDirectory()) {
|
|
157
|
-
watchTree(state, target);
|
|
158
|
-
return;
|
|
154
|
+
return void watchOne(state, path.dirname(target));
|
|
159
155
|
}
|
|
156
|
+
if (stat.isDirectory()) return void watchTree(state, target);
|
|
160
157
|
watchOne(state, path.dirname(target));
|
|
161
158
|
};
|
|
162
159
|
|
|
@@ -168,7 +165,7 @@ const watchGit = (state) => {
|
|
|
168
165
|
|
|
169
166
|
const stopState = (state) => {
|
|
170
167
|
state.closed = true;
|
|
171
|
-
if (state.timer !== null)
|
|
168
|
+
if (state.timer !== null) clearTimeout(state.timer);
|
|
172
169
|
state.timer = null;
|
|
173
170
|
while (state.watchers.length) closeWatcher(state.watchers.pop());
|
|
174
171
|
state.watched.clear();
|
|
@@ -186,11 +183,6 @@ const createDiskWatcher = (options) => {
|
|
|
186
183
|
const recursive = options.recursive ?? canWatchRecursive();
|
|
187
184
|
const state = {
|
|
188
185
|
root: path.resolve(options.root),
|
|
189
|
-
watchFn: options.watch ?? fs.watch,
|
|
190
|
-
readdirSync: options.readdirSync ?? fs.readdirSync,
|
|
191
|
-
statSync: options.statSync ?? fs.statSync,
|
|
192
|
-
setTimeoutFn: options.setTimeout ?? setTimeout,
|
|
193
|
-
clearTimeoutFn: options.clearTimeout ?? clearTimeout,
|
|
194
186
|
debounceMs: options.debounceMs ?? DEBOUNCE_MS,
|
|
195
187
|
onChange: options.onChange,
|
|
196
188
|
recursive,
|
package/lib/session.js
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const render = require('./render.js');
|
|
3
|
+
const render = require('./render/render.js');
|
|
4
4
|
const keys = require('./keys.js');
|
|
5
5
|
const review = require('./review.js');
|
|
6
|
-
const { copyText } = require('./clipboard.js');
|
|
7
6
|
const files = require('./files.js');
|
|
8
7
|
const { makeTodoItem } = files;
|
|
9
8
|
const { renderFrame } = render;
|
|
10
9
|
const { decodeChunk } = keys;
|
|
11
10
|
const { AUTOSAVE_MS, hasNotes } = review;
|
|
12
11
|
const capabilities = require('./capabilities.js');
|
|
13
|
-
const { sessionCapabilities
|
|
12
|
+
const { sessionCapabilities } = capabilities;
|
|
14
13
|
const { createTerminal, LEAVE_TERM } = require('./session/terminal.js');
|
|
15
14
|
const { createLoadCoordinator } = require('./session/load.js');
|
|
16
15
|
const items = require('./session/items.js');
|
|
@@ -35,13 +34,6 @@ const { createDispatcher, createInput } = require('./session/dispatch.js');
|
|
|
35
34
|
const CURSOR_BLINK_MS = 500;
|
|
36
35
|
const PROGRESS_MS = 80;
|
|
37
36
|
|
|
38
|
-
const systemNow = () => new Date();
|
|
39
|
-
|
|
40
|
-
const targetHasStaged = (session) => {
|
|
41
|
-
if (session.repo.hasStaged) return session.repo.hasStaged(session.top);
|
|
42
|
-
return session.items.some((item) => item.origin === 'staged');
|
|
43
|
-
};
|
|
44
|
-
|
|
45
37
|
const NAV_FIELDS = [
|
|
46
38
|
'pane',
|
|
47
39
|
'index',
|
|
@@ -51,6 +43,8 @@ const NAV_FIELDS = [
|
|
|
51
43
|
'scroll',
|
|
52
44
|
'listScroll',
|
|
53
45
|
'reviewPath',
|
|
46
|
+
'fileScope',
|
|
47
|
+
'unitLine',
|
|
54
48
|
'selection',
|
|
55
49
|
'mouseAnchor',
|
|
56
50
|
'pendingClick',
|
|
@@ -77,51 +71,34 @@ const assignOptions = (session, options) => {
|
|
|
77
71
|
session.stdout = options.stdout;
|
|
78
72
|
session.proc = options.proc ?? process;
|
|
79
73
|
session.color = options.color ?? true;
|
|
80
|
-
session.
|
|
81
|
-
const termSize = () => {
|
|
74
|
+
session.getSize = () => {
|
|
82
75
|
const width = session.stdout.columns ?? 80;
|
|
83
76
|
const height = session.stdout.rows ?? 24;
|
|
84
77
|
return { width, height };
|
|
85
78
|
};
|
|
86
|
-
session.getSize = options.getSize ?? termSize;
|
|
87
79
|
session.rev = options.rev ?? null;
|
|
88
80
|
session.revShort = '';
|
|
89
81
|
session.sourceLabel = options.sourceLabel ?? '';
|
|
90
82
|
session.repoName = options.repoName ?? '';
|
|
91
83
|
session.change = options.change ?? null;
|
|
92
|
-
session.now = options.now ?? systemNow;
|
|
93
84
|
session.forceNewReview = options.newReview === true;
|
|
94
85
|
session.readOnly = options.readOnly === true;
|
|
95
86
|
session.capabilities = sessionCapabilities(options);
|
|
96
|
-
const storage = options.reviewStorage ?? createReviewStorage(options);
|
|
97
87
|
session.audit = options.audit === true;
|
|
98
88
|
session.outdatedMap = options.outdatedMap;
|
|
99
89
|
session.auditMap = options.auditMap;
|
|
100
|
-
session.setInterval = options.setInterval ?? setInterval;
|
|
101
|
-
session.clearInterval = options.clearInterval ?? clearInterval;
|
|
102
|
-
session.watchOpts = {
|
|
103
|
-
watch: options.watch,
|
|
104
|
-
readdirSync: options.watchReaddir,
|
|
105
|
-
statSync: options.watchStat,
|
|
106
|
-
setTimeout: options.setTimeout ?? setTimeout,
|
|
107
|
-
clearTimeout: options.clearTimeout ?? clearTimeout,
|
|
108
|
-
debounceMs: options.watchDebounceMs,
|
|
109
|
-
recursive: options.watchRecursive,
|
|
110
|
-
};
|
|
111
90
|
session.updateOpts = options.update;
|
|
112
91
|
session.term = createTerminal({
|
|
113
92
|
stdin: session.stdin,
|
|
114
93
|
stdout: session.stdout,
|
|
115
94
|
proc: session.proc,
|
|
116
|
-
setInterval: session.setInterval,
|
|
117
|
-
clearInterval: session.clearInterval,
|
|
118
95
|
});
|
|
119
96
|
session.loader = createLoadCoordinator();
|
|
120
97
|
session.collection = createItemCollection();
|
|
121
98
|
session.nav = createNavigation({
|
|
122
99
|
startPane: options.startPane ?? 'files',
|
|
123
100
|
});
|
|
124
|
-
session.review = createReviewController(
|
|
101
|
+
session.review = createReviewController();
|
|
125
102
|
session.progress = createProgress(
|
|
126
103
|
session.term,
|
|
127
104
|
() => session.tickProgress(),
|
|
@@ -129,28 +106,6 @@ const assignOptions = (session, options) => {
|
|
|
129
106
|
);
|
|
130
107
|
};
|
|
131
108
|
|
|
132
|
-
const assignUpdater = (session) => {
|
|
133
|
-
session.updater = createUpdateCoordinator({
|
|
134
|
-
updateOpts: () => session.updateOpts,
|
|
135
|
-
didLoad: () => session.loader.didLoad,
|
|
136
|
-
getMode: () => session.mode,
|
|
137
|
-
setMode: (mode) => {
|
|
138
|
-
session.mode = mode;
|
|
139
|
-
},
|
|
140
|
-
isDone: () => session.done,
|
|
141
|
-
uiOpen: () => session.uiOpen,
|
|
142
|
-
paint: () => session.paint(),
|
|
143
|
-
setStatus: (value) => {
|
|
144
|
-
session.status = value;
|
|
145
|
-
},
|
|
146
|
-
startInstallProgress: () => {
|
|
147
|
-
session.progressFrame = 0;
|
|
148
|
-
session.progress.start('install');
|
|
149
|
-
},
|
|
150
|
-
stopInstallProgress: () => session.progress.stop('install'),
|
|
151
|
-
});
|
|
152
|
-
};
|
|
153
|
-
|
|
154
109
|
const assignComposer = (session) => {
|
|
155
110
|
session.composer = createComposer({
|
|
156
111
|
nav: session.nav,
|
|
@@ -168,77 +123,10 @@ const assignComposer = (session) => {
|
|
|
168
123
|
};
|
|
169
124
|
|
|
170
125
|
const assignControllers = (session) => {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
session.mode = mode;
|
|
176
|
-
};
|
|
177
|
-
session.ops = createOpsRunner({
|
|
178
|
-
top: () => session.top,
|
|
179
|
-
isDone: () => session.done,
|
|
180
|
-
getMode: () => session.mode,
|
|
181
|
-
setMode,
|
|
182
|
-
getStatus: () => session.status,
|
|
183
|
-
setStatus,
|
|
184
|
-
paint: () => session.paint(),
|
|
185
|
-
startProgress: () => session.startProgress(),
|
|
186
|
-
stopProgress: () => session.stopProgress(),
|
|
187
|
-
resetProgressFrame: () => {
|
|
188
|
-
session.progressFrame = 0;
|
|
189
|
-
},
|
|
190
|
-
});
|
|
191
|
-
session.changeActions = createChangeActions({
|
|
192
|
-
repo: () => session.repo,
|
|
193
|
-
top: () => session.top,
|
|
194
|
-
capabilities: () => session.capabilities,
|
|
195
|
-
uiOpen: () => session.uiOpen,
|
|
196
|
-
collection: session.collection,
|
|
197
|
-
getItems: () => session.items,
|
|
198
|
-
currentItem: () => session.current(),
|
|
199
|
-
nav: session.nav,
|
|
200
|
-
fileList: () => session.fileList(),
|
|
201
|
-
runBusy: session.ops.runBusy,
|
|
202
|
-
reloadAfterChange: (afterLoad) => session.reloadAfterChange(afterLoad),
|
|
203
|
-
setStatus,
|
|
204
|
-
syncReviewPath: () => session.syncReviewPath(),
|
|
205
|
-
syncFileCursor: () => session.syncFileCursor(),
|
|
206
|
-
ignoreWatch: () => session.lifecycle.ignoreWatch(),
|
|
207
|
-
});
|
|
208
|
-
session.commits = createCommitController({
|
|
209
|
-
repo: () => session.repo,
|
|
210
|
-
top: () => session.top,
|
|
211
|
-
capabilities: () => session.capabilities,
|
|
212
|
-
getItems: () => session.items,
|
|
213
|
-
uiOpen: () => session.uiOpen,
|
|
214
|
-
composer: session.composer,
|
|
215
|
-
collection: session.collection,
|
|
216
|
-
runner: session.ops,
|
|
217
|
-
refreshFromRepo: (opts) => session.refreshFromRepo(opts),
|
|
218
|
-
setMode,
|
|
219
|
-
setStatus,
|
|
220
|
-
isDone: () => session.done,
|
|
221
|
-
pendingExtras: () => session.pendingExtras,
|
|
222
|
-
getMode: () => session.mode,
|
|
223
|
-
getStatus: () => session.status,
|
|
224
|
-
nav: session.nav,
|
|
225
|
-
});
|
|
226
|
-
session.gitBranches = createBranchController({
|
|
227
|
-
repo: () => session.repo,
|
|
228
|
-
top: () => session.top,
|
|
229
|
-
capabilities: () => session.capabilities,
|
|
230
|
-
uiOpen: () => session.uiOpen,
|
|
231
|
-
composer: session.composer,
|
|
232
|
-
nav: session.nav,
|
|
233
|
-
runner: session.ops,
|
|
234
|
-
canCommit: session.commits.canCommit,
|
|
235
|
-
refreshFromRepo: (opts) => session.refreshFromRepo(opts),
|
|
236
|
-
showFiles: () => session.showFiles(),
|
|
237
|
-
getMode: () => session.mode,
|
|
238
|
-
setMode,
|
|
239
|
-
getStatus: () => session.status,
|
|
240
|
-
setStatus,
|
|
241
|
-
});
|
|
126
|
+
session.ops = createOpsRunner(session);
|
|
127
|
+
session.changeActions = createChangeActions(session);
|
|
128
|
+
session.commits = createCommitController(session);
|
|
129
|
+
session.gitBranches = createBranchController(session);
|
|
242
130
|
};
|
|
243
131
|
|
|
244
132
|
const bindSessionAccessors = (session) => {
|
|
@@ -302,29 +190,6 @@ const bindViewer = (session) => {
|
|
|
302
190
|
});
|
|
303
191
|
};
|
|
304
192
|
|
|
305
|
-
const bindFilesPane = (session) => {
|
|
306
|
-
session.filesPane = createFilesPane({
|
|
307
|
-
nav: session.nav,
|
|
308
|
-
composer: session.composer,
|
|
309
|
-
items: () => session.items,
|
|
310
|
-
todoItem: () => session.todoItem,
|
|
311
|
-
fileList: () => session.fileList(),
|
|
312
|
-
setStatus: (value) => {
|
|
313
|
-
session.status = value;
|
|
314
|
-
},
|
|
315
|
-
onQuit: () => session.onQuit(),
|
|
316
|
-
onCheckoutBranch: session.gitBranches.onCheckoutBranch,
|
|
317
|
-
onBranchMove: session.gitBranches.onBranchMove,
|
|
318
|
-
onCommitMove: session.commits.onCommitMove,
|
|
319
|
-
layout: () => session.layout,
|
|
320
|
-
setLayout: (value) => {
|
|
321
|
-
session.layout = value;
|
|
322
|
-
},
|
|
323
|
-
lastFrame: () => session.lastFrame,
|
|
324
|
-
getSize: () => session.lastSize ?? session.getSize(),
|
|
325
|
-
});
|
|
326
|
-
};
|
|
327
|
-
|
|
328
193
|
const bindLifecycle = (session) => {
|
|
329
194
|
session.lifecycle = createLifecycle({
|
|
330
195
|
loader: session.loader,
|
|
@@ -341,115 +206,24 @@ const bindLifecycle = (session) => {
|
|
|
341
206
|
outdatedMap: session.outdatedMap,
|
|
342
207
|
auditMap: session.auditMap,
|
|
343
208
|
},
|
|
344
|
-
watch: session.watchOpts,
|
|
345
209
|
ui: session,
|
|
346
210
|
});
|
|
347
211
|
};
|
|
348
212
|
|
|
349
213
|
const bindDispatch = (session) => {
|
|
350
|
-
|
|
351
|
-
session.
|
|
352
|
-
|
|
353
|
-
getPane: () => session.pane,
|
|
354
|
-
currentTarget: () => {
|
|
355
|
-
if (session.pane === 'files') return session.fileCursorEntry();
|
|
356
|
-
if (session.pane === 'branches') {
|
|
357
|
-
return session.branches[session.branchCursor];
|
|
358
|
-
}
|
|
359
|
-
if (session.pane === 'commits') {
|
|
360
|
-
const entry = session.commits.commits[session.commitCursor];
|
|
361
|
-
const staged = targetHasStaged(session);
|
|
362
|
-
if (!entry) return { canCommit: staged };
|
|
363
|
-
return { ...entry, canCommit: staged };
|
|
364
|
-
}
|
|
365
|
-
return session.current();
|
|
366
|
-
},
|
|
367
|
-
composer: session.composer,
|
|
368
|
-
changeActions: session.changeActions,
|
|
369
|
-
commits: session.commits,
|
|
370
|
-
gitBranches: session.gitBranches,
|
|
371
|
-
onFileMove: filesPane.onFileMove,
|
|
372
|
-
onBranchMove: session.gitBranches.onBranchMove,
|
|
373
|
-
onCommitMove: session.commits.onCommitMove,
|
|
374
|
-
onNext: filesPane.onNext,
|
|
375
|
-
onPrev: filesPane.onPrev,
|
|
376
|
-
onOpenFile: filesPane.onOpenFile,
|
|
377
|
-
onFiles: filesPane.onFiles,
|
|
378
|
-
onQuit: () => session.onQuit(),
|
|
379
|
-
onLayout: filesPane.onLayout,
|
|
380
|
-
onReload: session.lifecycle.onReload,
|
|
381
|
-
onScroll: filesPane.onScroll,
|
|
382
|
-
onHome: filesPane.onHome,
|
|
383
|
-
onEnd: filesPane.onEnd,
|
|
384
|
-
scrollStep: filesPane.scrollStep,
|
|
385
|
-
});
|
|
386
|
-
session.pointer = createPointer({
|
|
387
|
-
nav: session.nav,
|
|
388
|
-
getSize: () => session.lastSize ?? session.getSize(),
|
|
389
|
-
lastFrame: () => session.lastFrame,
|
|
390
|
-
copyText: (text, stdout) => session.copyText(text, stdout),
|
|
391
|
-
stdout: () => session.stdout,
|
|
392
|
-
setStatus: (value) => {
|
|
393
|
-
session.status = value;
|
|
394
|
-
},
|
|
395
|
-
dispatch: (action) => session.dispatcher.dispatch(action),
|
|
396
|
-
getPane: () => session.pane,
|
|
397
|
-
getMode: () => session.mode,
|
|
398
|
-
confirmChoice: (key) => session.input.confirmChoice(key),
|
|
399
|
-
onBranch: session.gitBranches.onBranch,
|
|
400
|
-
onOpenFile: filesPane.onOpenFile,
|
|
401
|
-
onCheckoutBranch: session.gitBranches.onCheckoutBranch,
|
|
402
|
-
onTodoHit: (cursor) => session.composer.onTodoHit(cursor),
|
|
403
|
-
selectTemplate: (index) => session.selectTemplate(index),
|
|
404
|
-
onScroll: filesPane.onScroll,
|
|
405
|
-
});
|
|
406
|
-
session.input = createInput({
|
|
407
|
-
getMode: () => session.mode,
|
|
408
|
-
getPane: () => session.pane,
|
|
409
|
-
composer: session.composer,
|
|
410
|
-
pointer: session.pointer,
|
|
411
|
-
dispatch: (action) => session.dispatcher.dispatch(action),
|
|
412
|
-
onEscape: filesPane.onEscape,
|
|
413
|
-
onQuit: () => session.onQuit(),
|
|
414
|
-
runComposeCommand: (result) => session.runComposeCommand(result),
|
|
415
|
-
quit: {
|
|
416
|
-
ready: () => session.finishQuit('ready'),
|
|
417
|
-
editing: () => session.finishQuit('editing'),
|
|
418
|
-
cancel: () => session.cancelQuit(),
|
|
419
|
-
},
|
|
420
|
-
update: {
|
|
421
|
-
accept: session.updater.accept,
|
|
422
|
-
decline: session.updater.decline,
|
|
423
|
-
},
|
|
424
|
-
drop: {
|
|
425
|
-
confirm: () => {
|
|
426
|
-
if (session.commits.dropName) {
|
|
427
|
-
return void session.commits.confirmDropCommit();
|
|
428
|
-
}
|
|
429
|
-
session.gitBranches.confirmDropBranch();
|
|
430
|
-
},
|
|
431
|
-
cancel: () => {
|
|
432
|
-
if (session.commits.dropName) {
|
|
433
|
-
return void session.commits.cancelDropCommit();
|
|
434
|
-
}
|
|
435
|
-
session.gitBranches.cancelDropBranch();
|
|
436
|
-
},
|
|
437
|
-
},
|
|
438
|
-
push: {
|
|
439
|
-
force: session.gitBranches.onForcePush,
|
|
440
|
-
cancel: session.gitBranches.cancelPush,
|
|
441
|
-
},
|
|
442
|
-
});
|
|
214
|
+
session.dispatcher = createDispatcher(session);
|
|
215
|
+
session.pointer = createPointer(session);
|
|
216
|
+
session.input = createInput(session);
|
|
443
217
|
};
|
|
444
218
|
|
|
445
219
|
const composeSession = (session, options) => {
|
|
446
220
|
assignOptions(session, options);
|
|
447
|
-
|
|
221
|
+
session.updater = createUpdateCoordinator(session);
|
|
448
222
|
assignComposer(session);
|
|
449
223
|
assignControllers(session);
|
|
450
224
|
bindSessionAccessors(session);
|
|
451
225
|
bindViewer(session);
|
|
452
|
-
|
|
226
|
+
session.filesPane = createFilesPane(session);
|
|
453
227
|
bindLifecycle(session);
|
|
454
228
|
bindDispatch(session);
|
|
455
229
|
};
|
|
@@ -514,6 +288,10 @@ class Session {
|
|
|
514
288
|
this.lifecycle.reloadAfterChange(afterLoad);
|
|
515
289
|
}
|
|
516
290
|
|
|
291
|
+
ignoreWatch(ms) {
|
|
292
|
+
this.lifecycle.ignoreWatch(ms);
|
|
293
|
+
}
|
|
294
|
+
|
|
517
295
|
applyLoaded(loaded) {
|
|
518
296
|
return this.lifecycle.applyLoaded(loaded);
|
|
519
297
|
}
|
|
@@ -605,7 +383,7 @@ class Session {
|
|
|
605
383
|
}
|
|
606
384
|
|
|
607
385
|
initReview() {
|
|
608
|
-
this.review.init(this.top,
|
|
386
|
+
this.review.init(this.top, new Date(), {
|
|
609
387
|
forceNew: this.forceNewReview,
|
|
610
388
|
});
|
|
611
389
|
}
|
|
@@ -742,10 +520,7 @@ class Session {
|
|
|
742
520
|
}
|
|
743
521
|
const onData = (chunk) => {
|
|
744
522
|
this.pushInput(chunk.toString('utf8'));
|
|
745
|
-
if (this.done)
|
|
746
|
-
finish();
|
|
747
|
-
return;
|
|
748
|
-
}
|
|
523
|
+
if (this.done) return void finish();
|
|
749
524
|
this.draw();
|
|
750
525
|
};
|
|
751
526
|
this.term.startListening({
|
package/lib/source.js
CHANGED
|
@@ -8,13 +8,13 @@ const selectChangeSource = (paths) => {
|
|
|
8
8
|
if (!paths.length) return { kind: 'local', paths };
|
|
9
9
|
const first = paths[0];
|
|
10
10
|
const pr = parseGithubPrUrl(first);
|
|
11
|
-
if (pr) return { kind: '
|
|
11
|
+
if (pr) return { kind: 'pr', pr, paths: paths.slice(1) };
|
|
12
12
|
const mr = parseGitlabMrUrl(first);
|
|
13
|
-
if (mr) return { kind: '
|
|
13
|
+
if (mr) return { kind: 'mr', mr, paths: paths.slice(1) };
|
|
14
14
|
return { kind: 'local', paths };
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
const createLoadedSource = (loaded, kind = '
|
|
17
|
+
const createLoadedSource = (loaded, kind = 'pr', extra = {}) => {
|
|
18
18
|
const load = () => {
|
|
19
19
|
const items = loaded.items ?? [];
|
|
20
20
|
return { ...loaded, items: [...items] };
|
package/lib/update.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { spawn } = require('node:child_process');
|
|
4
3
|
const fs = require('node:fs');
|
|
5
4
|
const os = require('node:os');
|
|
6
5
|
const path = require('node:path');
|
|
7
6
|
|
|
8
|
-
const {
|
|
7
|
+
const { jsonParse, isHashObject } = require('metautil');
|
|
8
|
+
const {
|
|
9
|
+
parseVersion,
|
|
10
|
+
cmpVersion,
|
|
11
|
+
npmBin,
|
|
12
|
+
npmOpts,
|
|
13
|
+
runProc,
|
|
14
|
+
oneLine,
|
|
15
|
+
} = require('./utilities.js');
|
|
9
16
|
|
|
10
17
|
const PKG = require('../package.json');
|
|
11
18
|
|
|
@@ -29,45 +36,28 @@ const asText = (value) => (typeof value === 'string' ? value : '');
|
|
|
29
36
|
|
|
30
37
|
const toInt = (text) => parseInt(text, 10);
|
|
31
38
|
|
|
32
|
-
const parseVersion = (text) => {
|
|
33
|
-
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(`${text ?? ''}`);
|
|
34
|
-
if (!match) return null;
|
|
35
|
-
return {
|
|
36
|
-
major: toInt(match[1]),
|
|
37
|
-
minor: toInt(match[2]),
|
|
38
|
-
patch: toInt(match[3]),
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const cmpVersion = (left, right) => {
|
|
43
|
-
if (left.major !== right.major) return left.major - right.major;
|
|
44
|
-
if (left.minor !== right.minor) return left.minor - right.minor;
|
|
45
|
-
return left.patch - right.patch;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
39
|
const isNpmInstall = (pkgDir) => {
|
|
49
40
|
const parent = path.basename(path.dirname(pkgDir));
|
|
50
41
|
return parent === 'node_modules';
|
|
51
42
|
};
|
|
52
43
|
|
|
53
|
-
const cacheFile = (env = process.env
|
|
54
|
-
const home = extra.home ?? os.homedir();
|
|
55
|
-
const platform = extra.platform ?? process.platform;
|
|
44
|
+
const cacheFile = (env = process.env) => {
|
|
56
45
|
if (env.XDG_CACHE_HOME) {
|
|
57
46
|
return path.join(env.XDG_CACHE_HOME, 'reslop', 'update.json');
|
|
58
47
|
}
|
|
59
|
-
if (platform === 'win32') {
|
|
60
|
-
const local =
|
|
48
|
+
if (process.platform === 'win32') {
|
|
49
|
+
const local =
|
|
50
|
+
env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
|
|
61
51
|
return path.join(local, 'reslop', 'update.json');
|
|
62
52
|
}
|
|
63
|
-
return path.join(
|
|
53
|
+
return path.join(os.homedir(), '.cache', 'reslop', 'update.json');
|
|
64
54
|
};
|
|
65
55
|
|
|
66
|
-
const readCache = (file
|
|
56
|
+
const readCache = (file) => {
|
|
67
57
|
try {
|
|
68
|
-
const text = readFileSync(file, 'utf8');
|
|
69
|
-
const data =
|
|
70
|
-
if (!
|
|
58
|
+
const text = fs.readFileSync(file, 'utf8');
|
|
59
|
+
const data = jsonParse(text);
|
|
60
|
+
if (!isHashObject(data)) {
|
|
71
61
|
return emptyCache();
|
|
72
62
|
}
|
|
73
63
|
const checkedAt = toInt(data.checkedAt) || 0;
|
|
@@ -80,17 +70,15 @@ const readCache = (file, readFileSync = fs.readFileSync) => {
|
|
|
80
70
|
}
|
|
81
71
|
};
|
|
82
72
|
|
|
83
|
-
const writeCache = (file, data
|
|
84
|
-
const mkdirSync = extra.mkdirSync ?? fs.mkdirSync;
|
|
85
|
-
const writeFileSync = extra.writeFileSync ?? fs.writeFileSync;
|
|
73
|
+
const writeCache = (file, data) => {
|
|
86
74
|
try {
|
|
87
|
-
mkdirSync(path.dirname(file), { recursive: true });
|
|
75
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
88
76
|
const checkedAt = data.checkedAt ?? 0;
|
|
89
77
|
const compatible = data.compatible ?? '';
|
|
90
78
|
const major = data.major ?? '';
|
|
91
79
|
const skipped = data.skipped ?? '';
|
|
92
80
|
const body = JSON.stringify({ checkedAt, compatible, major, skipped });
|
|
93
|
-
writeFileSync(file, `${body}\n`);
|
|
81
|
+
fs.writeFileSync(file, `${body}\n`);
|
|
94
82
|
} catch {
|
|
95
83
|
// ignore cache write errors
|
|
96
84
|
}
|
|
@@ -106,7 +94,7 @@ const listVersions = (data) => {
|
|
|
106
94
|
if (!data || typeof data !== 'object') return [];
|
|
107
95
|
const names = [];
|
|
108
96
|
const map = data.versions;
|
|
109
|
-
if (
|
|
97
|
+
if (isHashObject(map)) {
|
|
110
98
|
for (const name of Object.keys(map)) names.push(name);
|
|
111
99
|
}
|
|
112
100
|
const tags = data['dist-tags'];
|
|
@@ -187,12 +175,6 @@ const isEnabled = (options = {}) => {
|
|
|
187
175
|
return isNpmInstall(pkgDir);
|
|
188
176
|
};
|
|
189
177
|
|
|
190
|
-
const nowMs = (options = {}) => {
|
|
191
|
-
if (typeof options.now === 'function') return options.now();
|
|
192
|
-
if (typeof options.now === 'number') return options.now;
|
|
193
|
-
return Date.now();
|
|
194
|
-
};
|
|
195
|
-
|
|
196
178
|
const fetchTargets = async (current, options = {}) => {
|
|
197
179
|
const empty = { compatible: '', major: '' };
|
|
198
180
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
@@ -207,33 +189,18 @@ const fetchTargets = async (current, options = {}) => {
|
|
|
207
189
|
return pickTargets(current, listVersions(data));
|
|
208
190
|
};
|
|
209
191
|
|
|
210
|
-
const npmInstall = (spec, extra = {}) =>
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const timer = setTimeout(() => {
|
|
223
|
-
child.kill();
|
|
224
|
-
reject(new Error('timed out'));
|
|
225
|
-
}, timeout);
|
|
226
|
-
child.on('error', (error) => {
|
|
227
|
-
clearTimeout(timer);
|
|
228
|
-
reject(error);
|
|
229
|
-
});
|
|
230
|
-
child.on('close', (status) => {
|
|
231
|
-
clearTimeout(timer);
|
|
232
|
-
if (status === 0) return void resolve();
|
|
233
|
-
const line = stderr.trim().split('\n')[0];
|
|
234
|
-
reject(new Error(line || 'npm install failed'));
|
|
235
|
-
});
|
|
236
|
-
});
|
|
192
|
+
const npmInstall = async (spec, extra = {}) => {
|
|
193
|
+
const timeout = extra.timeout ?? INSTALL_MS;
|
|
194
|
+
const result = await runProc(
|
|
195
|
+
npmBin(),
|
|
196
|
+
['install', '-g', spec],
|
|
197
|
+
npmOpts({ timeout }),
|
|
198
|
+
);
|
|
199
|
+
if (result.error) throw result.error;
|
|
200
|
+
if (result.status !== 0) {
|
|
201
|
+
throw new Error(oneLine(result.stderr, 'npm install failed'));
|
|
202
|
+
}
|
|
203
|
+
};
|
|
237
204
|
|
|
238
205
|
const installUpdate = async (version, options = {}) => {
|
|
239
206
|
if (typeof options.install === 'function') {
|
|
@@ -246,19 +213,17 @@ const installUpdate = async (version, options = {}) => {
|
|
|
246
213
|
|
|
247
214
|
const markSkipped = (version, options = {}) => {
|
|
248
215
|
const file = options.cacheFile ?? cacheFile(options.env);
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
writeCache(file, { ...cache, skipped: version }, options);
|
|
216
|
+
const cache = readCache(file);
|
|
217
|
+
writeCache(file, { ...cache, skipped: version });
|
|
252
218
|
};
|
|
253
219
|
|
|
254
220
|
const checkUpdate = async (options = {}) => {
|
|
255
221
|
if (!isEnabled(options)) return { action: 'skip' };
|
|
256
|
-
const now =
|
|
222
|
+
const now = Date.now();
|
|
257
223
|
const current = options.current ?? PKG_VERSION;
|
|
258
224
|
const file = options.cacheFile ?? cacheFile(options.env);
|
|
259
|
-
const readFileSync = options.readFileSync ?? fs.readFileSync;
|
|
260
225
|
const interval = options.interval ?? CHECK_INTERVAL_MS;
|
|
261
|
-
const cache = readCache(file
|
|
226
|
+
const cache = readCache(file);
|
|
262
227
|
if (isFresh(cache, now, interval)) {
|
|
263
228
|
const plan = planUpdate(current, cache);
|
|
264
229
|
if (plan.action === 'install') return { action: 'none' };
|
|
@@ -277,10 +242,10 @@ const checkUpdate = async (options = {}) => {
|
|
|
277
242
|
major: targets.major,
|
|
278
243
|
};
|
|
279
244
|
if (!targets.compatible && !targets.major) {
|
|
280
|
-
writeCache(file, { ...cache, checkedAt: now }
|
|
245
|
+
writeCache(file, { ...cache, checkedAt: now });
|
|
281
246
|
return { action: 'none' };
|
|
282
247
|
}
|
|
283
|
-
writeCache(file, next
|
|
248
|
+
writeCache(file, next);
|
|
284
249
|
return planUpdate(current, next);
|
|
285
250
|
};
|
|
286
251
|
|