reslop 0.1.9 → 0.2.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.
Files changed (94) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/README.md +2 -1
  3. package/bin/reslop.js +2 -2
  4. package/lib/ansi.js +4 -2
  5. package/lib/api.js +2 -2
  6. package/lib/capabilities.js +7 -14
  7. package/lib/cli.js +28 -47
  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} +6 -5
  21. package/lib/{git/dependencies.js → git-deps.js} +111 -38
  22. package/lib/git.js +589 -68
  23. package/lib/github.js +28 -197
  24. package/lib/gitlab.js +24 -195
  25. package/lib/highlight/bash.js +1 -3
  26. package/lib/highlight/css.js +1 -3
  27. package/lib/highlight/js.js +11 -14
  28. package/lib/highlight.js +6 -9
  29. package/lib/keys.js +14 -8
  30. package/lib/manifest.js +330 -0
  31. package/lib/remote.js +374 -0
  32. package/lib/render/branches.js +14 -68
  33. package/lib/render/chrome.js +24 -9
  34. package/lib/render/commits.js +6 -59
  35. package/lib/render/diff.js +16 -16
  36. package/lib/render/files.js +6 -34
  37. package/lib/render/frame.js +23 -12
  38. package/lib/render/notes.js +4 -3
  39. package/lib/render/primitives.js +45 -0
  40. package/lib/{render.js → render/render.js} +5 -5
  41. package/lib/render/unit.js +41 -0
  42. package/lib/review.js +750 -14
  43. package/lib/select.js +5 -10
  44. package/lib/session/accessors.js +9 -1
  45. package/lib/session/actions.js +32 -16
  46. package/lib/session/branches.js +116 -128
  47. package/lib/session/change-actions.js +202 -123
  48. package/lib/session/commit.js +105 -112
  49. package/lib/session/compose.js +158 -13
  50. package/lib/session/dispatch.js +104 -60
  51. package/lib/session/files-pane.js +142 -148
  52. package/lib/session/items.js +22 -30
  53. package/lib/session/lifecycle.js +44 -44
  54. package/lib/session/load.js +19 -33
  55. package/lib/session/navigation.js +12 -1
  56. package/lib/session/ops.js +28 -41
  57. package/lib/session/pointer.js +54 -57
  58. package/lib/session/review.js +23 -37
  59. package/lib/session/terminal.js +6 -12
  60. package/lib/session/todos.js +8 -12
  61. package/lib/session/unit.js +208 -0
  62. package/lib/session/update.js +33 -46
  63. package/lib/session/view.js +78 -9
  64. package/lib/session/watch.js +11 -19
  65. package/lib/session.js +21 -246
  66. package/lib/source.js +3 -3
  67. package/lib/update.js +40 -75
  68. package/lib/{git/proc.js → utilities.js} +75 -11
  69. package/lib/wrap.js +1 -3
  70. package/package.json +1 -1
  71. package/lib/deps/items.js +0 -134
  72. package/lib/deps/manifest.js +0 -152
  73. package/lib/deps/merge.js +0 -120
  74. package/lib/deps/proposals.js +0 -164
  75. package/lib/deps/reports.js +0 -180
  76. package/lib/deps/rows.js +0 -176
  77. package/lib/deps/scan.js +0 -138
  78. package/lib/deps/usage.js +0 -68
  79. package/lib/deps/versions.js +0 -119
  80. package/lib/diff.js +0 -33
  81. package/lib/git/changes.js +0 -164
  82. package/lib/git/read.js +0 -253
  83. package/lib/git/run.js +0 -100
  84. package/lib/npm/commands.js +0 -100
  85. package/lib/remote/changes.js +0 -37
  86. package/lib/remote/headers.js +0 -36
  87. package/lib/remote/http.js +0 -122
  88. package/lib/remote/notes.js +0 -94
  89. package/lib/review/format.js +0 -434
  90. package/lib/review/storage.js +0 -150
  91. package/lib/review/store.js +0 -178
  92. package/lib/review/templates.js +0 -49
  93. package/lib/session/origins.js +0 -7
  94. package/lib/sys.js +0 -84
