reslop 0.1.9 → 0.2.1

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 (96) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/README.md +3 -1
  3. package/bin/reslop.js +2 -2
  4. package/lib/ansi.js +148 -5
  5. package/lib/api.js +4 -2
  6. package/lib/capabilities.js +13 -14
  7. package/lib/cli.js +38 -49
  8. package/lib/clipboard.js +1 -1
  9. package/lib/{deps/changes.js → deps-diff.js} +309 -19
  10. package/lib/deps.js +498 -29
  11. package/lib/detect.js +1 -7
  12. package/lib/diff/align.js +7 -10
  13. package/lib/diff/diff.js +53 -0
  14. package/lib/diff/display.js +5 -4
  15. package/lib/diff/inline.js +26 -21
  16. package/lib/diff/items.js +6 -4
  17. package/lib/diff/patch.js +1 -3
  18. package/lib/diff/unit.js +392 -0
  19. package/lib/files.js +5 -8
  20. package/lib/{git/branches.js → git-branches.js} +191 -11
  21. package/lib/{git/dependencies.js → git-deps.js} +111 -38
  22. package/lib/git-reword-todo.js +20 -0
  23. package/lib/git.js +606 -68
  24. package/lib/github.js +28 -197
  25. package/lib/gitlab.js +24 -195
  26. package/lib/highlight/bash.js +1 -3
  27. package/lib/highlight/css.js +1 -3
  28. package/lib/highlight/js.js +11 -14
  29. package/lib/highlight.js +6 -9
  30. package/lib/keys.js +14 -8
  31. package/lib/manifest.js +330 -0
  32. package/lib/remote.js +374 -0
  33. package/lib/render/branches.js +18 -100
  34. package/lib/render/chrome.js +71 -14
  35. package/lib/render/commits.js +69 -60
  36. package/lib/render/diff.js +20 -20
  37. package/lib/render/files.js +6 -34
  38. package/lib/render/frame.js +36 -13
  39. package/lib/render/notes.js +26 -8
  40. package/lib/render/primitives.js +99 -11
  41. package/lib/{render.js → render/render.js} +8 -8
  42. package/lib/render/unit.js +41 -0
  43. package/lib/review-merge.js +270 -0
  44. package/lib/review.js +782 -14
  45. package/lib/select.js +5 -10
  46. package/lib/session/accessors.js +9 -1
  47. package/lib/session/actions.js +61 -18
  48. package/lib/session/branches.js +116 -128
  49. package/lib/session/change-actions.js +202 -123
  50. package/lib/session/commit.js +201 -115
  51. package/lib/session/compose.js +181 -15
  52. package/lib/session/dispatch.js +110 -60
  53. package/lib/session/files-pane.js +156 -148
  54. package/lib/session/items.js +22 -30
  55. package/lib/session/lifecycle.js +151 -54
  56. package/lib/session/load.js +19 -33
  57. package/lib/session/navigation.js +12 -1
  58. package/lib/session/ops.js +28 -41
  59. package/lib/session/pointer.js +57 -57
  60. package/lib/session/review.js +23 -37
  61. package/lib/session/terminal.js +6 -12
  62. package/lib/session/todos.js +37 -13
  63. package/lib/session/unit.js +208 -0
  64. package/lib/session/update.js +33 -46
  65. package/lib/session/view.js +78 -9
  66. package/lib/session/watch.js +128 -24
  67. package/lib/session.js +22 -246
  68. package/lib/source.js +3 -3
  69. package/lib/update.js +40 -75
  70. package/lib/{git/proc.js → utilities.js} +75 -11
  71. package/lib/wrap.js +1 -3
  72. package/package.json +2 -2
  73. package/lib/deps/items.js +0 -134
  74. package/lib/deps/manifest.js +0 -152
  75. package/lib/deps/merge.js +0 -120
  76. package/lib/deps/proposals.js +0 -164
  77. package/lib/deps/reports.js +0 -180
  78. package/lib/deps/rows.js +0 -176
  79. package/lib/deps/scan.js +0 -138
  80. package/lib/deps/usage.js +0 -68
  81. package/lib/deps/versions.js +0 -119
  82. package/lib/diff.js +0 -33
  83. package/lib/git/changes.js +0 -164
  84. package/lib/git/read.js +0 -253
  85. package/lib/git/run.js +0 -100
  86. package/lib/npm/commands.js +0 -100
  87. package/lib/remote/changes.js +0 -37
  88. package/lib/remote/headers.js +0 -36
  89. package/lib/remote/http.js +0 -122
  90. package/lib/remote/notes.js +0 -94
  91. package/lib/review/format.js +0 -434
  92. package/lib/review/storage.js +0 -150
  93. package/lib/review/store.js +0 -178
  94. package/lib/review/templates.js +0 -49
  95. package/lib/session/origins.js +0 -7
  96. package/lib/sys.js +0 -84
