reslop 0.2.6 → 0.3.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 +27 -1
- package/README.md +16 -14
- package/bin/reslop.js +8 -5
- package/lib/agents/agents.js +504 -0
- package/lib/agents/allow.js +71 -0
- package/lib/agents/allow.json +16 -0
- package/lib/agents/models.js +460 -0
- package/lib/api.js +15 -12
- package/lib/cli.js +61 -66
- package/lib/{files.js → common/files.js} +51 -52
- package/lib/common/find.js +61 -0
- package/lib/common/format.js +101 -0
- package/lib/{utilities.js → common/process.js} +7 -95
- package/lib/common/tasks.json +27 -0
- package/lib/common/utilities.js +112 -0
- package/lib/dashboard/git.js +9 -16
- package/lib/dashboard/model.js +117 -51
- package/lib/dashboard/npm.js +133 -32
- package/lib/dashboard/tree.js +27 -30
- package/lib/{session → dashboard}/watch.js +41 -31
- package/lib/dashboard/workspace.js +151 -0
- package/lib/{deps.js → deps/deps.js} +31 -45
- package/lib/{deps-diff.js → deps/diff.js} +35 -25
- package/lib/{manifest.js → deps/manifest.js} +47 -30
- package/lib/diff/align.js +1 -2
- package/lib/diff/diff.js +2 -2
- package/lib/diff/display.js +29 -5
- package/lib/diff/inline.js +10 -14
- package/lib/diff/items.js +6 -4
- package/lib/diff/patch.js +39 -3
- package/lib/diff/unit.js +45 -16
- package/lib/{git-branches.js → git/branches.js} +53 -48
- package/lib/{git-deps.js → git/deps.js} +123 -95
- package/lib/git/git.js +71 -0
- package/lib/git/worktree.js +475 -0
- package/lib/highlight/bash.js +1 -2
- package/lib/highlight/csv.js +1 -2
- package/lib/highlight/dot.js +1 -2
- package/lib/{highlight.js → highlight/highlight.js} +42 -11
- package/lib/highlight/html.js +20 -18
- package/lib/highlight/json.js +1 -2
- package/lib/highlight/log.js +1 -2
- package/lib/highlight/txt.js +1 -2
- package/lib/{session → input}/actions.js +240 -71
- package/lib/{editor.js → input/editor.js} +7 -8
- package/lib/{keys.js → input/keys.js} +36 -39
- package/lib/input/line-edit.js +77 -0
- package/lib/input/menu-pick.js +70 -0
- package/lib/render/agents.js +640 -0
- package/lib/render/branches.js +4 -5
- package/lib/render/commits.js +54 -64
- package/lib/render/{dash-activity.js → dashboard/activity.js} +25 -40
- package/lib/render/dashboard/blocks.js +490 -0
- package/lib/render/{dashboard.js → dashboard/dashboard.js} +25 -22
- package/lib/render/diff.js +166 -45
- package/lib/render/field.js +131 -0
- package/lib/render/files.js +22 -8
- package/lib/render/footer.js +383 -0
- package/lib/render/frame.js +127 -25
- package/lib/render/header.js +176 -0
- package/lib/render/log.js +319 -0
- package/lib/render/menu.js +60 -0
- package/lib/render/notes.js +29 -13
- package/lib/render/npm.js +322 -196
- package/lib/render/packages.js +89 -0
- package/lib/render/primitives.js +109 -16
- package/lib/render/render.js +7 -11
- package/lib/render/repos.js +214 -0
- package/lib/render/status.js +370 -0
- package/lib/render/status.json +85 -0
- package/lib/render/{dash-table.js → table.js} +19 -61
- package/lib/render/tiles.js +203 -47
- package/lib/render/unit.js +32 -13
- package/lib/{report-model.js → report/model.js} +4 -6
- package/lib/{report-parse.js → report/parse.js} +9 -14
- package/lib/{report-render.js → report/render.js} +46 -14
- package/lib/{report-run.js → report/run.js} +42 -30
- package/lib/{report-test.js → report/test.js} +56 -55
- package/lib/{report-tools.js → report/tools.js} +24 -33
- package/lib/review/agent-instructions.md +9 -0
- package/lib/review/format.js +426 -0
- package/lib/{review-merge.js → review/merge.js} +9 -8
- package/lib/review/review.js +190 -0
- package/lib/review/store.js +281 -0
- package/lib/review/templates.js +83 -0
- package/lib/runs/commands.js +14 -0
- package/lib/runs/logs.js +299 -0
- package/lib/runs/output.js +174 -0
- package/lib/runs/process.js +113 -0
- package/lib/{runs.js → runs/runs.js} +84 -36
- package/lib/runs/scripts.js +150 -0
- package/lib/session/agents/agents.js +958 -0
- package/lib/session/agents/job.js +320 -0
- package/lib/session/agents/jobs.js +355 -0
- package/lib/session/agents/options.js +153 -0
- package/lib/session/agents/plans.js +83 -0
- package/lib/session/branches.js +36 -44
- package/lib/session/change-actions.js +34 -100
- package/lib/session/commit.js +122 -56
- package/lib/session/compose.js +197 -146
- package/lib/session/dashboard.js +183 -37
- package/lib/session/dispatch.js +120 -22
- package/lib/session/files-pane.js +60 -24
- package/lib/session/find.js +81 -0
- package/lib/session/import.js +82 -0
- package/lib/session/items.js +8 -17
- package/lib/session/lifecycle.js +96 -66
- package/lib/session/load.js +2 -4
- package/lib/session/navigation.js +8 -3
- package/lib/session/npm/history.js +249 -0
- package/lib/session/npm/npm.js +815 -0
- package/lib/session/npm/packages.js +150 -0
- package/lib/session/ops.js +125 -34
- package/lib/session/pointer.js +216 -10
- package/lib/session/prefs.js +112 -0
- package/lib/session/reads.js +109 -0
- package/lib/session/review.js +28 -10
- package/lib/session/run-panel.js +40 -0
- package/lib/{session.js → session/session.js} +166 -59
- package/lib/session/tasks.js +321 -66
- package/lib/session/templates.js +1 -1
- package/lib/session/terminal.js +94 -12
- package/lib/session/unit.js +35 -30
- package/lib/session/update.js +194 -6
- package/lib/session/view.js +72 -59
- package/lib/session/workspace.js +416 -0
- package/lib/{capabilities.js → source/capabilities.js} +3 -12
- package/lib/{github.js → source/github.js} +102 -32
- package/lib/{gitlab.js → source/gitlab.js} +87 -10
- package/lib/{remote.js → source/remote.js} +144 -40
- package/lib/source/review-threads.graphql +20 -0
- package/lib/source/source.js +74 -0
- package/lib/term/ansi.js +396 -0
- package/lib/{clipboard.js → term/clipboard.js} +3 -6
- package/lib/{select.js → term/select.js} +2 -2
- package/lib/term/themes/dark.json +122 -0
- package/lib/term/themes/light.json +122 -0
- package/lib/term/wide.txt +21 -0
- package/lib/{wrap.js → term/wrap.js} +4 -4
- package/package.json +2 -2
- package/lib/ansi.js +0 -505
- package/lib/detect.js +0 -34
- package/lib/diff/edit.js +0 -33
- package/lib/git-worktree.js +0 -437
- package/lib/git.js +0 -79
- package/lib/npm-commands.js +0 -522
- package/lib/render/chrome.js +0 -628
- package/lib/render/dash-blocks.js +0 -245
- package/lib/render/present.js +0 -19
- package/lib/render/report.js +0 -43
- package/lib/review.js +0 -744
- package/lib/session/accessors.js +0 -26
- package/lib/session/edit-click.js +0 -57
- package/lib/session/npm.js +0 -452
- package/lib/session/progress.js +0 -41
- package/lib/source.js +0 -36
- package/lib/update.js +0 -192
- /package/lib/{git-reword-todo.js → git/reword-todo.js} +0 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { isHashObject } = require('metautil');
|
|
4
|
+
const { trimText } = require('../../common/utilities.js');
|
|
5
|
+
|
|
6
|
+
const models = require('../../agents/models.js');
|
|
7
|
+
const { catalogOf, fitEffort, withoutFast, contextMenu } = models;
|
|
8
|
+
|
|
9
|
+
const RESTORED_FIELDS = ['model', 'effort', 'context'];
|
|
10
|
+
|
|
11
|
+
const concrete = (name) => {
|
|
12
|
+
const text = trimText(name);
|
|
13
|
+
return text === 'default' ? '' : text;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const menuNames = (catalog) => {
|
|
17
|
+
const names = catalog.entries.map((entry) => concrete(entry.model));
|
|
18
|
+
const known = new Set(names);
|
|
19
|
+
const shown = names.filter((name) => {
|
|
20
|
+
const base = withoutFast(name);
|
|
21
|
+
return name && (name === base || !known.has(base));
|
|
22
|
+
});
|
|
23
|
+
return [...new Set(shown)];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
class AgentOptions {
|
|
27
|
+
constructor(row) {
|
|
28
|
+
this.row = row;
|
|
29
|
+
this.catalog = catalogOf(row.models);
|
|
30
|
+
this.names = menuNames(this.catalog);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
shown(model) {
|
|
34
|
+
const saved = concrete(model);
|
|
35
|
+
const base = withoutFast(saved);
|
|
36
|
+
if (this.names.includes(base)) return base;
|
|
37
|
+
if (this.names.includes(saved)) return saved;
|
|
38
|
+
return this.names[0] || '';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// What the cli lists wins over the levels the spec falls back to
|
|
42
|
+
levels(model) {
|
|
43
|
+
const resolved = this.catalog.resolve({ model, effort: 'default' });
|
|
44
|
+
if (resolved.encoded) return resolved.levels;
|
|
45
|
+
const { modelEfforts, listedEfforts, spec } = this.row;
|
|
46
|
+
const own = modelEfforts ? modelEfforts.get(resolved.model) : null;
|
|
47
|
+
const listed = own ?? listedEfforts;
|
|
48
|
+
return listed ? ['default', ...listed] : spec.efforts;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
efforts(model) {
|
|
52
|
+
return this.levels(model).filter((level) => concrete(level));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
contexts(model) {
|
|
56
|
+
return contextMenu(
|
|
57
|
+
model,
|
|
58
|
+
this.row.wide,
|
|
59
|
+
this.catalog,
|
|
60
|
+
this.row.spec.contexts,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
hasFast(model) {
|
|
65
|
+
const base = withoutFast(concrete(model));
|
|
66
|
+
return Boolean(base) && this.catalog.find(`${base}-fast`) !== null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
firstModel(accept) {
|
|
70
|
+
return this.names.find(accept) ?? '';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
fitContext(model, context) {
|
|
74
|
+
const want = concrete(context);
|
|
75
|
+
if (!want) return '';
|
|
76
|
+
const sizes = this.contexts(model);
|
|
77
|
+
return sizes.includes(want) ? want : sizes[0] || '';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
align(picked, model = '') {
|
|
81
|
+
const shown = model || this.shown(picked.model);
|
|
82
|
+
const basis = concrete(picked.model) || shown;
|
|
83
|
+
picked.effort = fitEffort(this.levels(basis), picked.effort);
|
|
84
|
+
picked.context = this.fitContext(shown, picked.context);
|
|
85
|
+
const lost = this.catalog.hasFast() && !this.hasFast(shown);
|
|
86
|
+
if (picked.fast && lost) picked.fast = false;
|
|
87
|
+
return shown;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
normalize(picked, model = '') {
|
|
91
|
+
picked.model = this.catalog.resolve(picked).family;
|
|
92
|
+
return this.align(picked, model || this.shown(picked.model));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
preferEffort(picked, level) {
|
|
96
|
+
if (concrete(picked.effort)) return;
|
|
97
|
+
if (this.efforts(picked.model).includes(level)) picked.effort = level;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
pickModel(picked, model) {
|
|
101
|
+
picked.model = model;
|
|
102
|
+
this.align(picked, model);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
pickContext(picked, size) {
|
|
106
|
+
picked.context = size;
|
|
107
|
+
const offers = (model) => this.contexts(model).includes(size);
|
|
108
|
+
if (!offers(this.shown(picked.model))) {
|
|
109
|
+
picked.model = this.firstModel(offers) || picked.model;
|
|
110
|
+
}
|
|
111
|
+
const model = this.shown(picked.model);
|
|
112
|
+
this.align(picked, model);
|
|
113
|
+
if (offers(model)) picked.context = size;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
toggleFast(picked) {
|
|
117
|
+
picked.fast = picked.fast !== true;
|
|
118
|
+
const shown = this.shown(picked.model);
|
|
119
|
+
const missing = this.catalog.hasFast() && !this.hasFast(shown);
|
|
120
|
+
if (picked.fast && missing) {
|
|
121
|
+
const next = this.firstModel((name) => this.hasFast(name));
|
|
122
|
+
if (next) picked.model = next;
|
|
123
|
+
else picked.fast = false;
|
|
124
|
+
}
|
|
125
|
+
this.align(picked, this.shown(picked.model));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const restoredFields = (spec) =>
|
|
130
|
+
spec.savesExtra ? [...RESTORED_FIELDS, 'extra'] : RESTORED_FIELDS;
|
|
131
|
+
|
|
132
|
+
const restoreChoice = (picked, saved, spec) => {
|
|
133
|
+
if (!isHashObject(saved)) return;
|
|
134
|
+
for (const field of restoredFields(spec)) {
|
|
135
|
+
const value = concrete(saved[field]);
|
|
136
|
+
if (value) picked[field] = value;
|
|
137
|
+
}
|
|
138
|
+
if (saved.fast === true) picked.fast = true;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const storedChoice = (picked, spec) => {
|
|
142
|
+
const fields = [
|
|
143
|
+
['model', concrete(picked.model)],
|
|
144
|
+
['effort', concrete(picked.effort)],
|
|
145
|
+
['extra', spec.savesExtra ? concrete(picked.extra) : ''],
|
|
146
|
+
['fast', picked.fast === true],
|
|
147
|
+
['context', concrete(picked.context)],
|
|
148
|
+
];
|
|
149
|
+
const stored = Object.fromEntries(fields.filter((field) => field[1]));
|
|
150
|
+
return Object.keys(stored).length ? stored : null;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
module.exports = { AgentOptions, concrete, restoreChoice, storedChoice };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { visibleWidth } = require('../../term/ansi.js');
|
|
6
|
+
const { padVisible } = require('../../render/primitives.js');
|
|
7
|
+
const review = require('../../review/review.js');
|
|
8
|
+
const { parseReview, reviewProgress, frontmatterStatus } = review;
|
|
9
|
+
const { trimText } = require('../../common/utilities.js');
|
|
10
|
+
|
|
11
|
+
const planKey = (value) => {
|
|
12
|
+
const text = trimText(value);
|
|
13
|
+
if (!text) return '';
|
|
14
|
+
const gap = text.indexOf(' ');
|
|
15
|
+
return gap > 0 ? text.slice(0, gap) : path.basename(text);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const isLive = (store, file) => {
|
|
19
|
+
const live = store ? store.reviewPath : '';
|
|
20
|
+
return Boolean(live) && path.basename(live) === path.basename(file);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const planState = (file, store) => {
|
|
24
|
+
if (isLive(store, file)) {
|
|
25
|
+
const status = trimText(store.status) || 'editing';
|
|
26
|
+
return { status, ...reviewProgress(store) };
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const text = fs.readFileSync(file, 'utf8');
|
|
30
|
+
const parsed = parseReview(text, file);
|
|
31
|
+
const progress = reviewProgress(parsed);
|
|
32
|
+
const fallback = progress.total ? 'editing' : '';
|
|
33
|
+
const status = frontmatterStatus(text) ? parsed.status : fallback;
|
|
34
|
+
return { status, done: progress.done, total: progress.total };
|
|
35
|
+
} catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const planProgress = (file, root, store) => {
|
|
41
|
+
if (!file) return '';
|
|
42
|
+
const abs = path.isAbsolute(file) ? file : path.join(root, file);
|
|
43
|
+
const state = planState(abs, store);
|
|
44
|
+
return state && state.total ? `${state.done}/${state.total}` : '';
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const planPart = (dir, name, store) => {
|
|
48
|
+
const base = planKey(name);
|
|
49
|
+
if (!base) return null;
|
|
50
|
+
const state = planState(path.join(dir, base), store);
|
|
51
|
+
if (!state || !state.status) return { name: base, status: '', progress: '' };
|
|
52
|
+
const progress = `${state.done}/${state.total}`;
|
|
53
|
+
return { name: base, status: state.status, progress };
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const planLabel = (part) => {
|
|
57
|
+
if (!part.status) return part.name;
|
|
58
|
+
return `${part.name} ${part.status} ${part.progress}`;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const alignPlanLabels = (parts) => {
|
|
62
|
+
let nameW = 0;
|
|
63
|
+
let statusW = 0;
|
|
64
|
+
for (const part of parts) {
|
|
65
|
+
if (!part.status) continue;
|
|
66
|
+
nameW = Math.max(nameW, visibleWidth(part.name));
|
|
67
|
+
statusW = Math.max(statusW, visibleWidth(part.status));
|
|
68
|
+
}
|
|
69
|
+
return parts.map((part) => {
|
|
70
|
+
if (!part.status) return part.name;
|
|
71
|
+
const name = padVisible(part.name, nameW, 'end');
|
|
72
|
+
const status = padVisible(part.status, statusW, 'end');
|
|
73
|
+
return planLabel({ name, status, progress: part.progress });
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
module.exports = {
|
|
78
|
+
planKey,
|
|
79
|
+
planProgress,
|
|
80
|
+
planPart,
|
|
81
|
+
planLabel,
|
|
82
|
+
alignPlanLabels,
|
|
83
|
+
};
|
package/lib/session/branches.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const { pickRepoWrite } = ops;
|
|
3
|
+
const { clamp } = require('../common/utilities.js');
|
|
5
4
|
|
|
6
5
|
class BranchController {
|
|
7
6
|
constructor(ui) {
|
|
@@ -22,10 +21,10 @@ class BranchController {
|
|
|
22
21
|
return this.ui.nav.pane === 'branches' && this.ui.commits.canCommit();
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
loadBranches() {
|
|
24
|
+
async loadBranches() {
|
|
26
25
|
const ui = this.ui;
|
|
27
26
|
try {
|
|
28
|
-
this.branches = ui.repo.listBranches(ui.top);
|
|
27
|
+
this.branches = await ui.repo.listBranches(ui.top);
|
|
29
28
|
return true;
|
|
30
29
|
} catch (error) {
|
|
31
30
|
ui.status = error.message;
|
|
@@ -33,14 +32,32 @@ class BranchController {
|
|
|
33
32
|
}
|
|
34
33
|
}
|
|
35
34
|
|
|
35
|
+
async refresh() {
|
|
36
|
+
if (!this.ui.repo.listBranches) return;
|
|
37
|
+
const loaded = await this.loadBranches();
|
|
38
|
+
if (!loaded) return;
|
|
39
|
+
const last = this.branches.length - 1;
|
|
40
|
+
const nav = this.ui.nav;
|
|
41
|
+
if (nav.branchCursor > last) nav.branchCursor = Math.max(0, last);
|
|
42
|
+
}
|
|
43
|
+
|
|
36
44
|
refreshAfterWrite(extra = {}) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
this.ui
|
|
45
|
+
const ui = this.ui;
|
|
46
|
+
ui.ops.hold(Promise.all([this.refresh(), ui.commits.refresh()]));
|
|
47
|
+
ui.refreshFromRepo({ keepEmpty: true, ...extra });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async open() {
|
|
51
|
+
const ui = this.ui;
|
|
52
|
+
const loaded = await this.loadBranches();
|
|
53
|
+
if (!loaded) return;
|
|
54
|
+
const current = this.branches.findIndex((entry) => entry.current);
|
|
55
|
+
const nav = ui.nav;
|
|
56
|
+
nav.branchCursor = current < 0 ? 0 : current;
|
|
57
|
+
nav.pane = 'branches';
|
|
58
|
+
nav.resetListScroll('branches');
|
|
59
|
+
ui.status = '';
|
|
60
|
+
nav.clearSelection();
|
|
44
61
|
}
|
|
45
62
|
|
|
46
63
|
runWrite(name, args, kind, done) {
|
|
@@ -56,14 +73,7 @@ class BranchController {
|
|
|
56
73
|
ui.status = 'read only';
|
|
57
74
|
return;
|
|
58
75
|
}
|
|
59
|
-
|
|
60
|
-
const current = this.branches.findIndex((entry) => entry.current);
|
|
61
|
-
const nav = ui.nav;
|
|
62
|
-
nav.branchCursor = current < 0 ? 0 : current;
|
|
63
|
-
nav.pane = 'branches';
|
|
64
|
-
nav.resetListScroll('branches');
|
|
65
|
-
ui.status = '';
|
|
66
|
-
nav.clearSelection();
|
|
76
|
+
ui.ops.hold(this.open());
|
|
67
77
|
}
|
|
68
78
|
|
|
69
79
|
onBranchMove(delta) {
|
|
@@ -73,7 +83,7 @@ class BranchController {
|
|
|
73
83
|
return;
|
|
74
84
|
}
|
|
75
85
|
const last = this.branches.length - 1;
|
|
76
|
-
const next =
|
|
86
|
+
const next = clamp(nav.branchCursor + delta, 0, last);
|
|
77
87
|
if (next === nav.branchCursor) return;
|
|
78
88
|
nav.branchCursor = next;
|
|
79
89
|
this.ui.status = '';
|
|
@@ -83,13 +93,11 @@ class BranchController {
|
|
|
83
93
|
const ui = this.ui;
|
|
84
94
|
if (!ui.commits.canCommit()) return;
|
|
85
95
|
const entry = this.selected();
|
|
86
|
-
if (!entry) return;
|
|
87
|
-
if (entry.current) return void ui.showFiles();
|
|
96
|
+
if (!entry || entry.current) return;
|
|
88
97
|
const name = entry.name;
|
|
89
|
-
const after = (extra) =>
|
|
90
|
-
const leave = () => ui.showFiles();
|
|
98
|
+
const after = (extra) => this.refreshAfterWrite(extra);
|
|
91
99
|
const done = `checked out ${name}`;
|
|
92
|
-
ui.ops.runWrite('checkout', [name], 'checking out', done, after
|
|
100
|
+
ui.ops.runWrite('checkout', [name], 'checking out', done, after);
|
|
93
101
|
}
|
|
94
102
|
|
|
95
103
|
finishCreateBranch() {
|
|
@@ -161,25 +169,9 @@ class BranchController {
|
|
|
161
169
|
if (!ui.commits.canCommit()) return;
|
|
162
170
|
const done = force ? 'force pushed' : 'pushed';
|
|
163
171
|
const kind = force ? 'force pushing' : 'pushing';
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const after = () => this.refreshAfterWrite();
|
|
168
|
-
return void ui.ops.runBusy(kind, done, write, after);
|
|
169
|
-
}
|
|
170
|
-
try {
|
|
171
|
-
ui.repo.push(ui.top, force);
|
|
172
|
-
} catch (error) {
|
|
173
|
-
if (error.rejected && !force) {
|
|
174
|
-
ui.mode = 'confirmPush';
|
|
175
|
-
ui.status = '';
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
ui.status = error.message;
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
this.refreshAfterWrite();
|
|
182
|
-
ui.status = done;
|
|
172
|
+
const write = (top) => ui.repo.push(top, force);
|
|
173
|
+
const after = () => this.refreshAfterWrite({ doneStatus: done });
|
|
174
|
+
ui.ops.runBusy(kind, done, write, after);
|
|
183
175
|
}
|
|
184
176
|
|
|
185
177
|
onPush() {
|
|
@@ -2,13 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
|
+
const { withNewline } = require('../common/utilities.js');
|
|
5
6
|
|
|
6
|
-
const files = require('../files.js');
|
|
7
|
+
const files = require('../common/files.js');
|
|
7
8
|
const { listPath, isTaskItem, isTasksEntry, isReadOnlyOrigin } = files;
|
|
8
|
-
const
|
|
9
|
-
const {
|
|
10
|
-
const diff = require('../diff/diff.js');
|
|
11
|
-
const { hunkKey } = diff;
|
|
9
|
+
const { needsReload, npmBusyKind, sameBlock } = require('./items.js');
|
|
10
|
+
const { hunkKey } = require('../diff/diff.js');
|
|
12
11
|
|
|
13
12
|
const nextUnstageOrigin = (item) =>
|
|
14
13
|
item.file.isNew === true ? 'untracked' : 'unstaged';
|
|
@@ -39,7 +38,7 @@ const appendIgnoreFile = (top, name, pattern) => {
|
|
|
39
38
|
const text = exists ? fs.readFileSync(abs, 'utf8') : '';
|
|
40
39
|
const lines = text.split('\n');
|
|
41
40
|
if (lines.some((line) => line.trim() === pattern)) return false;
|
|
42
|
-
const base = text
|
|
41
|
+
const base = withNewline(text);
|
|
43
42
|
fs.writeFileSync(abs, `${base}${pattern}\n`);
|
|
44
43
|
return true;
|
|
45
44
|
};
|
|
@@ -116,26 +115,17 @@ const selectNextFileAfter = (ui, rel) => {
|
|
|
116
115
|
nav.index = entry.firstIndex;
|
|
117
116
|
};
|
|
118
117
|
|
|
119
|
-
const liveItem = (ui, item) =>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const applyGit = (ui, label, write, after) => {
|
|
123
|
-
try {
|
|
124
|
-
write(ui.top);
|
|
125
|
-
ui.status = label;
|
|
126
|
-
after();
|
|
127
|
-
} catch (error) {
|
|
128
|
-
ui.status = error.message;
|
|
129
|
-
}
|
|
118
|
+
const liveItem = (ui, item) => {
|
|
119
|
+
const match = (entry) => sameBlock(entry, item);
|
|
120
|
+
return ui.items.find(match) ?? item;
|
|
130
121
|
};
|
|
131
122
|
|
|
132
|
-
const applyFileGit = (ui, targets, label, write
|
|
123
|
+
const applyFileGit = async (ui, targets, label, write) => {
|
|
133
124
|
let written = 0;
|
|
134
125
|
try {
|
|
135
126
|
for (const target of targets) {
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
afterItem(current);
|
|
127
|
+
const item = liveItem(ui, target);
|
|
128
|
+
await ui.ops.exclusive((top) => write(top, item));
|
|
139
129
|
written++;
|
|
140
130
|
}
|
|
141
131
|
ui.status = label;
|
|
@@ -146,12 +136,6 @@ const applyFileGit = (ui, targets, label, write, afterItem = () => {}) => {
|
|
|
146
136
|
}
|
|
147
137
|
};
|
|
148
138
|
|
|
149
|
-
const reconcilePartial = (ui, result) => {
|
|
150
|
-
if (result.ok) return false;
|
|
151
|
-
if (result.written) ui.reloadAfterChange();
|
|
152
|
-
return true;
|
|
153
|
-
};
|
|
154
|
-
|
|
155
139
|
const enqueueOrigins = (ui, targets, spec) => {
|
|
156
140
|
const writes = targets.map((target) => {
|
|
157
141
|
const item = liveItem(ui, target);
|
|
@@ -164,36 +148,31 @@ const enqueueOrigins = (ui, targets, spec) => {
|
|
|
164
148
|
kind: spec.busy || spec.done,
|
|
165
149
|
done: spec.done,
|
|
166
150
|
reload: spec.reload === true,
|
|
167
|
-
write: async (
|
|
168
|
-
for (const item of writes) await spec.write(
|
|
151
|
+
write: async () => {
|
|
152
|
+
for (const item of writes) await spec.write(item);
|
|
169
153
|
},
|
|
170
154
|
});
|
|
171
155
|
};
|
|
172
156
|
|
|
173
157
|
const stageLazy = (ui, targets, extra = {}) => {
|
|
174
158
|
const busy = extra.busy || '';
|
|
175
|
-
const repo = ui.repo;
|
|
176
159
|
enqueueOrigins(ui, targets, {
|
|
177
160
|
done: 'staged',
|
|
178
161
|
busy,
|
|
179
162
|
reload: extra.reload === true,
|
|
180
163
|
originOf: () => 'staged',
|
|
181
164
|
afterUi: extra.afterUi,
|
|
182
|
-
write: (
|
|
183
|
-
if (busy && repo.addAsync) return repo.addAsync(top, item);
|
|
184
|
-
return repo.add(top, item);
|
|
185
|
-
},
|
|
165
|
+
write: (item) => ui.ops.add(item),
|
|
186
166
|
});
|
|
187
167
|
};
|
|
188
168
|
|
|
189
169
|
const unstageLazy = (ui, targets, extra = {}) => {
|
|
190
|
-
const repo = ui.repo;
|
|
191
170
|
enqueueOrigins(ui, targets, {
|
|
192
171
|
done: 'unstaged',
|
|
193
172
|
busy: '',
|
|
194
173
|
originOf: nextUnstageOrigin,
|
|
195
174
|
afterUi: extra.afterUi,
|
|
196
|
-
write: (
|
|
175
|
+
write: (item) => ui.ops.unstage(item),
|
|
197
176
|
});
|
|
198
177
|
};
|
|
199
178
|
|
|
@@ -205,41 +184,18 @@ const dismissReloadable = (ui, targets) => {
|
|
|
205
184
|
|
|
206
185
|
const onBlockAdd = (ui, item) => {
|
|
207
186
|
const reload = needsReload(item);
|
|
208
|
-
|
|
209
|
-
return void stageLazy(ui, [item], { busy: npmBusyKind(item), reload });
|
|
210
|
-
}
|
|
211
|
-
applyGit(
|
|
212
|
-
ui,
|
|
213
|
-
'staged',
|
|
214
|
-
(top) => ui.repo.add(top, item),
|
|
215
|
-
() => {
|
|
216
|
-
if (reload) ui.reloadAfterChange();
|
|
217
|
-
else keepOrigin(ui, item, 'staged');
|
|
218
|
-
},
|
|
219
|
-
);
|
|
187
|
+
stageLazy(ui, [item], { busy: npmBusyKind(item), reload });
|
|
220
188
|
};
|
|
221
189
|
|
|
222
|
-
const onBlockUnstage = (ui, item) =>
|
|
223
|
-
if (ui.uiOpen) return void unstageLazy(ui, [item]);
|
|
224
|
-
const origin = nextUnstageOrigin(item);
|
|
225
|
-
applyGit(
|
|
226
|
-
ui,
|
|
227
|
-
'unstaged',
|
|
228
|
-
(top) => ui.repo.unstage(top, item),
|
|
229
|
-
() => keepOrigin(ui, item, origin),
|
|
230
|
-
);
|
|
231
|
-
};
|
|
190
|
+
const onBlockUnstage = (ui, item) => unstageLazy(ui, [item]);
|
|
232
191
|
|
|
233
192
|
const onBlockRevert = (ui, item) => {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
ui.reloadAfterChange();
|
|
241
|
-
},
|
|
242
|
-
);
|
|
193
|
+
const write = (top) => ui.repo.revert(top, item);
|
|
194
|
+
const after = () => {
|
|
195
|
+
dismissReloadable(ui, [item]);
|
|
196
|
+
ui.reloadAfterChange();
|
|
197
|
+
};
|
|
198
|
+
ui.ops.track(ui.ops.apply('dropped', write, after));
|
|
243
199
|
};
|
|
244
200
|
|
|
245
201
|
const BLOCK = {
|
|
@@ -263,48 +219,26 @@ const onBlockCommand = (ui, name) => {
|
|
|
263
219
|
const stageTargets = (ui, targets, after = null) => {
|
|
264
220
|
const reload = targets.some(needsReload);
|
|
265
221
|
const busy = targets.map(npmBusyKind).find((kind) => kind) || '';
|
|
266
|
-
|
|
267
|
-
return void stageLazy(ui, targets, { busy, reload, afterUi: after });
|
|
268
|
-
}
|
|
269
|
-
const keepStaged = (entry) => keepOrigin(ui, entry, 'staged');
|
|
270
|
-
const result = applyFileGit(ui, targets, 'staged', ui.repo.add, keepStaged);
|
|
271
|
-
if (reconcilePartial(ui, result)) return;
|
|
272
|
-
if (reload) ui.reloadAfterChange();
|
|
273
|
-
if (after) after();
|
|
222
|
+
stageLazy(ui, targets, { busy, reload, afterUi: after });
|
|
274
223
|
};
|
|
275
224
|
|
|
276
|
-
const unstageTargets = (ui, targets, after = null) =>
|
|
277
|
-
|
|
278
|
-
const keepUnstaged = (entry) =>
|
|
279
|
-
keepOrigin(ui, entry, nextUnstageOrigin(entry));
|
|
280
|
-
const result = applyFileGit(
|
|
281
|
-
ui,
|
|
282
|
-
targets,
|
|
283
|
-
'unstaged',
|
|
284
|
-
ui.repo.unstage,
|
|
285
|
-
keepUnstaged,
|
|
286
|
-
);
|
|
287
|
-
if (reconcilePartial(ui, result)) return;
|
|
288
|
-
if (after) after();
|
|
289
|
-
};
|
|
225
|
+
const unstageTargets = (ui, targets, after = null) =>
|
|
226
|
+
unstageLazy(ui, targets, { afterUi: after });
|
|
290
227
|
|
|
291
228
|
const revertTargets = (ui, targets) => {
|
|
292
229
|
dismissReloadable(ui, targets);
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
230
|
+
const write = (top, item) => ui.repo.revert(top, item);
|
|
231
|
+
const done = (result) => {
|
|
232
|
+
if (result.ok || result.written) ui.reloadAfterChange();
|
|
233
|
+
};
|
|
234
|
+
ui.ops.track(applyFileGit(ui, targets, 'dropped', write).then(done));
|
|
296
235
|
};
|
|
297
236
|
|
|
298
237
|
const revertFile = (ui, rel, selected, after) => {
|
|
299
238
|
dismissReloadable(ui, selected);
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
} catch (error) {
|
|
304
|
-
ui.status = error.message;
|
|
305
|
-
return void ui.reloadAfterChange();
|
|
306
|
-
}
|
|
307
|
-
ui.reloadAfterChange(after);
|
|
239
|
+
const write = (top) => ui.repo.revertFile(top, rel, selected);
|
|
240
|
+
const done = (ok) => ui.reloadAfterChange(ok ? after : undefined);
|
|
241
|
+
ui.ops.track(ui.ops.apply('dropped', write).then(done));
|
|
308
242
|
};
|
|
309
243
|
|
|
310
244
|
const TARGETS = {
|