reslop 0.2.5 → 0.2.6

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.
Files changed (105) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/LICENSE +1 -1
  3. package/README.md +11 -11
  4. package/bin/reslop.js +3 -8
  5. package/lib/ansi.js +32 -19
  6. package/lib/capabilities.js +8 -8
  7. package/lib/cli.js +40 -74
  8. package/lib/clipboard.js +39 -47
  9. package/lib/dashboard/git.js +128 -0
  10. package/lib/dashboard/model.js +332 -0
  11. package/lib/dashboard/npm.js +193 -0
  12. package/lib/dashboard/tree.js +278 -0
  13. package/lib/deps-diff.js +197 -402
  14. package/lib/deps.js +113 -240
  15. package/lib/detect.js +8 -18
  16. package/lib/diff/align.js +41 -113
  17. package/lib/diff/diff.js +12 -43
  18. package/lib/diff/display.js +25 -36
  19. package/lib/diff/edit.js +6 -20
  20. package/lib/diff/inline.js +76 -144
  21. package/lib/diff/items.js +33 -47
  22. package/lib/diff/parse.js +28 -56
  23. package/lib/diff/patch.js +12 -33
  24. package/lib/diff/unit.js +79 -178
  25. package/lib/editor.js +52 -69
  26. package/lib/files.js +71 -89
  27. package/lib/git-branches.js +254 -416
  28. package/lib/git-deps.js +147 -272
  29. package/lib/git-reword-todo.js +5 -13
  30. package/lib/git-worktree.js +141 -212
  31. package/lib/git.js +21 -49
  32. package/lib/github.js +67 -181
  33. package/lib/gitlab.js +40 -90
  34. package/lib/highlight/bash.js +55 -177
  35. package/lib/highlight/core.js +155 -141
  36. package/lib/highlight/css.js +25 -58
  37. package/lib/highlight/csv.js +19 -37
  38. package/lib/highlight/dart.js +72 -213
  39. package/lib/highlight/dot.js +14 -14
  40. package/lib/highlight/html.js +42 -63
  41. package/lib/highlight/js.js +177 -506
  42. package/lib/highlight/json.js +17 -37
  43. package/lib/highlight/log.js +19 -25
  44. package/lib/highlight/txt.js +7 -10
  45. package/lib/highlight.js +17 -46
  46. package/lib/keys.js +49 -82
  47. package/lib/manifest.js +67 -126
  48. package/lib/npm-commands.js +120 -169
  49. package/lib/remote.js +140 -190
  50. package/lib/render/branches.js +63 -61
  51. package/lib/render/chrome.js +220 -333
  52. package/lib/render/commits.js +160 -361
  53. package/lib/render/dash-activity.js +212 -0
  54. package/lib/render/dash-blocks.js +245 -0
  55. package/lib/render/dash-table.js +290 -0
  56. package/lib/render/dashboard.js +62 -0
  57. package/lib/render/diff.js +123 -258
  58. package/lib/render/files.js +40 -85
  59. package/lib/render/frame.js +78 -117
  60. package/lib/render/notes.js +139 -210
  61. package/lib/render/npm.js +213 -177
  62. package/lib/render/present.js +4 -11
  63. package/lib/render/primitives.js +72 -174
  64. package/lib/render/render.js +5 -14
  65. package/lib/render/report.js +43 -0
  66. package/lib/render/tiles.js +260 -0
  67. package/lib/render/unit.js +15 -31
  68. package/lib/report-model.js +110 -33
  69. package/lib/report-parse.js +56 -37
  70. package/lib/report-render.js +102 -59
  71. package/lib/report-run.js +87 -77
  72. package/lib/report-test.js +221 -99
  73. package/lib/report-tools.js +96 -154
  74. package/lib/review-merge.js +33 -41
  75. package/lib/review.js +68 -129
  76. package/lib/runs.js +225 -0
  77. package/lib/select.js +53 -91
  78. package/lib/session/accessors.js +14 -21
  79. package/lib/session/actions.js +61 -43
  80. package/lib/session/branches.js +166 -222
  81. package/lib/session/change-actions.js +49 -119
  82. package/lib/session/commit.js +274 -355
  83. package/lib/session/compose.js +556 -714
  84. package/lib/session/dashboard.js +385 -0
  85. package/lib/session/dispatch.js +162 -170
  86. package/lib/session/files-pane.js +76 -44
  87. package/lib/session/items.js +6 -17
  88. package/lib/session/lifecycle.js +393 -466
  89. package/lib/session/load.js +1 -14
  90. package/lib/session/navigation.js +2 -2
  91. package/lib/session/npm.js +403 -420
  92. package/lib/session/ops.js +5 -13
  93. package/lib/session/pointer.js +16 -14
  94. package/lib/session/progress.js +39 -30
  95. package/lib/session/review.js +1 -3
  96. package/lib/session/tasks.js +276 -235
  97. package/lib/session/templates.js +51 -59
  98. package/lib/session/terminal.js +85 -132
  99. package/lib/session/view.js +47 -81
  100. package/lib/session/watch.js +196 -212
  101. package/lib/session.js +81 -161
  102. package/lib/update.js +44 -111
  103. package/lib/utilities.js +46 -88
  104. package/lib/wrap.js +29 -55
  105. package/package.json +1 -1