@@ -1,56 +1,68 @@
1
1
  'use strict';
2
2
 
3
- const { isReadOnlyOrigin } = require('./origins.js');
3
+ const { stateView } = require('./accessors.js');
4
+
5
+ const { isReadOnlyOrigin } = require('../files.js');
6
+ const { isFixupCommit } = require('./actions.js');
4
7
  const { pickRepoWrite } = require('./ops.js');
5
8
 
6
9
  const COMMIT_STATUS = {
7
10
  commit: 'committed',
8
11
  amend: 'amended',
9
12
  fixup: 'fixup',
13
+ reword: 'reworded',
14
+ };
15
+
16
+ const isInPlaceCommit = (kind) => kind === 'amend' || kind === 'reword';
17
+
18
+ const fixupMessage = (entry) => {
19
+ const subject = `${entry && entry.subject ? entry.subject : ''}`.trim();
20
+ if (!subject) return 'fixup!';
21
+ return `fixup! ${subject}`;
10
22
  };
11
23
 
12
- const canCommit = (options) => {
13
- const caps = options.capabilities();
24
+ const canCommit = (ui) => {
25
+ const caps = ui.capabilities;
14
26
  if (!caps.branches) {
15
- options.setStatus('read only');
27
+ ui.status = 'read only';
16
28
  return false;
17
29
  }
18
- for (const item of options.getItems()) {
30
+ for (const item of ui.items) {
19
31
  if (isReadOnlyOrigin(item.origin)) {
20
- options.setStatus('read only');
32
+ ui.status = 'read only';
21
33
  return false;
22
34
  }
23
35
  }
24
36
  return true;
25
37
  };
26
38
 
27
- const hasStagedChanges = (options) => {
28
- const repo = options.repo();
29
- if (repo.hasStaged) return repo.hasStaged(options.top());
30
- for (const item of options.getItems()) {
39
+ const hasStagedChanges = (ui) => {
40
+ const repo = ui.repo;
41
+ if (repo.hasStaged) return repo.hasStaged(ui.top);
42
+ for (const item of ui.items) {
31
43
  if (item.origin === 'staged') return true;
32
44
  }
33
45
  return false;
34
46
  };
35
47
 
36
48
  const selectedCommit = (api) => {
37
- const cursor = api.options.nav.commitCursor;
49
+ const cursor = api.ui.nav.commitCursor;
38
50
  return api.state.commits[cursor] ?? null;
39
51
  };
40
52
 
41
53
  const clampCommitCursor = (api) => {
42
54
  const last = api.state.commits.length - 1;
43
- const nav = api.options.nav;
55
+ const nav = api.ui.nav;
44
56
  if (nav.commitCursor > last) nav.commitCursor = Math.max(0, last);
45
57
  };
46
58
 
47
59
  const refreshCommitList = (api) => {
48
- const repo = api.options.repo();
60
+ const repo = api.ui.repo;
49
61
  if (!repo.listCommits) return;
50
62
  try {
51
- api.state.commits = repo.listCommits(api.options.top());
63
+ api.state.commits = repo.listCommits(api.ui.top);
52
64
  } catch (error) {
53
- api.options.setStatus(error.message);
65
+ api.ui.status = error.message;
54
66
  return;
55
67
  }
56
68
  clampCommitCursor(api);
@@ -58,185 +70,264 @@ const refreshCommitList = (api) => {
58
70
 
59
71
  const refreshAfterWrite = (api, extra = {}) => {
60
72
  refreshCommitList(api);
61
- api.options.refreshFromRepo({ keepEmpty: true, ...extra });
73
+ api.ui.refreshFromRepo({ keepEmpty: true, ...extra });
62
74
  };
63
75
 
64
76
  const showCommits = (api) => {
65
- if (api.options.getMode() === 'compose') return;
66
- if (!canCommit(api.options)) return;
67
- const repo = api.options.repo();
77
+ if (api.ui.mode === 'compose') return;
78
+ if (!canCommit(api.ui)) return;
79
+ const repo = api.ui.repo;
68
80
  if (!repo.listCommits) {
69
- api.options.setStatus('read only');
81
+ api.ui.status = 'read only';
70
82
  return;
71
83
  }
72
84
  try {
73
- api.state.commits = repo.listCommits(api.options.top());
85
+ api.state.commits = repo.listCommits(api.ui.top);
74
86
  } catch (error) {
75
- api.options.setStatus(error.message);
87
+ api.ui.status = error.message;
76
88
  return;
77
89
  }
78
- api.options.nav.commitCursor = 0;
79
- api.options.nav.pane = 'commits';
80
- api.options.setStatus('');
81
- api.options.nav.clearSelection();
90
+ api.ui.nav.commitCursor = 0;
91
+ api.ui.nav.pane = 'commits';
92
+ api.ui.status = '';
93
+ api.ui.nav.clearSelection();
82
94
  };
83
95
 
84
96
  const onCommitMove = (api, delta) => {
85
97
  const commits = api.state.commits;
86
98
  if (!commits.length) {
87
- api.options.setStatus('nothing to review');
99
+ api.ui.status = 'nothing to review';
88
100
  return;
89
101
  }
90
102
  const next = Math.min(
91
103
  commits.length - 1,
92
- Math.max(0, api.options.nav.commitCursor + delta),
104
+ Math.max(0, api.ui.nav.commitCursor + delta),
93
105
  );
94
- if (next === api.options.nav.commitCursor) return;
95
- api.options.nav.commitCursor = next;
96
- api.options.setStatus('');
106
+ if (next === api.ui.nav.commitCursor) return;
107
+ api.ui.nav.commitCursor = next;
108
+ api.ui.status = '';
97
109
  };
98
110
 
99
111
  const startGitCommit = (api, kind) => {
100
- const options = api.options;
101
- if (kind !== 'amend' && !hasStagedChanges(options)) {
102
- options.setMode('review');
103
- options.composer.state.commitKind = null;
104
- options.setStatus('nothing to commit');
112
+ const ui = api.ui;
113
+ if (!isInPlaceCommit(kind) && !hasStagedChanges(ui)) {
114
+ ui.mode = 'review';
115
+ ui.composer.state.commitKind = null;
116
+ ui.status = 'nothing to commit';
105
117
  return;
106
118
  }
107
- options.composer.state.commitKind = kind;
108
119
  let text = '';
109
120
  if (kind === 'fixup') {
110
- const entry = selectedCommit(api);
111
- text = entry ? entry.shortSha || entry.sha : 'HEAD';
121
+ text = fixupMessage(selectedCommit(api));
122
+ }
123
+ if (kind === 'amend') {
124
+ const headAt = api.state.commits.findIndex((entry) => entry.head);
125
+ api.ui.nav.commitCursor = headAt < 0 ? 0 : headAt;
126
+ if (ui.repo.lastMessage) {
127
+ text = ui.repo.lastMessage(ui.top) ?? '';
128
+ }
112
129
  }
113
- if (kind === 'amend' && options.repo().lastMessage) {
114
- text = options.repo().lastMessage(options.top()) ?? '';
130
+ if (kind === 'reword') {
131
+ const entry = selectedCommit(api);
132
+ if (!entry || !entry.sha) return;
133
+ if (typeof ui.repo.commitMessage === 'function') {
134
+ text = ui.repo.commitMessage(ui.top, entry.sha) ?? '';
135
+ } else {
136
+ text = `${entry.subject ?? ''}`;
137
+ }
115
138
  }
116
- options.composer.openCompose('commit', text);
139
+ ui.composer.state.commitKind = kind;
140
+ ui.composer.openCompose('commit', text);
117
141
  };
118
142
 
119
143
  const onCommit = (api) => {
120
- if (api.options.nav.pane !== 'commits') return void showCommits(api);
121
- if (!canCommit(api.options)) return;
144
+ if (api.ui.nav.pane !== 'commits') return void showCommits(api);
145
+ if (!canCommit(api.ui)) return;
122
146
  startGitCommit(api, 'commit');
123
147
  };
124
148
 
125
149
  const onAmend = (api) => {
126
- if (api.options.nav.pane !== 'commits') return;
127
- if (!canCommit(api.options)) return;
150
+ if (api.ui.nav.pane !== 'commits') return;
151
+ if (!canCommit(api.ui)) return;
152
+ startGitCommit(api, 'amend');
153
+ };
154
+
155
+ const onReword = (api) => {
156
+ if (api.ui.nav.pane !== 'commits') return;
157
+ if (!canCommit(api.ui)) return;
158
+ startGitCommit(api, 'reword');
159
+ };
160
+
161
+ const applyFixupNow = (api) => {
162
+ const entry = selectedCommit(api);
163
+ if (!entry || !entry.sha) return;
164
+ const done = 'applied';
165
+ const repo = api.ui.repo;
166
+ if (!repo.applyFixup) {
167
+ api.ui.status = 'read only';
168
+ return;
169
+ }
170
+ const picked = pickRepoWrite(repo, 'applyFixup', api.ui.uiOpen);
171
+ if (picked.async) {
172
+ const run = (top) => picked.write(top, entry.sha);
173
+ const after = () => refreshAfterWrite(api, { doneStatus: done });
174
+ return void api.ui.ops.runBusy('applying', done, run, after);
175
+ }
176
+ const ok = api.ui.ops.runRepo(done, (top) => {
177
+ repo.applyFixup(top, entry.sha);
178
+ });
179
+ if (!ok) return;
180
+ refreshAfterWrite(api);
181
+ if (!api.ui.status) api.ui.status = done;
182
+ };
183
+
184
+ const onApply = (api) => {
185
+ if (api.ui.nav.pane !== 'commits') return;
186
+ if (!canCommit(api.ui)) return;
187
+ const entry = selectedCommit(api);
188
+ if (isFixupCommit(entry)) return void applyFixupNow(api);
128
189
  startGitCommit(api, 'amend');
129
190
  };
130
191
 
131
192
  const onFixup = (api) => {
132
- if (api.options.nav.pane !== 'commits') return;
133
- if (!canCommit(api.options)) return;
193
+ if (api.ui.nav.pane !== 'commits') return;
194
+ if (!canCommit(api.ui)) return;
134
195
  startGitCommit(api, 'fixup');
135
196
  };
136
197
 
137
198
  const cancelCommit = (api) => {
138
- api.options.setMode('review');
139
- api.options.composer.state.commitKind = null;
140
- api.options.setStatus('');
199
+ api.ui.mode = 'review';
200
+ api.ui.composer.state.commitKind = null;
201
+ api.ui.status = '';
141
202
  };
142
203
 
143
204
  const finishAsyncCommit = (api, kind, message, done, write) => {
144
- const options = api.options;
145
- options.composer.closeCompose();
205
+ const ui = api.ui;
206
+ ui.composer.closeCompose();
146
207
  const run = (top) => write(top, kind, message);
147
208
  const after = () => refreshAfterWrite(api, { doneStatus: done });
148
- return void options.runner.runBusy('committing', done, run, after);
209
+ return void ui.ops.runBusy('committing', done, run, after);
149
210
  };
150
211
 
151
212
  const finishSyncCommit = (api, kind, message, done) => {
152
- const options = api.options;
213
+ const ui = api.ui;
153
214
  try {
154
- options.repo().commit(options.top(), kind, message);
215
+ ui.repo.commit(ui.top, kind, message);
155
216
  } catch (error) {
156
- options.setStatus(error.message);
217
+ ui.status = error.message;
157
218
  return;
158
219
  }
159
- options.composer.closeCompose();
160
- options.collection.clearDismissed();
220
+ ui.composer.closeCompose();
221
+ ui.collection.clearDismissed();
161
222
  refreshAfterWrite(api);
162
- if (!options.isDone() && !options.pendingExtras()) {
163
- options.setStatus(done);
223
+ if (!ui.done && !ui.pendingExtras) {
224
+ ui.status = done;
164
225
  }
165
226
  };
166
227
 
228
+ const finishReword = (api, message) => {
229
+ const ui = api.ui;
230
+ const entry = selectedCommit(api);
231
+ if (!entry || !entry.sha) {
232
+ ui.status = 'read only';
233
+ return;
234
+ }
235
+ const done = COMMIT_STATUS.reword;
236
+ const repo = ui.repo;
237
+ if (!repo.reword) {
238
+ ui.status = 'read only';
239
+ return;
240
+ }
241
+ const picked = pickRepoWrite(repo, 'reword', ui.uiOpen);
242
+ if (picked.async) {
243
+ ui.composer.closeCompose();
244
+ const run = (top) => picked.write(top, entry.sha, message);
245
+ const after = () => refreshAfterWrite(api, { doneStatus: done });
246
+ return void ui.ops.runBusy('rewording', done, run, after);
247
+ }
248
+ try {
249
+ repo.reword(ui.top, entry.sha, message);
250
+ } catch (error) {
251
+ ui.status = error.message;
252
+ return;
253
+ }
254
+ ui.composer.closeCompose();
255
+ refreshAfterWrite(api);
256
+ if (!ui.status) ui.status = done;
257
+ };
258
+
167
259
  const finishGitCommit = (api) => {
168
- const options = api.options;
169
- const kind = options.composer.state.commitKind;
170
- const editor = options.composer.state.editor;
260
+ const ui = api.ui;
261
+ const kind = ui.composer.state.commitKind;
262
+ const editor = ui.composer.state.editor;
171
263
  const message = editor ? editor.text : '';
172
- if (kind !== 'fixup' && !message.trim()) {
173
- options.setStatus('empty commit message');
264
+ if (!message.trim()) {
265
+ ui.status = 'empty commit message';
174
266
  return;
175
267
  }
176
- if (kind !== 'amend' && !hasStagedChanges(options)) {
177
- options.composer.closeCompose();
178
- options.setStatus('nothing to commit');
268
+ if (!isInPlaceCommit(kind) && !hasStagedChanges(ui)) {
269
+ ui.composer.closeCompose();
270
+ ui.status = 'nothing to commit';
179
271
  return;
180
272
  }
273
+ if (kind === 'reword') return void finishReword(api, message);
181
274
  const done = COMMIT_STATUS[kind] ?? 'committed';
182
- const uiOpen = options.uiOpen();
183
- const picked = pickRepoWrite(options.repo(), 'commit', uiOpen);
275
+ const uiOpen = ui.uiOpen;
276
+ const picked = pickRepoWrite(ui.repo, 'commit', uiOpen);
184
277
  if (picked.async) {
185
- finishAsyncCommit(api, kind, message, done, picked.write);
186
- return;
278
+ return void finishAsyncCommit(api, kind, message, done, picked.write);
187
279
  }
188
280
  finishSyncCommit(api, kind, message, done);
189
281
  };
190
282
 
191
283
  const onDropCommit = (api) => {
192
- if (api.options.nav.pane !== 'commits') return;
193
- if (!canCommit(api.options)) return;
284
+ if (api.ui.nav.pane !== 'commits') return;
285
+ if (!canCommit(api.ui)) return;
194
286
  const entry = selectedCommit(api);
195
287
  if (!entry) return;
196
288
  api.state.dropName = entry.shortSha || entry.sha;
197
289
  api.state.dropSha = entry.sha;
198
- api.options.setMode('confirmDrop');
199
- api.options.setStatus('');
290
+ api.ui.mode = 'confirmDrop';
291
+ api.ui.status = '';
200
292
  };
201
293
 
202
294
  const cancelDropCommit = (api) => {
203
- api.options.setMode('review');
295
+ api.ui.mode = 'review';
204
296
  api.state.dropName = '';
205
297
  api.state.dropSha = '';
206
- api.options.setStatus('');
298
+ api.ui.status = '';
207
299
  };
208
300
 
209
301
  const dropAsync = (api, sha, done, write) => {
210
302
  const run = (top) => write(top, sha);
211
303
  const after = () => refreshAfterWrite(api, { doneStatus: done });
212
- return void api.options.runner.runBusy('dropping', done, run, after);
304
+ return void api.ui.ops.runBusy('dropping', done, run, after);
213
305
  };
214
306
 
215
307
  const confirmDropCommit = (api) => {
216
308
  const sha = api.state.dropSha;
217
309
  const name = api.state.dropName;
218
- api.options.setMode('review');
310
+ api.ui.mode = 'review';
219
311
  api.state.dropName = '';
220
312
  api.state.dropSha = '';
221
313
  if (!sha) return;
222
314
  const done = `dropped ${name}`;
223
- const repo = api.options.repo();
315
+ const repo = api.ui.repo;
224
316
  if (!repo.dropCommit) {
225
- api.options.setStatus('read only');
317
+ api.ui.status = 'read only';
226
318
  return;
227
319
  }
228
- const uiOpen = api.options.uiOpen();
320
+ const uiOpen = api.ui.uiOpen;
229
321
  const picked = pickRepoWrite(repo, 'dropCommit', uiOpen);
230
322
  if (picked.async) {
231
- dropAsync(api, sha, done, picked.write);
232
- return;
323
+ return void dropAsync(api, sha, done, picked.write);
233
324
  }
234
- const ok = api.options.runner.runRepo(done, (top) => {
325
+ const ok = api.ui.ops.runRepo(done, (top) => {
235
326
  repo.dropCommit(top, sha);
236
327
  });
237
328
  if (!ok) return;
238
329
  refreshAfterWrite(api);
239
- if (!api.options.getStatus()) api.options.setStatus(done);
330
+ if (!api.ui.status) api.ui.status = done;
240
331
  };
241
332
 
242
333
  const reset = (state) => {
@@ -245,39 +336,34 @@ const reset = (state) => {
245
336
  state.dropSha = '';
246
337
  };
247
338
 
248
- const createCommitController = (options) => {
339
+ const createCommitController = (ui) => {
249
340
  const state = {
250
341
  commits: [],
251
342
  dropName: '',
252
343
  dropSha: '',
253
344
  };
254
- const api = { options, state };
255
- return {
256
- get commits() {
257
- return state.commits;
345
+ const api = { ui, state };
346
+ return stateView(
347
+ state,
348
+ {
349
+ reset: () => reset(state),
350
+ canCommit: () => canCommit(ui),
351
+ onCommit: () => onCommit(api),
352
+ onAmend: () => onAmend(api),
353
+ onReword: () => onReword(api),
354
+ onApply: () => onApply(api),
355
+ onFixup: () => onFixup(api),
356
+ onCommitMove: (delta) => onCommitMove(api, delta),
357
+ cancelCommit: () => cancelCommit(api),
358
+ startGitCommit: (kind) => startGitCommit(api, kind),
359
+ finishGitCommit: () => finishGitCommit(api),
360
+ refresh: () => refreshCommitList(api),
361
+ onDropCommit: () => onDropCommit(api),
362
+ cancelDropCommit: () => cancelDropCommit(api),
363
+ confirmDropCommit: () => confirmDropCommit(api),
258
364
  },
259
- set commits(value) {
260
- state.commits = value;
261
- },
262
- get dropName() {
263
- return state.dropName;
264
- },
265
- set dropName(value) {
266
- state.dropName = value;
267
- },
268
- reset: () => reset(state),
269
- canCommit: () => canCommit(options),
270
- onCommit: () => onCommit(api),
271
- onAmend: () => onAmend(api),
272
- onFixup: () => onFixup(api),
273
- onCommitMove: (delta) => onCommitMove(api, delta),
274
- cancelCommit: () => cancelCommit(api),
275
- startGitCommit: (kind) => startGitCommit(api, kind),
276
- finishGitCommit: () => finishGitCommit(api),
277
- onDropCommit: () => onDropCommit(api),
278
- cancelDropCommit: () => cancelDropCommit(api),
279
- confirmDropCommit: () => confirmDropCommit(api),
280
- };
365
+ ['commits', 'dropName'],
366
+ );
281
367
  };
282
368
 
283
369
  module.exports = {