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.
- package/CHANGELOG.md +12 -1
- package/LICENSE +1 -1
- package/README.md +11 -11
- package/bin/reslop.js +3 -8
- package/lib/ansi.js +32 -19
- package/lib/capabilities.js +8 -8
- package/lib/cli.js +40 -74
- package/lib/clipboard.js +39 -47
- package/lib/dashboard/git.js +128 -0
- package/lib/dashboard/model.js +332 -0
- package/lib/dashboard/npm.js +193 -0
- package/lib/dashboard/tree.js +278 -0
- package/lib/deps-diff.js +197 -402
- package/lib/deps.js +113 -240
- package/lib/detect.js +8 -18
- package/lib/diff/align.js +41 -113
- package/lib/diff/diff.js +12 -43
- package/lib/diff/display.js +25 -36
- package/lib/diff/edit.js +6 -20
- package/lib/diff/inline.js +76 -144
- package/lib/diff/items.js +33 -47
- package/lib/diff/parse.js +28 -56
- package/lib/diff/patch.js +12 -33
- package/lib/diff/unit.js +79 -178
- package/lib/editor.js +52 -69
- package/lib/files.js +71 -89
- package/lib/git-branches.js +254 -416
- package/lib/git-deps.js +147 -272
- package/lib/git-reword-todo.js +5 -13
- package/lib/git-worktree.js +141 -212
- package/lib/git.js +21 -49
- package/lib/github.js +67 -181
- package/lib/gitlab.js +40 -90
- package/lib/highlight/bash.js +55 -177
- package/lib/highlight/core.js +155 -141
- package/lib/highlight/css.js +25 -58
- package/lib/highlight/csv.js +19 -37
- package/lib/highlight/dart.js +72 -213
- package/lib/highlight/dot.js +14 -14
- package/lib/highlight/html.js +42 -63
- package/lib/highlight/js.js +177 -506
- package/lib/highlight/json.js +17 -37
- package/lib/highlight/log.js +19 -25
- package/lib/highlight/txt.js +7 -10
- package/lib/highlight.js +17 -46
- package/lib/keys.js +49 -82
- package/lib/manifest.js +67 -126
- package/lib/npm-commands.js +120 -169
- package/lib/remote.js +140 -190
- package/lib/render/branches.js +63 -61
- package/lib/render/chrome.js +220 -333
- package/lib/render/commits.js +160 -361
- package/lib/render/dash-activity.js +212 -0
- package/lib/render/dash-blocks.js +245 -0
- package/lib/render/dash-table.js +290 -0
- package/lib/render/dashboard.js +62 -0
- package/lib/render/diff.js +123 -258
- package/lib/render/files.js +40 -85
- package/lib/render/frame.js +78 -117
- package/lib/render/notes.js +139 -210
- package/lib/render/npm.js +213 -177
- package/lib/render/present.js +4 -11
- package/lib/render/primitives.js +72 -174
- package/lib/render/render.js +5 -14
- package/lib/render/report.js +43 -0
- package/lib/render/tiles.js +260 -0
- package/lib/render/unit.js +15 -31
- package/lib/report-model.js +110 -33
- package/lib/report-parse.js +56 -37
- package/lib/report-render.js +102 -59
- package/lib/report-run.js +87 -77
- package/lib/report-test.js +221 -99
- package/lib/report-tools.js +96 -154
- package/lib/review-merge.js +33 -41
- package/lib/review.js +68 -129
- package/lib/runs.js +225 -0
- package/lib/select.js +53 -91
- package/lib/session/accessors.js +14 -21
- package/lib/session/actions.js +61 -43
- package/lib/session/branches.js +166 -222
- package/lib/session/change-actions.js +49 -119
- package/lib/session/commit.js +274 -355
- package/lib/session/compose.js +556 -714
- package/lib/session/dashboard.js +385 -0
- package/lib/session/dispatch.js +162 -170
- package/lib/session/files-pane.js +76 -44
- package/lib/session/items.js +6 -17
- package/lib/session/lifecycle.js +393 -466
- package/lib/session/load.js +1 -14
- package/lib/session/navigation.js +2 -2
- package/lib/session/npm.js +403 -420
- package/lib/session/ops.js +5 -13
- package/lib/session/pointer.js +16 -14
- package/lib/session/progress.js +39 -30
- package/lib/session/review.js +1 -3
- package/lib/session/tasks.js +276 -235
- package/lib/session/templates.js +51 -59
- package/lib/session/terminal.js +85 -132
- package/lib/session/view.js +47 -81
- package/lib/session/watch.js +196 -212
- package/lib/session.js +81 -161
- package/lib/update.js +44 -111
- package/lib/utilities.js +46 -88
- package/lib/wrap.js +29 -55
- package/package.json +1 -1
package/lib/render/commits.js
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
const ansi = require('../ansi.js');
|
|
4
4
|
const files = require('../files.js');
|
|
5
|
-
const primitives = require('./primitives.js');
|
|
6
5
|
const wrap = require('../wrap.js');
|
|
6
|
+
const primitives = require('./primitives.js');
|
|
7
|
+
|
|
7
8
|
const { shortAge } = files;
|
|
8
9
|
const { wrapMultiline, wrapDoc, cursorInWrap } = wrap;
|
|
10
|
+
const { THEME, paint, visibleWidth, trimVisible, seq, EL } = ansi;
|
|
11
|
+
const { MARK_W, markPrefix, padVisible } = primitives;
|
|
12
|
+
const { editSpan, fieldWindow, paintBodyFill, listLine } = primitives;
|
|
13
|
+
const { paintListWindow, paintCursorList } = primitives;
|
|
9
14
|
const { measureColumns, padColumns, joinColumns, paintColumns } = primitives;
|
|
10
|
-
const { padVisible } = primitives;
|
|
11
|
-
|
|
12
|
-
const { THEME, paint, visibleWidth, truncateVisible, seq, EL } = ansi;
|
|
13
|
-
const { FILE_MARK, paintCursorList, fieldWindow } = primitives;
|
|
14
|
-
const { paintBodyFill, listLine, paintListWindow } = primitives;
|
|
15
15
|
|
|
16
16
|
const COMMIT_COL_ALIGN = {
|
|
17
17
|
sha: 'end',
|
|
@@ -21,6 +21,8 @@ const COMMIT_COL_ALIGN = {
|
|
|
21
21
|
date: 'start',
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
+
const COUNT_ALIGN = { add: 'start', del: 'start' };
|
|
25
|
+
|
|
24
26
|
const COMMIT_COL_FG = {
|
|
25
27
|
sha: THEME.shaFg,
|
|
26
28
|
refs: THEME.warnFg,
|
|
@@ -30,6 +32,7 @@ const COMMIT_COL_FG = {
|
|
|
30
32
|
};
|
|
31
33
|
|
|
32
34
|
const COMMIT_CURRENT_COL_FG = {
|
|
35
|
+
...COMMIT_COL_FG,
|
|
33
36
|
sha: THEME.shaDarkFg,
|
|
34
37
|
refs: THEME.shaDarkFg,
|
|
35
38
|
date: THEME.headerFg,
|
|
@@ -37,63 +40,45 @@ const COMMIT_CURRENT_COL_FG = {
|
|
|
37
40
|
|
|
38
41
|
const REFS_MAX = 28;
|
|
39
42
|
const COMMIT_TAIL = 1;
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
};
|
|
43
|
+
const DIFF_LABEL = 'Diffs:';
|
|
44
|
+
const DIFF_GAP = ' ';
|
|
45
|
+
const COL_GAP = 2;
|
|
46
|
+
const MIN_LEFT = 8;
|
|
47
|
+
const FULL_INDENT = ' '.repeat(MARK_W);
|
|
48
|
+
const NO_COUNT = { add: '', del: '' };
|
|
46
49
|
|
|
47
50
|
const countLabel = (entry) => {
|
|
48
|
-
if (entry
|
|
51
|
+
if (entry.pending) {
|
|
49
52
|
const stagedAdd = entry.stagedAdded ?? 0;
|
|
50
53
|
const unstagedAdd = entry.unstagedAdded ?? 0;
|
|
51
54
|
const stagedDel = entry.stagedRemoved ?? 0;
|
|
52
55
|
const unstagedDel = entry.unstagedRemoved ?? 0;
|
|
53
56
|
const any = stagedAdd || unstagedAdd || stagedDel || unstagedDel;
|
|
54
|
-
if (!any) return
|
|
57
|
+
if (!any) return NO_COUNT;
|
|
55
58
|
return {
|
|
56
59
|
add: `+${stagedAdd}/${unstagedAdd}`,
|
|
57
60
|
del: `-${stagedDel}/${unstagedDel}`,
|
|
58
61
|
};
|
|
59
62
|
}
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
const added = entry.added ?? 0;
|
|
64
|
+
const removed = entry.removed ?? 0;
|
|
65
|
+
if (!added && !removed) return NO_COUNT;
|
|
66
|
+
return { add: `+${added}`, del: `-${removed}` };
|
|
63
67
|
};
|
|
64
68
|
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const entryCountWidth = (entry) => {
|
|
69
|
-
const label = countLabel(entry);
|
|
70
|
-
if (!label) return { add: 0, del: 0 };
|
|
71
|
-
return {
|
|
72
|
-
add: visibleWidth(label.add),
|
|
73
|
-
del: visibleWidth(label.del),
|
|
74
|
-
};
|
|
75
|
-
};
|
|
69
|
+
const countWidths = (entries) =>
|
|
70
|
+
measureColumns(entries, countLabel, COUNT_ALIGN);
|
|
76
71
|
|
|
77
|
-
const
|
|
78
|
-
let add = 0;
|
|
79
|
-
let del = 0;
|
|
80
|
-
for (const entry of commits) {
|
|
81
|
-
const width = entryCountWidth(entry);
|
|
82
|
-
add = Math.max(add, width.add);
|
|
83
|
-
del = Math.max(del, width.del);
|
|
84
|
-
}
|
|
85
|
-
return { add, del };
|
|
86
|
-
};
|
|
72
|
+
const refsText = (entry) => `${entry.refs ?? ''}`.trim();
|
|
87
73
|
|
|
88
74
|
const commitRowFields = (entry) => {
|
|
89
|
-
const
|
|
90
|
-
const label = countLabel(entry);
|
|
75
|
+
const { add, del } = countLabel(entry);
|
|
91
76
|
return {
|
|
92
77
|
sha: entry.shortSha ?? '',
|
|
93
|
-
refs:
|
|
94
|
-
add
|
|
95
|
-
del
|
|
96
|
-
date: shortAge(entry.date
|
|
78
|
+
refs: trimVisible(refsText(entry), REFS_MAX),
|
|
79
|
+
add,
|
|
80
|
+
del,
|
|
81
|
+
date: shortAge(entry.date),
|
|
97
82
|
};
|
|
98
83
|
};
|
|
99
84
|
|
|
@@ -115,18 +100,11 @@ const commitText = (entry) => {
|
|
|
115
100
|
const commitLine = (entry) => splitMessage(commitText(entry)).line;
|
|
116
101
|
|
|
117
102
|
const viewedCommit = (entry, rev, revShort) => {
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
const sha = entry && entry.sha ? `${entry.sha}` : '';
|
|
122
|
-
const brief = entry && entry.shortSha ? `${entry.shortSha}` : '';
|
|
123
|
-
if (want && (sha === want || brief === want)) return true;
|
|
124
|
-
if (short && (sha === short || brief === short)) return true;
|
|
125
|
-
return false;
|
|
103
|
+
if (entry.pending) return !rev && !revShort;
|
|
104
|
+
const ids = [rev, revShort].filter(Boolean);
|
|
105
|
+
return ids.some((id) => entry.sha === id || entry.shortSha === id);
|
|
126
106
|
};
|
|
127
107
|
|
|
128
|
-
const FULL_INDENT = ' '.repeat(visibleWidth(` ${FILE_MARK} `));
|
|
129
|
-
|
|
130
108
|
const commitTone = (selected, current) => {
|
|
131
109
|
if (current) {
|
|
132
110
|
return {
|
|
@@ -153,111 +131,79 @@ const commitTone = (selected, current) => {
|
|
|
153
131
|
};
|
|
154
132
|
|
|
155
133
|
const paintCommitRow = (entry, width, color, selected, cols, current) => {
|
|
156
|
-
const
|
|
157
|
-
const mark = selected ? FILE_MARK : ' ';
|
|
158
|
-
const prefix = ` ${mark} `;
|
|
134
|
+
const prefix = markPrefix(selected);
|
|
159
135
|
const parts = padColumns(commitRowFields(entry), cols, COMMIT_COL_ALIGN);
|
|
160
136
|
const suffix = joinColumns(parts, cols);
|
|
161
|
-
const rest = Math.max(1,
|
|
162
|
-
const subject =
|
|
163
|
-
const
|
|
164
|
-
const after = `${' '.repeat(pad)}`;
|
|
165
|
-
const left = `${prefix}${subject}${after}`;
|
|
137
|
+
const rest = Math.max(1, contentWidth(width) - visibleWidth(prefix + suffix));
|
|
138
|
+
const subject = trimVisible(commitLine(entry), rest);
|
|
139
|
+
const after = ' '.repeat(Math.max(0, rest - visibleWidth(subject)));
|
|
166
140
|
const tail = ' '.repeat(COMMIT_TAIL);
|
|
141
|
+
if (!color) return `${prefix}${subject}${after}${suffix}${tail}`;
|
|
167
142
|
const tone = commitTone(selected, current);
|
|
168
|
-
const tones = current
|
|
169
|
-
? { ...COMMIT_COL_FG, ...COMMIT_CURRENT_COL_FG }
|
|
170
|
-
: COMMIT_COL_FG;
|
|
171
|
-
if (!color) return `${left}${suffix}${tail}`;
|
|
143
|
+
const tones = current ? COMMIT_CURRENT_COL_FG : COMMIT_COL_FG;
|
|
172
144
|
let out = `${seq(tone.fgRgb, tone.bgRgb)}${EL}`;
|
|
173
145
|
out += paint(prefix, tone.fgRgb, tone.bgRgb, color);
|
|
174
146
|
out += paint(subject, tone.nameFg, tone.bgRgb, color);
|
|
175
147
|
out += paint(after, tone.fgRgb, tone.bgRgb, color);
|
|
176
148
|
out += paintColumns(parts, cols, tone.fgRgb, tone.bgRgb, color, tones);
|
|
177
|
-
out
|
|
178
|
-
return out;
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
const lineEdit = (text, textX, span, scroll) => ({
|
|
182
|
-
x0: 1,
|
|
183
|
-
textX,
|
|
184
|
-
x1: textX + span,
|
|
185
|
-
start: 0,
|
|
186
|
-
text,
|
|
187
|
-
limit: text.length,
|
|
188
|
-
scroll,
|
|
189
|
-
live: true,
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
const subjectWindow = (compose, rest) => {
|
|
193
|
-
const line = splitMessage(compose.text).line;
|
|
194
|
-
const at = Math.min(compose.cursor ?? 0, line.length);
|
|
195
|
-
return fieldWindow(line, at, compose.scrollCol, rest);
|
|
149
|
+
return out + paint(tail, tone.fgRgb, tone.bgRgb, color);
|
|
196
150
|
};
|
|
197
151
|
|
|
198
|
-
const paintCommitEdit = (compose, width, color, entry
|
|
152
|
+
const paintCommitEdit = (compose, width, color, entry, cols, index) => {
|
|
199
153
|
const text = splitMessage(compose.text).line;
|
|
200
|
-
const prefix =
|
|
201
|
-
const prefixW = visibleWidth(prefix);
|
|
154
|
+
const prefix = markPrefix(true);
|
|
202
155
|
const fields = commitRowFields(entry ?? {});
|
|
203
156
|
const suffix = joinColumns(padColumns(fields, cols, COMMIT_COL_ALIGN), cols);
|
|
204
157
|
const rest = Math.max(1, contentWidth(width) - visibleWidth(prefix + suffix));
|
|
205
|
-
const win =
|
|
158
|
+
const win = fieldWindow(text, compose.cursor, compose.scrollCol, rest);
|
|
206
159
|
const shown = { ...entry, body: win.text, subject: win.text };
|
|
160
|
+
const textX = MARK_W + 1;
|
|
161
|
+
const edit = editSpan(1, textX, textX + rest, 0, text);
|
|
207
162
|
return {
|
|
208
163
|
row: paintCommitRow(shown, width, color, true, cols),
|
|
209
|
-
cursor: { x:
|
|
164
|
+
cursor: { x: textX + win.col, scroll: win.scroll },
|
|
210
165
|
at: entry ? 'replace' : 'start',
|
|
211
|
-
|
|
166
|
+
index,
|
|
167
|
+
edits: [{ ...edit, scroll: win.scroll }],
|
|
212
168
|
};
|
|
213
169
|
};
|
|
214
170
|
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
171
|
+
const editTarget = (compose, commits, cursor) => {
|
|
172
|
+
if (compose?.kind !== 'commit') return null;
|
|
173
|
+
const kind = compose.commitKind;
|
|
174
|
+
if (kind === 'amend' || kind === 'reword') return cursor;
|
|
175
|
+
if (kind === 'commit' || kind === 'fixup') {
|
|
176
|
+
return commits.findIndex((entry) => entry.pending);
|
|
218
177
|
}
|
|
219
178
|
return -1;
|
|
220
179
|
};
|
|
221
180
|
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const commitEditRow = (compose, commits, cursor, width, color, cols) => {
|
|
229
|
-
if (!compose || compose.kind !== 'commit') return null;
|
|
230
|
-
let at = -1;
|
|
231
|
-
if (editsCursor(compose)) at = cursor;
|
|
232
|
-
if (editsPending(compose)) at = pendingAt(commits);
|
|
233
|
-
if (at >= 0) {
|
|
234
|
-
const entry = commits[at];
|
|
235
|
-
if (!entry) return null;
|
|
236
|
-
const painted = paintCommitEdit(compose, width, color, entry, cols);
|
|
237
|
-
painted.index = at;
|
|
238
|
-
return painted;
|
|
239
|
-
}
|
|
240
|
-
return paintCommitEdit(compose, width, color);
|
|
181
|
+
const commitEditRow = (compose, commits, target, width, color, cols) => {
|
|
182
|
+
if (target === null) return null;
|
|
183
|
+
if (target < 0) return paintCommitEdit(compose, width, color, null, {}, -1);
|
|
184
|
+
const entry = commits[target];
|
|
185
|
+
if (!entry) return null;
|
|
186
|
+
return paintCommitEdit(compose, width, color, entry, cols, target);
|
|
241
187
|
};
|
|
242
188
|
|
|
243
|
-
const
|
|
244
|
-
const tone = commitTone(selected, current);
|
|
245
|
-
const inner = contentWidth(width);
|
|
189
|
+
const fitPieces = (pieces, width) => {
|
|
246
190
|
const shown = [];
|
|
247
191
|
let used = 0;
|
|
248
192
|
for (const piece of pieces) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
const text = truncateVisible(piece.text, room);
|
|
193
|
+
if (used >= width) break;
|
|
194
|
+
const text = trimVisible(piece.text, width - used);
|
|
252
195
|
if (!text) continue;
|
|
253
196
|
shown.push({ text, fg: piece.fg });
|
|
254
197
|
used += visibleWidth(text);
|
|
255
198
|
}
|
|
256
|
-
|
|
199
|
+
return { shown, pad: ' '.repeat(Math.max(0, width - used)) };
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const paintPieces = (pieces, width, color, tone) => {
|
|
203
|
+
const { shown, pad } = fitPieces(pieces, contentWidth(width));
|
|
257
204
|
const tail = ' '.repeat(COMMIT_TAIL);
|
|
258
205
|
if (!color) {
|
|
259
|
-
|
|
260
|
-
for (const piece of shown) plain += piece.text;
|
|
206
|
+
const plain = shown.map((piece) => piece.text).join('');
|
|
261
207
|
return `${plain}${pad}${tail}`;
|
|
262
208
|
}
|
|
263
209
|
let out = `${seq(tone.fgRgb, tone.bgRgb)}${EL}`;
|
|
@@ -265,41 +211,25 @@ const paintPieces = (pieces, width, color, selected, current) => {
|
|
|
265
211
|
out += paint(piece.text, piece.fg, tone.bgRgb, color);
|
|
266
212
|
}
|
|
267
213
|
out += paint(pad, tone.fgRgb, tone.bgRgb, color);
|
|
268
|
-
out
|
|
269
|
-
return out;
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
const COL_GAP = 2;
|
|
273
|
-
const MIN_LEFT = 8;
|
|
274
|
-
|
|
275
|
-
const messageText = (entry) => {
|
|
276
|
-
const body = `${entry.body ?? ''}`.replace(/\s+$/, '');
|
|
277
|
-
if (body) return body;
|
|
278
|
-
return `${entry.subject ?? ''}`.replace(/\s+$/, '');
|
|
214
|
+
return out + paint(tail, tone.fgRgb, tone.bgRgb, color);
|
|
279
215
|
};
|
|
280
216
|
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
const lines = [];
|
|
284
|
-
for (const row of rows) lines.push({ text: row, kind });
|
|
285
|
-
return lines;
|
|
286
|
-
};
|
|
217
|
+
const messageText = (entry) =>
|
|
218
|
+
`${entry.body ?? ''}`.trimEnd() || `${entry.subject ?? ''}`.trimEnd();
|
|
287
219
|
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const
|
|
295
|
-
|
|
220
|
+
const messageParts = (text, width) => {
|
|
221
|
+
if (!text) return [{ text: '', kind: 'text' }];
|
|
222
|
+
const parts = [];
|
|
223
|
+
const lines = text.split('\n');
|
|
224
|
+
for (let i = 0; i < lines.length; i++) {
|
|
225
|
+
const kind = i ? 'body' : 'text';
|
|
226
|
+
for (const row of wrapMultiline(lines[i], Math.max(1, width))) {
|
|
227
|
+
parts.push({ text: row, kind });
|
|
228
|
+
}
|
|
296
229
|
}
|
|
297
|
-
return
|
|
230
|
+
return parts;
|
|
298
231
|
};
|
|
299
232
|
|
|
300
|
-
const messageParts = (entry, width) =>
|
|
301
|
-
messagePartsFrom(messageText(entry), width);
|
|
302
|
-
|
|
303
233
|
const authorParts = (entry) => {
|
|
304
234
|
const name = `${entry.author ?? ''}`.trim();
|
|
305
235
|
const email = `${entry.email ?? ''}`.trim();
|
|
@@ -314,24 +244,23 @@ const authorParts = (entry) => {
|
|
|
314
244
|
return [];
|
|
315
245
|
};
|
|
316
246
|
|
|
317
|
-
const partsWidth = (parts) =>
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
247
|
+
const partsWidth = (parts) =>
|
|
248
|
+
parts.reduce((width, part) => width + visibleWidth(part.text), 0);
|
|
249
|
+
|
|
250
|
+
const timeFields = (entry) => ({
|
|
251
|
+
when: `${entry.when ?? ''}`.trim(),
|
|
252
|
+
ago: shortAge(entry.date),
|
|
253
|
+
});
|
|
322
254
|
|
|
323
255
|
const timeNeed = (entry) => {
|
|
324
|
-
const when
|
|
325
|
-
const ago = shortAge(entry.date ?? '');
|
|
256
|
+
const { when, ago } = timeFields(entry);
|
|
326
257
|
if (when && ago) return visibleWidth(when) + COL_GAP + visibleWidth(ago);
|
|
327
258
|
return visibleWidth(when || ago);
|
|
328
259
|
};
|
|
329
260
|
|
|
330
|
-
const refsText = (entry) => `${entry.refs ?? ''}`.trim();
|
|
331
|
-
|
|
332
261
|
const diffLine = (entry, widths) => {
|
|
333
262
|
const label = countLabel(entry);
|
|
334
|
-
if (!label) return null;
|
|
263
|
+
if (!label.add) return null;
|
|
335
264
|
const add = padVisible(label.add, widths.add, 'start');
|
|
336
265
|
const del = padVisible(label.del, widths.del, 'start');
|
|
337
266
|
const text = `${DIFF_LABEL}${DIFF_GAP}${add}${DIFF_GAP}${del}`;
|
|
@@ -352,7 +281,7 @@ const diffCountParts = (entry, widths, rightW) => {
|
|
|
352
281
|
];
|
|
353
282
|
};
|
|
354
283
|
|
|
355
|
-
const rightNeed = (entry, widths
|
|
284
|
+
const rightNeed = (entry, widths) => {
|
|
356
285
|
const line = diffLine(entry, widths);
|
|
357
286
|
return Math.max(
|
|
358
287
|
partsWidth(authorParts(entry)),
|
|
@@ -365,11 +294,11 @@ const rightNeed = (entry, widths = entryCountWidth(entry)) => {
|
|
|
365
294
|
const spreadTime = (when, ago, width) => {
|
|
366
295
|
const agoW = visibleWidth(ago);
|
|
367
296
|
if (agoW >= width) {
|
|
368
|
-
return [{ text:
|
|
297
|
+
return [{ text: trimVisible(ago, width), kind: 'meta' }];
|
|
369
298
|
}
|
|
370
299
|
const room = width - agoW;
|
|
371
300
|
const minGap = Math.min(COL_GAP, room);
|
|
372
|
-
const left =
|
|
301
|
+
const left = trimVisible(when, room - minGap);
|
|
373
302
|
const gap = width - visibleWidth(left) - agoW;
|
|
374
303
|
return [
|
|
375
304
|
{ text: left, kind: 'meta' },
|
|
@@ -379,7 +308,7 @@ const spreadTime = (when, ago, width) => {
|
|
|
379
308
|
};
|
|
380
309
|
|
|
381
310
|
const alignEnd = (text, width, kind) => {
|
|
382
|
-
const shown =
|
|
311
|
+
const shown = trimVisible(text, width);
|
|
383
312
|
const pad = Math.max(0, width - visibleWidth(shown));
|
|
384
313
|
return [
|
|
385
314
|
{ text: ' '.repeat(pad), kind },
|
|
@@ -388,12 +317,11 @@ const alignEnd = (text, width, kind) => {
|
|
|
388
317
|
};
|
|
389
318
|
|
|
390
319
|
const timeParts = (entry, width) => {
|
|
391
|
-
const when = `${entry.when ?? ''}`.trim();
|
|
392
|
-
const ago = shortAge(entry.date ?? '');
|
|
393
320
|
if (!width) return [];
|
|
321
|
+
const { when, ago } = timeFields(entry);
|
|
394
322
|
if (when && ago) return spreadTime(when, ago, width);
|
|
395
323
|
if (ago) return alignEnd(ago, width, 'meta');
|
|
396
|
-
if (when) return [{ text:
|
|
324
|
+
if (when) return [{ text: trimVisible(when, width), kind: 'meta' }];
|
|
397
325
|
return [];
|
|
398
326
|
};
|
|
399
327
|
|
|
@@ -411,23 +339,17 @@ const rightRows = (entry, rightW, widths) => {
|
|
|
411
339
|
};
|
|
412
340
|
|
|
413
341
|
const panelWidths = (need, leftNeed, inner) => {
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
const room = Math.max(1, inner - indent);
|
|
417
|
-
if (!rightW || room <= MIN_LEFT) {
|
|
418
|
-
return { rightW: 0, leftW: room, gapW: 0 };
|
|
419
|
-
}
|
|
342
|
+
const room = Math.max(1, inner - MARK_W);
|
|
343
|
+
if (!need || room <= MIN_LEFT) return { rightW: 0, leftW: room, gapW: 0 };
|
|
420
344
|
const reserve = Math.max(MIN_LEFT, leftNeed);
|
|
421
345
|
const minLeft = Math.min(reserve, room - COL_GAP - 1);
|
|
422
346
|
const maxRight = room - COL_GAP - minLeft;
|
|
423
|
-
|
|
347
|
+
const rightW = need > maxRight ? Math.max(1, maxRight) : need;
|
|
424
348
|
return { rightW, leftW: room - COL_GAP - rightW, gapW: COL_GAP };
|
|
425
349
|
};
|
|
426
350
|
|
|
427
351
|
const panelLeft = (entry, leftW, edit) => {
|
|
428
|
-
const parts = edit
|
|
429
|
-
? messagePartsFrom(edit.text ?? '', leftW)
|
|
430
|
-
: messageParts(entry, leftW);
|
|
352
|
+
const parts = messageParts(edit ? edit.text : messageText(entry), leftW);
|
|
431
353
|
if (!entry.sha) return parts;
|
|
432
354
|
return [{ text: entry.sha, kind: 'hash' }, ...parts];
|
|
433
355
|
};
|
|
@@ -435,25 +357,12 @@ const panelLeft = (entry, leftW, edit) => {
|
|
|
435
357
|
const commitMessageWidth = (entry, screenWidth) => {
|
|
436
358
|
const width = Math.max(20, screenWidth || 80);
|
|
437
359
|
const inner = contentWidth(width);
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
const
|
|
441
|
-
const cols = panelWidths(rightNeed(entry), hashW, inner);
|
|
360
|
+
if (!entry) return Math.max(1, inner - MARK_W);
|
|
361
|
+
const need = rightNeed(entry, countWidths([entry]));
|
|
362
|
+
const cols = panelWidths(need, visibleWidth(entry.sha ?? ''), inner);
|
|
442
363
|
return Math.max(1, cols.leftW);
|
|
443
364
|
};
|
|
444
365
|
|
|
445
|
-
const prefixWidth = (first, selected) => {
|
|
446
|
-
const mark = first && selected ? FILE_MARK : ' ';
|
|
447
|
-
const prefix = first ? ` ${mark} ` : FULL_INDENT;
|
|
448
|
-
return visibleWidth(prefix);
|
|
449
|
-
};
|
|
450
|
-
|
|
451
|
-
const messageCaret = (text, cursor, width, row, first, selected) => {
|
|
452
|
-
const pos = cursorInWrap(`${text ?? ''}`, cursor ?? 0, width);
|
|
453
|
-
if (pos.row !== row) return null;
|
|
454
|
-
return { x: prefixWidth(first, selected) + pos.col + 1 };
|
|
455
|
-
};
|
|
456
|
-
|
|
457
366
|
const pieceFg = (kind, tone) => {
|
|
458
367
|
if (kind === 'add') return THEME.addLineFg;
|
|
459
368
|
if (kind === 'del') return THEME.delLineFg;
|
|
@@ -472,190 +381,85 @@ const pieceFg = (kind, tone) => {
|
|
|
472
381
|
return tone.fgRgb;
|
|
473
382
|
};
|
|
474
383
|
|
|
475
|
-
const clipParts = (parts, width,
|
|
476
|
-
const
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
if (!text) continue;
|
|
483
|
-
const kind = part.kind ?? fallbackKind;
|
|
484
|
-
shown.push({ text, fg: pieceFg(kind, tone) });
|
|
485
|
-
used += visibleWidth(text);
|
|
486
|
-
}
|
|
487
|
-
const pad = Math.max(0, width - used);
|
|
488
|
-
if (pad) shown.push({ text: ' '.repeat(pad), fg: tone.fgRgb });
|
|
384
|
+
const clipParts = (parts, width, tone) => {
|
|
385
|
+
const pieces = parts.map((part) => ({
|
|
386
|
+
text: part.text,
|
|
387
|
+
fg: pieceFg(part.kind, tone),
|
|
388
|
+
}));
|
|
389
|
+
const { shown, pad } = fitPieces(pieces, width);
|
|
390
|
+
if (pad) shown.push({ text: pad, fg: tone.fgRgb });
|
|
489
391
|
return shown;
|
|
490
392
|
};
|
|
491
393
|
|
|
492
|
-
const paintPanelLine = (spec) => {
|
|
394
|
+
const paintPanelLine = (left, right, cols, first, spec) => {
|
|
493
395
|
const tone = commitTone(spec.selected, spec.current);
|
|
494
|
-
const
|
|
495
|
-
const
|
|
496
|
-
const
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
{ text: prefix, fg: tone.fgRgb },
|
|
505
|
-
{ text: left, fg: pieceFg(spec.leftKind, tone) },
|
|
506
|
-
{ text: `${' '.repeat(leftPad)}${gap}`, fg: tone.fgRgb },
|
|
507
|
-
...clipParts(right, spec.rightW, spec.rightKind, tone),
|
|
508
|
-
],
|
|
509
|
-
spec.width,
|
|
510
|
-
spec.color,
|
|
511
|
-
spec.selected,
|
|
512
|
-
spec.current,
|
|
513
|
-
);
|
|
514
|
-
};
|
|
515
|
-
|
|
516
|
-
const editFromDoc = (doc, row, textX, span) => {
|
|
517
|
-
if (row < 0 || row >= doc.length) return null;
|
|
518
|
-
const piece = doc[row];
|
|
519
|
-
return {
|
|
520
|
-
x0: 1,
|
|
521
|
-
textX,
|
|
522
|
-
x1: textX + span,
|
|
523
|
-
start: piece.start,
|
|
524
|
-
text: piece.text,
|
|
525
|
-
limit: piece.start + piece.text.length,
|
|
526
|
-
live: true,
|
|
527
|
-
};
|
|
396
|
+
const prefix = first ? markPrefix(spec.selected) : FULL_INDENT;
|
|
397
|
+
const text = trimVisible(left.text, cols.leftW);
|
|
398
|
+
const gapW = Math.max(0, cols.leftW - visibleWidth(text)) + cols.gapW;
|
|
399
|
+
const pieces = [
|
|
400
|
+
{ text: prefix, fg: tone.fgRgb },
|
|
401
|
+
{ text, fg: pieceFg(left.kind, tone) },
|
|
402
|
+
{ text: ' '.repeat(gapW), fg: tone.fgRgb },
|
|
403
|
+
...clipParts(right, cols.rightW, tone),
|
|
404
|
+
];
|
|
405
|
+
return paintPieces(pieces, spec.width, spec.color, tone);
|
|
528
406
|
};
|
|
529
407
|
|
|
530
|
-
const editLines = (editHit) => (editHit ? [editHit] : undefined);
|
|
531
|
-
|
|
532
408
|
const commitLines = (entry, index, spec) => {
|
|
533
|
-
const { width,
|
|
534
|
-
const
|
|
409
|
+
const { width, edit, diffWidths } = spec;
|
|
410
|
+
const need = rightNeed(entry, diffWidths);
|
|
535
411
|
const hashW = visibleWidth(entry.sha ?? '');
|
|
536
|
-
const
|
|
537
|
-
const
|
|
538
|
-
const rights = rightRows(entry, cols.rightW, widths);
|
|
412
|
+
const cols = panelWidths(need, hashW, contentWidth(width));
|
|
413
|
+
const rights = rightRows(entry, cols.rightW, diffWidths);
|
|
539
414
|
const left = panelLeft(entry, cols.leftW, edit);
|
|
415
|
+
const doc = edit ? wrapDoc(edit.text, cols.leftW) : [];
|
|
416
|
+
const caret = edit ? cursorInWrap(edit.text, edit.cursor, cols.leftW) : null;
|
|
417
|
+
const skip = entry.sha ? 1 : 0;
|
|
418
|
+
const textX = MARK_W + 1;
|
|
419
|
+
const x1 = textX + cols.leftW;
|
|
540
420
|
const count = Math.max(left.length, rights.length);
|
|
541
|
-
const doc = edit ? wrapDoc(`${edit.text ?? ''}`, cols.leftW) : null;
|
|
542
421
|
const lines = [];
|
|
543
422
|
for (let row = 0; row < count; row++) {
|
|
544
423
|
const line = left[row] ?? { text: '', kind: 'text' };
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
first: row === 0,
|
|
556
|
-
leftKind: line.kind,
|
|
557
|
-
rightKind: 'meta',
|
|
558
|
-
});
|
|
559
|
-
const messageRow = entry.sha ? row - 1 : row;
|
|
560
|
-
const first = !entry.sha && row === 0;
|
|
561
|
-
const caret = edit
|
|
562
|
-
? messageCaret(
|
|
563
|
-
edit.text,
|
|
564
|
-
edit.cursor,
|
|
565
|
-
cols.leftW,
|
|
566
|
-
messageRow,
|
|
567
|
-
first,
|
|
568
|
-
selected,
|
|
569
|
-
)
|
|
570
|
-
: null;
|
|
571
|
-
const textX = prefixWidth(first, selected) + 1;
|
|
572
|
-
const editHit = doc
|
|
573
|
-
? editFromDoc(doc, messageRow, textX, cols.leftW)
|
|
574
|
-
: null;
|
|
575
|
-
lines.push(listLine(painted, index, caret, editLines(editHit)));
|
|
424
|
+
const right = rights[row] ?? [];
|
|
425
|
+
const painted = paintPanelLine(line, right, cols, row === 0, spec);
|
|
426
|
+
const messageRow = row - skip;
|
|
427
|
+
const atCaret = caret && caret.row === messageRow;
|
|
428
|
+
const cursor = atCaret ? { x: textX + caret.col } : null;
|
|
429
|
+
const piece = doc[messageRow];
|
|
430
|
+
const edits = piece
|
|
431
|
+
? [editSpan(1, textX, x1, piece.start, piece.text)]
|
|
432
|
+
: [];
|
|
433
|
+
lines.push(listLine(painted, index, cursor, edits));
|
|
576
434
|
}
|
|
577
435
|
return lines;
|
|
578
436
|
};
|
|
579
437
|
|
|
580
|
-
const
|
|
581
|
-
const inner = contentWidth(width);
|
|
582
|
-
const leftW = Math.max(1, inner - visibleWidth(FULL_INDENT));
|
|
583
|
-
const parts = messagePartsFrom(text, leftW);
|
|
584
|
-
const doc = wrapDoc(`${text ?? ''}`, leftW);
|
|
585
|
-
const lines = [];
|
|
586
|
-
for (let row = 0; row < parts.length; row++) {
|
|
587
|
-
const line = parts[row];
|
|
588
|
-
const painted = paintPanelLine({
|
|
589
|
-
left: line.text,
|
|
590
|
-
right: '',
|
|
591
|
-
leftW,
|
|
592
|
-
rightW: 0,
|
|
593
|
-
gapW: 0,
|
|
594
|
-
width,
|
|
595
|
-
color,
|
|
596
|
-
selected: true,
|
|
597
|
-
first: row === 0,
|
|
598
|
-
leftKind: line.kind,
|
|
599
|
-
rightKind: 'meta',
|
|
600
|
-
});
|
|
601
|
-
const caret = messageCaret(text, cursor, leftW, row, row === 0, true);
|
|
602
|
-
const textX = prefixWidth(row === 0, true) + 1;
|
|
603
|
-
const editHit = editFromDoc(doc, row, textX, leftW);
|
|
604
|
-
lines.push(listLine(painted, -1, caret, editLines(editHit)));
|
|
605
|
-
}
|
|
606
|
-
return lines;
|
|
607
|
-
};
|
|
608
|
-
|
|
609
|
-
const composeEditsIndex = (compose, commits, index, cursor) => {
|
|
610
|
-
if (!compose || compose.kind !== 'commit') return false;
|
|
611
|
-
if (editsCursor(compose)) return index === cursor;
|
|
612
|
-
if (!editsPending(compose)) return false;
|
|
613
|
-
const at = pendingAt(commits);
|
|
614
|
-
return at >= 0 && index === at;
|
|
615
|
-
};
|
|
616
|
-
|
|
617
|
-
const composeShowsDraft = (compose, commits) => {
|
|
618
|
-
if (!compose || compose.kind !== 'commit') return false;
|
|
619
|
-
if (editsCursor(compose)) return false;
|
|
620
|
-
if (editsPending(compose)) return pendingAt(commits) < 0;
|
|
621
|
-
return true;
|
|
622
|
-
};
|
|
623
|
-
|
|
624
|
-
const flattenCommits = (view, commits, width, color) => {
|
|
438
|
+
const flattenCommits = (view, commits, target, width, color) => {
|
|
625
439
|
const cursor = view.commitCursor ?? 0;
|
|
626
|
-
const compose = view
|
|
627
|
-
const
|
|
628
|
-
const
|
|
629
|
-
const gap = listLine(paintBodyFill(width, color, 'files'), -1);
|
|
440
|
+
const { compose } = view;
|
|
441
|
+
const edit = { text: compose?.text ?? '', cursor: compose?.cursor ?? 0 };
|
|
442
|
+
const spec = { width, color, diffWidths: countWidths(commits) };
|
|
630
443
|
const lines = [];
|
|
631
|
-
if (
|
|
632
|
-
|
|
633
|
-
lines.push(...draftLines(text, compose.cursor ?? 0, width, color));
|
|
444
|
+
if (target === -1) {
|
|
445
|
+
lines.push(...commitLines({}, -1, { ...spec, selected: true, edit }));
|
|
634
446
|
}
|
|
447
|
+
const gap = listLine(paintBodyFill(width, color, 'files'), -1);
|
|
635
448
|
for (let index = 0; index < commits.length; index++) {
|
|
636
449
|
if (lines.length) lines.push(gap);
|
|
637
450
|
const entry = commits[index];
|
|
638
|
-
const inplace =
|
|
639
|
-
const selected = inplace || (
|
|
640
|
-
const text = compose?.text ?? '';
|
|
641
|
-
const caret = compose?.cursor ?? 0;
|
|
642
|
-
const edit = inplace ? { text, cursor: caret } : null;
|
|
451
|
+
const inplace = index === target;
|
|
452
|
+
const selected = inplace || (target === null && index === cursor);
|
|
643
453
|
const current = viewedCommit(entry, view.rev, view.revShort);
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
selected,
|
|
648
|
-
edit,
|
|
649
|
-
current,
|
|
650
|
-
diffWidths,
|
|
651
|
-
});
|
|
652
|
-
lines.push(...block);
|
|
454
|
+
const entryEdit = inplace ? edit : null;
|
|
455
|
+
const entrySpec = { ...spec, selected, current, edit: entryEdit };
|
|
456
|
+
lines.push(...commitLines(entry, index, entrySpec));
|
|
653
457
|
}
|
|
654
458
|
return lines;
|
|
655
459
|
};
|
|
656
460
|
|
|
657
|
-
const focusLine = (lines, cursor,
|
|
658
|
-
if (
|
|
461
|
+
const focusLine = (lines, cursor, editing) => {
|
|
462
|
+
if (editing) {
|
|
659
463
|
const at = lines.findIndex((line) => line.cursor);
|
|
660
464
|
if (at >= 0) return at;
|
|
661
465
|
}
|
|
@@ -667,25 +471,20 @@ const paintBodyCommits = (view, width, color, bodyH, headerLines) => {
|
|
|
667
471
|
const pane = { width, color, bodyH, headerLines, offset: view.listScroll };
|
|
668
472
|
const commits = view.commits ?? [];
|
|
669
473
|
const cursor = view.commitCursor ?? 0;
|
|
474
|
+
const { compose } = view;
|
|
475
|
+
const target = editTarget(compose, commits, cursor);
|
|
670
476
|
if (view.commitView === 'full') {
|
|
671
|
-
const lines = flattenCommits(view, commits, width, color);
|
|
672
|
-
const focus = focusLine(lines, cursor,
|
|
477
|
+
const lines = flattenCommits(view, commits, target, width, color);
|
|
478
|
+
const focus = focusLine(lines, cursor, target !== null);
|
|
673
479
|
return paintListWindow(lines.length, focus, pane, (i) => lines[i]);
|
|
674
480
|
}
|
|
675
481
|
const cols = measureColumns(commits, commitRowFields, COMMIT_COL_ALIGN);
|
|
676
|
-
const
|
|
677
|
-
view.compose,
|
|
678
|
-
commits,
|
|
679
|
-
cursor,
|
|
680
|
-
width,
|
|
681
|
-
color,
|
|
682
|
-
cols,
|
|
683
|
-
);
|
|
482
|
+
const edit = commitEditRow(compose, commits, target, width, color, cols);
|
|
684
483
|
const paintRow = (entry, selected) => {
|
|
685
484
|
const current = viewedCommit(entry, view.rev, view.revShort);
|
|
686
485
|
return paintCommitRow(entry, width, color, selected, cols, current);
|
|
687
486
|
};
|
|
688
|
-
return paintCursorList(commits, cursor, pane, paintRow,
|
|
487
|
+
return paintCursorList(commits, cursor, pane, paintRow, edit);
|
|
689
488
|
};
|
|
690
489
|
|
|
691
490
|
module.exports = {
|