@@ -1,8 +1,5 @@
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
4
  const { isReadOnlyOrigin } = files;
8
5
  const actions = require('./actions.js');
@@ -37,10 +34,6 @@ const PENDING_COMMIT = {
37
34
 
38
35
  const listedCommits = (commits) => [PENDING_COMMIT].concat(commits ?? []);
39
36
 
40
- const isPending = (entry) => Boolean(entry && entry.pending);
41
-
42
- const briefCommit = (ui) => ui.commits.commitView !== 'full';
43
-
44
37
  const joinMessage = (line, rest) => (rest ? `${line}\n${rest}` : line);
45
38
 
46
39
  const tidyCommitMessage = (message) => {
@@ -50,402 +43,328 @@ const tidyCommitMessage = (message) => {
50
43
  return lines.join('\n');
51
44
  };
52
45
 
53
- const keepBriefBody = (ui, text) => {
54
- if (!briefCommit(ui)) return text;
55
- const parts = splitMessage(text);
56
- ui.composer.state.commitBody = parts.rest;
57
- return parts.line;
58
- };
59
-
60
46
  const fixupMessage = (entry) => {
61
47
  const subject = commitLine(entry).trim();
62
48
  if (!subject) return 'fixup!';
63
49
  return `fixup! ${subject}`;
64
50
  };
65
51
 
66
- const canCommit = (ui) => {
67
- const caps = ui.capabilities;
68
- if (!caps.branches) {
69
- ui.status = 'read only';
70
- return false;
71
- }
72
- for (const item of ui.items) {
73
- if (isReadOnlyOrigin(item.origin)) {
74
- ui.status = 'read only';
75
- return false;
76
- }
77
- }
78
- return true;
79
- };
80
-
81
52
  const hasStagedChanges = (ui) => {
82
53
  const repo = ui.repo;
83
54
  if (repo.hasStaged) return repo.hasStaged(ui.top);
84
- for (const item of ui.items) {
85
- if (item.origin === 'staged') return true;
86
- }
87
- return false;
88
- };
89
-
90
- const selectedCommit = (api) => {
91
- const cursor = api.ui.nav.commitCursor;
92
- return listedCommits(api.state.commits)[cursor] ?? null;
55
+ return ui.items.some((item) => item.origin === 'staged');
93
56
  };
94
57
 
95
- const clampCommitCursor = (api) => {
96
- const last = listedCommits(api.state.commits).length - 1;
97
- const nav = api.ui.nav;
98
- if (nav.commitCursor > last) nav.commitCursor = Math.max(0, last);
99
- };
58
+ class CommitController {
59
+ constructor(ui) {
60
+ this.ui = ui;
61
+ this.reset();
62
+ }
100
63
 
101
- const commitIndexForView = (api) => {
102
- const ui = api.ui;
103
- if (hasStagedChanges(ui)) return 0;
104
- const list = listedCommits(api.state.commits);
105
- const want = `${ui.rev ?? ''}`;
106
- const short = `${ui.revShort ?? ''}`;
107
- if (!want && !short) return 0;
108
- for (let i = 0; i < list.length; i++) {
109
- const entry = list[i];
110
- if (!entry || entry.pending) continue;
111
- const sha = `${entry.sha ?? ''}`;
112
- const brief = `${entry.shortSha ?? ''}`;
113
- if (want && sha === want) return i;
114
- if (short && (brief === short || sha.startsWith(short))) return i;
64
+ reset() {
65
+ this.commits = [];
66
+ this.dropName = '';
67
+ this.dropSha = '';
68
+ this.commitView = 'brief';
115
69
  }
116
- return 0;
117
- };
118
70
 
119
- const refreshCommitList = (api) => {
120
- const repo = api.ui.repo;
121
- if (!repo.listCommits) return;
122
- try {
123
- api.state.commits = repo.listCommits(api.ui.top);
124
- } catch (error) {
125
- api.ui.status = error.message;
126
- return;
71
+ get brief() {
72
+ return this.commitView !== 'full';
127
73
  }
128
- clampCommitCursor(api);
129
- };
130
74
 
131
- const refreshAfterWrite = (api, extra = {}) => {
132
- refreshCommitList(api);
133
- api.ui.refreshFromRepo({ keepEmpty: true, ...extra });
134
- };
75
+ canCommit() {
76
+ const ui = this.ui;
77
+ const readOnly =
78
+ !ui.capabilities.branches ||
79
+ ui.items.some((item) => isReadOnlyOrigin(item.origin));
80
+ if (readOnly) ui.status = 'read only';
81
+ return !readOnly;
82
+ }
135
83
 
136
- const showCommits = (api) => {
137
- if (api.ui.mode === 'compose') return;
138
- const repo = api.ui.repo;
139
- if (!repo.listCommits) {
140
- api.ui.status = 'read only';
141
- return;
84
+ canWrite() {
85
+ return this.ui.nav.pane === 'commits' && this.canCommit();
142
86
  }
143
- try {
144
- api.state.commits = repo.listCommits(api.ui.top);
145
- } catch (error) {
146
- api.ui.status = error.message;
147
- return;
87
+
88
+ selected() {
89
+ return listedCommits(this.commits)[this.ui.nav.commitCursor] ?? null;
148
90
  }
149
- const nav = api.ui.nav;
150
- nav.commitCursor = commitIndexForView(api);
151
- nav.pane = 'commits';
152
- nav.resetListScroll('commits');
153
- api.ui.status = '';
154
- nav.clearSelection();
155
- };
156
91
 
157
- const onCommitMove = (api, delta) => {
158
- if (api.ui.mode === 'compose') return;
159
- const commits = listedCommits(api.state.commits);
160
- const next = Math.min(
161
- commits.length - 1,
162
- Math.max(0, api.ui.nav.commitCursor + delta),
163
- );
164
- if (next === api.ui.nav.commitCursor) return;
165
- api.ui.nav.commitCursor = next;
166
- api.ui.status = '';
167
- };
92
+ loadCommits() {
93
+ const ui = this.ui;
94
+ try {
95
+ this.commits = ui.repo.listCommits(ui.top);
96
+ return true;
97
+ } catch (error) {
98
+ ui.status = error.message;
99
+ return false;
100
+ }
101
+ }
168
102
 
169
- const startGitCommit = (api, kind) => {
170
- const ui = api.ui;
171
- if (!isInPlaceCommit(kind) && !hasStagedChanges(ui)) {
172
- ui.mode = 'review';
173
- ui.composer.state.commitKind = null;
174
- ui.status = 'nothing to commit';
175
- return;
103
+ refresh() {
104
+ if (!this.ui.repo.listCommits || !this.loadCommits()) return;
105
+ const last = listedCommits(this.commits).length - 1;
106
+ const nav = this.ui.nav;
107
+ if (nav.commitCursor > last) nav.commitCursor = Math.max(0, last);
176
108
  }
177
- let text = '';
178
- if (kind === 'fixup') text = fixupMessage(selectedCommit(api));
179
- if (kind === 'commit') api.ui.nav.commitCursor = 0;
180
- if (kind === 'amend') {
181
- const headAt = api.state.commits.findIndex((entry) => entry.head);
182
- const at = headAt < 0 ? 0 : headAt;
183
- api.ui.nav.commitCursor = api.state.commits.length ? at + 1 : 0;
184
- if (ui.repo.lastMessage) text = ui.repo.lastMessage(ui.top) ?? '';
109
+
110
+ refreshAfterWrite(extra = {}) {
111
+ this.refresh();
112
+ this.ui.refreshFromRepo({ keepEmpty: true, ...extra });
185
113
  }
186
- if (kind === 'reword') {
187
- const entry = selectedCommit(api);
188
- if (!entry || !entry.sha) return;
189
- if (typeof ui.repo.commitMessage === 'function') {
190
- text = ui.repo.commitMessage(ui.top, entry.sha) ?? '';
191
- } else {
192
- text = commitText(entry);
114
+
115
+ indexForView() {
116
+ const ui = this.ui;
117
+ if (hasStagedChanges(ui)) return 0;
118
+ const list = listedCommits(this.commits);
119
+ const want = `${ui.rev ?? ''}`;
120
+ const short = `${ui.revShort ?? ''}`;
121
+ if (!want && !short) return 0;
122
+ for (let i = 0; i < list.length; i++) {
123
+ const entry = list[i];
124
+ if (entry.pending) continue;
125
+ const sha = `${entry.sha ?? ''}`;
126
+ const brief = `${entry.shortSha ?? ''}`;
127
+ if (want && sha === want) return i;
128
+ if (short && (brief === short || sha.startsWith(short))) return i;
193
129
  }
130
+ return 0;
194
131
  }
195
- ui.composer.state.commitBody = '';
196
- if (isInPlaceCommit(kind)) text = keepBriefBody(ui, text);
197
- ui.composer.state.commitKind = kind;
198
- ui.composer.openCompose('commit', text);
199
- const editor = ui.composer.state.editor;
200
- if (!editor) return;
201
- const lineEnd = editor.text.indexOf('\n');
202
- editor.cursor = lineEnd < 0 ? editor.text.length : lineEnd;
203
- };
204
132
 
205
- const onCommit = (api) => {
206
- if (api.ui.nav.pane !== 'commits') return void showCommits(api);
207
- if (!canCommit(api.ui)) return;
208
- api.ui.nav.commitCursor = 0;
209
- startGitCommit(api, 'commit');
210
- };
211
-
212
- const openLoadedDiff = (ui) => {
213
- ui.nav.tasksOpen = false;
214
- ui.nav.scroll = 0;
215
- ui.nav.index = 0;
216
- ui.nav.pane = ui.items.length ? 'diff' : 'files';
217
- ui.syncReviewPath();
218
- ui.syncFileCursor();
219
- ui.status = '';
220
- };
221
-
222
- const showSelectedDiff = (api) => {
223
- if (api.ui.mode === 'compose') return;
224
- const entry = selectedCommit(api);
225
- if (!entry) return;
226
- const ui = api.ui;
227
- const viewingCommit = Boolean(ui.rev || ui.revShort);
228
- if (isPending(entry) && !viewingCommit) return void onCommit(api);
229
- if (isPending(entry) || !entry.sha) {
230
- ui.rev = '';
231
- ui.revShort = '';
232
- } else {
233
- ui.rev = entry.sha;
234
- ui.revShort = entry.shortSha || '';
133
+ show() {
134
+ const ui = this.ui;
135
+ if (ui.mode === 'compose') return;
136
+ if (!ui.repo.listCommits) {
137
+ ui.status = 'read only';
138
+ return;
139
+ }
140
+ if (!this.loadCommits()) return;
141
+ const nav = ui.nav;
142
+ nav.commitCursor = this.indexForView();
143
+ nav.pane = 'commits';
144
+ nav.resetListScroll('commits');
145
+ ui.status = '';
146
+ nav.clearSelection();
235
147
  }
236
- ui.refreshFromRepo({
237
- keepEmpty: true,
238
- afterLoad: () => openLoadedDiff(ui),
239
- });
240
- };
241
148
 
242
- const onAmend = (api) => {
243
- if (api.ui.nav.pane !== 'commits') return;
244
- if (!canCommit(api.ui)) return;
245
- startGitCommit(api, 'amend');
246
- };
149
+ onCommitMove(delta) {
150
+ const nav = this.ui.nav;
151
+ if (this.ui.mode === 'compose') return;
152
+ const last = listedCommits(this.commits).length - 1;
153
+ const next = Math.min(last, Math.max(0, nav.commitCursor + delta));
154
+ if (next === nav.commitCursor) return;
155
+ nav.commitCursor = next;
156
+ this.ui.status = '';
157
+ }
247
158
 
248
- const onReword = (api) => {
249
- if (api.ui.nav.pane !== 'commits') return;
250
- if (!canCommit(api.ui)) return;
251
- startGitCommit(api, 'reword');
252
- };
159
+ messageFor(kind) {
160
+ const ui = this.ui;
161
+ if (kind === 'fixup') return fixupMessage(this.selected());
162
+ if (kind === 'amend') {
163
+ const headAt = this.commits.findIndex((entry) => entry.head);
164
+ const at = headAt < 0 ? 0 : headAt;
165
+ ui.nav.commitCursor = this.commits.length ? at + 1 : 0;
166
+ if (ui.repo.lastMessage) return ui.repo.lastMessage(ui.top) ?? '';
167
+ return '';
168
+ }
169
+ if (kind === 'reword') {
170
+ const entry = this.selected();
171
+ if (!entry || !entry.sha) return null;
172
+ if (!ui.repo.commitMessage) return commitText(entry);
173
+ return ui.repo.commitMessage(ui.top, entry.sha) ?? '';
174
+ }
175
+ ui.nav.commitCursor = 0;
176
+ return '';
177
+ }
253
178
 
254
- const applyFixupNow = (api) => {
255
- const entry = selectedCommit(api);
256
- if (!entry || !entry.sha) return;
257
- const done = 'applied';
258
- const repo = api.ui.repo;
259
- if (!repo.applyFixup) {
260
- api.ui.status = 'read only';
261
- return;
179
+ start(kind) {
180
+ const ui = this.ui;
181
+ const composer = ui.composer;
182
+ if (!isInPlaceCommit(kind) && !hasStagedChanges(ui)) {
183
+ ui.mode = 'review';
184
+ composer.commitKind = null;
185
+ ui.status = 'nothing to commit';
186
+ return;
187
+ }
188
+ let text = this.messageFor(kind);
189
+ if (text === null) return;
190
+ composer.commitBody = '';
191
+ if (isInPlaceCommit(kind) && this.brief) {
192
+ const parts = splitMessage(text);
193
+ composer.commitBody = parts.rest;
194
+ text = parts.line;
195
+ }
196
+ composer.commitKind = kind;
197
+ ui.composer.openCompose('commit', text);
198
+ const editor = composer.editor;
199
+ const lineEnd = editor.text.indexOf('\n');
200
+ editor.cursor = lineEnd < 0 ? editor.text.length : lineEnd;
262
201
  }
263
- api.ui.ops.runWrite('applyFixup', [entry.sha], 'applying', done, (extra) =>
264
- refreshAfterWrite(api, extra),
265
- );
266
- };
267
202
 
268
- const onApply = (api) => {
269
- if (api.ui.nav.pane !== 'commits') return;
270
- if (!canCommit(api.ui)) return;
271
- const entry = selectedCommit(api);
272
- if (isFixupCommit(entry)) return void applyFixupNow(api);
273
- startGitCommit(api, 'amend');
274
- };
203
+ onCommit() {
204
+ if (this.ui.nav.pane !== 'commits') return void this.show();
205
+ if (!this.canCommit()) return;
206
+ this.start('commit');
207
+ }
275
208
 
276
- const onFixup = (api) => {
277
- if (api.ui.nav.pane !== 'commits') return;
278
- if (!canCommit(api.ui)) return;
279
- startGitCommit(api, 'fixup');
280
- };
209
+ onShowDiff() {
210
+ const ui = this.ui;
211
+ if (ui.mode === 'compose') return;
212
+ const entry = this.selected();
213
+ if (!entry) return;
214
+ const viewingCommit = Boolean(ui.rev || ui.revShort);
215
+ if (entry.pending && !viewingCommit) return void this.onCommit();
216
+ const sha = entry.pending ? '' : entry.sha || '';
217
+ ui.rev = sha;
218
+ ui.revShort = sha ? entry.shortSha || '' : '';
219
+ const afterLoad = () => {
220
+ const nav = ui.nav;
221
+ nav.tasksOpen = false;
222
+ nav.scroll = 0;
223
+ nav.index = 0;
224
+ nav.pane = ui.items.length ? 'diff' : 'files';
225
+ ui.syncReviewPath();
226
+ ui.syncFileCursor();
227
+ ui.status = '';
228
+ };
229
+ ui.refreshFromRepo({ keepEmpty: true, afterLoad });
230
+ }
281
231
 
282
- const finishAsyncCommit = (api, kind, message, done, write) => {
283
- const ui = api.ui;
284
- ui.composer.closeCompose();
285
- const run = (top) => write(top, kind, message);
286
- const after = () => refreshAfterWrite(api, { doneStatus: done });
287
- return void ui.ops.runBusy('committing', done, run, after);
288
- };
232
+ onAmend() {
233
+ if (this.canWrite()) this.start('amend');
234
+ }
289
235
 
290
- const finishSyncCommit = (api, kind, message, done) => {
291
- const ui = api.ui;
292
- try {
293
- ui.repo.commit(ui.top, kind, message);
294
- } catch (error) {
295
- ui.status = error.message;
296
- return;
236
+ onReword() {
237
+ if (this.canWrite()) this.start('reword');
297
238
  }
298
- ui.composer.closeCompose();
299
- ui.collection.clearDismissed();
300
- refreshAfterWrite(api);
301
- if (!ui.done && !ui.pendingExtras) {
302
- ui.status = done;
239
+
240
+ onFixup() {
241
+ if (this.canWrite()) this.start('fixup');
303
242
  }
304
- };
305
243
 
306
- const finishReword = (api, message) => {
307
- const ui = api.ui;
308
- const entry = selectedCommit(api);
309
- if (!entry || !entry.sha) {
310
- ui.status = 'read only';
311
- return;
244
+ onApply() {
245
+ if (!this.canWrite()) return;
246
+ const entry = this.selected();
247
+ if (!isFixupCommit(entry)) return void this.start('amend');
248
+ if (!entry.sha) return;
249
+ if (!this.ui.repo.applyFixup) {
250
+ this.ui.status = 'read only';
251
+ return;
252
+ }
253
+ const after = (extra) => this.refreshAfterWrite(extra);
254
+ this.ui.ops.runWrite(
255
+ 'applyFixup',
256
+ [entry.sha],
257
+ 'applying',
258
+ 'applied',
259
+ after,
260
+ );
312
261
  }
313
- const done = COMMIT_STATUS.reword;
314
- const repo = ui.repo;
315
- if (!repo.reword) {
316
- ui.status = 'read only';
317
- return;
262
+
263
+ runAsync(kind, done, run) {
264
+ this.ui.composer.closeCompose();
265
+ const after = () => this.refreshAfterWrite({ doneStatus: done });
266
+ this.ui.ops.runBusy(kind, done, run, after);
318
267
  }
319
- const picked = pickRepoWrite(repo, 'reword', ui.uiOpen);
320
- if (picked.async) {
268
+
269
+ finishReword(message) {
270
+ const ui = this.ui;
271
+ const entry = this.selected();
272
+ if (!entry || !entry.sha || !ui.repo.reword) {
273
+ ui.status = 'read only';
274
+ return;
275
+ }
276
+ const done = COMMIT_STATUS.reword;
277
+ const picked = pickRepoWrite(ui.repo, 'reword', ui.uiOpen);
278
+ if (picked.async) {
279
+ const run = (top) => picked.write(top, entry.sha, message);
280
+ return void this.runAsync('rewording', done, run);
281
+ }
282
+ try {
283
+ ui.repo.reword(ui.top, entry.sha, message);
284
+ } catch (error) {
285
+ ui.status = error.message;
286
+ return;
287
+ }
321
288
  ui.composer.closeCompose();
322
- const run = (top) => picked.write(top, entry.sha, message);
323
- const after = () => refreshAfterWrite(api, { doneStatus: done });
324
- return void ui.ops.runBusy('rewording', done, run, after);
325
- }
326
- try {
327
- repo.reword(ui.top, entry.sha, message);
328
- } catch (error) {
329
- ui.status = error.message;
330
- return;
289
+ this.refreshAfterWrite();
290
+ if (!ui.status) ui.status = done;
331
291
  }
332
- ui.composer.closeCompose();
333
- refreshAfterWrite(api);
334
- if (!ui.status) ui.status = done;
335
- };
336
292
 
337
- const finishGitCommit = (api) => {
338
- const ui = api.ui;
339
- const kind = ui.composer.state.commitKind;
340
- const editor = ui.composer.state.editor;
341
- let message = editor ? editor.text : '';
342
- if (isInPlaceCommit(kind) && briefCommit(ui)) {
343
- message = joinMessage(message, ui.composer.state.commitBody);
344
- }
345
- message = tidyCommitMessage(message);
346
- if (!message.trim()) {
347
- ui.status = 'empty commit message';
348
- return;
349
- }
350
- if (!isInPlaceCommit(kind) && !hasStagedChanges(ui)) {
293
+ finishGitCommit() {
294
+ const ui = this.ui;
295
+ const composer = ui.composer;
296
+ const kind = composer.commitKind;
297
+ let message = composer.editor ? composer.editor.text : '';
298
+ if (isInPlaceCommit(kind) && this.brief) {
299
+ message = joinMessage(message, composer.commitBody);
300
+ }
301
+ message = tidyCommitMessage(message);
302
+ if (!message.trim()) {
303
+ ui.status = 'empty commit message';
304
+ return;
305
+ }
306
+ if (!isInPlaceCommit(kind) && !hasStagedChanges(ui)) {
307
+ ui.composer.closeCompose();
308
+ ui.status = 'nothing to commit';
309
+ return;
310
+ }
311
+ if (kind === 'reword') return void this.finishReword(message);
312
+ const done = COMMIT_STATUS[kind] ?? 'committed';
313
+ const picked = pickRepoWrite(ui.repo, 'commit', ui.uiOpen);
314
+ if (picked.async) {
315
+ const run = (top) => picked.write(top, kind, message);
316
+ return void this.runAsync('committing', done, run);
317
+ }
318
+ try {
319
+ ui.repo.commit(ui.top, kind, message);
320
+ } catch (error) {
321
+ ui.status = error.message;
322
+ return;
323
+ }
351
324
  ui.composer.closeCompose();
352
- ui.status = 'nothing to commit';
353
- return;
325
+ ui.collection.clearDismissed();
326
+ this.refreshAfterWrite();
327
+ if (!ui.done && !ui.pendingExtras) ui.status = done;
354
328
  }
355
- if (kind === 'reword') return void finishReword(api, message);
356
- const done = COMMIT_STATUS[kind] ?? 'committed';
357
- const uiOpen = ui.uiOpen;
358
- const picked = pickRepoWrite(ui.repo, 'commit', uiOpen);
359
- if (picked.async) {
360
- return void finishAsyncCommit(api, kind, message, done, picked.write);
361
- }
362
- finishSyncCommit(api, kind, message, done);
363
- };
364
329
 
365
- const onDropCommit = (api) => {
366
- if (api.ui.nav.pane !== 'commits') return;
367
- if (!canCommit(api.ui)) return;
368
- const entry = selectedCommit(api);
369
- if (!entry) return;
370
- api.state.dropName = entry.shortSha || entry.sha;
371
- api.state.dropSha = entry.sha;
372
- api.ui.mode = 'confirmDrop';
373
- api.ui.status = '';
374
- };
375
-
376
- const cancelDropCommit = (api) => {
377
- api.ui.mode = 'review';
378
- api.state.dropName = '';
379
- api.state.dropSha = '';
380
- api.ui.status = '';
381
- };
382
-
383
- const confirmDropCommit = (api) => {
384
- const sha = api.state.dropSha;
385
- const name = api.state.dropName;
386
- api.ui.mode = 'review';
387
- api.state.dropName = '';
388
- api.state.dropSha = '';
389
- if (!sha) return;
390
- const done = `dropped ${name}`;
391
- const repo = api.ui.repo;
392
- if (!repo.dropCommit) {
393
- api.ui.status = 'read only';
394
- return;
330
+ onDropCommit() {
331
+ if (!this.canWrite()) return;
332
+ const entry = this.selected();
333
+ if (!entry) return;
334
+ this.dropName = entry.shortSha || entry.sha;
335
+ this.dropSha = entry.sha;
336
+ this.ui.mode = 'confirmDrop';
337
+ this.ui.status = '';
395
338
  }
396
- api.ui.ops.runWrite('dropCommit', [sha], 'dropping', done, (extra) =>
397
- refreshAfterWrite(api, extra),
398
- );
399
- };
400
339
 
401
- const onView = (api) => {
402
- if (api.ui.nav.pane !== 'commits') return;
403
- const next = api.state.commitView === 'full' ? 'brief' : 'full';
404
- api.state.commitView = next;
405
- api.ui.nav.listScroll = 0;
406
- api.ui.status = next;
407
- };
340
+ cancelDropCommit() {
341
+ this.ui.mode = 'review';
342
+ this.dropName = '';
343
+ this.dropSha = '';
344
+ this.ui.status = '';
345
+ }
408
346
 
409
- const reset = (state) => {
410
- state.commits = [];
411
- state.dropName = '';
412
- state.dropSha = '';
413
- state.commitView = 'brief';
414
- };
347
+ confirmDropCommit() {
348
+ const sha = this.dropSha;
349
+ const done = `dropped ${this.dropName}`;
350
+ this.ui.mode = 'review';
351
+ this.dropName = '';
352
+ this.dropSha = '';
353
+ if (!sha) return;
354
+ if (!this.ui.repo.dropCommit) {
355
+ this.ui.status = 'read only';
356
+ return;
357
+ }
358
+ const after = (extra) => this.refreshAfterWrite(extra);
359
+ this.ui.ops.runWrite('dropCommit', [sha], 'dropping', done, after);
360
+ }
415
361
 
416
- const createCommitController = (ui) => {
417
- const state = {
418
- commits: [],
419
- dropName: '',
420
- dropSha: '',
421
- commitView: 'brief',
422
- };
423
- const api = { ui, state };
424
- return stateView(
425
- state,
426
- {
427
- reset: () => reset(state),
428
- canCommit: () => canCommit(ui),
429
- onCommit: () => onCommit(api),
430
- onAmend: () => onAmend(api),
431
- onReword: () => onReword(api),
432
- onApply: () => onApply(api),
433
- onFixup: () => onFixup(api),
434
- onCommitMove: (delta) => onCommitMove(api, delta),
435
- onShowDiff: () => showSelectedDiff(api),
436
- onView: () => onView(api),
437
- finishGitCommit: () => finishGitCommit(api),
438
- refresh: () => refreshCommitList(api),
439
- onDropCommit: () => onDropCommit(api),
440
- cancelDropCommit: () => cancelDropCommit(api),
441
- confirmDropCommit: () => confirmDropCommit(api),
442
- },
443
- ['commits', 'dropName', 'commitView'],
444
- );
445
- };
362
+ onView() {
363
+ if (this.ui.nav.pane !== 'commits') return;
364
+ this.commitView = this.brief ? 'full' : 'brief';
365
+ this.ui.nav.listScroll = 0;
366
+ this.ui.status = this.commitView;
367
+ }
368
+ }
446
369
 
447
- module.exports = {
448
- hasStagedChanges,
449
- createCommitController,
450
- listedCommits,
451
- };
370
+ module.exports = { hasStagedChanges, listedCommits, CommitController };