reslop 0.2.4 → 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 +14 -1
- package/README.md +1 -2
- package/lib/capabilities.js +0 -4
- package/lib/clipboard.js +39 -1
- package/lib/deps-diff.js +0 -3
- package/lib/deps.js +144 -176
- package/lib/diff/inline.js +66 -1
- package/lib/diff/unit.js +11 -28
- package/lib/editor.js +69 -9
- package/lib/files.js +17 -16
- package/lib/git-deps.js +58 -95
- package/lib/github.js +7 -7
- package/lib/gitlab.js +3 -3
- package/lib/highlight/core.js +11 -0
- package/lib/highlight/dot.js +2 -12
- package/lib/highlight/log.js +4 -11
- package/lib/highlight/txt.js +2 -12
- package/lib/keys.js +22 -12
- package/lib/manifest.js +0 -4
- package/lib/npm-commands.js +1 -22
- package/lib/remote.js +1 -7
- package/lib/render/branches.js +4 -13
- package/lib/render/chrome.js +85 -31
- package/lib/render/commits.js +95 -207
- package/lib/render/diff.js +31 -1
- package/lib/render/files.js +7 -16
- package/lib/render/frame.js +95 -110
- package/lib/render/notes.js +71 -16
- package/lib/render/npm.js +104 -18
- package/lib/render/primitives.js +108 -45
- package/lib/render/unit.js +4 -1
- package/lib/report-parse.js +0 -1
- package/lib/report-run.js +0 -1
- package/lib/report-test.js +40 -47
- package/lib/report-tools.js +0 -7
- package/lib/review-merge.js +101 -131
- package/lib/review.js +64 -77
- package/lib/select.js +52 -0
- package/lib/session/actions.js +23 -22
- package/lib/session/branches.js +5 -3
- package/lib/session/change-actions.js +173 -146
- package/lib/session/commit.js +32 -13
- package/lib/session/compose.js +120 -77
- package/lib/session/dispatch.js +36 -21
- package/lib/session/edit-click.js +57 -0
- package/lib/session/files-pane.js +24 -24
- package/lib/session/items.js +69 -89
- package/lib/session/lifecycle.js +31 -96
- package/lib/session/load.js +51 -58
- package/lib/session/navigation.js +105 -96
- package/lib/session/npm.js +158 -77
- package/lib/session/ops.js +192 -51
- package/lib/session/pointer.js +11 -5
- package/lib/session/progress.js +9 -6
- package/lib/session/review.js +35 -54
- package/lib/session/tasks.js +292 -0
- package/lib/session/terminal.js +13 -17
- package/lib/session/unit.js +3 -4
- package/lib/session/update.js +76 -97
- package/lib/session/view.js +17 -14
- package/lib/session/watch.js +47 -27
- package/lib/session.js +34 -79
- package/lib/update.js +1 -5
- package/lib/utilities.js +21 -1
- package/lib/wrap.js +15 -0
- package/package.json +1 -1
- package/lib/session/todos.js +0 -293
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const ansi = require('../ansi.js');
|
|
4
4
|
const files = require('../files.js');
|
|
5
5
|
const { THEME_NAMES, setTheme, themeName } = ansi;
|
|
6
|
-
const {
|
|
6
|
+
const { isTasksEntry, TASKS_FILE } = files;
|
|
7
7
|
const npm = require('../render/npm.js');
|
|
8
8
|
const { logViewRows } = npm;
|
|
9
9
|
const unit = require('./unit.js');
|
|
@@ -13,7 +13,7 @@ const { setFileScope, openUnitFile, moveUnitBlock } = unit;
|
|
|
13
13
|
|
|
14
14
|
const LAYOUTS = ['unified', 'mixed', 'side'];
|
|
15
15
|
|
|
16
|
-
const current = (ui) => ui.nav.current(ui.items, ui.
|
|
16
|
+
const current = (ui) => ui.nav.current(ui.items, ui.tasksItem);
|
|
17
17
|
|
|
18
18
|
const clampIndex = (ui) => ui.nav.clampIndex(ui.items.length);
|
|
19
19
|
|
|
@@ -32,14 +32,15 @@ const syncFileCursor = (ui) => {
|
|
|
32
32
|
const showFiles = (ui) => {
|
|
33
33
|
const nav = ui.nav;
|
|
34
34
|
const held =
|
|
35
|
-
nav.
|
|
35
|
+
nav.tasksOpen === true ||
|
|
36
36
|
nav.pane === 'branches' ||
|
|
37
37
|
nav.pane === 'commits' ||
|
|
38
38
|
nav.pane === 'npm';
|
|
39
39
|
nav.pane = 'files';
|
|
40
|
+
nav.resetListScroll('files');
|
|
40
41
|
nav.clearSelection();
|
|
41
42
|
followReviewPath(ui);
|
|
42
|
-
nav.
|
|
43
|
+
nav.tasksOpen = false;
|
|
43
44
|
if (held) ui.lifecycle.catchUpWatch();
|
|
44
45
|
};
|
|
45
46
|
|
|
@@ -54,12 +55,12 @@ const onFiles = (ui) => {
|
|
|
54
55
|
if (ui.nav.fileScope === 'file') {
|
|
55
56
|
const list = ui.fileList();
|
|
56
57
|
const entry = list[ui.nav.fileCursor];
|
|
57
|
-
if (entry && !
|
|
58
|
+
if (entry && !isTasksEntry(entry)) openUnitFile(ui, entry);
|
|
58
59
|
return;
|
|
59
60
|
}
|
|
60
61
|
ui.nav.pane = 'diff';
|
|
61
62
|
ui.nav.clearSelection();
|
|
62
|
-
if (ui.nav.reviewPath ===
|
|
63
|
+
if (ui.nav.reviewPath === TASKS_FILE) ui.composer.openTasksPage();
|
|
63
64
|
return;
|
|
64
65
|
}
|
|
65
66
|
showFiles(ui);
|
|
@@ -72,18 +73,18 @@ const onOpenFile = (ui) => {
|
|
|
72
73
|
if (ui.nav.pane === 'commits') return void ui.commits.onShowDiff();
|
|
73
74
|
if (ui.nav.pane === 'npm') return void ui.npm.run();
|
|
74
75
|
if (ui.nav.pane !== 'files') {
|
|
75
|
-
if (ui.nav.
|
|
76
|
+
if (ui.nav.tasksOpen) ui.composer.editFocusedTask();
|
|
76
77
|
return;
|
|
77
78
|
}
|
|
78
79
|
const list = ui.fileList();
|
|
79
80
|
const entry = list[ui.nav.fileCursor];
|
|
80
81
|
if (!entry) return;
|
|
81
|
-
if (
|
|
82
|
-
ui.composer.
|
|
82
|
+
if (isTasksEntry(entry)) {
|
|
83
|
+
ui.composer.openTasksPage();
|
|
83
84
|
ui.status = '';
|
|
84
85
|
return;
|
|
85
86
|
}
|
|
86
|
-
ui.nav.
|
|
87
|
+
ui.nav.tasksOpen = false;
|
|
87
88
|
ui.nav.index = entry.openIndex ?? entry.firstIndex;
|
|
88
89
|
if (ui.nav.fileScope === 'file') {
|
|
89
90
|
return void openUnitFile(ui, entry);
|
|
@@ -107,8 +108,8 @@ const onFileMove = (ui, delta) => {
|
|
|
107
108
|
if (next === ui.nav.fileCursor) return;
|
|
108
109
|
ui.nav.fileCursor = next;
|
|
109
110
|
const entry = list[next];
|
|
110
|
-
if (
|
|
111
|
-
ui.nav.reviewPath =
|
|
111
|
+
if (isTasksEntry(entry)) {
|
|
112
|
+
ui.nav.reviewPath = TASKS_FILE;
|
|
112
113
|
} else {
|
|
113
114
|
ui.nav.reviewPath = entry.path;
|
|
114
115
|
ui.nav.index = entry.firstIndex;
|
|
@@ -133,8 +134,8 @@ const moveRemaining = (ui, step) => {
|
|
|
133
134
|
|
|
134
135
|
const onNext = (ui) => {
|
|
135
136
|
if (ui.nav.pane === 'unit') return void moveUnitBlock(ui, 1);
|
|
136
|
-
if (ui.nav.
|
|
137
|
-
ui.composer.
|
|
137
|
+
if (ui.nav.tasksOpen) {
|
|
138
|
+
ui.composer.closeTasksPage();
|
|
138
139
|
if (ui.items.length) ui.nav.index = 0;
|
|
139
140
|
ui.nav.scroll = 0;
|
|
140
141
|
syncReviewPath(ui);
|
|
@@ -146,8 +147,8 @@ const onNext = (ui) => {
|
|
|
146
147
|
|
|
147
148
|
const onPrev = (ui) => {
|
|
148
149
|
if (ui.nav.pane === 'unit') return void moveUnitBlock(ui, -1);
|
|
149
|
-
if (ui.nav.
|
|
150
|
-
if (ui.nav.index <= 0) return void ui.composer.
|
|
150
|
+
if (ui.nav.tasksOpen) return;
|
|
151
|
+
if (ui.nav.index <= 0) return void ui.composer.openTasksPage();
|
|
151
152
|
moveRemaining(ui, -1);
|
|
152
153
|
};
|
|
153
154
|
|
|
@@ -175,8 +176,8 @@ const onScroll = (ui, delta) => {
|
|
|
175
176
|
return void ui.commits.onCommitMove(delta);
|
|
176
177
|
}
|
|
177
178
|
if (ui.nav.pane === 'npm') return void ui.npm.move(delta);
|
|
178
|
-
if (ui.nav.
|
|
179
|
-
return void ui.composer.
|
|
179
|
+
if (ui.nav.tasksOpen && ui.composer.taskRowCount() > 1) {
|
|
180
|
+
return void ui.composer.moveTaskFocus(delta);
|
|
180
181
|
}
|
|
181
182
|
const frame = ui.lastFrame;
|
|
182
183
|
const max = frame ? frame.scrollMax : null;
|
|
@@ -193,11 +194,11 @@ const onJump = (ui, toEnd) => {
|
|
|
193
194
|
const delta = toEnd ? Number.MAX_SAFE_INTEGER : -Number.MAX_SAFE_INTEGER;
|
|
194
195
|
return void onScroll(ui, delta);
|
|
195
196
|
}
|
|
196
|
-
if (ui.nav.
|
|
197
|
-
const last = ui.composer.
|
|
197
|
+
if (ui.nav.tasksOpen && ui.composer.taskRowCount() > 0) {
|
|
198
|
+
const last = ui.composer.taskRowCount() - 1;
|
|
198
199
|
const target = toEnd ? last : 0;
|
|
199
|
-
const at = ui.composer.
|
|
200
|
-
return void ui.composer.
|
|
200
|
+
const at = ui.composer.clampedTaskFocus();
|
|
201
|
+
return void ui.composer.moveTaskFocus(target - at);
|
|
201
202
|
}
|
|
202
203
|
const frame = ui.lastFrame;
|
|
203
204
|
const max = frame ? frame.scrollMax : 0;
|
|
@@ -230,7 +231,6 @@ const createFilesPane = (ui) => ({
|
|
|
230
231
|
onEscape: () => onEscape(ui),
|
|
231
232
|
onFiles: () => onFiles(ui),
|
|
232
233
|
onOpenFile: () => onOpenFile(ui),
|
|
233
|
-
onFileMove: (delta) => onFileMove(ui, delta),
|
|
234
234
|
onNext: () => onNext(ui),
|
|
235
235
|
onPrev: () => onPrev(ui),
|
|
236
236
|
scrollStep: (fraction) => scrollStep(ui, fraction),
|
|
@@ -243,4 +243,4 @@ const createFilesPane = (ui) => ({
|
|
|
243
243
|
onDiffScope: () => setFileScope(ui, 'diff'),
|
|
244
244
|
});
|
|
245
245
|
|
|
246
|
-
module.exports = {
|
|
246
|
+
module.exports = { createFilesPane };
|
package/lib/session/items.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const accessors = require('./accessors.js');
|
|
4
|
-
const { stateView } = accessors;
|
|
5
|
-
|
|
6
3
|
const files = require('../files.js');
|
|
7
|
-
const { itemPath,
|
|
4
|
+
const { itemPath, TASKS_FILE, isTaskItem } = files;
|
|
8
5
|
const review = require('../review.js');
|
|
9
6
|
const { feedbackKey } = review;
|
|
10
7
|
const diff = require('../diff/diff.js');
|
|
@@ -29,7 +26,7 @@ const npmBusyKind = (item) => {
|
|
|
29
26
|
const blockKey = (item) => {
|
|
30
27
|
if (!item || !item.file) return '';
|
|
31
28
|
const rel = itemPath(item);
|
|
32
|
-
if (
|
|
29
|
+
if (isTaskItem(item)) return `todo:${TASKS_FILE}`;
|
|
33
30
|
const depId = depChangeKey(item);
|
|
34
31
|
if (depId) return `${rel}:dep:${depId}`;
|
|
35
32
|
if (!item.hunk) {
|
|
@@ -44,13 +41,13 @@ const blockKey = (item) => {
|
|
|
44
41
|
|
|
45
42
|
const itemKey = (item) => {
|
|
46
43
|
const key = blockKey(item);
|
|
47
|
-
if (!key ||
|
|
44
|
+
if (!key || isTaskItem(item)) return key;
|
|
48
45
|
const origin = item.origin ?? '';
|
|
49
46
|
return `${origin}:${key}`;
|
|
50
47
|
};
|
|
51
48
|
|
|
52
49
|
const blockLinesKey = (item) => {
|
|
53
|
-
if (!item || !item.file ||
|
|
50
|
+
if (!item || !item.file || isTaskItem(item)) return '';
|
|
54
51
|
if (!item.hunk) return '';
|
|
55
52
|
const parts = [itemPath(item)];
|
|
56
53
|
for (const line of item.hunk.lines) {
|
|
@@ -132,27 +129,25 @@ const alignLoadedItems = (prev, next) => {
|
|
|
132
129
|
return ordered;
|
|
133
130
|
};
|
|
134
131
|
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
132
|
+
const originOf = (item) => item.origin ?? '';
|
|
133
|
+
|
|
134
|
+
const hunkStart = (item) => (item.hunk ? item.hunk.newStart : 0);
|
|
135
|
+
|
|
136
|
+
const sameLinesIndex = (items, item) => {
|
|
138
137
|
const lines = blockLinesKey(item);
|
|
139
|
-
if (lines)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
if (any >= 0) return any;
|
|
147
|
-
}
|
|
148
|
-
const key = itemKey(item);
|
|
149
|
-
if (key) {
|
|
150
|
-
const exact = items.findIndex((entry) => itemKey(entry) === key);
|
|
151
|
-
if (exact >= 0) return exact;
|
|
138
|
+
if (!lines) return -1;
|
|
139
|
+
let fallback = -1;
|
|
140
|
+
for (let i = 0; i < items.length; i++) {
|
|
141
|
+
if (blockLinesKey(items[i]) !== lines) continue;
|
|
142
|
+
if (originOf(items[i]) === originOf(item)) return i;
|
|
143
|
+
if (fallback < 0) fallback = i;
|
|
152
144
|
}
|
|
145
|
+
return fallback;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const nearestHunkIndex = (items, item) => {
|
|
153
149
|
const rel = itemPath(item);
|
|
154
150
|
if (!rel) return -1;
|
|
155
|
-
const start = item.hunk ? item.hunk.newStart : 0;
|
|
156
151
|
let fileMatch = -1;
|
|
157
152
|
let best = -1;
|
|
158
153
|
let bestDist = Infinity;
|
|
@@ -160,19 +155,27 @@ const restoredIndex = (items, item) => {
|
|
|
160
155
|
const entry = items[i];
|
|
161
156
|
if (itemPath(entry) !== rel) continue;
|
|
162
157
|
if (fileMatch < 0) fileMatch = i;
|
|
163
|
-
if ((entry
|
|
164
|
-
const
|
|
165
|
-
const dist = Math.abs(at - start);
|
|
158
|
+
if (originOf(entry) !== originOf(item)) continue;
|
|
159
|
+
const dist = Math.abs(hunkStart(entry) - hunkStart(item));
|
|
166
160
|
if (dist >= bestDist) continue;
|
|
167
161
|
bestDist = dist;
|
|
168
162
|
best = i;
|
|
169
163
|
}
|
|
170
|
-
|
|
171
|
-
|
|
164
|
+
return best >= 0 ? best : fileMatch;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const restoredIndex = (items, item) => {
|
|
168
|
+
if (!item || !item.file) return -1;
|
|
169
|
+
const byLines = sameLinesIndex(items, item);
|
|
170
|
+
if (byLines >= 0) return byLines;
|
|
171
|
+
const key = itemKey(item);
|
|
172
|
+
const exact = key ? items.findIndex((entry) => itemKey(entry) === key) : -1;
|
|
173
|
+
if (exact >= 0) return exact;
|
|
174
|
+
return nearestHunkIndex(items, item);
|
|
172
175
|
};
|
|
173
176
|
|
|
174
177
|
const itemFeedKey = (item) => {
|
|
175
|
-
if (!item || !item.file ||
|
|
178
|
+
if (!item || !item.file || isTaskItem(item)) return '';
|
|
176
179
|
const hunk = item.hunk;
|
|
177
180
|
const depId = depChangeKey(item);
|
|
178
181
|
const file = depId ? `${itemPath(item)}#${depId}` : itemPath(item);
|
|
@@ -190,88 +193,65 @@ const sameBlock = (left, right) => {
|
|
|
190
193
|
};
|
|
191
194
|
|
|
192
195
|
const canEditCode = (item) => {
|
|
193
|
-
if (!item ||
|
|
196
|
+
if (!item || isTaskItem(item)) return false;
|
|
194
197
|
if (!item.hunk || !item.file) return false;
|
|
195
198
|
if (item.file.isBinary) return false;
|
|
196
199
|
return true;
|
|
197
200
|
};
|
|
198
201
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
items
|
|
202
|
-
dismissed
|
|
203
|
-
}
|
|
202
|
+
class ItemCollection {
|
|
203
|
+
constructor() {
|
|
204
|
+
this.items = [];
|
|
205
|
+
this.dismissed = new Set();
|
|
206
|
+
}
|
|
204
207
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
208
|
+
reset() {
|
|
209
|
+
this.items = [];
|
|
210
|
+
this.dismissed = new Set();
|
|
211
|
+
}
|
|
209
212
|
|
|
210
|
-
|
|
213
|
+
isDismissed(item) {
|
|
214
|
+
return this.dismissed.has(itemKey(item));
|
|
215
|
+
}
|
|
211
216
|
|
|
212
|
-
|
|
213
|
-
const aligned = alignLoadedItems(
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
next.push(item);
|
|
218
|
-
}
|
|
219
|
-
state.items = next;
|
|
220
|
-
return next;
|
|
221
|
-
};
|
|
217
|
+
replace(items) {
|
|
218
|
+
const aligned = alignLoadedItems(this.items, items);
|
|
219
|
+
this.items = aligned.filter((item) => !this.isDismissed(item));
|
|
220
|
+
return this.items;
|
|
221
|
+
}
|
|
222
222
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
223
|
+
dismiss(item) {
|
|
224
|
+
this.dismissed.add(itemKey(item));
|
|
225
|
+
}
|
|
226
226
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
227
|
+
clearDismissed() {
|
|
228
|
+
this.dismissed.clear();
|
|
229
|
+
}
|
|
230
230
|
|
|
231
|
-
|
|
231
|
+
findRestoredIndex(item) {
|
|
232
|
+
return restoredIndex(this.items, item);
|
|
233
|
+
}
|
|
232
234
|
|
|
233
|
-
|
|
235
|
+
keepOrigin(item, origin) {
|
|
234
236
|
const next = { ...item, origin };
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const entry = state.items[i];
|
|
239
|
-
replaced[i] = sameBlock(entry, item) ? next : entry;
|
|
240
|
-
}
|
|
237
|
+
const replaced = this.items.map((entry) =>
|
|
238
|
+
sameBlock(entry, item) ? next : entry,
|
|
239
|
+
);
|
|
241
240
|
if (next.dep) {
|
|
242
|
-
|
|
241
|
+
this.items = replaced;
|
|
243
242
|
return;
|
|
244
243
|
}
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return stateView(
|
|
249
|
-
state,
|
|
250
|
-
{
|
|
251
|
-
reset,
|
|
252
|
-
replace,
|
|
253
|
-
dismiss,
|
|
254
|
-
clearDismissed,
|
|
255
|
-
isDismissed,
|
|
256
|
-
findRestoredIndex,
|
|
257
|
-
keepOrigin,
|
|
258
|
-
},
|
|
259
|
-
['items'],
|
|
260
|
-
['dismissed'],
|
|
261
|
-
);
|
|
262
|
-
};
|
|
244
|
+
this.items = refreshIndexPatches(replaced, next);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
263
247
|
|
|
264
248
|
module.exports = {
|
|
265
|
-
depChangeKey,
|
|
266
249
|
needsReload,
|
|
267
250
|
npmBusyKind,
|
|
268
|
-
blockKey,
|
|
269
|
-
itemKey,
|
|
270
251
|
itemFeedKey,
|
|
271
|
-
blockLinesKey,
|
|
272
252
|
alignLoadedItems,
|
|
273
253
|
restoredIndex,
|
|
274
254
|
sameBlock,
|
|
275
255
|
canEditCode,
|
|
276
|
-
|
|
256
|
+
ItemCollection,
|
|
277
257
|
};
|
package/lib/session/lifecycle.js
CHANGED
|
@@ -4,11 +4,11 @@ const fs = require('node:fs');
|
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
|
|
6
6
|
const sessionLoad = require('./load.js');
|
|
7
|
-
const { isAbortError } = sessionLoad;
|
|
7
|
+
const { isAbortError, tagLoaded, fetchSnapshot, fetchExtras } = sessionLoad;
|
|
8
8
|
const watch = require('./watch.js');
|
|
9
|
-
const { createDiskWatcher,
|
|
9
|
+
const { createDiskWatcher, DEBOUNCE_MS } = watch;
|
|
10
10
|
const files = require('../files.js');
|
|
11
|
-
const { itemPath, relativeAge
|
|
11
|
+
const { itemPath, relativeAge } = files;
|
|
12
12
|
const deps = require('../deps.js');
|
|
13
13
|
const { MANIFEST, LOCKFILE, depFileMeta } = deps;
|
|
14
14
|
const review = require('../review.js');
|
|
@@ -82,7 +82,7 @@ const failOpen = (api, error) => {
|
|
|
82
82
|
|
|
83
83
|
const restoreAfterLoad = (api, here, wasTodo) => {
|
|
84
84
|
const { collection, nav, ui } = api;
|
|
85
|
-
nav.
|
|
85
|
+
nav.tasksOpen = wasTodo;
|
|
86
86
|
if (here && !wasTodo) {
|
|
87
87
|
const idx = collection.findRestoredIndex(here);
|
|
88
88
|
if (idx >= 0) nav.index = idx;
|
|
@@ -152,7 +152,7 @@ const applyLoaded = (api, loaded) => {
|
|
|
152
152
|
if (loaded.generation !== undefined) {
|
|
153
153
|
if (!loader.accept(loaded.generation, ui.done)) return false;
|
|
154
154
|
}
|
|
155
|
-
const wasTodo = nav.
|
|
155
|
+
const wasTodo = nav.tasksOpen === true;
|
|
156
156
|
const here = ui.current();
|
|
157
157
|
ui.top = loaded.top ?? api.loadConfig.cwd;
|
|
158
158
|
collection.replace(loaded.items);
|
|
@@ -243,6 +243,13 @@ const clearNpmExtras = (api) => {
|
|
|
243
243
|
api.npmExtras = null;
|
|
244
244
|
};
|
|
245
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
|
+
|
|
246
253
|
const followExtras = async (api, loaded, gen, kind) => {
|
|
247
254
|
const extra = loadOptions(api, {
|
|
248
255
|
paths: api.loadConfig.paths,
|
|
@@ -250,25 +257,18 @@ const followExtras = async (api, loaded, gen, kind) => {
|
|
|
250
257
|
});
|
|
251
258
|
let next;
|
|
252
259
|
try {
|
|
253
|
-
next = await
|
|
260
|
+
next = await fetchExtras(api.loadConfig.repo, loaded, extra);
|
|
254
261
|
} catch (error) {
|
|
255
|
-
if (
|
|
256
|
-
return void failOpen(api, error);
|
|
257
|
-
}
|
|
258
|
-
if (!api.loader.isCurrent(gen)) return;
|
|
259
|
-
if (api.ui.done) {
|
|
260
|
-
api.ops.clearBusy();
|
|
262
|
+
if (api.loader.isCurrent(gen)) failOpen(api, error);
|
|
261
263
|
return;
|
|
262
264
|
}
|
|
263
|
-
if (!
|
|
264
|
-
|
|
265
|
+
if (!isWanted(api, gen)) return;
|
|
266
|
+
if (next) {
|
|
267
|
+
applyLoaded(api, tagLoaded(next, gen));
|
|
268
|
+
cacheNpmExtras(api, next);
|
|
269
|
+
} else {
|
|
265
270
|
api.loader.pendingExtras = false;
|
|
266
|
-
api.ui.paint();
|
|
267
|
-
finishIfEmpty(api, kind);
|
|
268
|
-
return;
|
|
269
271
|
}
|
|
270
|
-
applyLoaded(api, api.loader.tag(next, gen));
|
|
271
|
-
cacheNpmExtras(api, next);
|
|
272
272
|
api.ops.clearBusy();
|
|
273
273
|
api.ui.paint();
|
|
274
274
|
finishIfEmpty(api, kind);
|
|
@@ -301,7 +301,13 @@ const resetNavAfterLoad = (api, extra = {}) => {
|
|
|
301
301
|
if (extra.afterLoad) extra.afterLoad();
|
|
302
302
|
};
|
|
303
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
|
+
|
|
304
309
|
const afterSnapshot = (api, kind, extra = {}) => {
|
|
310
|
+
refreshListsForPane(api);
|
|
305
311
|
if (kind !== 'open' && kind !== 'watch') {
|
|
306
312
|
return void resetNavAfterLoad(api, extra);
|
|
307
313
|
}
|
|
@@ -319,7 +325,7 @@ const runSnapshot = async (api, gen, kind, extra = {}) => {
|
|
|
319
325
|
beginSnapshot(api, extra);
|
|
320
326
|
let loaded;
|
|
321
327
|
try {
|
|
322
|
-
loaded = await
|
|
328
|
+
loaded = await fetchSnapshot(
|
|
323
329
|
api.loadConfig.repo,
|
|
324
330
|
api.loadConfig.cwd,
|
|
325
331
|
api.loadConfig.paths,
|
|
@@ -329,15 +335,11 @@ const runSnapshot = async (api, gen, kind, extra = {}) => {
|
|
|
329
335
|
}),
|
|
330
336
|
);
|
|
331
337
|
} catch (error) {
|
|
332
|
-
if (
|
|
333
|
-
return void failOpen(api, error);
|
|
334
|
-
}
|
|
335
|
-
if (!api.loader.isCurrent(gen)) return;
|
|
336
|
-
if (api.ui.done) {
|
|
337
|
-
api.ops.clearBusy();
|
|
338
|
+
if (api.loader.isCurrent(gen)) failOpen(api, error);
|
|
338
339
|
return;
|
|
339
340
|
}
|
|
340
|
-
|
|
341
|
+
if (!isWanted(api, gen)) return;
|
|
342
|
+
applyLoaded(api, tagLoaded(loaded, gen));
|
|
341
343
|
afterSnapshot(api, kind, extra);
|
|
342
344
|
if (loaded.pending === true || npmExtrasStale(api)) {
|
|
343
345
|
api.ops.setBusy('checking npm');
|
|
@@ -383,7 +385,7 @@ const refreshFromRepo = (api, extra = {}) => {
|
|
|
383
385
|
api.loadConfig.paths,
|
|
384
386
|
snapshotOpts(api, extra),
|
|
385
387
|
);
|
|
386
|
-
applyLoaded(api,
|
|
388
|
+
applyLoaded(api, tagLoaded(loaded, gen));
|
|
387
389
|
afterSnapshot(api, kind, extra);
|
|
388
390
|
if (loaded.pending === true || npmExtrasStale(api)) {
|
|
389
391
|
api.ops.setBusy('checking npm');
|
|
@@ -467,59 +469,6 @@ const catchUpWatch = (api) => {
|
|
|
467
469
|
refreshFromRepo(api, { keepView: true, quiet: true });
|
|
468
470
|
};
|
|
469
471
|
|
|
470
|
-
const stampFile = (root, rel) => {
|
|
471
|
-
const full = path.join(root, rel);
|
|
472
|
-
let stat;
|
|
473
|
-
try {
|
|
474
|
-
stat = fs.statSync(full);
|
|
475
|
-
} catch {
|
|
476
|
-
return `${rel}:missing`;
|
|
477
|
-
}
|
|
478
|
-
return `${rel}:${stat.mtimeMs}:${stat.size}`;
|
|
479
|
-
};
|
|
480
|
-
|
|
481
|
-
const worktreeStamp = (api) => {
|
|
482
|
-
const root = api.ui.top || api.loadConfig.cwd;
|
|
483
|
-
const parts = [];
|
|
484
|
-
for (const rel of itemRels(api.collection)) {
|
|
485
|
-
if (!rel || rel === TODO_FILE) continue;
|
|
486
|
-
parts.push(stampFile(root, rel));
|
|
487
|
-
}
|
|
488
|
-
return parts.join('\n');
|
|
489
|
-
};
|
|
490
|
-
|
|
491
|
-
const watchesWorktree = (nav) =>
|
|
492
|
-
nav.todoOpen === true ||
|
|
493
|
-
nav.pane === 'branches' ||
|
|
494
|
-
nav.pane === 'commits' ||
|
|
495
|
-
nav.pane === 'npm';
|
|
496
|
-
|
|
497
|
-
const currentFileStamp = (api) => {
|
|
498
|
-
const { nav } = api;
|
|
499
|
-
const root = api.ui.top || api.loadConfig.cwd;
|
|
500
|
-
if (watchesWorktree(nav)) return worktreeStamp(api);
|
|
501
|
-
const pane = nav.pane;
|
|
502
|
-
if (pane !== 'diff' && pane !== 'unit') return '';
|
|
503
|
-
const rel = pane === 'unit' ? nav.reviewPath : itemPath(api.ui.current());
|
|
504
|
-
if (!rel) return '';
|
|
505
|
-
return stampFile(root, rel);
|
|
506
|
-
};
|
|
507
|
-
|
|
508
|
-
const pollCurrentFile = (api) => {
|
|
509
|
-
const stamp = currentFileStamp(api);
|
|
510
|
-
if (!stamp) {
|
|
511
|
-
api.watchStamp = '';
|
|
512
|
-
return;
|
|
513
|
-
}
|
|
514
|
-
if (!api.watchStamp) {
|
|
515
|
-
api.watchStamp = stamp;
|
|
516
|
-
return;
|
|
517
|
-
}
|
|
518
|
-
if (stamp === api.watchStamp) return;
|
|
519
|
-
api.watchStamp = stamp;
|
|
520
|
-
onDiskChange(api);
|
|
521
|
-
};
|
|
522
|
-
|
|
523
472
|
const reviewFileStamp = (api) => {
|
|
524
473
|
const store = api.review && api.review.store;
|
|
525
474
|
if (!store || !store.reviewPath) return '';
|
|
@@ -553,14 +502,12 @@ const applyReviewChange = (api) => {
|
|
|
553
502
|
}
|
|
554
503
|
mergeReview(store, disk);
|
|
555
504
|
api.reviewStamp = stamp;
|
|
556
|
-
|
|
505
|
+
api.ui.paint();
|
|
557
506
|
};
|
|
558
507
|
|
|
559
508
|
const stopWatch = (api) => {
|
|
560
509
|
clearWatchRetry(api);
|
|
561
|
-
api.watchStamp = '';
|
|
562
510
|
api.reviewStamp = '';
|
|
563
|
-
if (api.ui.term) api.ui.term.stopTimer('watch');
|
|
564
511
|
if (!api.diskWatcher) return;
|
|
565
512
|
api.diskWatcher.close();
|
|
566
513
|
api.diskWatcher = null;
|
|
@@ -579,26 +526,15 @@ const startWatch = (api) => {
|
|
|
579
526
|
});
|
|
580
527
|
syncItemWatches(api);
|
|
581
528
|
noteReviewStamp(api);
|
|
582
|
-
if (!api.ui.term) return;
|
|
583
|
-
api.ui.term.startTimer(
|
|
584
|
-
'watch',
|
|
585
|
-
() => {
|
|
586
|
-
pollCurrentFile(api);
|
|
587
|
-
applyReviewChange(api);
|
|
588
|
-
},
|
|
589
|
-
POLL_MS,
|
|
590
|
-
);
|
|
591
529
|
};
|
|
592
530
|
|
|
593
531
|
const createLifecycle = (api) => {
|
|
594
532
|
api.diskWatcher = null;
|
|
595
533
|
api.watchRetry = null;
|
|
596
|
-
api.watchStamp = '';
|
|
597
534
|
api.reviewStamp = '';
|
|
598
535
|
api.ignoreWatchUntil = 0;
|
|
599
536
|
api.npmExtras = null;
|
|
600
537
|
return {
|
|
601
|
-
applyLoaded: (loaded) => applyLoaded(api, loaded),
|
|
602
538
|
load: () => load(api),
|
|
603
539
|
openLoad: () => openLoad(api),
|
|
604
540
|
loadReady: () => loadReady(api),
|
|
@@ -609,7 +545,6 @@ const createLifecycle = (api) => {
|
|
|
609
545
|
applyReviewChange: () => applyReviewChange(api),
|
|
610
546
|
noteReviewStamp: () => noteReviewStamp(api),
|
|
611
547
|
catchUpWatch: () => catchUpWatch(api),
|
|
612
|
-
pollCurrentFile: () => pollCurrentFile(api),
|
|
613
548
|
startWatch: () => startWatch(api),
|
|
614
549
|
stopWatch: () => stopWatch(api),
|
|
615
550
|
ignoreWatch: (ms) => ignoreWatch(api, ms),
|