@@ -2,147 +2,147 @@
2
2
 
3
3
  const files = require('../files.js');
4
4
  const { isTodosEntry, TODO_FILE } = files;
5
+ const unit = require('./unit.js');
6
+ const { setFileScope, openUnitFile, moveUnitBlock } = unit;
5
7
 
6
8
  const LAYOUTS = ['unified', 'mixed', 'side'];
7
9
 
8
- const current = (options) =>
9
- options.nav.current(options.items(), options.todoItem());
10
+ const current = (ui) => ui.nav.current(ui.items, ui.todoItem);
10
11
 
11
- const clampIndex = (options) => options.nav.clampIndex(options.items().length);
12
+ const clampIndex = (ui) => ui.nav.clampIndex(ui.items.length);
12
13
 
13
- const syncReviewPath = (options) =>
14
- options.nav.syncReviewPath(current(options));
14
+ const syncReviewPath = (ui) => ui.nav.syncReviewPath(current(ui));
15
15
 
16
- const clampFileCursor = (options) =>
17
- options.nav.clampFileCursor(options.fileList().length);
16
+ const clampFileCursor = (ui) => ui.nav.clampFileCursor(ui.fileList().length);
18
17
 
19
- const followReviewPath = (options) =>
20
- options.nav.followReviewPath(options.fileList(), current(options));
18
+ const followReviewPath = (ui) =>
19
+ ui.nav.followReviewPath(ui.fileList(), current(ui));
21
20
 
