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/dispatch.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const compose = require('./compose.js');
|
|
4
|
-
const
|
|
4
|
+
const commit = require('./commit.js');
|
|
5
|
+
const { hasStagedChanges, listedCommits } = commit;
|
|
5
6
|
const { COMPOSE_LEAVE } = compose;
|
|
6
7
|
const actions = require('./actions.js');
|
|
7
|
-
const {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
const {
|
|
9
|
+
LIST_PANES,
|
|
10
|
+
CONFIRM_MODE,
|
|
11
|
+
disabledActions,
|
|
12
|
+
actionFromKey,
|
|
13
|
+
applyConfirm,
|
|
14
|
+
} = actions;
|
|
13
15
|
|
|
14
16
|
const currentTarget = (ui) => {
|
|
15
17
|
if (ui.pane === 'files') return ui.fileCursorEntry();
|
|
@@ -18,9 +20,9 @@ const currentTarget = (ui) => {
|
|
|
18
20
|
}
|
|
19
21
|
if (ui.pane === 'commits') {
|
|
20
22
|
const entry = listedCommits(ui.commits.commits)[ui.commitCursor];
|
|
21
|
-
const staged =
|
|
22
|
-
|
|
23
|
-
return { ...entry, canCommit: staged };
|
|
23
|
+
const staged = hasStagedChanges(ui);
|
|
24
|
+
const unstagedCurrent = !ui.rev && !ui.revShort;
|
|
25
|
+
return { ...entry, canCommit: staged, unstagedCurrent };
|
|
24
26
|
}
|
|
25
27
|
return ui.current();
|
|
26
28
|
};
|
|
@@ -30,13 +32,14 @@ const createDispatcher = (ui) => {
|
|
|
30
32
|
add: ui.changeActions.onAdd,
|
|
31
33
|
unstage: ui.changeActions.onUnstage,
|
|
32
34
|
revert: ui.changeActions.onRevert,
|
|
35
|
+
ignore: ui.changeActions.onIgnore,
|
|
33
36
|
next: ui.filesPane.onNext,
|
|
34
37
|
prev: ui.filesPane.onPrev,
|
|
35
38
|
layout: ui.filesPane.onLayout,
|
|
36
39
|
theme: ui.filesPane.onTheme,
|
|
37
40
|
files: ui.filesPane.onFiles,
|
|
38
41
|
feedback: ui.composer.onFeedback,
|
|
39
|
-
|
|
42
|
+
tasks: ui.composer.onTasks,
|
|
40
43
|
code: ui.composer.onCode,
|
|
41
44
|
reload: ui.lifecycle.onReload,
|
|
42
45
|
commit: ui.commits.onCommit,
|
|
@@ -62,12 +65,16 @@ const createDispatcher = (ui) => {
|
|
|
62
65
|
npm: ui.npm.open,
|
|
63
66
|
npmEdit: ui.npm.edit,
|
|
64
67
|
npmNew: ui.npm.create,
|
|
68
|
+
npmDrop: () => ui.npm.askDrop(),
|
|
69
|
+
npmLogs: () => ui.npm.askLogs(),
|
|
65
70
|
npmStop: () => ui.npm.closeView(),
|
|
71
|
+
back: () => ui.filesPane.onEscape(),
|
|
72
|
+
npmVerbose: () => ui.npm.toggleVerbose(),
|
|
66
73
|
npmRerun: () => ui.npm.rerun(),
|
|
67
|
-
check: () => ui.composer.
|
|
68
|
-
|
|
74
|
+
check: () => ui.composer.toggleFocusedTask(),
|
|
75
|
+
removeTask: () => {
|
|
69
76
|
if (ui.pane === 'npm') return void ui.npm.askDrop();
|
|
70
|
-
ui.composer.
|
|
77
|
+
ui.composer.removeFocusedTask();
|
|
71
78
|
},
|
|
72
79
|
quit: () => ui.onQuit(),
|
|
73
80
|
scrollUp: () => ui.filesPane.onScroll(-1),
|
|
@@ -88,21 +95,9 @@ const createDispatcher = (ui) => {
|
|
|
88
95
|
if (result && result.kind === 'error') return;
|
|
89
96
|
}
|
|
90
97
|
const pane = ui.pane;
|
|
91
|
-
if (pane === '
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
if (pane === 'branches') {
|
|
96
|
-
if (action === 'next') return void ui.gitBranches.onBranchMove(1);
|
|
97
|
-
if (action === 'prev') return void ui.gitBranches.onBranchMove(-1);
|
|
98
|
-
}
|
|
99
|
-
if (pane === 'commits') {
|
|
100
|
-
if (action === 'next') return void ui.commits.onCommitMove(1);
|
|
101
|
-
if (action === 'prev') return void ui.commits.onCommitMove(-1);
|
|
102
|
-
}
|
|
103
|
-
if (pane === 'npm') {
|
|
104
|
-
if (action === 'next') return void ui.npm.move(1);
|
|
105
|
-
if (action === 'prev') return void ui.npm.move(-1);
|
|
98
|
+
if (LIST_PANES.includes(pane) && (action === 'next' || action === 'prev')) {
|
|
99
|
+
ui.filesPane.onScroll(action === 'next' ? 1 : -1);
|
|
100
|
+
return;
|
|
106
101
|
}
|
|
107
102
|
const hidden = disabledActions(pane, currentTarget(ui));
|
|
108
103
|
if (hidden.includes(action)) return;
|
|
@@ -122,8 +117,8 @@ const createInput = (ui) => {
|
|
|
122
117
|
cancel: () => ui.cancelQuit(),
|
|
123
118
|
},
|
|
124
119
|
confirmUpdate: {
|
|
125
|
-
accept: ui.updater.accept,
|
|
126
|
-
decline: ui.updater.decline,
|
|
120
|
+
accept: () => ui.updater.accept(),
|
|
121
|
+
decline: () => ui.updater.decline(),
|
|
127
122
|
},
|
|
128
123
|
confirmDrop: {
|
|
129
124
|
confirm: () => {
|
|
@@ -153,30 +148,42 @@ const createInput = (ui) => {
|
|
|
153
148
|
applyConfirm(mode, key, confirmations[mode]);
|
|
154
149
|
};
|
|
155
150
|
|
|
151
|
+
const npmViewKeys = {
|
|
152
|
+
escape: () => ui.npm.closeView(),
|
|
153
|
+
r: () => ui.npm.rerun(),
|
|
154
|
+
v: () => ui.npm.toggleVerbose(),
|
|
155
|
+
e: () => {},
|
|
156
|
+
n: () => {},
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const npmListKeys = {
|
|
160
|
+
'ctrl-up': () => ui.npm.reorder(-1),
|
|
161
|
+
'ctrl-down': () => ui.npm.reorder(1),
|
|
162
|
+
up: () => ui.npm.move(-1),
|
|
163
|
+
down: () => ui.npm.move(1),
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const npmKey = (key) => {
|
|
167
|
+
if (ui.pane !== 'npm') return null;
|
|
168
|
+
if (ui.npm.viewing && Object.hasOwn(npmViewKeys, key)) {
|
|
169
|
+
return npmViewKeys[key];
|
|
170
|
+
}
|
|
171
|
+
return Object.hasOwn(npmListKeys, key) ? npmListKeys[key] : null;
|
|
172
|
+
};
|
|
173
|
+
|
|
156
174
|
const handleKey = (key) => {
|
|
157
175
|
const mode = ui.mode;
|
|
158
176
|
if (CONFIRM_MODE.includes(mode)) return void confirmChoice(key);
|
|
159
177
|
if (mode === 'compose') {
|
|
160
|
-
if (key === 'ctrl-c') return void ui.onQuit();
|
|
161
178
|
const command = ui.composer.handleKey(key);
|
|
162
179
|
return void ui.runComposeCommand(command);
|
|
163
180
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (key === 'r') return void ui.npm.rerun();
|
|
167
|
-
if (key === 'e' || key === 'n') return;
|
|
168
|
-
}
|
|
169
|
-
if (ui.pane === 'npm' && key === 'ctrl-up') return void ui.npm.reorder(-1);
|
|
170
|
-
if (ui.pane === 'npm' && key === 'ctrl-down') {
|
|
171
|
-
return void ui.npm.reorder(1);
|
|
172
|
-
}
|
|
173
|
-
if (ui.pane === 'npm' && (key === 'up' || key === 'down')) {
|
|
174
|
-
return void ui.npm.move(key === 'up' ? -1 : 1);
|
|
175
|
-
}
|
|
181
|
+
const onNpmKey = npmKey(key);
|
|
182
|
+
if (onNpmKey) return void onNpmKey();
|
|
176
183
|
if (key === 'escape') return void ui.filesPane.onEscape();
|
|
177
184
|
const toggle = key === 'x' || key === ' ';
|
|
178
|
-
if (toggle && ui.composer.
|
|
179
|
-
if (ui.composer.
|
|
185
|
+
if (toggle && ui.composer.toggleFocusedTask()) return;
|
|
186
|
+
if (ui.composer.typeIntoTask(key)) return;
|
|
180
187
|
const action = actionFromKey(key, ui.pane, ui.fileScope);
|
|
181
188
|
if (action) ui.dispatch(action);
|
|
182
189
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const wrap = require('../wrap.js');
|
|
4
|
+
const diff = require('../render/diff.js');
|
|
5
|
+
|
|
6
|
+
const { indexAtRow } = wrap;
|
|
7
|
+
const { codeInnerWidth } = diff;
|
|
8
|
+
|
|
9
|
+
const hitAt = (hits, cell) => {
|
|
10
|
+
for (const hit of hits) {
|
|
11
|
+
if (hit.y !== cell.y) continue;
|
|
12
|
+
const x0 = hit.x0 ?? hit.textX;
|
|
13
|
+
if (cell.x < x0) continue;
|
|
14
|
+
if (hit.x1 !== undefined && cell.x >= hit.x1) continue;
|
|
15
|
+
return hit;
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const offsetOf = (hit, cellX) => {
|
|
21
|
+
const shown = cellX - hit.textX;
|
|
22
|
+
const col = Math.max(0, shown) + (hit.scroll || 0);
|
|
23
|
+
const at = indexAtRow(hit.start || 0, hit.text, col);
|
|
24
|
+
if (hit.limit !== undefined && at > hit.limit) return hit.limit;
|
|
25
|
+
return at;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const revealCode = (ui) => {
|
|
29
|
+
const kind = ui.composeKind;
|
|
30
|
+
if (kind !== 'code' && kind !== 'file') return;
|
|
31
|
+
const size = ui.lastSize ?? ui.getSize();
|
|
32
|
+
ui.editor.reveal(codeInnerWidth(size.width, ui.layout));
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const focusField = (ui, hit) => {
|
|
36
|
+
if (!hit.field || !ui.npm) return true;
|
|
37
|
+
if (hit.field === ui.npm.editField) return true;
|
|
38
|
+
return ui.npm.focusField(hit.field);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const placeClick = (ui, cell) => {
|
|
42
|
+
if (ui.mode !== 'compose' || !ui.editor) return false;
|
|
43
|
+
const hits = ui.lastFrame && ui.lastFrame.editHits;
|
|
44
|
+
if (!hits || !hits.length) return false;
|
|
45
|
+
const hit = hitAt(hits, cell);
|
|
46
|
+
if (!hit) return false;
|
|
47
|
+
if (!focusField(ui, hit)) {
|
|
48
|
+
ui.paint();
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
ui.editor.place(offsetOf(hit, cell.x));
|
|
52
|
+
revealCode(ui);
|
|
53
|
+
ui.paint();
|
|
54
|
+
return true;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
module.exports = { placeClick };
|
|
@@ -3,14 +3,17 @@
|
|
|
3
3
|
const ansi = require('../ansi.js');
|
|
4
4
|
const files = require('../files.js');
|
|
5
5
|
const { THEME_NAMES, setTheme, themeName } = ansi;
|
|
6
|
-
const {
|
|
7
|
-
const
|
|
6
|
+
const { isTasksEntry, TASKS_FILE } = files;
|
|
7
|
+
const npm = require('../render/npm.js');
|
|
8
|
+
const { logViewRows } = npm;
|
|
8
9
|
const unit = require('./unit.js');
|
|
10
|
+
const actions = require('./actions.js');
|
|
11
|
+
const { LIST_PANES } = actions;
|
|
9
12
|
const { setFileScope, openUnitFile, moveUnitBlock } = unit;
|
|
10
13
|
|
|
11
14
|
const LAYOUTS = ['unified', 'mixed', 'side'];
|
|
12
15
|
|
|
13
|
-
const current = (ui) => ui.nav.current(ui.items, ui.
|
|
16
|
+
const current = (ui) => ui.nav.current(ui.items, ui.tasksItem);
|
|
14
17
|
|
|
15
18
|
const clampIndex = (ui) => ui.nav.clampIndex(ui.items.length);
|
|
16
19
|
|
|
@@ -29,25 +32,19 @@ const syncFileCursor = (ui) => {
|
|
|
29
32
|
const showFiles = (ui) => {
|
|
30
33
|
const nav = ui.nav;
|
|
31
34
|
const held =
|
|
32
|
-
nav.
|
|
35
|
+
nav.tasksOpen === true ||
|
|
33
36
|
nav.pane === 'branches' ||
|
|
34
37
|
nav.pane === 'commits' ||
|
|
35
38
|
nav.pane === 'npm';
|
|
36
39
|
nav.pane = 'files';
|
|
40
|
+
nav.resetListScroll('files');
|
|
37
41
|
nav.clearSelection();
|
|
38
42
|
followReviewPath(ui);
|
|
39
|
-
nav.
|
|
43
|
+
nav.tasksOpen = false;
|
|
40
44
|
if (held) ui.lifecycle.catchUpWatch();
|
|
41
45
|
};
|
|
42
46
|
|
|
43
47
|
const onEscape = (ui) => {
|
|
44
|
-
if (
|
|
45
|
-
ui.nav.pane === 'branches' ||
|
|
46
|
-
ui.nav.pane === 'commits' ||
|
|
47
|
-
ui.nav.pane === 'npm'
|
|
48
|
-
) {
|
|
49
|
-
return void showFiles(ui);
|
|
50
|
-
}
|
|
51
48
|
if (ui.nav.pane === 'files') return void ui.onQuit();
|
|
52
49
|
showFiles(ui);
|
|
53
50
|
};
|
|
@@ -58,12 +55,12 @@ const onFiles = (ui) => {
|
|
|
58
55
|
if (ui.nav.fileScope === 'file') {
|
|
59
56
|
const list = ui.fileList();
|
|
60
57
|
const entry = list[ui.nav.fileCursor];
|
|
61
|
-
if (entry && !
|
|
58
|
+
if (entry && !isTasksEntry(entry)) openUnitFile(ui, entry);
|
|
62
59
|
return;
|
|
63
60
|
}
|
|
64
61
|
ui.nav.pane = 'diff';
|
|
65
62
|
ui.nav.clearSelection();
|
|
66
|
-
if (ui.nav.reviewPath ===
|
|
63
|
+
if (ui.nav.reviewPath === TASKS_FILE) ui.composer.openTasksPage();
|
|
67
64
|
return;
|
|
68
65
|
}
|
|
69
66
|
showFiles(ui);
|
|
@@ -76,18 +73,18 @@ const onOpenFile = (ui) => {
|
|
|
76
73
|
if (ui.nav.pane === 'commits') return void ui.commits.onShowDiff();
|
|
77
74
|
if (ui.nav.pane === 'npm') return void ui.npm.run();
|
|
78
75
|
if (ui.nav.pane !== 'files') {
|
|
79
|
-
if (ui.nav.
|
|
76
|
+
if (ui.nav.tasksOpen) ui.composer.editFocusedTask();
|
|
80
77
|
return;
|
|
81
78
|
}
|
|
82
79
|
const list = ui.fileList();
|
|
83
80
|
const entry = list[ui.nav.fileCursor];
|
|
84
81
|
if (!entry) return;
|
|
85
|
-
if (
|
|
86
|
-
ui.composer.
|
|
82
|
+
if (isTasksEntry(entry)) {
|
|
83
|
+
ui.composer.openTasksPage();
|
|
87
84
|
ui.status = '';
|
|
88
85
|
return;
|
|
89
86
|
}
|
|
90
|
-
ui.nav.
|
|
87
|
+
ui.nav.tasksOpen = false;
|
|
91
88
|
ui.nav.index = entry.openIndex ?? entry.firstIndex;
|
|
92
89
|
if (ui.nav.fileScope === 'file') {
|
|
93
90
|
return void openUnitFile(ui, entry);
|
|
@@ -111,8 +108,8 @@ const onFileMove = (ui, delta) => {
|
|
|
111
108
|
if (next === ui.nav.fileCursor) return;
|
|
112
109
|
ui.nav.fileCursor = next;
|
|
113
110
|
const entry = list[next];
|
|
114
|
-
if (
|
|
115
|
-
ui.nav.reviewPath =
|
|
111
|
+
if (isTasksEntry(entry)) {
|
|
112
|
+
ui.nav.reviewPath = TASKS_FILE;
|
|
116
113
|
} else {
|
|
117
114
|
ui.nav.reviewPath = entry.path;
|
|
118
115
|
ui.nav.index = entry.firstIndex;
|
|
@@ -137,8 +134,8 @@ const moveRemaining = (ui, step) => {
|
|
|
137
134
|
|
|
138
135
|
const onNext = (ui) => {
|
|
139
136
|
if (ui.nav.pane === 'unit') return void moveUnitBlock(ui, 1);
|
|
140
|
-
if (ui.nav.
|
|
141
|
-
ui.composer.
|
|
137
|
+
if (ui.nav.tasksOpen) {
|
|
138
|
+
ui.composer.closeTasksPage();
|
|
142
139
|
if (ui.items.length) ui.nav.index = 0;
|
|
143
140
|
ui.nav.scroll = 0;
|
|
144
141
|
syncReviewPath(ui);
|
|
@@ -150,8 +147,8 @@ const onNext = (ui) => {
|
|
|
150
147
|
|
|
151
148
|
const onPrev = (ui) => {
|
|
152
149
|
if (ui.nav.pane === 'unit') return void moveUnitBlock(ui, -1);
|
|
153
|
-
if (ui.nav.
|
|
154
|
-
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();
|
|
155
152
|
moveRemaining(ui, -1);
|
|
156
153
|
};
|
|
157
154
|
|
|
@@ -179,8 +176,8 @@ const onScroll = (ui, delta) => {
|
|
|
179
176
|
return void ui.commits.onCommitMove(delta);
|
|
180
177
|
}
|
|
181
178
|
if (ui.nav.pane === 'npm') return void ui.npm.move(delta);
|
|
182
|
-
if (ui.nav.
|
|
183
|
-
return void ui.composer.
|
|
179
|
+
if (ui.nav.tasksOpen && ui.composer.taskRowCount() > 1) {
|
|
180
|
+
return void ui.composer.moveTaskFocus(delta);
|
|
184
181
|
}
|
|
185
182
|
const frame = ui.lastFrame;
|
|
186
183
|
const max = frame ? frame.scrollMax : null;
|
|
@@ -193,32 +190,15 @@ const onScroll = (ui, delta) => {
|
|
|
193
190
|
};
|
|
194
191
|
|
|
195
192
|
const onJump = (ui, toEnd) => {
|
|
196
|
-
if (ui.nav.pane
|
|
197
|
-
const list = ui.fileList();
|
|
198
|
-
if (!list.length) {
|
|
199
|
-
ui.status = 'nothing to review';
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
const target = toEnd ? list.length - 1 : 0;
|
|
203
|
-
return void onFileMove(ui, target - ui.nav.fileCursor);
|
|
204
|
-
}
|
|
205
|
-
if (ui.nav.pane === 'branches') {
|
|
206
|
-
const delta = toEnd ? Number.MAX_SAFE_INTEGER : -Number.MAX_SAFE_INTEGER;
|
|
207
|
-
return void ui.gitBranches.onBranchMove(delta);
|
|
208
|
-
}
|
|
209
|
-
if (ui.nav.pane === 'commits') {
|
|
210
|
-
const delta = toEnd ? Number.MAX_SAFE_INTEGER : -Number.MAX_SAFE_INTEGER;
|
|
211
|
-
return void ui.commits.onCommitMove(delta);
|
|
212
|
-
}
|
|
213
|
-
if (ui.nav.pane === 'npm') {
|
|
193
|
+
if (LIST_PANES.includes(ui.nav.pane)) {
|
|
214
194
|
const delta = toEnd ? Number.MAX_SAFE_INTEGER : -Number.MAX_SAFE_INTEGER;
|
|
215
|
-
return void ui
|
|
195
|
+
return void onScroll(ui, delta);
|
|
216
196
|
}
|
|
217
|
-
if (ui.nav.
|
|
218
|
-
const last = ui.composer.
|
|
197
|
+
if (ui.nav.tasksOpen && ui.composer.taskRowCount() > 0) {
|
|
198
|
+
const last = ui.composer.taskRowCount() - 1;
|
|
219
199
|
const target = toEnd ? last : 0;
|
|
220
|
-
const at = ui.composer.
|
|
221
|
-
return void ui.composer.
|
|
200
|
+
const at = ui.composer.clampedTaskFocus();
|
|
201
|
+
return void ui.composer.moveTaskFocus(target - at);
|
|
222
202
|
}
|
|
223
203
|
const frame = ui.lastFrame;
|
|
224
204
|
const max = frame ? frame.scrollMax : 0;
|
|
@@ -251,7 +231,6 @@ const createFilesPane = (ui) => ({
|
|
|
251
231
|
onEscape: () => onEscape(ui),
|
|
252
232
|
onFiles: () => onFiles(ui),
|
|
253
233
|
onOpenFile: () => onOpenFile(ui),
|
|
254
|
-
onFileMove: (delta) => onFileMove(ui, delta),
|
|
255
234
|
onNext: () => onNext(ui),
|
|
256
235
|
onPrev: () => onPrev(ui),
|
|
257
236
|
scrollStep: (fraction) => scrollStep(ui, fraction),
|
|
@@ -264,4 +243,4 @@ const createFilesPane = (ui) => ({
|
|
|
264
243
|
onDiffScope: () => setFileScope(ui, 'diff'),
|
|
265
244
|
});
|
|
266
245
|
|
|
267
|
-
module.exports = {
|
|
246
|
+
module.exports = { createFilesPane };
|
package/lib/session/items.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { stateView } = require('./accessors.js');
|
|
4
|
-
|
|
5
3
|
const files = require('../files.js');
|
|
6
|
-
const { itemPath,
|
|
7
|
-
const
|
|
8
|
-
const {
|
|
4
|
+
const { itemPath, TASKS_FILE, isTaskItem } = files;
|
|
5
|
+
const review = require('../review.js');
|
|
6
|
+
const { feedbackKey } = review;
|
|
7
|
+
const diff = require('../diff/diff.js');
|
|
8
|
+
const { refreshIndexPatches } = diff;
|
|
9
9
|
|
|
10
10
|
const depChangeKey = (item) => {
|
|
11
11
|
const change = item.dep && item.dep.change;
|
|
@@ -26,7 +26,7 @@ const npmBusyKind = (item) => {
|
|
|
26
26
|
const blockKey = (item) => {
|
|
27
27
|
if (!item || !item.file) return '';
|
|
28
28
|
const rel = itemPath(item);
|
|
29
|
-
if (
|
|
29
|
+
if (isTaskItem(item)) return `todo:${TASKS_FILE}`;
|
|
30
30
|
const depId = depChangeKey(item);
|
|
31
31
|
if (depId) return `${rel}:dep:${depId}`;
|
|
32
32
|
if (!item.hunk) {
|
|
@@ -41,13 +41,13 @@ const blockKey = (item) => {
|
|
|
41
41
|
|
|
42
42
|
const itemKey = (item) => {
|
|
43
43
|
const key = blockKey(item);
|
|
44
|
-
if (!key ||
|
|
44
|
+
if (!key || isTaskItem(item)) return key;
|
|
45
45
|
const origin = item.origin ?? '';
|
|
46
46
|
return `${origin}:${key}`;
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
const blockLinesKey = (item) => {
|
|
50
|
-
if (!item || !item.file ||
|
|
50
|
+
if (!item || !item.file || isTaskItem(item)) return '';
|
|
51
51
|
if (!item.hunk) return '';
|
|
52
52
|
const parts = [itemPath(item)];
|
|
53
53
|
for (const line of item.hunk.lines) {
|
|
@@ -129,27 +129,25 @@ const alignLoadedItems = (prev, next) => {
|
|
|
129
129
|
return ordered;
|
|
130
130
|
};
|
|
131
131
|
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
const originOf = (item) => item.origin ?? '';
|
|
133
|
+
|
|
134
|
+
const hunkStart = (item) => (item.hunk ? item.hunk.newStart : 0);
|
|
135
|
+
|
|
136
|
+
const sameLinesIndex = (items, item) => {
|
|
135
137
|
const lines = blockLinesKey(item);
|
|
136
|
-
if (lines)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
if (any >= 0) return any;
|
|
144
|
-
}
|
|
145
|
-
const key = itemKey(item);
|
|
146
|
-
if (key) {
|
|
147
|
-
const exact = items.findIndex((entry) => itemKey(entry) === key);
|
|
148
|
-
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;
|
|
149
144
|
}
|
|
145
|
+
return fallback;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const nearestHunkIndex = (items, item) => {
|
|
150
149
|
const rel = itemPath(item);
|
|
151
150
|
if (!rel) return -1;
|
|
152
|
-
const start = item.hunk ? item.hunk.newStart : 0;
|
|
153
151
|
let fileMatch = -1;
|
|
154
152
|
let best = -1;
|
|
155
153
|
let bestDist = Infinity;
|
|
@@ -157,19 +155,27 @@ const restoredIndex = (items, item) => {
|
|
|
157
155
|
const entry = items[i];
|
|
158
156
|
if (itemPath(entry) !== rel) continue;
|
|
159
157
|
if (fileMatch < 0) fileMatch = i;
|
|
160
|
-
if ((entry
|
|
161
|
-
const
|
|
162
|
-
const dist = Math.abs(at - start);
|
|
158
|
+
if (originOf(entry) !== originOf(item)) continue;
|
|
159
|
+
const dist = Math.abs(hunkStart(entry) - hunkStart(item));
|
|
163
160
|
if (dist >= bestDist) continue;
|
|
164
161
|
bestDist = dist;
|
|
165
162
|
best = i;
|
|
166
163
|
}
|
|
167
|
-
|
|
168
|
-
|
|
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);
|
|
169
175
|
};
|
|
170
176
|
|
|
171
177
|
const itemFeedKey = (item) => {
|
|
172
|
-
if (!item || !item.file ||
|
|
178
|
+
if (!item || !item.file || isTaskItem(item)) return '';
|
|
173
179
|
const hunk = item.hunk;
|
|
174
180
|
const depId = depChangeKey(item);
|
|
175
181
|
const file = depId ? `${itemPath(item)}#${depId}` : itemPath(item);
|
|
@@ -187,88 +193,65 @@ const sameBlock = (left, right) => {
|
|
|
187
193
|
};
|
|
188
194
|
|
|
189
195
|
const canEditCode = (item) => {
|
|
190
|
-
if (!item ||
|
|
196
|
+
if (!item || isTaskItem(item)) return false;
|
|
191
197
|
if (!item.hunk || !item.file) return false;
|
|
192
198
|
if (item.file.isBinary) return false;
|
|
193
199
|
return true;
|
|
194
200
|
};
|
|
195
201
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
items
|
|
199
|
-
dismissed
|
|
200
|
-
}
|
|
202
|
+
class ItemCollection {
|
|
203
|
+
constructor() {
|
|
204
|
+
this.items = [];
|
|
205
|
+
this.dismissed = new Set();
|
|
206
|
+
}
|
|
201
207
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
208
|
+
reset() {
|
|
209
|
+
this.items = [];
|
|
210
|
+
this.dismissed = new Set();
|
|
211
|
+
}
|
|
206
212
|
|
|
207
|
-
|
|
213
|
+
isDismissed(item) {
|
|
214
|
+
return this.dismissed.has(itemKey(item));
|
|
215
|
+
}
|
|
208
216
|
|
|
209
|
-
|
|
210
|
-
const aligned = alignLoadedItems(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
next.push(item);
|
|
215
|
-
}
|
|
216
|
-
state.items = next;
|
|
217
|
-
return next;
|
|
218
|
-
};
|
|
217
|
+
replace(items) {
|
|
218
|
+
const aligned = alignLoadedItems(this.items, items);
|
|
219
|
+
this.items = aligned.filter((item) => !this.isDismissed(item));
|
|
220
|
+
return this.items;
|
|
221
|
+
}
|
|
219
222
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
+
dismiss(item) {
|
|
224
|
+
this.dismissed.add(itemKey(item));
|
|
225
|
+
}
|
|
223
226
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
227
|
+
clearDismissed() {
|
|
228
|
+
this.dismissed.clear();
|
|
229
|
+
}
|
|
227
230
|
|
|
228
|
-
|
|
231
|
+
findRestoredIndex(item) {
|
|
232
|
+
return restoredIndex(this.items, item);
|
|
233
|
+
}
|
|
229
234
|
|
|
230
|
-
|
|
235
|
+
keepOrigin(item, origin) {
|
|
231
236
|
const next = { ...item, origin };
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const entry = state.items[i];
|
|
236
|
-
replaced[i] = sameBlock(entry, item) ? next : entry;
|
|
237
|
-
}
|
|
237
|
+
const replaced = this.items.map((entry) =>
|
|
238
|
+
sameBlock(entry, item) ? next : entry,
|
|
239
|
+
);
|
|
238
240
|
if (next.dep) {
|
|
239
|
-
|
|
241
|
+
this.items = replaced;
|
|
240
242
|
return;
|
|
241
243
|
}
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return stateView(
|
|
246
|
-
state,
|
|
247
|
-
{
|
|
248
|
-
reset,
|
|
249
|
-
replace,
|
|
250
|
-
dismiss,
|
|
251
|
-
clearDismissed,
|
|
252
|
-
isDismissed,
|
|
253
|
-
findRestoredIndex,
|
|
254
|
-
keepOrigin,
|
|
255
|
-
},
|
|
256
|
-
['items'],
|
|
257
|
-
['dismissed'],
|
|
258
|
-
);
|
|
259
|
-
};
|
|
244
|
+
this.items = refreshIndexPatches(replaced, next);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
260
247
|
|
|
261
248
|
module.exports = {
|
|
262
|
-
depChangeKey,
|
|
263
249
|
needsReload,
|
|
264
250
|
npmBusyKind,
|
|
265
|
-
blockKey,
|
|
266
|
-
itemKey,
|
|
267
251
|
itemFeedKey,
|
|
268
|
-
blockLinesKey,
|
|
269
252
|
alignLoadedItems,
|
|
270
253
|
restoredIndex,
|
|
271
254
|
sameBlock,
|
|
272
255
|
canEditCode,
|
|
273
|
-
|
|
256
|
+
ItemCollection,
|
|
274
257
|
};
|