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/review.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const { createStore, applyImportedNotes, setStatus } = store;
|
|
5
|
-
const storage = require('../review/storage.js');
|
|
6
|
-
const { listReviewNames, loadTemplates, resolveReviewPath } = storage;
|
|
7
|
-
const { loadReview, flushReview } = storage;
|
|
3
|
+
const { stateView } = require('./accessors.js');
|
|
8
4
|
|
|
9
|
-
const
|
|
10
|
-
|
|
5
|
+
const review = require('../review.js');
|
|
6
|
+
const { createStore, applyImportedNotes, setStatus } = review;
|
|
7
|
+
const { listReviewNames, loadTemplates, resolveReviewPath } = review;
|
|
8
|
+
const { loadReview, flushReview } = review;
|
|
9
|
+
|
|
10
|
+
const createReviewController = () => {
|
|
11
11
|
const state = {
|
|
12
12
|
store: null,
|
|
13
13
|
didResume: false,
|
|
@@ -21,15 +21,11 @@ const createReviewController = (options) => {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
const init = (dir, date, extra = {}) => {
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const resolved = resolveReviewPath(dir, date, names, {
|
|
28
|
-
forceNew,
|
|
29
|
-
readFileSync: io.readFileSync,
|
|
30
|
-
});
|
|
24
|
+
const names = listReviewNames(dir);
|
|
25
|
+
const templates = loadTemplates(dir);
|
|
26
|
+
const resolved = resolveReviewPath(dir, date, names, extra);
|
|
31
27
|
if (resolved.resume) {
|
|
32
|
-
state.store = loadReview(resolved.reviewPath, templates
|
|
28
|
+
state.store = loadReview(resolved.reviewPath, templates);
|
|
33
29
|
state.didResume = true;
|
|
34
30
|
return state.store;
|
|
35
31
|
}
|
|
@@ -49,13 +45,7 @@ const createReviewController = (options) => {
|
|
|
49
45
|
const flush = (force = false) => {
|
|
50
46
|
if (!state.store) return { ok: true, wrote: false };
|
|
51
47
|
try {
|
|
52
|
-
const
|
|
53
|
-
const mkdirSync = io.mkdirSync;
|
|
54
|
-
const wrote = flushReview(state.store, {
|
|
55
|
-
writeFileSync,
|
|
56
|
-
mkdirSync,
|
|
57
|
-
force,
|
|
58
|
-
});
|
|
48
|
+
const wrote = flushReview(state.store, force);
|
|
59
49
|
return { ok: true, wrote };
|
|
60
50
|
} catch (error) {
|
|
61
51
|
return { ok: false, wrote: false, error };
|
|
@@ -67,22 +57,18 @@ const createReviewController = (options) => {
|
|
|
67
57
|
return flush();
|
|
68
58
|
};
|
|
69
59
|
|
|
70
|
-
return
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
60
|
+
return stateView(
|
|
61
|
+
state,
|
|
62
|
+
{
|
|
63
|
+
reset,
|
|
64
|
+
init,
|
|
65
|
+
applyImported,
|
|
66
|
+
flush,
|
|
67
|
+
finishQuit,
|
|
76
68
|
},
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
reset,
|
|
81
|
-
init,
|
|
82
|
-
applyImported,
|
|
83
|
-
flush,
|
|
84
|
-
finishQuit,
|
|
85
|
-
};
|
|
69
|
+
[],
|
|
70
|
+
['store', 'didResume', 'importedRemote'],
|
|
71
|
+
);
|
|
86
72
|
};
|
|
87
73
|
|
|
88
74
|
module.exports = { createReviewController };
|
package/lib/session/terminal.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { presentRows, presentCursor } = require('../render.js');
|
|
4
|
-
const { listen, watchResize } = require('../
|
|
3
|
+
const { presentRows, presentCursor } = require('../render/render.js');
|
|
4
|
+
const { listen, watchResize } = require('../utilities.js');
|
|
5
5
|
|
|
6
6
|
const ENTER_TERM = '\x1b[?1049h\x1b[?25l\x1b[?7l\x1b[?1002h\x1b[?1006h';
|
|
7
7
|
const LEAVE_TERM =
|
|
@@ -18,14 +18,16 @@ const hasTimer = (api, name) => api.timers.has(name);
|
|
|
18
18
|
const stopTimer = (api, name) => {
|
|
19
19
|
const id = api.timers.get(name);
|
|
20
20
|
if (id === undefined) return;
|
|
21
|
-
|
|
21
|
+
clearInterval(id);
|
|
22
22
|
api.timers.delete(name);
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
const startTimer = (api, name, fn, ms) => {
|
|
26
26
|
if (api.state.disposed) return;
|
|
27
27
|
if (api.timers.has(name)) return;
|
|
28
|
-
|
|
28
|
+
const id = setInterval(fn, ms);
|
|
29
|
+
if (id && typeof id.unref === 'function') id.unref();
|
|
30
|
+
api.timers.set(name, id);
|
|
29
31
|
};
|
|
30
32
|
|
|
31
33
|
const restartTimer = (api, name, fn, ms) => {
|
|
@@ -118,8 +120,6 @@ const createTerminal = (options) => {
|
|
|
118
120
|
stdin: options.stdin ?? null,
|
|
119
121
|
stdout: options.stdout,
|
|
120
122
|
proc: options.proc ?? process,
|
|
121
|
-
setIntervalFn: options.setInterval ?? setInterval,
|
|
122
|
-
clearIntervalFn: options.clearInterval ?? clearInterval,
|
|
123
123
|
timers: new Map(),
|
|
124
124
|
disposers: [],
|
|
125
125
|
state,
|
|
@@ -131,18 +131,12 @@ const createTerminal = (options) => {
|
|
|
131
131
|
get lastSize() {
|
|
132
132
|
return state.lastSize;
|
|
133
133
|
},
|
|
134
|
-
get lastPresentedCursor() {
|
|
135
|
-
return state.lastPresentedCursor;
|
|
136
|
-
},
|
|
137
134
|
get carry() {
|
|
138
135
|
return state.carry;
|
|
139
136
|
},
|
|
140
137
|
set carry(value) {
|
|
141
138
|
state.carry = value;
|
|
142
139
|
},
|
|
143
|
-
get isOpen() {
|
|
144
|
-
return state.open;
|
|
145
|
-
},
|
|
146
140
|
get disposed() {
|
|
147
141
|
return state.disposed;
|
|
148
142
|
},
|
package/lib/session/todos.js
CHANGED
|
@@ -48,17 +48,19 @@ const isDraftCompose = (api) => {
|
|
|
48
48
|
const todoTexts = (api) => {
|
|
49
49
|
const nav = api.nav;
|
|
50
50
|
if (!nav.todoOpen) return [];
|
|
51
|
-
const
|
|
51
|
+
const todos = listTodos(api);
|
|
52
|
+
const texts = new Array(todos.length + 1);
|
|
52
53
|
const { state } = api;
|
|
53
54
|
const editingTodo = state.composeKind === 'todo';
|
|
54
55
|
const editingId = editingTodo ? state.composeTodoId : null;
|
|
55
|
-
for (
|
|
56
|
+
for (let i = 0; i < todos.length; i++) {
|
|
57
|
+
const todo = todos[i];
|
|
56
58
|
const live = editingId === todo.id && state.editor;
|
|
57
59
|
const text = live ? state.editor.text : todo.text;
|
|
58
|
-
texts
|
|
60
|
+
texts[i] = checkLabel(text, todo.done);
|
|
59
61
|
}
|
|
60
62
|
const draft = isDraftCompose(api) && state.editor ? state.editor.text : '';
|
|
61
|
-
texts.
|
|
63
|
+
texts[todos.length] = checkLabel(draft, false);
|
|
62
64
|
return texts;
|
|
63
65
|
};
|
|
64
66
|
|
|
@@ -126,10 +128,7 @@ const editFocusedTodo = (api) => {
|
|
|
126
128
|
if (!api.nav.todoOpen) return;
|
|
127
129
|
const todos = listTodos(api);
|
|
128
130
|
const todo = todos[clampedTodoFocus(api)];
|
|
129
|
-
if (!todo)
|
|
130
|
-
startDraftCompose(api);
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
131
|
+
if (!todo) return void startDraftCompose(api);
|
|
133
132
|
api.open('todo', todo.text, todo.id);
|
|
134
133
|
};
|
|
135
134
|
|
|
@@ -234,10 +233,7 @@ const commitTodo = (api, text) => {
|
|
|
234
233
|
const id = api.state.composeTodoId;
|
|
235
234
|
const todos = notes.todos;
|
|
236
235
|
const prev = id === null ? null : todos.find((entry) => entry.id === id);
|
|
237
|
-
if (prev)
|
|
238
|
-
setTodoText(notes, id, text);
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
236
|
+
if (prev) return void setTodoText(notes, id, text);
|
|
241
237
|
if (!text.trim()) return;
|
|
242
238
|
const todo = addTodo(notes, TODO_FILE, text);
|
|
243
239
|
api.state.composeTodoId = todo.id;
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const files = require('../files.js');
|
|
4
|
+
const { listPath, isTodosEntry } = files;
|
|
5
|
+
const diff = require('../diff/diff.js');
|
|
6
|
+
const { unitLines, itemsForPath, blockLineIndex } = diff;
|
|
7
|
+
const { hunkLineRange, sameHunk, hunkKey } = diff;
|
|
8
|
+
const { fileOffset, splitEditor, mergeEditRows, fileLineIndex } = diff;
|
|
9
|
+
|
|
10
|
+
const fileText = (ui, rel) => {
|
|
11
|
+
if (!rel) return '';
|
|
12
|
+
const repo = ui.repo;
|
|
13
|
+
if (repo && typeof repo.fileText === 'function') {
|
|
14
|
+
return repo.fileText(ui.top, rel, ui.rev);
|
|
15
|
+
}
|
|
16
|
+
return '';
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const pathItems = (ui, rel) => itemsForPath(ui.items ?? [], rel);
|
|
20
|
+
|
|
21
|
+
const linesFor = (
|
|
22
|
+
ui,
|
|
23
|
+
rel,
|
|
24
|
+
overlay = ui.composer.liveFileText(),
|
|
25
|
+
base = null,
|
|
26
|
+
) => {
|
|
27
|
+
const text = overlay === null ? fileText(ui, rel) : overlay;
|
|
28
|
+
const group = pathItems(ui, rel);
|
|
29
|
+
if (overlay !== null && base && base.length) {
|
|
30
|
+
return mergeEditRows(base, splitEditor(text));
|
|
31
|
+
}
|
|
32
|
+
return unitLines(text, group);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const BODY_SHIFT = 1;
|
|
36
|
+
|
|
37
|
+
const clampUnitLine = (nav, length) => {
|
|
38
|
+
if (!length) {
|
|
39
|
+
nav.unitLine = 0;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (nav.unitLine < 0) nav.unitLine = 0;
|
|
43
|
+
if (nav.unitLine >= length) nav.unitLine = length - 1;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const CONTEXT = 1;
|
|
47
|
+
|
|
48
|
+
const frameBodyH = (frame, ui) => {
|
|
49
|
+
if (frame && frame.bodyH) return frame.bodyH;
|
|
50
|
+
const size = ui?.lastSize ?? ui?.getSize?.();
|
|
51
|
+
const height = size && size.height ? size.height : 24;
|
|
52
|
+
return Math.max(1, height - 4);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const ensureRangeVisible = (nav, firstLine, lastLine, frame, ui) => {
|
|
56
|
+
const bodyH = frameBodyH(frame, ui);
|
|
57
|
+
const diffFirst = firstLine + BODY_SHIFT;
|
|
58
|
+
const diffLast = lastLine + BODY_SHIFT;
|
|
59
|
+
const diffSpan = diffLast - diffFirst + 1;
|
|
60
|
+
let first = diffFirst;
|
|
61
|
+
let last = diffLast;
|
|
62
|
+
if (diffSpan <= bodyH) {
|
|
63
|
+
const padFirst = Math.max(0, firstLine - CONTEXT) + BODY_SHIFT;
|
|
64
|
+
const padLast = lastLine + CONTEXT + BODY_SHIFT;
|
|
65
|
+
if (padLast - padFirst + 1 <= bodyH) {
|
|
66
|
+
first = padFirst;
|
|
67
|
+
last = padLast;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const span = last - first + 1;
|
|
71
|
+
let scroll = nav.scroll ?? 0;
|
|
72
|
+
if (span > bodyH) {
|
|
73
|
+
scroll = first;
|
|
74
|
+
} else {
|
|
75
|
+
if (first < scroll) scroll = first;
|
|
76
|
+
const viewLast = scroll + bodyH - 1;
|
|
77
|
+
if (last > viewLast) scroll = last - bodyH + 1;
|
|
78
|
+
}
|
|
79
|
+
nav.scroll = Math.max(0, scroll);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const ensureVisible = (nav, length, frame) => {
|
|
83
|
+
clampUnitLine(nav, length);
|
|
84
|
+
ensureRangeVisible(nav, nav.unitLine, nav.unitLine, frame);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const followFileCursor = (nav, editor, frame, rows) => {
|
|
88
|
+
if (!editor) return;
|
|
89
|
+
const fileLine = editor.linePos().line;
|
|
90
|
+
if (rows && rows.length) {
|
|
91
|
+
nav.unitLine = fileLineIndex(rows, fileLine);
|
|
92
|
+
ensureVisible(nav, rows.length, frame);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const length = `${editor.text}`.split('\n').length;
|
|
96
|
+
nav.unitLine = fileLine;
|
|
97
|
+
ensureVisible(nav, length, frame);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const jumpToItem = (ui, item) => {
|
|
101
|
+
const nav = ui.nav;
|
|
102
|
+
const rel = nav.reviewPath;
|
|
103
|
+
const lines = linesFor(ui, rel);
|
|
104
|
+
const items = ui.items ?? [];
|
|
105
|
+
const idx = items.indexOf(item);
|
|
106
|
+
if (idx >= 0) nav.index = idx;
|
|
107
|
+
const range = hunkLineRange(lines, item);
|
|
108
|
+
nav.unitLine = range.start;
|
|
109
|
+
ensureRangeVisible(nav, range.start, range.end, ui.lastFrame, ui);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const viewLine = (nav, length) => {
|
|
113
|
+
if (!length) return 0;
|
|
114
|
+
const top = Math.max(0, (nav.scroll ?? 0) - 1);
|
|
115
|
+
return Math.min(top, length - 1);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const moveUnitBlock = (ui, step) => {
|
|
119
|
+
const nav = ui.nav;
|
|
120
|
+
const rel = nav.reviewPath;
|
|
121
|
+
if (!rel) return;
|
|
122
|
+
const items = ui.items ?? [];
|
|
123
|
+
const indexes = [];
|
|
124
|
+
const seen = new Set();
|
|
125
|
+
for (let i = 0; i < items.length; i++) {
|
|
126
|
+
if (listPath(items[i]) !== rel) continue;
|
|
127
|
+
const key = hunkKey(items[i]);
|
|
128
|
+
if (seen.has(key)) continue;
|
|
129
|
+
seen.add(key);
|
|
130
|
+
indexes.push(i);
|
|
131
|
+
}
|
|
132
|
+
if (!indexes.length) {
|
|
133
|
+
ui.status = 'not a diff block';
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
let at = -1;
|
|
137
|
+
const current = items[nav.index];
|
|
138
|
+
for (let i = 0; i < indexes.length; i++) {
|
|
139
|
+
if (!sameHunk(items[indexes[i]], current)) continue;
|
|
140
|
+
at = i;
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
if (at < 0) at = step > 0 ? -1 : 0;
|
|
144
|
+
const next = at + step;
|
|
145
|
+
if (next < 0 || next >= indexes.length) return;
|
|
146
|
+
const item = items[indexes[next]];
|
|
147
|
+
jumpToItem(ui, item);
|
|
148
|
+
ui.status = '';
|
|
149
|
+
nav.clearSelection();
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const openUnitFile = (ui, entry) => {
|
|
153
|
+
if (!entry || isTodosEntry(entry)) return;
|
|
154
|
+
const nav = ui.nav;
|
|
155
|
+
nav.reviewPath = entry.path;
|
|
156
|
+
nav.todoOpen = false;
|
|
157
|
+
nav.pane = 'unit';
|
|
158
|
+
nav.scroll = 0;
|
|
159
|
+
nav.unitLine = 0;
|
|
160
|
+
nav.clearSelection();
|
|
161
|
+
const items = ui.items ?? [];
|
|
162
|
+
const group = pathItems(ui, entry.path);
|
|
163
|
+
if (group.length) {
|
|
164
|
+
const want = items[entry.openIndex] ?? items[entry.firstIndex] ?? group[0];
|
|
165
|
+
const item = group.includes(want) ? want : group[0];
|
|
166
|
+
jumpToItem(ui, item);
|
|
167
|
+
} else {
|
|
168
|
+
nav.unitLine = 0;
|
|
169
|
+
}
|
|
170
|
+
ui.status = '';
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const setFileScope = (ui, next) => {
|
|
174
|
+
if (ui.nav.pane !== 'files') return;
|
|
175
|
+
if (ui.nav.fileScope === next) return;
|
|
176
|
+
ui.nav.fileScope = next;
|
|
177
|
+
ui.nav.restoreFileCursor(ui.fileList());
|
|
178
|
+
ui.status = next === 'file' ? 'file scope' : 'diff mode';
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const editCursor = (ui) => {
|
|
182
|
+
const rel = ui.nav.reviewPath;
|
|
183
|
+
const text = fileText(ui, rel);
|
|
184
|
+
const group = pathItems(ui, rel);
|
|
185
|
+
const lines = unitLines(text, group);
|
|
186
|
+
const items = ui.items ?? [];
|
|
187
|
+
const item = items[ui.nav.index];
|
|
188
|
+
const atBlock = item && listPath(item) === rel;
|
|
189
|
+
const at = atBlock
|
|
190
|
+
? blockLineIndex(lines, item)
|
|
191
|
+
: viewLine(ui.nav, lines.length);
|
|
192
|
+
return {
|
|
193
|
+
text,
|
|
194
|
+
cursor: fileOffset(lines, at),
|
|
195
|
+
rows: lines,
|
|
196
|
+
};
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
module.exports = {
|
|
200
|
+
fileText,
|
|
201
|
+
linesFor,
|
|
202
|
+
moveUnitBlock,
|
|
203
|
+
openUnitFile,
|
|
204
|
+
setFileScope,
|
|
205
|
+
editCursor,
|
|
206
|
+
ensureVisible,
|
|
207
|
+
followFileCursor,
|
|
208
|
+
};
|
package/lib/session/update.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { stateView } = require('./accessors.js');
|
|
4
|
+
|
|
3
5
|
const update = require('../update.js');
|
|
4
6
|
const { checkUpdate, installUpdate, markSkipped } = update;
|
|
5
7
|
|
|
6
8
|
const optsOf = (api) => {
|
|
7
|
-
const value = api.
|
|
9
|
+
const value = api.ui.updateOpts;
|
|
8
10
|
if (!value || value === true) return {};
|
|
9
11
|
return value;
|
|
10
12
|
};
|
|
@@ -19,15 +21,16 @@ const reset = (state) => {
|
|
|
19
21
|
|
|
20
22
|
const maybePrompt = (api) => {
|
|
21
23
|
if (!api.state.offer) return;
|
|
22
|
-
if (!api.
|
|
23
|
-
if (api.
|
|
24
|
-
api.
|
|
24
|
+
if (!api.ui.loader.didLoad) return;
|
|
25
|
+
if (api.ui.mode !== 'review') return;
|
|
26
|
+
api.ui.mode = 'confirmUpdate';
|
|
25
27
|
};
|
|
26
28
|
|
|
27
29
|
const runInstall = async (api, version) => {
|
|
28
|
-
api.
|
|
29
|
-
api.
|
|
30
|
-
api.
|
|
30
|
+
api.ui.status = 'updating reslop';
|
|
31
|
+
api.ui.progressFrame = 0;
|
|
32
|
+
api.ui.progress.start('install');
|
|
33
|
+
api.ui.paint();
|
|
31
34
|
let ok = false;
|
|
32
35
|
try {
|
|
33
36
|
await installUpdate(version, optsOf(api));
|
|
@@ -35,23 +38,23 @@ const runInstall = async (api, version) => {
|
|
|
35
38
|
} catch {
|
|
36
39
|
// ignore install errors
|
|
37
40
|
}
|
|
38
|
-
api.
|
|
39
|
-
if (api.
|
|
40
|
-
api.
|
|
41
|
-
api.
|
|
41
|
+
api.ui.progress.stop('install');
|
|
42
|
+
if (api.ui.done || !api.ui.uiOpen) return;
|
|
43
|
+
api.ui.status = ok ? 'updated' : 'update failed';
|
|
44
|
+
api.ui.paint();
|
|
42
45
|
};
|
|
43
46
|
|
|
44
47
|
const acceptUpdate = (api) => {
|
|
45
|
-
api.
|
|
48
|
+
api.ui.mode = 'review';
|
|
46
49
|
api.state.offer = false;
|
|
47
50
|
const version = api.state.to;
|
|
48
51
|
api.state.installPromise = runInstall(api, version);
|
|
49
52
|
};
|
|
50
53
|
|
|
51
54
|
const declineUpdate = (api) => {
|
|
52
|
-
api.
|
|
55
|
+
api.ui.mode = 'review';
|
|
53
56
|
api.state.offer = false;
|
|
54
|
-
api.
|
|
57
|
+
api.ui.status = '';
|
|
55
58
|
markSkipped(api.state.to, optsOf(api));
|
|
56
59
|
};
|
|
57
60
|
|
|
@@ -62,7 +65,7 @@ const runOpenUpdate = async (api) => {
|
|
|
62
65
|
} catch {
|
|
63
66
|
return;
|
|
64
67
|
}
|
|
65
|
-
if (api.
|
|
68
|
+
if (api.ui.done) return;
|
|
66
69
|
if (plan.action === 'install') {
|
|
67
70
|
api.state.installPromise = runInstall(api, plan.latest);
|
|
68
71
|
await api.state.installPromise;
|
|
@@ -73,17 +76,17 @@ const runOpenUpdate = async (api) => {
|
|
|
73
76
|
api.state.to = plan.latest;
|
|
74
77
|
api.state.offer = true;
|
|
75
78
|
maybePrompt(api);
|
|
76
|
-
api.
|
|
79
|
+
api.ui.paint();
|
|
77
80
|
};
|
|
78
81
|
|
|
79
82
|
const openUpdate = (api) => {
|
|
80
|
-
if (api.
|
|
83
|
+
if (api.ui.updateOpts === false) return Promise.resolve();
|
|
81
84
|
if (api.state.checkPromise) return api.state.checkPromise;
|
|
82
85
|
api.state.checkPromise = runOpenUpdate(api);
|
|
83
86
|
return api.state.checkPromise;
|
|
84
87
|
};
|
|
85
88
|
|
|
86
|
-
const createUpdateCoordinator = (
|
|
89
|
+
const createUpdateCoordinator = (ui) => {
|
|
87
90
|
const state = {
|
|
88
91
|
offer: false,
|
|
89
92
|
from: '',
|
|
@@ -91,35 +94,19 @@ const createUpdateCoordinator = (options) => {
|
|
|
91
94
|
checkPromise: null,
|
|
92
95
|
installPromise: null,
|
|
93
96
|
};
|
|
94
|
-
const api = {
|
|
95
|
-
return
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return state.from;
|
|
104
|
-
},
|
|
105
|
-
get to() {
|
|
106
|
-
return state.to;
|
|
97
|
+
const api = { ui, state };
|
|
98
|
+
return stateView(
|
|
99
|
+
state,
|
|
100
|
+
{
|
|
101
|
+
reset: () => reset(state),
|
|
102
|
+
maybePrompt: () => maybePrompt(api),
|
|
103
|
+
accept: () => acceptUpdate(api),
|
|
104
|
+
decline: () => declineUpdate(api),
|
|
105
|
+
open: () => openUpdate(api),
|
|
107
106
|
},
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
get installPromise() {
|
|
112
|
-
return state.installPromise;
|
|
113
|
-
},
|
|
114
|
-
set installPromise(value) {
|
|
115
|
-
state.installPromise = value;
|
|
116
|
-
},
|
|
117
|
-
reset: () => reset(state),
|
|
118
|
-
maybePrompt: () => maybePrompt(api),
|
|
119
|
-
accept: () => acceptUpdate(api),
|
|
120
|
-
decline: () => declineUpdate(api),
|
|
121
|
-
open: () => openUpdate(api),
|
|
122
|
-
};
|
|
107
|
+
['offer', 'installPromise'],
|
|
108
|
+
['from', 'to', 'checkPromise'],
|
|
109
|
+
);
|
|
123
110
|
};
|
|
124
111
|
|
|
125
112
|
module.exports = { createUpdateCoordinator };
|
package/lib/session/view.js
CHANGED
|
@@ -6,11 +6,8 @@ const files = require('../files.js');
|
|
|
6
6
|
const { fileEntries, REPO_TODOS_LABEL } = files;
|
|
7
7
|
const review = require('../review.js');
|
|
8
8
|
const { noteCounts } = review;
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
'github-pr': 'pr',
|
|
12
|
-
'gitlab-mr': 'mr',
|
|
13
|
-
};
|
|
9
|
+
const diff = require('../diff/diff.js');
|
|
10
|
+
const { unitLines, itemsForPath, splitEditor, mergeEditRows } = diff;
|
|
14
11
|
|
|
15
12
|
const relabelStatus = (entries, from, to) => {
|
|
16
13
|
if (!to) return entries;
|
|
@@ -43,14 +40,60 @@ const todosEntry = (counts) => ({
|
|
|
43
40
|
unstaged: 0,
|
|
44
41
|
});
|
|
45
42
|
|
|
43
|
+
const blankEntry = (rel) => ({
|
|
44
|
+
path: rel,
|
|
45
|
+
origins: [],
|
|
46
|
+
firstIndex: 0,
|
|
47
|
+
openIndex: 0,
|
|
48
|
+
remaining: 0,
|
|
49
|
+
added: 0,
|
|
50
|
+
removed: 0,
|
|
51
|
+
stagedAdded: 0,
|
|
52
|
+
stagedRemoved: 0,
|
|
53
|
+
unstagedAdded: 0,
|
|
54
|
+
unstagedRemoved: 0,
|
|
55
|
+
staged: 0,
|
|
56
|
+
unstaged: 0,
|
|
57
|
+
date: '',
|
|
58
|
+
status: '',
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const listedNames = (api) => {
|
|
62
|
+
const repo = api.ui.repo;
|
|
63
|
+
if (!repo || typeof repo.listFiles !== 'function') return [];
|
|
64
|
+
try {
|
|
65
|
+
return repo.listFiles(api.ui.top || api.ui.cwd, api.ui.paths ?? [], {
|
|
66
|
+
commit: api.ui.rev,
|
|
67
|
+
});
|
|
68
|
+
} catch {
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const mergeUnitFiles = (entries, names) => {
|
|
74
|
+
const have = new Set();
|
|
75
|
+
for (const entry of entries) have.add(entry.path);
|
|
76
|
+
const extra = [];
|
|
77
|
+
for (const rel of names) {
|
|
78
|
+
if (!rel || have.has(rel)) continue;
|
|
79
|
+
extra.push(blankEntry(rel));
|
|
80
|
+
}
|
|
81
|
+
const merged = [...entries, ...extra];
|
|
82
|
+
merged.sort((left, right) => left.path.localeCompare(right.path, 'en'));
|
|
83
|
+
return merged;
|
|
84
|
+
};
|
|
85
|
+
|
|
46
86
|
const fileList = (api, notes = noteCounts(api.review.store)) => {
|
|
47
87
|
const entries = fileEntries(api.collection.items);
|
|
48
88
|
const sourceLabel = api.ui.sourceLabel;
|
|
49
89
|
const from = sourceLabel ? 'pr' : 'commit';
|
|
50
90
|
const to = sourceLabel || api.ui.revShort;
|
|
51
91
|
const labeled = relabelStatus(entries, from, to);
|
|
52
|
-
|
|
53
|
-
|
|
92
|
+
const names = listedNames(api);
|
|
93
|
+
const scoped =
|
|
94
|
+
api.nav.fileScope === 'file' ? mergeUnitFiles(labeled, names) : labeled;
|
|
95
|
+
scoped.unshift(todosEntry(notes));
|
|
96
|
+
return scoped;
|
|
54
97
|
};
|
|
55
98
|
|
|
56
99
|
const counts = (api, notes = noteCounts(api.review.store)) => {
|
|
@@ -76,11 +119,34 @@ const counts = (api, notes = noteCounts(api.review.store)) => {
|
|
|
76
119
|
|
|
77
120
|
const viewStatus = (api) => api.ui.status || api.ui.busy;
|
|
78
121
|
|
|
122
|
+
const unitFileText = (api) => {
|
|
123
|
+
const rel = api.nav.reviewPath;
|
|
124
|
+
if (!rel) return '';
|
|
125
|
+
const repo = api.ui.repo;
|
|
126
|
+
if (repo && typeof repo.fileText === 'function') {
|
|
127
|
+
return repo.fileText(api.ui.top, rel, api.ui.rev);
|
|
128
|
+
}
|
|
129
|
+
return '';
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const unitBodyLines = (api) => {
|
|
133
|
+
if (api.nav.pane !== 'unit') return [];
|
|
134
|
+
const rel = api.nav.reviewPath;
|
|
135
|
+
const group = itemsForPath(api.collection.items, rel);
|
|
136
|
+
const live = api.composer.liveFileText();
|
|
137
|
+
if (live !== null) {
|
|
138
|
+
const base = api.composer.fileEditRows();
|
|
139
|
+
if (base && base.length) return mergeEditRows(base, splitEditor(live));
|
|
140
|
+
return mergeEditRows(unitLines(live, group), splitEditor(live));
|
|
141
|
+
}
|
|
142
|
+
return unitLines(unitFileText(api), group);
|
|
143
|
+
};
|
|
144
|
+
|
|
79
145
|
const view = (api) => {
|
|
80
146
|
const { composer, nav, ui, gitBranches, commits, updater } = api;
|
|
81
147
|
const templates = composer.shownTemplates();
|
|
82
148
|
const change = ui.change;
|
|
83
|
-
const sourceKind = (change &&
|
|
149
|
+
const sourceKind = (change && change.source) || '';
|
|
84
150
|
const notes = noteCounts(api.review.store);
|
|
85
151
|
return {
|
|
86
152
|
item: ui.current(),
|
|
@@ -94,9 +160,12 @@ const view = (api) => {
|
|
|
94
160
|
counts: counts(api, notes),
|
|
95
161
|
selection: nav.selection,
|
|
96
162
|
pane: nav.pane,
|
|
163
|
+
fileScope: nav.fileScope,
|
|
97
164
|
files: fileList(api, notes),
|
|
98
165
|
fileCursor: nav.fileCursor,
|
|
99
166
|
reviewPath: nav.reviewPath,
|
|
167
|
+
unitLine: nav.unitLine,
|
|
168
|
+
unitLines: unitBodyLines(api),
|
|
100
169
|
branch: ui.branch,
|
|
101
170
|
branches: gitBranches.branches,
|
|
102
171
|
branchCursor: nav.branchCursor,
|
|
@@ -128,4 +197,4 @@ const createView = (api) => ({
|
|
|
128
197
|
view: () => view(api),
|
|
129
198
|
});
|
|
130
199
|
|
|
131
|
-
module.exports = {
|
|
200
|
+
module.exports = { relabelStatus, createView };
|