22
- const syncFileCursor = (options) => {
23
- if (options.nav.pane === 'files') {
24
- clampFileCursor(options);
25
- return;
26
- }
27
- followReviewPath(options);
21
+ const syncFileCursor = (ui) => {
22
+ if (ui.nav.pane === 'files') return void clampFileCursor(ui);
23
+ followReviewPath(ui);
28
24
  };
29
25
 
30
- const showFiles = (options) => {
31
- options.nav.pane = 'files';
32
- options.nav.clearSelection();
33
- followReviewPath(options);
34
- options.nav.todoOpen = false;
26
+ const showFiles = (ui) => {
27
+ ui.nav.pane = 'files';
28
+ ui.nav.clearSelection();
29
+ followReviewPath(ui);
30
+ ui.nav.todoOpen = false;
35
31
  };
36
32
 
37
- const onEscape = (options) => {
38
- if (options.nav.pane === 'branches' || options.nav.pane === 'commits') {
39
- showFiles(options);
40
- return;
33
+ const onEscape = (ui) => {
34
+ if (ui.nav.pane === 'branches' || ui.nav.pane === 'commits') {
35
+ return void showFiles(ui);
41
36
  }
42
- if (options.nav.pane === 'files') {
43
- options.onQuit();
44
- return;
45
- }
46
- showFiles(options);
37
+ if (ui.nav.pane === 'files') return void ui.onQuit();
38
+ showFiles(ui);
47
39
  };
48
40
 
49
- const onFiles = (options) => {
50
- if (options.nav.pane === 'files') {
51
- if (!options.nav.reviewPath) return;
52
- options.nav.pane = 'diff';
53
- options.nav.clearSelection();
54
- if (options.nav.reviewPath === TODO_FILE) options.composer.openTodoPage();
41
+ const onFiles = (ui) => {
42
+ if (ui.nav.pane === 'files') {
43
+ if (!ui.nav.reviewPath) return;
44
+ if (ui.nav.fileScope === 'file') {
45
+ const list = ui.fileList();
46
+ const entry = list[ui.nav.fileCursor];
47
+ if (entry && !isTodosEntry(entry)) openUnitFile(ui, entry);
48
+ return;
49
+ }
50
+ ui.nav.pane = 'diff';
51
+ ui.nav.clearSelection();
52
+ if (ui.nav.reviewPath === TODO_FILE) ui.composer.openTodoPage();
55
53
  return;
56
54
  }
57
- showFiles(options);
55
+ showFiles(ui);
58
56
  };
59
57
 
60
- const onOpenFile = (options) => {
61
- if (options.nav.pane === 'branches') {
62
- return void options.onCheckoutBranch();
58
+ const onOpenFile = (ui) => {
59
+ if (ui.nav.pane === 'branches') {
60
+ return void ui.gitBranches.onCheckoutBranch();
63
61
  }
64
- if (options.nav.pane === 'commits') return;
65
- if (options.nav.pane !== 'files') {
66
- if (options.nav.todoOpen) options.composer.editFocusedTodo();
62
+ if (ui.nav.pane === 'commits') return;
63
+ if (ui.nav.pane !== 'files') {
64
+ if (ui.nav.todoOpen) ui.composer.editFocusedTodo();
67
65
  return;
68
66
  }
69
- const list = options.fileList();
70
- const entry = list[options.nav.fileCursor];
67
+ const list = ui.fileList();
68
+ const entry = list[ui.nav.fileCursor];
71
69
  if (!entry) return;
72
70
  if (isTodosEntry(entry)) {
73
- options.composer.openTodoPage();
74
- options.setStatus('');
71
+ ui.composer.openTodoPage();
72
+ ui.status = '';
75
73
  return;
76
74
  }
77
- options.nav.todoOpen = false;
78
- options.nav.index = entry.openIndex ?? entry.firstIndex;
79
- options.nav.pane = 'diff';
80
- options.nav.scroll = 0;
81
- options.setStatus('');
82
- options.nav.clearSelection();
83
- syncReviewPath(options);
75
+ ui.nav.todoOpen = false;
76
+ ui.nav.index = entry.openIndex ?? entry.firstIndex;
77
+ if (ui.nav.fileScope === 'file') {
78
+ return void openUnitFile(ui, entry);
79
+ }
80
+ ui.nav.pane = 'diff';
81
+ ui.nav.scroll = 0;
82
+ ui.status = '';
83
+ ui.nav.clearSelection();
84
+ syncReviewPath(ui);
84
85
  };
85
86
 
86
- const onFileMove = (options, delta) => {
87
- const list = options.fileList();
87
+ const onFileMove = (ui, delta) => {
88
+ const list = ui.fileList();
88
89
  if (!list.length) {
89
- options.setStatus('nothing to review');
90
+ ui.status = 'nothing to review';
90
91
  return;
91
92
  }
92
93
  const last = list.length - 1;
93
- const moved = options.nav.fileCursor + delta;
94
+ const moved = ui.nav.fileCursor + delta;
94
95
  const next = Math.min(last, Math.max(0, moved));
95
- if (next === options.nav.fileCursor) return;
96
- options.nav.fileCursor = next;
96
+ if (next === ui.nav.fileCursor) return;
97
+ ui.nav.fileCursor = next;
97
98
  const entry = list[next];
98
99
  if (isTodosEntry(entry)) {
99
- options.nav.reviewPath = TODO_FILE;
100
+ ui.nav.reviewPath = TODO_FILE;
100
101
  } else {
101
- options.nav.reviewPath = entry.path;
102
- options.nav.index = entry.firstIndex;
102
+ ui.nav.reviewPath = entry.path;
103
+ ui.nav.index = entry.firstIndex;
103
104
  }
104
- options.setStatus('');
105
+ ui.status = '';
105
106
  };
106
107
 
107
- const moveRemaining = (options, step) => {
108
- const list = options.items();
108
+ const moveRemaining = (ui, step) => {
109
+ const list = ui.items;
109
110
  if (!list.length) {
110
- options.setStatus('nothing to review');
111
+ ui.status = 'nothing to review';
111
112
  return;
112
113
  }
113
- const next = options.nav.index + step;
114
+ const next = ui.nav.index + step;
114
115
  if (next < 0 || next >= list.length) return;
115
- options.nav.index = next;
116
- options.nav.scroll = 0;
117
- options.setStatus('');
118
- options.nav.clearSelection();
119
- syncReviewPath(options);
116
+ ui.nav.index = next;
117
+ ui.nav.scroll = 0;
118
+ ui.status = '';
119
+ ui.nav.clearSelection();
120
+ syncReviewPath(ui);
120
121
  };
121
122
 
122
- const onNext = (options) => {
123
- if (options.nav.todoOpen) {
124
- options.composer.closeTodoPage();
125
- if (options.items().length) options.nav.index = 0;
126
- options.nav.scroll = 0;
127
- syncReviewPath(options);
128
- syncFileCursor(options);
123
+ const onNext = (ui) => {
124
+ if (ui.nav.pane === 'unit') return void moveUnitBlock(ui, 1);
125
+ if (ui.nav.todoOpen) {
126
+ ui.composer.closeTodoPage();
127
+ if (ui.items.length) ui.nav.index = 0;
128
+ ui.nav.scroll = 0;
129
+ syncReviewPath(ui);
130
+ syncFileCursor(ui);
129
131
  return;
130
132
  }
131
- moveRemaining(options, 1);
133
+ moveRemaining(ui, 1);
132
134
  };
133
135
 
134
- const onPrev = (options) => {
135
- if (options.nav.todoOpen) return;
136
- if (options.nav.index <= 0) {
137
- options.composer.openTodoPage();
138
- return;
139
- }
140
- moveRemaining(options, -1);
136
+ const onPrev = (ui) => {
137
+ if (ui.nav.pane === 'unit') return void moveUnitBlock(ui, -1);
138
+ if (ui.nav.todoOpen) return;
139
+ if (ui.nav.index <= 0) return void ui.composer.openTodoPage();
140
+ moveRemaining(ui, -1);
141
141
  };
142
142
 
143
- const scrollStep = (options, fraction) => {
144
- const frame = options.lastFrame();
145
- const size = options.getSize();
143
+ const scrollStep = (ui, fraction) => {
144
+ const frame = ui.lastFrame;
145
+ const size = ui.lastSize ?? ui.getSize();
146
146
  const rows = size.height ?? 24;
147
147
  const fallback = Math.max(1, rows - 4);
148
148
  const bodyH = frame?.bodyH ?? fallback;
@@ -153,91 +153,85 @@ const scrollStep = (options, fraction) => {
153
153
  return n;
154
154
  };
155
155
 
156
- const onScroll = (options, delta) => {
157
- if (options.nav.pane === 'files') return void onFileMove(options, delta);
158
- if (options.nav.pane === 'branches') {
159
- return void options.onBranchMove(delta);
156
+ const onScroll = (ui, delta) => {
157
+ if (ui.nav.pane === 'files') return void onFileMove(ui, delta);
158
+ if (ui.nav.pane === 'branches') {
159
+ return void ui.gitBranches.onBranchMove(delta);
160
160
  }
161
- if (options.nav.pane === 'commits') {
162
- return void options.onCommitMove(delta);
161
+ if (ui.nav.pane === 'commits') {
162
+ return void ui.commits.onCommitMove(delta);
163
163
  }
164
- if (options.nav.todoOpen && options.composer.todoRowCount() > 1) {
165
- return void options.composer.moveTodoFocus(delta);
164
+ if (ui.nav.todoOpen && ui.composer.todoRowCount() > 1) {
165
+ return void ui.composer.moveTodoFocus(delta);
166
166
  }
167
- const frame = options.lastFrame();
167
+ const frame = ui.lastFrame;
168
168
  const max = frame ? frame.scrollMax : null;
169
- const scroll = options.nav.scroll;
169
+ const scroll = ui.nav.scroll;
170
170
  const current = max === null ? scroll : Math.min(scroll, max);
171
171
  const moved = current + delta;
172
172
  const next = max === null ? moved : Math.min(max, moved);
173
- options.nav.scroll = Math.max(0, next);
174
- options.nav.clearSelection();
173
+ ui.nav.scroll = Math.max(0, next);
174
+ ui.nav.clearSelection();
175
175
  };
176
176
 
177
- const onJump = (options, toEnd) => {
178
- if (options.nav.pane === 'files') {
179
- const list = options.fileList();
177
+ const onJump = (ui, toEnd) => {
178
+ if (ui.nav.pane === 'files') {
179
+ const list = ui.fileList();
180
180
  if (!list.length) {
181
- options.setStatus('nothing to review');
181
+ ui.status = 'nothing to review';
182
182
  return;
183
183
  }
184
184
  const target = toEnd ? list.length - 1 : 0;
185
- onFileMove(options, target - options.nav.fileCursor);
186
- return;
185
+ return void onFileMove(ui, target - ui.nav.fileCursor);
187
186
  }
188
- if (options.nav.pane === 'branches') {
187
+ if (ui.nav.pane === 'branches') {
189
188
  const delta = toEnd ? Number.MAX_SAFE_INTEGER : -Number.MAX_SAFE_INTEGER;
190
- options.onBranchMove(delta);
191
- return;
189
+ return void ui.gitBranches.onBranchMove(delta);
192
190
  }
193
- if (options.nav.pane === 'commits') {
191
+ if (ui.nav.pane === 'commits') {
194
192
  const delta = toEnd ? Number.MAX_SAFE_INTEGER : -Number.MAX_SAFE_INTEGER;
195
- options.onCommitMove(delta);
196
- return;
193
+ return void ui.commits.onCommitMove(delta);
197
194
  }
198
- if (options.nav.todoOpen && options.composer.todoRowCount() > 0) {
199
- const last = options.composer.todoRowCount() - 1;
195
+ if (ui.nav.todoOpen && ui.composer.todoRowCount() > 0) {
196
+ const last = ui.composer.todoRowCount() - 1;
200
197
  const target = toEnd ? last : 0;
201
- const at = options.composer.clampedTodoFocus();
202
- options.composer.moveTodoFocus(target - at);
203
- return;
198
+ const at = ui.composer.clampedTodoFocus();
199
+ return void ui.composer.moveTodoFocus(target - at);
204
200
  }
205
- const frame = options.lastFrame();
201
+ const frame = ui.lastFrame;
206
202
  const max = frame ? frame.scrollMax : 0;
207
- options.nav.scroll = toEnd ? max : 0;
208
- options.nav.clearSelection();
203
+ ui.nav.scroll = toEnd ? max : 0;
204
+ ui.nav.clearSelection();
209
205
  };
210
206
 
211
- const onLayout = (options) => {
212
- const i = LAYOUTS.indexOf(options.layout());
207
+ const onLayout = (ui) => {
208
+ const i = LAYOUTS.indexOf(ui.layout);
213
209
  const next = (i < 0 ? 0 : i + 1) % LAYOUTS.length;
214
- options.setLayout(LAYOUTS[next]);
210
+ ui.layout = LAYOUTS[next];
215
211
  const name = LAYOUTS[next] === 'side' ? 'side-by-side' : LAYOUTS[next];
216
- options.setStatus(name);
217
- options.nav.scroll = 0;
218
- options.nav.clearSelection();
212
+ ui.status = name;
213
+ ui.nav.scroll = 0;
214
+ ui.nav.clearSelection();
219
215
  };
220
216
 
221
- const createFilesPane = (options) => ({
222
- current: () => current(options),
223
- clampIndex: () => clampIndex(options),
224
- syncReviewPath: () => syncReviewPath(options),
225
- clampFileCursor: () => clampFileCursor(options),
226
- followReviewPath: () => followReviewPath(options),
227
- syncFileCursor: () => syncFileCursor(options),
228
- showFiles: () => showFiles(options),
229
- onEscape: () => onEscape(options),
230
- onFiles: () => onFiles(options),
231
- onOpenFile: () => onOpenFile(options),
232
- onFileMove: (delta) => onFileMove(options, delta),
233
- moveRemaining: (step) => moveRemaining(options, step),
234
- onNext: () => onNext(options),
235
- onPrev: () => onPrev(options),
236
- scrollStep: (fraction) => scrollStep(options, fraction),
237
- onScroll: (delta) => onScroll(options, delta),
238
- onHome: () => onJump(options, false),
239
- onEnd: () => onJump(options, true),
240
- onLayout: () => onLayout(options),
217
+ const createFilesPane = (ui) => ({
218
+ clampIndex: () => clampIndex(ui),
219
+ syncReviewPath: () => syncReviewPath(ui),
220
+ syncFileCursor: () => syncFileCursor(ui),
221
+ showFiles: () => showFiles(ui),
222
+ onEscape: () => onEscape(ui),
223
+ onFiles: () => onFiles(ui),
224
+ onOpenFile: () => onOpenFile(ui),
225
+ onFileMove: (delta) => onFileMove(ui, delta),
226
+ onNext: () => onNext(ui),
227
+ onPrev: () => onPrev(ui),
228
+ scrollStep: (fraction) => scrollStep(ui, fraction),
229
+ onScroll: (delta) => onScroll(ui, delta),
230
+ onHome: () => onJump(ui, false),
231
+ onEnd: () => onJump(ui, true),
232
+ onLayout: () => onLayout(ui),
233
+ onFileScope: () => setFileScope(ui, 'file'),
234
+ onDiffScope: () => setFileScope(ui, 'diff'),
241
235
  });
242
236
 
243
237
  module.exports = { LAYOUTS, createFilesPane };
@@ -1,9 +1,11 @@
1
1
  'use strict';
2
2
 
3
+ const { stateView } = require('./accessors.js');
4
+
3
5
  const files = require('../files.js');
4
6
  const { itemPath, TODO_FILE, isTodoItem } = files;
5
7
  const { feedbackKey } = require('../review.js');
6
- const { refreshIndexPatches } = require('../diff.js');
8
+ const { refreshIndexPatches } = require('../diff/diff.js');
7
9
 
8
10
  const depChangeKey = (item) => {
9
11
  const change = item.dep && item.dep.change;
@@ -74,12 +76,10 @@ const takeBucket = (map, key, used) => {
74
76
  return null;
75
77
  };
76
78
 
77
- const pathOf = (item) => itemPath(item) || '';
78
-
79
79
  const groupByPath = (items) => {
80
80
  const groups = new Map();
81
81
  for (const item of items) {
82
- const rel = pathOf(item);
82
+ const rel = itemPath(item) || '';
83
83
  const list = groups.get(rel) ?? [];
84
84
  list.push(item);
85
85
  groups.set(rel, list);
@@ -225,18 +225,15 @@ const createItemCollection = () => {
225
225
  state.dismissed.clear();
226
226
  };
227
227
 
228
- const findIndexByKey = (key) => {
229
- if (!key) return -1;
230
- return state.items.findIndex((item) => itemKey(item) === key);
231
- };
232
-
233
228
  const findRestoredIndex = (item) => restoredIndex(state.items, item);
234
229
 
235
230
  const keepOrigin = (item, origin) => {
236
231
  const next = { ...item, origin };
237
- const replaced = [];
238
- for (const entry of state.items) {
239
- replaced.push(sameBlock(entry, item) ? next : entry);
232
+ const { length } = state.items;
233
+ const replaced = new Array(length);
234
+ for (let i = 0; i < length; i++) {
235
+ const entry = state.items[i];
236
+ replaced[i] = sameBlock(entry, item) ? next : entry;
240
237
  }
241
238
  if (next.dep) {
242
239
  state.items = replaced;
@@ -245,25 +242,20 @@ const createItemCollection = () => {
245
242
  state.items = refreshIndexPatches(replaced, next);
246
243
  };
247
244
 
248
- return {
249
- get items() {
250
- return state.items;
245
+ return stateView(
246
+ state,
247
+ {
248
+ reset,
249
+ replace,
250
+ dismiss,
251
+ clearDismissed,
252
+ isDismissed,
253
+ findRestoredIndex,
254
+ keepOrigin,
251
255
  },
252
- set items(value) {
253
- state.items = value;
254
- },
255
- get dismissed() {
256
- return state.dismissed;
257
- },
258
- reset,
259
- replace,
260
- dismiss,
261
- clearDismissed,
262
- isDismissed,
263
- findIndexByKey,
264
- findRestoredIndex,
265
- keepOrigin,
266
- };
256
+ ['items'],
257
+ ['dismissed'],
258
+ );
267
259
  };
268
260
 
269
261
  module.exports = {