reslop 0.2.5 → 0.2.7

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