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/view.js
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
const path = require('node:path');
|
|
4
4
|
|
|
5
5
|
const files = require('../files.js');
|
|
6
|
-
const
|
|
7
|
-
const {
|
|
6
|
+
const commit = require('./commit.js');
|
|
7
|
+
const { listedCommits } = commit;
|
|
8
|
+
const { fileEntries, fileTotals, REPO_TASKS_LABEL } = files;
|
|
8
9
|
const review = require('../review.js');
|
|
9
10
|
const { noteCounts } = review;
|
|
10
11
|
const diff = require('../diff/diff.js');
|
|
@@ -24,12 +25,12 @@ const repoNameOf = (api) => {
|
|
|
24
25
|
return path.basename(ui.top || ui.cwd || '');
|
|
25
26
|
};
|
|
26
27
|
|
|
27
|
-
const
|
|
28
|
-
path:
|
|
29
|
-
kind: '
|
|
30
|
-
status: '
|
|
31
|
-
remaining: counts.
|
|
32
|
-
staged: counts.
|
|
28
|
+
const tasksEntry = (counts) => ({
|
|
29
|
+
path: REPO_TASKS_LABEL,
|
|
30
|
+
kind: 'tasks',
|
|
31
|
+
status: 'tasks',
|
|
32
|
+
remaining: counts.tasks,
|
|
33
|
+
staged: counts.tasksDone,
|
|
33
34
|
firstIndex: 0,
|
|
34
35
|
openIndex: 0,
|
|
35
36
|
added: 0,
|
|
@@ -93,7 +94,7 @@ const fileList = (api, notes = noteCounts(api.review.store)) => {
|
|
|
93
94
|
const names = listedNames(api);
|
|
94
95
|
const scoped =
|
|
95
96
|
api.nav.fileScope === 'file' ? mergeUnitFiles(labeled, names) : labeled;
|
|
96
|
-
scoped.unshift(
|
|
97
|
+
scoped.unshift(tasksEntry(notes));
|
|
97
98
|
return scoped;
|
|
98
99
|
};
|
|
99
100
|
|
|
@@ -104,7 +105,7 @@ const counts = (api, notes = noteCounts(api.review.store)) => {
|
|
|
104
105
|
untracked: 0,
|
|
105
106
|
commit: 0,
|
|
106
107
|
pr: 0,
|
|
107
|
-
|
|
108
|
+
tasks: 0,
|
|
108
109
|
feedback: 0,
|
|
109
110
|
code: 0,
|
|
110
111
|
};
|
|
@@ -113,7 +114,7 @@ const counts = (api, notes = noteCounts(api.review.store)) => {
|
|
|
113
114
|
next[item.origin] += 1;
|
|
114
115
|
}
|
|
115
116
|
next.feedback = notes.feedback;
|
|
116
|
-
next.
|
|
117
|
+
next.tasks = notes.tasks;
|
|
117
118
|
next.code = notes.code;
|
|
118
119
|
return next;
|
|
119
120
|
};
|
|
@@ -143,6 +144,30 @@ const unitBodyLines = (api) => {
|
|
|
143
144
|
return unitLines(unitFileText(api), group);
|
|
144
145
|
};
|
|
145
146
|
|
|
147
|
+
const pendingLines = (items, rev, revShort) => {
|
|
148
|
+
if (rev || revShort) return null;
|
|
149
|
+
for (const item of items) {
|
|
150
|
+
if (item.origin === 'commit' || item.origin === 'pr') return null;
|
|
151
|
+
}
|
|
152
|
+
return fileTotals(fileEntries(items));
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const commitRows = (api) => {
|
|
156
|
+
const rows = listedCommits(api.commits.commits);
|
|
157
|
+
const lines = pendingLines(api.collection.items, api.ui.rev, api.ui.revShort);
|
|
158
|
+
if (!lines || !rows[0]) return rows;
|
|
159
|
+
rows[0] = {
|
|
160
|
+
...rows[0],
|
|
161
|
+
added: lines.added,
|
|
162
|
+
removed: lines.removed,
|
|
163
|
+
stagedAdded: lines.stagedAdded,
|
|
164
|
+
unstagedAdded: lines.unstagedAdded,
|
|
165
|
+
stagedRemoved: lines.stagedRemoved,
|
|
166
|
+
unstagedRemoved: lines.unstagedRemoved,
|
|
167
|
+
};
|
|
168
|
+
return rows;
|
|
169
|
+
};
|
|
170
|
+
|
|
146
171
|
const view = (api) => {
|
|
147
172
|
const { composer, nav, ui, gitBranches, commits, npm, updater } = api;
|
|
148
173
|
const templates = composer.shownTemplates();
|
|
@@ -170,7 +195,7 @@ const view = (api) => {
|
|
|
170
195
|
branch: ui.branch,
|
|
171
196
|
branches: gitBranches.branches,
|
|
172
197
|
branchCursor: nav.branchCursor,
|
|
173
|
-
commits:
|
|
198
|
+
commits: commitRows(api),
|
|
174
199
|
commitCursor: nav.commitCursor,
|
|
175
200
|
commitView: commits.commitView,
|
|
176
201
|
npmCommands: npm.commands,
|
|
@@ -180,7 +205,11 @@ const view = (api) => {
|
|
|
180
205
|
npmFollow: npm.followEnd,
|
|
181
206
|
npmScroll: npm.logScroll,
|
|
182
207
|
npmEditName: npm.editName,
|
|
208
|
+
npmEditField: npm.editField,
|
|
209
|
+
npmDraftName: npm.draftName,
|
|
210
|
+
npmDraftCommand: npm.draftCommand,
|
|
183
211
|
npmRunning: npm.running,
|
|
212
|
+
npmLogs: nav.pane === 'npm' ? npm.logLabel : '',
|
|
184
213
|
repoName: repoNameOf(api),
|
|
185
214
|
rev: ui.rev || '',
|
|
186
215
|
revShort: ui.revShort || '',
|
|
@@ -189,9 +218,9 @@ const view = (api) => {
|
|
|
189
218
|
mode: ui.mode,
|
|
190
219
|
compose: composer.composeView(),
|
|
191
220
|
noteText: composer.idleNoteText(),
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
221
|
+
tasks: composer.taskTexts(),
|
|
222
|
+
tasksFocus: composer.clampedTaskFocus(),
|
|
223
|
+
taskEdit: composer.taskEditView(),
|
|
195
224
|
codeOverlay: composer.codeOverlayView(),
|
|
196
225
|
templates,
|
|
197
226
|
templateIndex: composer.clampedTemplateIndex(templates),
|
|
@@ -208,4 +237,4 @@ const createView = (api) => ({
|
|
|
208
237
|
view: () => view(api),
|
|
209
238
|
});
|
|
210
239
|
|
|
211
|
-
module.exports = {
|
|
240
|
+
module.exports = { createView };
|
package/lib/session/watch.js
CHANGED
|
@@ -4,7 +4,6 @@ const fs = require('node:fs');
|
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
|
|
6
6
|
const DEBOUNCE_MS = 200;
|
|
7
|
-
const POLL_MS = 250;
|
|
8
7
|
|
|
9
8
|
const SKIP_DIRS = ['node_modules', '.review', 'coverage', 'dist', '.cache'];
|
|
10
9
|
|
|
@@ -45,14 +44,17 @@ const ignoredName = (name) => {
|
|
|
45
44
|
};
|
|
46
45
|
|
|
47
46
|
const ignoredGitRel = (parts) => {
|
|
47
|
+
if (parts[1] === 'logs' && parts[2] === 'HEAD') return false;
|
|
48
48
|
const name = parts[1] ?? '';
|
|
49
49
|
if (!name) return false;
|
|
50
50
|
if (GIT_WATCH_NAMES.includes(name)) return false;
|
|
51
51
|
return true;
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
+
const slashRel = (rel) => `${rel}`.replaceAll('\\', '/');
|
|
55
|
+
|
|
54
56
|
const ignoredRel = (rel) => {
|
|
55
|
-
const norm =
|
|
57
|
+
const norm = slashRel(rel);
|
|
56
58
|
if (!norm || norm === '.') return false;
|
|
57
59
|
const parts = norm.split('/');
|
|
58
60
|
for (const part of parts) {
|
|
@@ -95,7 +97,7 @@ const hasFile = (full) => {
|
|
|
95
97
|
};
|
|
96
98
|
|
|
97
99
|
const isBareReviewName = (root, dir, rel) => {
|
|
98
|
-
if (!rel || rel.includes('/')
|
|
100
|
+
if (!rel || rel.includes('/')) return false;
|
|
99
101
|
const base = path.basename(rel);
|
|
100
102
|
if (hasFile(path.join(dir, base))) return false;
|
|
101
103
|
return hasFile(path.join(root, '.review', base));
|
|
@@ -133,6 +135,15 @@ const isOwnDirEvent = (dir, name) => {
|
|
|
133
135
|
}
|
|
134
136
|
};
|
|
135
137
|
|
|
138
|
+
const unchangedSince = (full, since) => {
|
|
139
|
+
try {
|
|
140
|
+
const stat = fs.statSync(full);
|
|
141
|
+
return Math.max(stat.mtimeMs, stat.ctimeMs) < since;
|
|
142
|
+
} catch {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
136
147
|
const schedule = (state, timerKey, fire) => {
|
|
137
148
|
if (state.closed || !fire) return;
|
|
138
149
|
if (state[timerKey] !== null) clearTimeout(state[timerKey]);
|
|
@@ -154,9 +165,10 @@ const reviewBurst = (state) => {
|
|
|
154
165
|
return Date.now() - seen < hold;
|
|
155
166
|
};
|
|
156
167
|
|
|
157
|
-
const scheduleChange = (state) => {
|
|
168
|
+
const scheduleChange = (state, loose = false) => {
|
|
169
|
+
state.looseChange = loose && (state.timer === null || state.looseChange);
|
|
158
170
|
schedule(state, 'timer', () => {
|
|
159
|
-
if (reviewBurst(state)) return;
|
|
171
|
+
if (state.looseChange && reviewBurst(state)) return;
|
|
160
172
|
if (state.onChange) state.onChange();
|
|
161
173
|
});
|
|
162
174
|
};
|
|
@@ -164,34 +176,39 @@ const scheduleChange = (state) => {
|
|
|
164
176
|
const scheduleUnnamed = (state) => {
|
|
165
177
|
if (reviewBurst(state)) return;
|
|
166
178
|
schedule(state, 'looseTimer', () => {
|
|
167
|
-
if (reviewBurst(state))
|
|
168
|
-
scheduleChange(state);
|
|
179
|
+
if (!reviewBurst(state)) scheduleChange(state, true);
|
|
169
180
|
});
|
|
170
181
|
};
|
|
171
182
|
|
|
172
|
-
const
|
|
173
|
-
if (state
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
183
|
+
const clearTimer = (state, timerKey) => {
|
|
184
|
+
if (state[timerKey] !== null) clearTimeout(state[timerKey]);
|
|
185
|
+
state[timerKey] = null;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const dropLooseChange = (state) => {
|
|
189
|
+
clearTimer(state, 'looseTimer');
|
|
190
|
+
if (state.looseChange) clearTimer(state, 'timer');
|
|
177
191
|
};
|
|
178
192
|
|
|
179
193
|
const handleWatchEvent = (state, dir, eventType, filename) => {
|
|
180
194
|
if (state.closed) return;
|
|
181
195
|
const name = filename ? `${filename}` : '';
|
|
182
|
-
const rel = name
|
|
196
|
+
const rel = name
|
|
197
|
+
? slashRel(path.relative(state.root, path.join(dir, name)))
|
|
198
|
+
: '';
|
|
199
|
+
const wanted = state.files.has(rel);
|
|
183
200
|
if (isReviewEvent(state.root, dir, name)) {
|
|
184
201
|
state.reviewSeenAt = Date.now();
|
|
185
|
-
|
|
186
|
-
|
|
202
|
+
dropLooseChange(state);
|
|
203
|
+
schedule(state, 'reviewTimer', state.onReview);
|
|
204
|
+
if (!wanted) return;
|
|
187
205
|
}
|
|
188
|
-
if (rel && ignoredRel(rel)) return;
|
|
189
|
-
if (reviewBurst(state)) return;
|
|
190
206
|
if (!name) return void scheduleUnnamed(state);
|
|
207
|
+
if (!wanted && ignoredRel(rel)) return;
|
|
191
208
|
if (isOwnDirEvent(dir, name)) return;
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
209
|
+
const full = path.join(dir, name);
|
|
210
|
+
if (unchangedSince(full, state.startedAt)) return;
|
|
211
|
+
if (eventType === 'rename') state.watchNewDir(full);
|
|
195
212
|
scheduleChange(state);
|
|
196
213
|
};
|
|
197
214
|
|
|
@@ -263,24 +280,24 @@ const watchGit = (state) => {
|
|
|
263
280
|
const gitDir = path.resolve(state.root, '.git');
|
|
264
281
|
watchOne(state, gitDir);
|
|
265
282
|
watchOne(state, path.resolve(gitDir, 'refs'));
|
|
283
|
+
watchOne(state, path.resolve(gitDir, 'logs'));
|
|
266
284
|
};
|
|
267
285
|
|
|
268
286
|
const stopState = (state) => {
|
|
269
287
|
state.closed = true;
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
state.timer = null;
|
|
274
|
-
state.reviewTimer = null;
|
|
275
|
-
state.looseTimer = null;
|
|
288
|
+
clearTimer(state, 'timer');
|
|
289
|
+
clearTimer(state, 'reviewTimer');
|
|
290
|
+
clearTimer(state, 'looseTimer');
|
|
276
291
|
while (state.watchers.length) closeWatcher(state.watchers.pop());
|
|
277
292
|
state.watched.clear();
|
|
278
293
|
};
|
|
279
294
|
|
|
280
295
|
const watchFiles = (state, rels) => {
|
|
296
|
+
state.files = new Set();
|
|
281
297
|
for (const rel of rels) {
|
|
282
298
|
if (!rel) continue;
|
|
283
299
|
const full = path.resolve(state.root, rel);
|
|
300
|
+
state.files.add(slashRel(path.relative(state.root, full)));
|
|
284
301
|
watchOne(state, path.dirname(full));
|
|
285
302
|
}
|
|
286
303
|
};
|
|
@@ -295,11 +312,14 @@ const createDiskWatcher = (options) => {
|
|
|
295
312
|
dirWatchOpts: recursive ? { recursive: true } : {},
|
|
296
313
|
watchers: [],
|
|
297
314
|
watched: new Set(),
|
|
315
|
+
files: new Set(),
|
|
298
316
|
onReview: options.onReview,
|
|
299
317
|
timer: null,
|
|
300
318
|
reviewTimer: null,
|
|
301
319
|
looseTimer: null,
|
|
320
|
+
looseChange: false,
|
|
302
321
|
reviewSeenAt: 0,
|
|
322
|
+
startedAt: Date.now(),
|
|
303
323
|
closed: false,
|
|
304
324
|
watchNewDir: (full) => maybeWatchNewDir(state, full),
|
|
305
325
|
};
|
|
@@ -315,9 +335,9 @@ const createDiskWatcher = (options) => {
|
|
|
315
335
|
|
|
316
336
|
module.exports = {
|
|
317
337
|
DEBOUNCE_MS,
|
|
318
|
-
POLL_MS,
|
|
319
338
|
canWatchRecursive,
|
|
320
339
|
ignoredRel,
|
|
321
340
|
isOwnDirEvent,
|
|
341
|
+
unchangedSince,
|
|
322
342
|
createDiskWatcher,
|
|
323
343
|
};
|
package/lib/session.js
CHANGED
|
@@ -4,35 +4,53 @@ const render = require('./render/render.js');
|
|
|
4
4
|
const keys = require('./keys.js');
|
|
5
5
|
const review = require('./review.js');
|
|
6
6
|
const files = require('./files.js');
|
|
7
|
-
const {
|
|
7
|
+
const { makeTaskItem } = files;
|
|
8
8
|
const { renderFrame } = render;
|
|
9
9
|
const { decodeChunk } = keys;
|
|
10
10
|
const { AUTOSAVE_MS, hasNotes } = review;
|
|
11
11
|
const capabilities = require('./capabilities.js');
|
|
12
|
+
const actions = require('./session/actions.js');
|
|
13
|
+
const { LIST_PANES } = actions;
|
|
12
14
|
const { sessionCapabilities } = capabilities;
|
|
13
|
-
const
|
|
14
|
-
const {
|
|
15
|
+
const terminal = require('./session/terminal.js');
|
|
16
|
+
const { createTerminal, LEAVE_TERM } = terminal;
|
|
17
|
+
const load = require('./session/load.js');
|
|
18
|
+
const { LoadCoordinator } = load;
|
|
15
19
|
const items = require('./session/items.js');
|
|
16
|
-
const {
|
|
17
|
-
const
|
|
18
|
-
const {
|
|
19
|
-
const
|
|
20
|
-
const {
|
|
21
|
-
const
|
|
22
|
-
const {
|
|
23
|
-
const
|
|
24
|
-
const {
|
|
25
|
-
const
|
|
26
|
-
const {
|
|
20
|
+
const { ItemCollection } = items;
|
|
21
|
+
const navigation = require('./session/navigation.js');
|
|
22
|
+
const { Navigation } = navigation;
|
|
23
|
+
const sessionReview = require('./session/review.js');
|
|
24
|
+
const { ReviewController } = sessionReview;
|
|
25
|
+
const update = require('./session/update.js');
|
|
26
|
+
const { UpdateCoordinator } = update;
|
|
27
|
+
const progress = require('./session/progress.js');
|
|
28
|
+
const { createProgress } = progress;
|
|
29
|
+
const compose = require('./session/compose.js');
|
|
30
|
+
const { createComposer } = compose;
|
|
31
|
+
const ops = require('./session/ops.js');
|
|
32
|
+
const { OpsRunner } = ops;
|
|
33
|
+
const changeActions = require('./session/change-actions.js');
|
|
34
|
+
const { createChangeActions } = changeActions;
|
|
35
|
+
const commit = require('./session/commit.js');
|
|
36
|
+
const { createCommitController } = commit;
|
|
37
|
+
const branches = require('./session/branches.js');
|
|
38
|
+
const { createBranchController } = branches;
|
|
39
|
+
const npm = require('./session/npm.js');
|
|
40
|
+
const { createNpmController } = npm;
|
|
27
41
|
const accessors = require('./session/accessors.js');
|
|
28
42
|
const { bindAccessors, bindGetters, bindAlias } = accessors;
|
|
29
|
-
const
|
|
30
|
-
const {
|
|
31
|
-
const
|
|
32
|
-
const {
|
|
33
|
-
const
|
|
43
|
+
const view = require('./session/view.js');
|
|
44
|
+
const { createView } = view;
|
|
45
|
+
const filesPane = require('./session/files-pane.js');
|
|
46
|
+
const { createFilesPane } = filesPane;
|
|
47
|
+
const lifecycle = require('./session/lifecycle.js');
|
|
48
|
+
const { createLifecycle } = lifecycle;
|
|
49
|
+
const pointer = require('./session/pointer.js');
|
|
50
|
+
const { createPointer } = pointer;
|
|
51
|
+
const dispatch = require('./session/dispatch.js');
|
|
52
|
+
const { createDispatcher, createInput } = dispatch;
|
|
34
53
|
|
|
35
|
-
const CURSOR_BLINK_MS = 500;
|
|
36
54
|
const PROGRESS_MS = 160;
|
|
37
55
|
|
|
38
56
|
const NAV_FIELDS = [
|
|
@@ -50,19 +68,18 @@ const NAV_FIELDS = [
|
|
|
50
68
|
'selection',
|
|
51
69
|
'mouseAnchor',
|
|
52
70
|
'pendingClick',
|
|
53
|
-
'
|
|
54
|
-
'
|
|
71
|
+
'tasksOpen',
|
|
72
|
+
'tasksFocus',
|
|
55
73
|
];
|
|
56
74
|
|
|
57
75
|
const COMPOSER_FIELDS = [
|
|
58
76
|
'editor',
|
|
59
77
|
'composeKind',
|
|
60
78
|
'commitKind',
|
|
61
|
-
'
|
|
79
|
+
'composeTaskId',
|
|
62
80
|
'composeBaseline',
|
|
63
81
|
'templateIndex',
|
|
64
82
|
'templateFocus',
|
|
65
|
-
'blinkOn',
|
|
66
83
|
];
|
|
67
84
|
|
|
68
85
|
const assignOptions = (session, options) => {
|
|
@@ -95,12 +112,10 @@ const assignOptions = (session, options) => {
|
|
|
95
112
|
stdout: session.stdout,
|
|
96
113
|
proc: session.proc,
|
|
97
114
|
});
|
|
98
|
-
session.loader =
|
|
99
|
-
session.collection =
|
|
100
|
-
session.nav =
|
|
101
|
-
|
|
102
|
-
});
|
|
103
|
-
session.review = createReviewController();
|
|
115
|
+
session.loader = new LoadCoordinator();
|
|
116
|
+
session.collection = new ItemCollection();
|
|
117
|
+
session.nav = new Navigation({ startPane: options.startPane ?? 'files' });
|
|
118
|
+
session.review = new ReviewController();
|
|
104
119
|
session.progress = createProgress(
|
|
105
120
|
session.term,
|
|
106
121
|
() => session.tickProgress(),
|
|
@@ -113,19 +128,11 @@ const assignComposer = (session) => {
|
|
|
113
128
|
nav: session.nav,
|
|
114
129
|
review: session.review,
|
|
115
130
|
ui: session,
|
|
116
|
-
restartBlink: () => {
|
|
117
|
-
if (!session.term.hasTimer('blink')) return;
|
|
118
|
-
session.term.restartTimer(
|
|
119
|
-
'blink',
|
|
120
|
-
() => session.tickBlink(),
|
|
121
|
-
CURSOR_BLINK_MS,
|
|
122
|
-
);
|
|
123
|
-
},
|
|
124
131
|
});
|
|
125
132
|
};
|
|
126
133
|
|
|
127
134
|
const assignControllers = (session) => {
|
|
128
|
-
session.ops =
|
|
135
|
+
session.ops = new OpsRunner(session);
|
|
129
136
|
session.changeActions = createChangeActions(session);
|
|
130
137
|
session.commits = createCommitController(session);
|
|
131
138
|
session.gitBranches = createBranchController(session);
|
|
@@ -136,48 +143,14 @@ const bindSessionAccessors = (session) => {
|
|
|
136
143
|
bindAccessors(session, session.nav, NAV_FIELDS);
|
|
137
144
|
bindAccessors(session, session.composer.state, COMPOSER_FIELDS);
|
|
138
145
|
bindAccessors(session, session.loader, ['pendingExtras']);
|
|
139
|
-
|
|
140
|
-
session,
|
|
141
|
-
'loadPromise',
|
|
142
|
-
() => session.loader.promise,
|
|
143
|
-
(value) => {
|
|
144
|
-
session.loader.promise = value;
|
|
145
|
-
},
|
|
146
|
-
);
|
|
146
|
+
bindGetters(session, session.loader, ['didLoad']);
|
|
147
147
|
bindAccessors(session, session.collection, ['items']);
|
|
148
148
|
bindGetters(session, session.collection, ['dismissed']);
|
|
149
|
-
|
|
149
|
+
bindGetters(session, session.updater, ['installPromise']);
|
|
150
150
|
bindAccessors(session, session.ops, ['gitBusy', 'busy']);
|
|
151
151
|
bindAccessors(session, session.gitBranches, ['branches', 'dropName']);
|
|
152
152
|
bindGetters(session, session.term, ['lastFrame', 'lastSize']);
|
|
153
|
-
bindAlias(session, 'loadGen', () => session.loader.gen);
|
|
154
|
-
bindAlias(
|
|
155
|
-
session,
|
|
156
|
-
'didLoad',
|
|
157
|
-
() => session.loader.didLoad,
|
|
158
|
-
(value) => {
|
|
159
|
-
session.loader.didLoad = value;
|
|
160
|
-
if (value) session.updater.maybePrompt();
|
|
161
|
-
},
|
|
162
|
-
);
|
|
163
153
|
bindAlias(session, 'notes', () => session.review.store);
|
|
164
|
-
bindAlias(session, 'didResumeReview', () => session.review.didResume);
|
|
165
|
-
bindAlias(
|
|
166
|
-
session,
|
|
167
|
-
'importedRemoteNotes',
|
|
168
|
-
() => session.review.importedRemote,
|
|
169
|
-
);
|
|
170
|
-
bindAlias(
|
|
171
|
-
session,
|
|
172
|
-
'updateOffer',
|
|
173
|
-
() => session.updater.offer,
|
|
174
|
-
(value) => {
|
|
175
|
-
session.updater.offer = value;
|
|
176
|
-
},
|
|
177
|
-
);
|
|
178
|
-
bindAlias(session, 'updateFrom', () => session.updater.from);
|
|
179
|
-
bindAlias(session, 'updateTo', () => session.updater.to);
|
|
180
|
-
bindAlias(session, 'updatePromise', () => session.updater.checkPromise);
|
|
181
154
|
};
|
|
182
155
|
|
|
183
156
|
const bindViewer = (session) => {
|
|
@@ -222,7 +195,7 @@ const bindDispatch = (session) => {
|
|
|
222
195
|
|
|
223
196
|
const composeSession = (session, options) => {
|
|
224
197
|
assignOptions(session, options);
|
|
225
|
-
session.updater =
|
|
198
|
+
session.updater = new UpdateCoordinator(session);
|
|
226
199
|
assignComposer(session);
|
|
227
200
|
assignControllers(session);
|
|
228
201
|
bindSessionAccessors(session);
|
|
@@ -254,7 +227,7 @@ class Session {
|
|
|
254
227
|
this.done = false;
|
|
255
228
|
this.exitCode = 0;
|
|
256
229
|
this.mode = 'review';
|
|
257
|
-
this.
|
|
230
|
+
this.tasksItem = makeTaskItem();
|
|
258
231
|
this.progressFrame = 0;
|
|
259
232
|
this.emptyReview = false;
|
|
260
233
|
this.loadError = null;
|
|
@@ -297,10 +270,6 @@ class Session {
|
|
|
297
270
|
this.lifecycle.ignoreWatch(ms);
|
|
298
271
|
}
|
|
299
272
|
|
|
300
|
-
applyLoaded(loaded) {
|
|
301
|
-
return this.lifecycle.applyLoaded(loaded);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
273
|
paint() {
|
|
305
274
|
if (!this.uiOpen) return;
|
|
306
275
|
this.draw();
|
|
@@ -314,18 +283,24 @@ class Session {
|
|
|
314
283
|
height: size.height,
|
|
315
284
|
color: this.color,
|
|
316
285
|
});
|
|
317
|
-
if (
|
|
318
|
-
this.pane === 'files' ||
|
|
319
|
-
this.pane === 'branches' ||
|
|
320
|
-
this.pane === 'commits' ||
|
|
321
|
-
this.pane === 'npm'
|
|
322
|
-
) {
|
|
286
|
+
if (LIST_PANES.includes(this.pane)) {
|
|
323
287
|
this.nav.listScroll = frame.listScroll;
|
|
324
288
|
}
|
|
325
|
-
if (this.nav.
|
|
289
|
+
if (this.nav.tasksOpen) this.nav.scroll = frame.scroll;
|
|
290
|
+
const editScroll = frame.cursor && frame.cursor.scroll;
|
|
291
|
+
if (this.editor && typeof editScroll === 'number') {
|
|
292
|
+
this.editor.scrollCol = editScroll;
|
|
293
|
+
}
|
|
326
294
|
const composing = this.mode === 'compose';
|
|
327
|
-
|
|
328
|
-
this.
|
|
295
|
+
this.term.paint(frame, size, composing ? frame.cursor : null);
|
|
296
|
+
this.scheduleAutosave();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
scheduleAutosave() {
|
|
300
|
+
if (!this.finishReadLoop) return;
|
|
301
|
+
const dirty = this.notes && this.notes.dirty;
|
|
302
|
+
if (!dirty && this.composeKind === null) return;
|
|
303
|
+
this.term.later('save', () => this.autosave(), AUTOSAVE_MS);
|
|
329
304
|
}
|
|
330
305
|
|
|
331
306
|
view() {
|
|
@@ -345,7 +320,7 @@ class Session {
|
|
|
345
320
|
}
|
|
346
321
|
|
|
347
322
|
current() {
|
|
348
|
-
return this.nav.current(this.items, this.
|
|
323
|
+
return this.nav.current(this.items, this.tasksItem);
|
|
349
324
|
}
|
|
350
325
|
|
|
351
326
|
fileCursorEntry() {
|
|
@@ -428,10 +403,6 @@ class Session {
|
|
|
428
403
|
this.composer.startDraftCompose();
|
|
429
404
|
}
|
|
430
405
|
|
|
431
|
-
tickBlink() {
|
|
432
|
-
this.composer.tickBlink();
|
|
433
|
-
}
|
|
434
|
-
|
|
435
406
|
autosave() {
|
|
436
407
|
this.composer.autosave();
|
|
437
408
|
}
|
|
@@ -536,8 +507,6 @@ class Session {
|
|
|
536
507
|
onData,
|
|
537
508
|
onResize: () => this.draw(),
|
|
538
509
|
});
|
|
539
|
-
this.term.startTimer('save', () => this.autosave(), AUTOSAVE_MS);
|
|
540
|
-
this.term.startTimer('blink', () => this.tickBlink(), CURSOR_BLINK_MS);
|
|
541
510
|
this.lifecycle.startWatch();
|
|
542
511
|
});
|
|
543
512
|
}
|
package/lib/source.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const {
|
|
5
|
-
const
|
|
3
|
+
const github = require('./github.js');
|
|
4
|
+
const { parseGithubPrUrl } = github;
|
|
5
|
+
const gitlab = require('./gitlab.js');
|
|
6
|
+
const { parseGitlabMrUrl } = gitlab;
|
|
7
|
+
const capabilities = require('./capabilities.js');
|
|
8
|
+
const { capabilitiesFor, attachCapabilities } = capabilities;
|
|
6
9
|
|
|
7
10
|
const selectChangeSource = (paths) => {
|
|
8
11
|
if (!paths.length) return { kind: 'local', paths };
|
package/lib/update.js
CHANGED
|
@@ -4,15 +4,11 @@ const fs = require('node:fs');
|
|
|
4
4
|
const os = require('node:os');
|
|
5
5
|
const path = require('node:path');
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
const {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
npmOpts,
|
|
13
|
-
runProc,
|
|
14
|
-
oneLine,
|
|
15
|
-
} = require('./utilities.js');
|
|
7
|
+
const metautil = require('metautil');
|
|
8
|
+
const { jsonParse, isHashObject } = metautil;
|
|
9
|
+
const utilities = require('./utilities.js');
|
|
10
|
+
const { parseVersion, cmpVersion, npmBin } = utilities;
|
|
11
|
+
const { npmOpts, runProc, oneLine, asText } = utilities;
|
|
16
12
|
|
|
17
13
|
const PKG = require('../package.json');
|
|
18
14
|
|
|
@@ -32,8 +28,6 @@ const emptyCache = () => ({
|
|
|
32
28
|
skipped: '',
|
|
33
29
|
});
|
|
34
30
|
|
|
35
|
-
const asText = (value) => (typeof value === 'string' ? value : '');
|
|
36
|
-
|
|
37
31
|
const toInt = (text) => parseInt(text, 10);
|
|
38
32
|
|
|
39
33
|
const isNpmInstall = (pkgDir) => {
|
|
@@ -251,8 +245,6 @@ const checkUpdate = async (options = {}) => {
|
|
|
251
245
|
|
|
252
246
|
module.exports = {
|
|
253
247
|
CHECK_INTERVAL_MS,
|
|
254
|
-
PKG_NAME,
|
|
255
|
-
PKG_VERSION,
|
|
256
248
|
parseVersion,
|
|
257
249
|
cmpVersion,
|
|
258
250
|
isNpmInstall,
|
package/lib/utilities.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const {
|
|
3
|
+
const childProcess = require('node:child_process');
|
|
4
|
+
const { spawn, spawnSync } = childProcess;
|
|
5
|
+
const metautil = require('metautil');
|
|
6
|
+
const { split } = metautil;
|
|
5
7
|
|
|
6
8
|
const IS_WIN = process.platform === 'win32';
|
|
7
9
|
const DEFAULT_ENCODING = 'utf8';
|
|
@@ -28,6 +30,23 @@ const cmpVersion = (left, right) =>
|
|
|
28
30
|
const oneLine = (text, fallback = '') =>
|
|
29
31
|
split(`${text ?? ''}`.trim(), '\n')[0] || fallback;
|
|
30
32
|
|
|
33
|
+
const asText = (value) => (typeof value === 'string' ? value : '');
|
|
34
|
+
|
|
35
|
+
const unquote = (value) => {
|
|
36
|
+
const text = `${value ?? ''}`;
|
|
37
|
+
if (text.length < 2) return text;
|
|
38
|
+
const open = text[0];
|
|
39
|
+
const quoted = open === text.at(-1) && (open === '"' || open === '\x27');
|
|
40
|
+
return quoted ? text.slice(1, -1) : text;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const pad2 = (n) => `${n}`.padStart(2, '0');
|
|
44
|
+
|
|
45
|
+
const dateStamp = (date) => {
|
|
46
|
+
const month = pad2(date.getMonth() + 1);
|
|
47
|
+
return `${date.getFullYear()}-${month}-${pad2(date.getDate())}`;
|
|
48
|
+
};
|
|
49
|
+
|
|
31
50
|
const npmBin = (platform = process.platform) =>
|
|
32
51
|
platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
33
52
|
|
|
@@ -212,6 +231,10 @@ module.exports = {
|
|
|
212
231
|
parseVersion,
|
|
213
232
|
cmpVersion,
|
|
214
233
|
oneLine,
|
|
234
|
+
asText,
|
|
235
|
+
unquote,
|
|
236
|
+
pad2,
|
|
237
|
+
dateStamp,
|
|
215
238
|
npmBin,
|
|
216
239
|
spawnBase,
|
|
217
240
|
npmOpts,
|
|
@@ -220,7 +243,6 @@ module.exports = {
|
|
|
220
243
|
watchResize,
|
|
221
244
|
abortError,
|
|
222
245
|
isAbort,
|
|
223
|
-
procResult,
|
|
224
246
|
runProcSync,
|
|
225
247
|
runProc,
|
|
226
248
|
};
|