reslop 0.2.1 → 0.2.4
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 +27 -1
- package/README.md +19 -4
- package/bin/reslop.js +4 -2
- package/lib/ansi.js +120 -4
- package/lib/api.js +2 -1
- package/lib/cli.js +21 -9
- package/lib/clipboard.js +6 -3
- package/lib/deps-diff.js +61 -115
- package/lib/deps.js +57 -54
- package/lib/detect.js +4 -2
- package/lib/diff/align.js +2 -1
- package/lib/diff/diff.js +6 -3
- package/lib/diff/display.js +2 -1
- package/lib/diff/inline.js +2 -1
- package/lib/diff/patch.js +2 -1
- package/lib/diff/unit.js +4 -2
- package/lib/editor.js +22 -4
- package/lib/files.js +38 -12
- package/lib/git-branches.js +150 -165
- package/lib/git-deps.js +20 -60
- package/lib/git-worktree.js +508 -0
- package/lib/git.js +23 -580
- package/lib/github.js +2 -1
- package/lib/gitlab.js +2 -1
- package/lib/highlight/bash.js +2 -1
- package/lib/highlight/css.js +2 -1
- package/lib/highlight/csv.js +2 -1
- package/lib/highlight/dot.js +2 -1
- package/lib/highlight/html.js +2 -1
- package/lib/highlight/json.js +2 -1
- package/lib/highlight/log.js +2 -1
- package/lib/highlight/txt.js +2 -1
- package/lib/highlight.js +2 -1
- package/lib/keys.js +12 -6
- package/lib/manifest.js +4 -2
- package/lib/npm-commands.js +592 -0
- package/lib/remote.js +20 -13
- package/lib/render/branches.js +25 -41
- package/lib/render/chrome.js +163 -128
- package/lib/render/commits.js +727 -65
- package/lib/render/diff.js +130 -47
- package/lib/render/files.js +4 -12
- package/lib/render/frame.js +41 -13
- package/lib/render/notes.js +4 -11
- package/lib/render/npm.js +216 -0
- package/lib/render/present.js +2 -1
- package/lib/render/primitives.js +34 -50
- package/lib/render/render.js +2 -1
- package/lib/render/unit.js +14 -11
- package/lib/report-model.js +129 -0
- package/lib/report-parse.js +89 -0
- package/lib/report-render.js +104 -0
- package/lib/report-run.js +166 -0
- package/lib/report-test.js +507 -0
- package/lib/report-tools.js +337 -0
- package/lib/review-merge.js +2 -1
- package/lib/review.js +6 -2
- package/lib/session/actions.js +77 -44
- package/lib/session/branches.js +13 -49
- package/lib/session/change-actions.js +2 -1
- package/lib/session/commit.js +113 -53
- package/lib/session/compose.js +83 -14
- package/lib/session/dispatch.js +42 -22
- package/lib/session/files-pane.js +16 -21
- package/lib/session/items.js +6 -3
- package/lib/session/lifecycle.js +12 -5
- package/lib/session/load.js +4 -2
- package/lib/session/navigation.js +2 -0
- package/lib/session/npm.js +388 -0
- package/lib/session/ops.js +15 -1
- package/lib/session/pointer.js +30 -3
- package/lib/session/review.js +2 -1
- package/lib/session/terminal.js +4 -2
- package/lib/session/update.js +2 -1
- package/lib/session/view.js +41 -4
- package/lib/session.js +44 -22
- package/lib/source.js +6 -3
- package/lib/update.js +5 -9
- package/lib/utilities.js +4 -2
- package/lib/wrap.js +3 -1
- package/package.json +6 -6
package/lib/render/commits.js
CHANGED
|
@@ -1,78 +1,195 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const ansi = require('../ansi.js');
|
|
4
|
+
const files = require('../files.js');
|
|
4
5
|
const primitives = require('./primitives.js');
|
|
6
|
+
const wrap = require('../wrap.js');
|
|
7
|
+
const { shortAge } = files;
|
|
8
|
+
const { wrapMultiline, cursorInWrap } = wrap;
|
|
5
9
|
const { measureColumns, padColumns, joinColumns, paintColumns } = primitives;
|
|
10
|
+
const { padVisible } = primitives;
|
|
6
11
|
|
|
7
12
|
const { THEME, paint, visibleWidth, truncateVisible, seq, EL } = ansi;
|
|
8
|
-
const {
|
|
13
|
+
const { FILE_MARK, paneResult, paintCursorList } = primitives;
|
|
14
|
+
const { paintBodyFill, listWindowStart } = primitives;
|
|
9
15
|
|
|
10
16
|
const COMMIT_COL_ALIGN = {
|
|
11
17
|
sha: 'end',
|
|
12
|
-
|
|
18
|
+
refs: 'end',
|
|
19
|
+
add: 'start',
|
|
20
|
+
del: 'start',
|
|
13
21
|
date: 'start',
|
|
14
22
|
};
|
|
15
23
|
|
|
16
24
|
const COMMIT_COL_FG = {
|
|
17
25
|
sha: THEME.shaFg,
|
|
18
|
-
|
|
26
|
+
refs: THEME.warnFg,
|
|
27
|
+
add: THEME.addLineFg,
|
|
28
|
+
del: THEME.delLineFg,
|
|
19
29
|
date: THEME.mutedFg,
|
|
20
30
|
};
|
|
21
31
|
|
|
22
|
-
const
|
|
32
|
+
const COMMIT_CURRENT_COL_FG = {
|
|
33
|
+
sha: THEME.shaDarkFg,
|
|
34
|
+
date: THEME.headerFg,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const REFS_MAX = 28;
|
|
38
|
+
const LIST_PAD = 1;
|
|
23
39
|
const COMMIT_TAIL = 1;
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
40
|
+
const lineCounts = (entry) => {
|
|
41
|
+
const added = entry.added ?? 0;
|
|
42
|
+
const removed = entry.removed ?? 0;
|
|
43
|
+
if (!added && !removed) return null;
|
|
44
|
+
return { added, removed };
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const countLabel = (entry) => {
|
|
48
|
+
if (entry && entry.pending) {
|
|
49
|
+
const stagedAdd = entry.stagedAdded ?? 0;
|
|
50
|
+
const unstagedAdd = entry.unstagedAdded ?? 0;
|
|
51
|
+
const stagedDel = entry.stagedRemoved ?? 0;
|
|
52
|
+
const unstagedDel = entry.unstagedRemoved ?? 0;
|
|
53
|
+
const any = stagedAdd || unstagedAdd || stagedDel || unstagedDel;
|
|
54
|
+
if (!any) return null;
|
|
55
|
+
return {
|
|
56
|
+
add: `+${stagedAdd}/${unstagedAdd}`,
|
|
57
|
+
del: `-${stagedDel}/${unstagedDel}`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const counts = lineCounts(entry);
|
|
61
|
+
if (!counts) return null;
|
|
62
|
+
return { add: `+${counts.added}`, del: `-${counts.removed}` };
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const DIFF_LABEL = 'Diffs:';
|
|
66
|
+
const DIFF_GAP = ' ';
|
|
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
|
+
};
|
|
76
|
+
|
|
77
|
+
const listCountWidth = (commits) => {
|
|
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
|
+
};
|
|
87
|
+
|
|
88
|
+
const commitRowFields = (entry) => {
|
|
89
|
+
const refs = `${entry.refs ?? ''}`.trim();
|
|
90
|
+
const label = countLabel(entry);
|
|
91
|
+
return {
|
|
92
|
+
sha: entry.shortSha ?? '',
|
|
93
|
+
refs: refs ? truncateVisible(refs, REFS_MAX) : '',
|
|
94
|
+
add: label ? label.add : '',
|
|
95
|
+
del: label ? label.del : '',
|
|
96
|
+
date: shortAge(entry.date ?? ''),
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const contentWidth = (width) => Math.max(1, width - COMMIT_TAIL);
|
|
101
|
+
|
|
102
|
+
const splitMessage = (message) => {
|
|
103
|
+
const text = `${message ?? ''}`;
|
|
104
|
+
const at = text.indexOf('\n');
|
|
105
|
+
if (at < 0) return { line: text, rest: '' };
|
|
106
|
+
return { line: text.slice(0, at), rest: text.slice(at + 1) };
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const commitText = (entry) => {
|
|
110
|
+
if (!entry) return '';
|
|
111
|
+
if (entry.body) return `${entry.body}`;
|
|
112
|
+
return `${entry.subject ?? ''}`;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const commitLine = (entry) => splitMessage(commitText(entry)).line;
|
|
29
116
|
|
|
30
|
-
const
|
|
31
|
-
const
|
|
117
|
+
const viewedCommit = (entry, rev, revShort) => {
|
|
118
|
+
const want = `${rev ?? ''}`;
|
|
119
|
+
const short = `${revShort ?? ''}`;
|
|
120
|
+
if (entry && entry.pending) return !want && !short;
|
|
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;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const FULL_INDENT = ' '.repeat(visibleWidth(` ${FILE_MARK} `));
|
|
129
|
+
|
|
130
|
+
const commitTone = (selected, current) => {
|
|
131
|
+
if (current) {
|
|
132
|
+
return {
|
|
133
|
+
current: true,
|
|
134
|
+
fgRgb: THEME.headerFg,
|
|
135
|
+
bgRgb: THEME.currentBg,
|
|
136
|
+
nameFg: THEME.mutedFg,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (selected) {
|
|
140
|
+
return {
|
|
141
|
+
current: false,
|
|
142
|
+
fgRgb: THEME.chromeFg,
|
|
143
|
+
bgRgb: THEME.buttonBg,
|
|
144
|
+
nameFg: THEME.buttonHotFg,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
current: false,
|
|
149
|
+
fgRgb: THEME.mutedFg,
|
|
150
|
+
bgRgb: THEME.ctxBg,
|
|
151
|
+
nameFg: THEME.chromeFg,
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const paintCommitRow = (entry, width, color, selected, cols, current) => {
|
|
156
|
+
const inner = contentWidth(width);
|
|
32
157
|
const mark = selected ? FILE_MARK : ' ';
|
|
33
158
|
const prefix = ` ${mark} `;
|
|
34
|
-
const lead = ' ';
|
|
35
159
|
const parts = padColumns(commitRowFields(entry), cols, COMMIT_COL_ALIGN);
|
|
36
160
|
const suffix = joinColumns(parts, cols);
|
|
37
|
-
const rest = Math.max(1, inner - visibleWidth(prefix +
|
|
38
|
-
const subject = truncateVisible(entry
|
|
161
|
+
const rest = Math.max(1, inner - visibleWidth(prefix + suffix));
|
|
162
|
+
const subject = truncateVisible(commitLine(entry), rest);
|
|
39
163
|
const pad = Math.max(0, rest - visibleWidth(subject));
|
|
40
164
|
const after = `${' '.repeat(pad)}`;
|
|
41
|
-
const left = `${prefix}${
|
|
42
|
-
const fgRgb = selected ? THEME.chromeFg : THEME.mutedFg;
|
|
43
|
-
const bgRgb = selected ? THEME.buttonBg : THEME.ctxBg;
|
|
44
|
-
const nameFg = selected ? THEME.buttonHotFg : THEME.chromeFg;
|
|
165
|
+
const left = `${prefix}${subject}${after}`;
|
|
45
166
|
const tail = ' '.repeat(COMMIT_TAIL);
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
out
|
|
52
|
-
out += paint(
|
|
53
|
-
out +=
|
|
54
|
-
out += paint(
|
|
55
|
-
|
|
167
|
+
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}`;
|
|
172
|
+
let out = `${seq(tone.fgRgb, tone.bgRgb)}${EL}`;
|
|
173
|
+
out += paint(prefix, tone.fgRgb, tone.bgRgb, color);
|
|
174
|
+
out += paint(subject, tone.nameFg, tone.bgRgb, color);
|
|
175
|
+
out += paint(after, tone.fgRgb, tone.bgRgb, color);
|
|
176
|
+
out += paintColumns(parts, cols, tone.fgRgb, tone.bgRgb, color, tones);
|
|
177
|
+
out += paint(tail, tone.fgRgb, tone.bgRgb, color);
|
|
178
|
+
return out;
|
|
56
179
|
};
|
|
57
180
|
|
|
58
181
|
const paintCommitEdit = (compose, width, color, entry, cols) => {
|
|
59
|
-
const text =
|
|
182
|
+
const text = splitMessage(compose.text).line;
|
|
60
183
|
if (entry && cols) {
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
color,
|
|
65
|
-
true,
|
|
66
|
-
cols,
|
|
67
|
-
);
|
|
68
|
-
const inner = Math.max(1, width - COMMIT_EDGE - COMMIT_TAIL);
|
|
184
|
+
const shown = { ...entry, body: text, subject: text };
|
|
185
|
+
const row = paintCommitRow(shown, width, color, true, cols);
|
|
186
|
+
const inner = contentWidth(width);
|
|
69
187
|
const prefix = ` ${FILE_MARK} `;
|
|
70
|
-
const lead = ' ';
|
|
71
188
|
const parts = padColumns(commitRowFields(entry), cols, COMMIT_COL_ALIGN);
|
|
72
189
|
const suffix = joinColumns(parts, cols);
|
|
73
|
-
const rest = Math.max(1, inner - visibleWidth(prefix +
|
|
190
|
+
const rest = Math.max(1, inner - visibleWidth(prefix + suffix));
|
|
74
191
|
const clipped = truncateVisible(text, rest);
|
|
75
|
-
const prefixW = visibleWidth(prefix
|
|
192
|
+
const prefixW = visibleWidth(prefix);
|
|
76
193
|
const col = Math.min(compose.cursor ?? 0, visibleWidth(clipped));
|
|
77
194
|
return {
|
|
78
195
|
row,
|
|
@@ -80,50 +197,567 @@ const paintCommitEdit = (compose, width, color, entry, cols) => {
|
|
|
80
197
|
at: 'replace',
|
|
81
198
|
};
|
|
82
199
|
}
|
|
83
|
-
const inner =
|
|
200
|
+
const inner = contentWidth(width);
|
|
84
201
|
const prefix = ` ${FILE_MARK} `;
|
|
85
|
-
const
|
|
86
|
-
const prefixW = visibleWidth(prefix + lead);
|
|
202
|
+
const prefixW = visibleWidth(prefix);
|
|
87
203
|
const rest = Math.max(1, inner - prefixW);
|
|
88
204
|
const clipped = truncateVisible(text, rest);
|
|
89
205
|
const pad = Math.max(0, rest - visibleWidth(clipped));
|
|
90
206
|
const gap = ' '.repeat(pad);
|
|
207
|
+
const tail = ' '.repeat(COMMIT_TAIL);
|
|
91
208
|
const fgRgb = THEME.chromeFg;
|
|
92
209
|
const bgRgb = THEME.buttonBg;
|
|
93
210
|
const nameFg = THEME.buttonHotFg;
|
|
94
|
-
|
|
95
|
-
let row = `${prefix}${lead}${clipped}${gap}${edge}`;
|
|
211
|
+
let row = `${prefix}${clipped}${gap}${tail}`;
|
|
96
212
|
if (color) {
|
|
97
213
|
row = `${seq(fgRgb, bgRgb)}${EL}`;
|
|
98
214
|
row += paint(prefix, fgRgb, bgRgb, color);
|
|
99
|
-
row += paint(lead, nameFg, bgRgb, color);
|
|
100
215
|
row += paint(clipped, nameFg, bgRgb, color);
|
|
101
216
|
row += paint(gap, fgRgb, bgRgb, color);
|
|
102
|
-
row +=
|
|
217
|
+
row += paint(tail, fgRgb, bgRgb, color);
|
|
103
218
|
}
|
|
104
219
|
const col = Math.min(compose.cursor ?? 0, visibleWidth(clipped));
|
|
105
220
|
return { row, cursor: { x: prefixW + col + 1 }, at: 'start' };
|
|
106
221
|
};
|
|
107
222
|
|
|
223
|
+
const pendingAt = (commits) => {
|
|
224
|
+
for (let i = 0; i < commits.length; i++) {
|
|
225
|
+
if (commits[i] && commits[i].pending) return i;
|
|
226
|
+
}
|
|
227
|
+
return -1;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const editsCursor = (compose) =>
|
|
231
|
+
compose.commitKind === 'amend' || compose.commitKind === 'reword';
|
|
232
|
+
|
|
233
|
+
const editsPending = (compose) =>
|
|
234
|
+
compose.commitKind === 'commit' || compose.commitKind === 'fixup';
|
|
235
|
+
|
|
108
236
|
const commitEditRow = (compose, commits, cursor, width, color, cols) => {
|
|
109
237
|
if (!compose || compose.kind !== 'commit') return null;
|
|
110
|
-
|
|
111
|
-
|
|
238
|
+
let at = -1;
|
|
239
|
+
if (editsCursor(compose)) at = cursor;
|
|
240
|
+
if (editsPending(compose)) at = pendingAt(commits);
|
|
241
|
+
if (at >= 0) {
|
|
242
|
+
const entry = commits[at];
|
|
112
243
|
if (!entry) return null;
|
|
113
|
-
|
|
244
|
+
const painted = paintCommitEdit(compose, width, color, entry, cols);
|
|
245
|
+
painted.index = at;
|
|
246
|
+
return painted;
|
|
114
247
|
}
|
|
115
248
|
return paintCommitEdit(compose, width, color);
|
|
116
249
|
};
|
|
117
250
|
|
|
118
|
-
const
|
|
251
|
+
const paintPieces = (pieces, width, color, selected, current) => {
|
|
252
|
+
const tone = commitTone(selected, current);
|
|
253
|
+
const inner = contentWidth(width);
|
|
254
|
+
const shown = [];
|
|
255
|
+
let used = 0;
|
|
256
|
+
for (const piece of pieces) {
|
|
257
|
+
const room = inner - used;
|
|
258
|
+
if (room <= 0) break;
|
|
259
|
+
const text = truncateVisible(piece.text, room);
|
|
260
|
+
if (!text) continue;
|
|
261
|
+
shown.push({ text, fg: piece.fg });
|
|
262
|
+
used += visibleWidth(text);
|
|
263
|
+
}
|
|
264
|
+
const pad = ' '.repeat(Math.max(0, inner - used));
|
|
265
|
+
const tail = ' '.repeat(COMMIT_TAIL);
|
|
266
|
+
if (!color) {
|
|
267
|
+
let plain = '';
|
|
268
|
+
for (const piece of shown) plain += piece.text;
|
|
269
|
+
return `${plain}${pad}${tail}`;
|
|
270
|
+
}
|
|
271
|
+
let out = `${seq(tone.fgRgb, tone.bgRgb)}${EL}`;
|
|
272
|
+
for (const piece of shown) {
|
|
273
|
+
out += paint(piece.text, piece.fg, tone.bgRgb, color);
|
|
274
|
+
}
|
|
275
|
+
out += paint(pad, tone.fgRgb, tone.bgRgb, color);
|
|
276
|
+
out += paint(tail, tone.fgRgb, tone.bgRgb, color);
|
|
277
|
+
return out;
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const COL_GAP = 2;
|
|
281
|
+
const MIN_LEFT = 8;
|
|
282
|
+
|
|
283
|
+
const messageText = (entry) => {
|
|
284
|
+
const body = `${entry.body ?? ''}`.replace(/\s+$/, '');
|
|
285
|
+
if (body) return body;
|
|
286
|
+
return `${entry.subject ?? ''}`.replace(/\s+$/, '');
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const wrapKind = (text, width, kind) => {
|
|
290
|
+
const rows = wrapMultiline(text, Math.max(1, width));
|
|
291
|
+
const lines = [];
|
|
292
|
+
for (const row of rows) lines.push({ text: row, kind });
|
|
293
|
+
return lines;
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const messagePartsFrom = (text, width) => {
|
|
297
|
+
const raw = `${text ?? ''}`;
|
|
298
|
+
if (!raw) return [{ text: '', kind: 'text' }];
|
|
299
|
+
const split = raw.split('\n');
|
|
300
|
+
const lines = wrapKind(split[0], width, 'text');
|
|
301
|
+
for (let i = 1; i < split.length; i++) {
|
|
302
|
+
const rest = wrapKind(split[i], width, 'body');
|
|
303
|
+
for (const line of rest) lines.push(line);
|
|
304
|
+
}
|
|
305
|
+
return lines;
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
const messageParts = (entry, width) =>
|
|
309
|
+
messagePartsFrom(messageText(entry), width);
|
|
310
|
+
|
|
311
|
+
const authorParts = (entry) => {
|
|
312
|
+
const name = `${entry.author ?? ''}`.trim();
|
|
313
|
+
const email = `${entry.email ?? ''}`.trim();
|
|
314
|
+
if (name && email) {
|
|
315
|
+
return [
|
|
316
|
+
{ text: name, kind: 'author' },
|
|
317
|
+
{ text: ` <${email}>`, kind: 'email' },
|
|
318
|
+
];
|
|
319
|
+
}
|
|
320
|
+
if (email) return [{ text: email, kind: 'email' }];
|
|
321
|
+
if (name) return [{ text: name, kind: 'author' }];
|
|
322
|
+
return [];
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const partsWidth = (parts) => {
|
|
326
|
+
let width = 0;
|
|
327
|
+
for (const part of parts) width += visibleWidth(part.text);
|
|
328
|
+
return width;
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const timeNeed = (entry) => {
|
|
332
|
+
const when = `${entry.when ?? ''}`.trim();
|
|
333
|
+
const ago = shortAge(entry.date ?? '');
|
|
334
|
+
if (when && ago) return visibleWidth(when) + COL_GAP + visibleWidth(ago);
|
|
335
|
+
return visibleWidth(when || ago);
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const refsText = (entry) => `${entry.refs ?? ''}`.trim();
|
|
339
|
+
|
|
340
|
+
const diffLine = (entry, widths) => {
|
|
341
|
+
const label = countLabel(entry);
|
|
342
|
+
if (!label) return null;
|
|
343
|
+
const add = padVisible(label.add, widths.add, 'start');
|
|
344
|
+
const del = padVisible(label.del, widths.del, 'start');
|
|
345
|
+
const text = `${DIFF_LABEL}${DIFF_GAP}${add}${DIFF_GAP}${del}`;
|
|
346
|
+
return { add, del, text };
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
const diffCountParts = (entry, widths, rightW) => {
|
|
350
|
+
const line = diffLine(entry, widths);
|
|
351
|
+
if (!line || !rightW) return [];
|
|
352
|
+
const pad = Math.max(0, rightW - visibleWidth(line.text));
|
|
353
|
+
return [
|
|
354
|
+
{ text: ' '.repeat(pad), kind: 'meta' },
|
|
355
|
+
{ text: DIFF_LABEL, kind: 'meta' },
|
|
356
|
+
{ text: DIFF_GAP, kind: 'meta' },
|
|
357
|
+
{ text: line.add, kind: 'add' },
|
|
358
|
+
{ text: DIFF_GAP, kind: 'meta' },
|
|
359
|
+
{ text: line.del, kind: 'del' },
|
|
360
|
+
];
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const rightNeed = (entry, widths = entryCountWidth(entry)) => {
|
|
364
|
+
const line = diffLine(entry, widths);
|
|
365
|
+
return Math.max(
|
|
366
|
+
partsWidth(authorParts(entry)),
|
|
367
|
+
timeNeed(entry),
|
|
368
|
+
visibleWidth(refsText(entry)),
|
|
369
|
+
line ? visibleWidth(line.text) : 0,
|
|
370
|
+
);
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
const spreadTime = (when, ago, width) => {
|
|
374
|
+
const agoW = visibleWidth(ago);
|
|
375
|
+
if (agoW >= width) {
|
|
376
|
+
return [{ text: truncateVisible(ago, width), kind: 'meta' }];
|
|
377
|
+
}
|
|
378
|
+
const room = width - agoW;
|
|
379
|
+
const minGap = Math.min(COL_GAP, room);
|
|
380
|
+
const left = truncateVisible(when, room - minGap);
|
|
381
|
+
const gap = width - visibleWidth(left) - agoW;
|
|
382
|
+
return [
|
|
383
|
+
{ text: left, kind: 'meta' },
|
|
384
|
+
{ text: ' '.repeat(gap), kind: 'meta' },
|
|
385
|
+
{ text: ago, kind: 'meta' },
|
|
386
|
+
];
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
const alignEnd = (text, width, kind) => {
|
|
390
|
+
const shown = truncateVisible(text, width);
|
|
391
|
+
const pad = Math.max(0, width - visibleWidth(shown));
|
|
392
|
+
return [
|
|
393
|
+
{ text: ' '.repeat(pad), kind },
|
|
394
|
+
{ text: shown, kind },
|
|
395
|
+
];
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
const timeParts = (entry, width) => {
|
|
399
|
+
const when = `${entry.when ?? ''}`.trim();
|
|
400
|
+
const ago = shortAge(entry.date ?? '');
|
|
401
|
+
if (!width) return [];
|
|
402
|
+
if (when && ago) return spreadTime(when, ago, width);
|
|
403
|
+
if (ago) return alignEnd(ago, width, 'meta');
|
|
404
|
+
if (when) return [{ text: truncateVisible(when, width), kind: 'meta' }];
|
|
405
|
+
return [];
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
const rightRows = (entry, rightW, widths) => {
|
|
409
|
+
const rows = [];
|
|
410
|
+
const author = authorParts(entry);
|
|
411
|
+
if (author.length) rows.push(author);
|
|
412
|
+
const time = timeParts(entry, rightW);
|
|
413
|
+
if (time.length) rows.push(time);
|
|
414
|
+
const refs = refsText(entry);
|
|
415
|
+
if (refs) rows.push([{ text: refs, kind: 'refs' }]);
|
|
416
|
+
const diff = diffCountParts(entry, widths, rightW);
|
|
417
|
+
if (diff.length) rows.push(diff);
|
|
418
|
+
return rows;
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
const panelWidths = (need, leftNeed, inner) => {
|
|
422
|
+
const indent = visibleWidth(FULL_INDENT);
|
|
423
|
+
let rightW = need;
|
|
424
|
+
const room = Math.max(1, inner - indent);
|
|
425
|
+
if (!rightW || room <= MIN_LEFT) {
|
|
426
|
+
return { rightW: 0, leftW: room, gapW: 0 };
|
|
427
|
+
}
|
|
428
|
+
const reserve = Math.max(MIN_LEFT, leftNeed);
|
|
429
|
+
const minLeft = Math.min(reserve, room - COL_GAP - 1);
|
|
430
|
+
const maxRight = room - COL_GAP - minLeft;
|
|
431
|
+
if (rightW > maxRight) rightW = Math.max(1, maxRight);
|
|
432
|
+
return { rightW, leftW: room - COL_GAP - rightW, gapW: COL_GAP };
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
const panelLeft = (entry, leftW, edit) => {
|
|
436
|
+
const parts = edit
|
|
437
|
+
? messagePartsFrom(edit.text ?? '', leftW)
|
|
438
|
+
: messageParts(entry, leftW);
|
|
439
|
+
if (!entry.sha) return parts;
|
|
440
|
+
return [{ text: entry.sha, kind: 'hash' }, ...parts];
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
const commitMessageWidth = (entry, screenWidth) => {
|
|
444
|
+
const width = Math.max(20, screenWidth || 80);
|
|
445
|
+
const inner = contentWidth(width);
|
|
446
|
+
const indent = visibleWidth(FULL_INDENT);
|
|
447
|
+
if (!entry) return Math.max(1, inner - indent);
|
|
448
|
+
const hashW = visibleWidth(entry.sha ?? '');
|
|
449
|
+
const cols = panelWidths(rightNeed(entry), hashW, inner);
|
|
450
|
+
return Math.max(1, cols.leftW);
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
const prefixWidth = (first, selected) => {
|
|
454
|
+
const mark = first && selected ? FILE_MARK : ' ';
|
|
455
|
+
const prefix = first ? ` ${mark} ` : FULL_INDENT;
|
|
456
|
+
return visibleWidth(prefix);
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const messageCaret = (text, cursor, width, row, first, selected) => {
|
|
460
|
+
const pos = cursorInWrap(`${text ?? ''}`, cursor ?? 0, width);
|
|
461
|
+
if (pos.row !== row) return null;
|
|
462
|
+
return { x: prefixWidth(first, selected) + pos.col + 1 };
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
const pieceFg = (kind, tone) => {
|
|
466
|
+
if (kind === 'add') return THEME.addLineFg;
|
|
467
|
+
if (kind === 'del') return THEME.delLineFg;
|
|
468
|
+
if (tone.current) {
|
|
469
|
+
if (kind === 'hash') return THEME.shaDarkFg;
|
|
470
|
+
if (kind === 'refs') return THEME.warnFg;
|
|
471
|
+
if (kind === 'text' || kind === 'body') return THEME.mutedFg;
|
|
472
|
+
return tone.fgRgb;
|
|
473
|
+
}
|
|
474
|
+
if (kind === 'hash') return THEME.shaFg;
|
|
475
|
+
if (kind === 'author') return THEME.addLineFg;
|
|
476
|
+
if (kind === 'email') return THEME.mutedFg;
|
|
477
|
+
if (kind === 'refs') return THEME.warnFg;
|
|
478
|
+
if (kind === 'text') return tone.nameFg;
|
|
479
|
+
if (kind === 'body') return THEME.mutedFg;
|
|
480
|
+
return tone.fgRgb;
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
const clipParts = (parts, width, fallbackKind, tone) => {
|
|
484
|
+
const shown = [];
|
|
485
|
+
let used = 0;
|
|
486
|
+
for (const part of parts) {
|
|
487
|
+
const room = width - used;
|
|
488
|
+
if (room <= 0) break;
|
|
489
|
+
const text = truncateVisible(part.text ?? '', room);
|
|
490
|
+
if (!text) continue;
|
|
491
|
+
const kind = part.kind ?? fallbackKind;
|
|
492
|
+
shown.push({ text, fg: pieceFg(kind, tone) });
|
|
493
|
+
used += visibleWidth(text);
|
|
494
|
+
}
|
|
495
|
+
const pad = Math.max(0, width - used);
|
|
496
|
+
if (pad) shown.push({ text: ' '.repeat(pad), fg: tone.fgRgb });
|
|
497
|
+
return shown;
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
const paintPanelLine = (spec) => {
|
|
501
|
+
const tone = commitTone(spec.selected, spec.current);
|
|
502
|
+
const mark = spec.first && spec.selected ? FILE_MARK : ' ';
|
|
503
|
+
const prefix = spec.first ? ` ${mark} ` : FULL_INDENT;
|
|
504
|
+
const left = truncateVisible(spec.left, spec.leftW);
|
|
505
|
+
const leftPad = Math.max(0, spec.leftW - visibleWidth(left));
|
|
506
|
+
const gap = ' '.repeat(spec.gapW);
|
|
507
|
+
const right = Array.isArray(spec.right)
|
|
508
|
+
? spec.right
|
|
509
|
+
: [{ text: spec.right ?? '', kind: spec.rightKind }];
|
|
510
|
+
return paintPieces(
|
|
511
|
+
[
|
|
512
|
+
{ text: prefix, fg: tone.fgRgb },
|
|
513
|
+
{ text: left, fg: pieceFg(spec.leftKind, tone) },
|
|
514
|
+
{ text: `${' '.repeat(leftPad)}${gap}`, fg: tone.fgRgb },
|
|
515
|
+
...clipParts(right, spec.rightW, spec.rightKind, tone),
|
|
516
|
+
],
|
|
517
|
+
spec.width,
|
|
518
|
+
spec.color,
|
|
519
|
+
spec.selected,
|
|
520
|
+
spec.current,
|
|
521
|
+
);
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
const lineRecord = (commit, row, hit, cursor = null) => ({
|
|
525
|
+
commit,
|
|
526
|
+
row,
|
|
527
|
+
hit,
|
|
528
|
+
cursor,
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
const commitLines = (
|
|
532
|
+
entry,
|
|
533
|
+
index,
|
|
534
|
+
width,
|
|
535
|
+
color,
|
|
536
|
+
selected,
|
|
537
|
+
edit,
|
|
538
|
+
current,
|
|
539
|
+
diffWidths,
|
|
540
|
+
) => {
|
|
541
|
+
const inner = contentWidth(width);
|
|
542
|
+
const hashW = visibleWidth(entry.sha ?? '');
|
|
543
|
+
const widths = diffWidths ?? entryCountWidth(entry);
|
|
544
|
+
const cols = panelWidths(rightNeed(entry, widths), hashW, inner);
|
|
545
|
+
const rights = rightRows(entry, cols.rightW, widths);
|
|
546
|
+
const left = panelLeft(entry, cols.leftW, edit);
|
|
547
|
+
const count = Math.max(left.length, rights.length);
|
|
548
|
+
const lines = [];
|
|
549
|
+
for (let row = 0; row < count; row++) {
|
|
550
|
+
const line = left[row] ?? { text: '', kind: 'text' };
|
|
551
|
+
const painted = paintPanelLine({
|
|
552
|
+
left: line.text,
|
|
553
|
+
right: rights[row] ?? [],
|
|
554
|
+
leftW: cols.leftW,
|
|
555
|
+
rightW: cols.rightW,
|
|
556
|
+
gapW: cols.gapW,
|
|
557
|
+
width,
|
|
558
|
+
color,
|
|
559
|
+
selected,
|
|
560
|
+
current,
|
|
561
|
+
first: row === 0,
|
|
562
|
+
leftKind: line.kind,
|
|
563
|
+
rightKind: 'meta',
|
|
564
|
+
});
|
|
565
|
+
const messageRow = entry.sha ? row - 1 : row;
|
|
566
|
+
const caret = edit
|
|
567
|
+
? messageCaret(
|
|
568
|
+
edit.text,
|
|
569
|
+
edit.cursor,
|
|
570
|
+
cols.leftW,
|
|
571
|
+
messageRow,
|
|
572
|
+
!entry.sha && row === 0,
|
|
573
|
+
selected,
|
|
574
|
+
)
|
|
575
|
+
: null;
|
|
576
|
+
lines.push(lineRecord(index, painted, true, caret));
|
|
577
|
+
}
|
|
578
|
+
return lines;
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
const draftLines = (text, cursor, width, color) => {
|
|
582
|
+
const inner = contentWidth(width);
|
|
583
|
+
const leftW = Math.max(1, inner - visibleWidth(FULL_INDENT));
|
|
584
|
+
const parts = messagePartsFrom(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
|
+
lines.push(lineRecord(-1, painted, false, caret));
|
|
603
|
+
}
|
|
604
|
+
return lines;
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
const composeEditsIndex = (compose, commits, index, cursor) => {
|
|
608
|
+
if (!compose || compose.kind !== 'commit') return false;
|
|
609
|
+
if (editsCursor(compose)) return index === cursor;
|
|
610
|
+
if (!editsPending(compose)) return false;
|
|
611
|
+
const at = pendingAt(commits);
|
|
612
|
+
return at >= 0 && index === at;
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
const composeShowsDraft = (compose, commits) => {
|
|
616
|
+
if (!compose || compose.kind !== 'commit') return false;
|
|
617
|
+
if (editsCursor(compose)) return false;
|
|
618
|
+
if (editsPending(compose)) return pendingAt(commits) < 0;
|
|
619
|
+
return true;
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
const gapLine = (width, color) =>
|
|
623
|
+
lineRecord(-1, paintBodyFill(width, color, 'files'), false);
|
|
624
|
+
|
|
625
|
+
const flattenCommits = (commits, cursor, width, color, compose, rev, short) => {
|
|
626
|
+
const lines = [];
|
|
627
|
+
const diffWidths = listCountWidth(commits);
|
|
628
|
+
const editing = compose && compose.kind === 'commit';
|
|
629
|
+
const pushGap = () => {
|
|
630
|
+
if (lines.length) lines.push(gapLine(width, color));
|
|
631
|
+
};
|
|
632
|
+
if (composeShowsDraft(compose, commits)) {
|
|
633
|
+
const draft = draftLines(
|
|
634
|
+
compose.text ?? '',
|
|
635
|
+
compose.cursor ?? 0,
|
|
636
|
+
width,
|
|
637
|
+
color,
|
|
638
|
+
);
|
|
639
|
+
for (const line of draft) lines.push(line);
|
|
640
|
+
}
|
|
641
|
+
for (let index = 0; index < commits.length; index++) {
|
|
642
|
+
pushGap();
|
|
643
|
+
const inplace = composeEditsIndex(compose, commits, index, cursor);
|
|
644
|
+
const selected = inplace || (!editing && index === cursor);
|
|
645
|
+
const edit = inplace
|
|
646
|
+
? { text: compose.text ?? '', cursor: compose.cursor ?? 0 }
|
|
647
|
+
: null;
|
|
648
|
+
const block = commitLines(
|
|
649
|
+
commits[index],
|
|
650
|
+
index,
|
|
651
|
+
width,
|
|
652
|
+
color,
|
|
653
|
+
selected,
|
|
654
|
+
edit,
|
|
655
|
+
viewedCommit(commits[index], rev, short),
|
|
656
|
+
diffWidths,
|
|
657
|
+
);
|
|
658
|
+
for (const line of block) lines.push(line);
|
|
659
|
+
}
|
|
660
|
+
return lines;
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
const focusLine = (lines, cursor, compose) => {
|
|
664
|
+
if (compose && compose.kind === 'commit') {
|
|
665
|
+
for (let i = 0; i < lines.length; i++) {
|
|
666
|
+
if (lines[i].cursor) return i;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
for (let i = 0; i < lines.length; i++) {
|
|
670
|
+
const line = lines[i];
|
|
671
|
+
if (line.hit && line.commit === cursor) return i;
|
|
672
|
+
}
|
|
673
|
+
return 0;
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
const paintLineWindow = (
|
|
677
|
+
lines,
|
|
678
|
+
focus,
|
|
679
|
+
width,
|
|
680
|
+
color,
|
|
681
|
+
bodyH,
|
|
682
|
+
headerLines,
|
|
683
|
+
offset,
|
|
684
|
+
) => {
|
|
685
|
+
const pad = bodyH >= LIST_PAD * 2 + 1 ? LIST_PAD : 0;
|
|
686
|
+
const listH = bodyH - pad * 2;
|
|
687
|
+
const start = listWindowStart(focus, offset, listH, lines.length);
|
|
688
|
+
const blank = paintBodyFill(width, color, 'files');
|
|
689
|
+
const body = [];
|
|
690
|
+
const fileHits = [];
|
|
691
|
+
let textCursor = null;
|
|
692
|
+
if (pad) body.push(blank);
|
|
693
|
+
for (let i = 0; i < listH; i++) {
|
|
694
|
+
const line = lines[start + i];
|
|
695
|
+
if (!line) break;
|
|
696
|
+
body.push(line.row);
|
|
697
|
+
if (line.cursor) {
|
|
698
|
+
textCursor = { x: line.cursor.x, row: body.length - 1 };
|
|
699
|
+
}
|
|
700
|
+
if (!line.hit) continue;
|
|
701
|
+
const y = headerLines + pad + i + 1;
|
|
702
|
+
fileHits.push({ y, cursor: line.commit });
|
|
703
|
+
}
|
|
704
|
+
if (pad) body.push(blank);
|
|
705
|
+
return paneResult({
|
|
706
|
+
body,
|
|
707
|
+
fileHits,
|
|
708
|
+
cursor: textCursor,
|
|
709
|
+
listScroll: start,
|
|
710
|
+
});
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
const paintFullCommits = (view, width, color, bodyH, headerLines) => {
|
|
119
714
|
const commits = view.commits ?? [];
|
|
120
|
-
const cols = measureColumns(commits, commitRowFields, COMMIT_COL_ALIGN);
|
|
121
715
|
const cursor = view.commitCursor ?? 0;
|
|
122
|
-
const compose = view.compose;
|
|
123
|
-
const
|
|
716
|
+
const compose = view.compose ?? null;
|
|
717
|
+
const lines = flattenCommits(
|
|
718
|
+
commits,
|
|
719
|
+
cursor,
|
|
720
|
+
width,
|
|
721
|
+
color,
|
|
722
|
+
compose,
|
|
723
|
+
view.rev,
|
|
724
|
+
view.revShort,
|
|
725
|
+
);
|
|
726
|
+
const focus = focusLine(lines, cursor, compose);
|
|
727
|
+
return paintLineWindow(
|
|
728
|
+
lines,
|
|
729
|
+
focus,
|
|
730
|
+
width,
|
|
731
|
+
color,
|
|
732
|
+
bodyH,
|
|
733
|
+
headerLines,
|
|
734
|
+
view.listScroll,
|
|
735
|
+
);
|
|
736
|
+
};
|
|
737
|
+
|
|
738
|
+
const paintBriefCommits = (
|
|
739
|
+
commits,
|
|
740
|
+
cursor,
|
|
741
|
+
width,
|
|
742
|
+
color,
|
|
743
|
+
bodyH,
|
|
744
|
+
headerLines,
|
|
745
|
+
extra,
|
|
746
|
+
offset,
|
|
747
|
+
cols,
|
|
748
|
+
rev,
|
|
749
|
+
revShort,
|
|
750
|
+
) => {
|
|
124
751
|
const paintRow = (entry, rowWidth, rowColor, selected) =>
|
|
125
|
-
paintCommitRow(
|
|
126
|
-
|
|
752
|
+
paintCommitRow(
|
|
753
|
+
entry,
|
|
754
|
+
rowWidth,
|
|
755
|
+
rowColor,
|
|
756
|
+
selected,
|
|
757
|
+
cols,
|
|
758
|
+
viewedCommit(entry, rev, revShort),
|
|
759
|
+
);
|
|
760
|
+
return paintCursorList(
|
|
127
761
|
commits,
|
|
128
762
|
cursor,
|
|
129
763
|
width,
|
|
@@ -132,16 +766,44 @@ const paintBodyCommits = (view, width, color, bodyH, headerLines) => {
|
|
|
132
766
|
headerLines,
|
|
133
767
|
paintRow,
|
|
134
768
|
extra,
|
|
769
|
+
offset,
|
|
770
|
+
);
|
|
771
|
+
};
|
|
772
|
+
|
|
773
|
+
const paintBodyCommits = (view, width, color, bodyH, headerLines) => {
|
|
774
|
+
if (view.commitView === 'full') {
|
|
775
|
+
return paintFullCommits(view, width, color, bodyH, headerLines);
|
|
776
|
+
}
|
|
777
|
+
const commits = view.commits ?? [];
|
|
778
|
+
const cols = measureColumns(commits, commitRowFields, COMMIT_COL_ALIGN);
|
|
779
|
+
const cursor = view.commitCursor ?? 0;
|
|
780
|
+
const extra = commitEditRow(
|
|
781
|
+
view.compose,
|
|
782
|
+
commits,
|
|
783
|
+
cursor,
|
|
784
|
+
width,
|
|
785
|
+
color,
|
|
786
|
+
cols,
|
|
787
|
+
);
|
|
788
|
+
return paintBriefCommits(
|
|
789
|
+
commits,
|
|
790
|
+
cursor,
|
|
791
|
+
width,
|
|
792
|
+
color,
|
|
793
|
+
bodyH,
|
|
794
|
+
headerLines,
|
|
795
|
+
extra,
|
|
135
796
|
view.listScroll,
|
|
797
|
+
cols,
|
|
798
|
+
view.rev,
|
|
799
|
+
view.revShort,
|
|
136
800
|
);
|
|
137
|
-
return paneResult({
|
|
138
|
-
body: painted.body,
|
|
139
|
-
fileHits: painted.fileHits,
|
|
140
|
-
todoOwners: [],
|
|
141
|
-
cursor: painted.cursor,
|
|
142
|
-
splitBody: false,
|
|
143
|
-
listScroll: painted.offset,
|
|
144
|
-
});
|
|
145
801
|
};
|
|
146
802
|
|
|
147
|
-
module.exports = {
|
|
803
|
+
module.exports = {
|
|
804
|
+
paintBodyCommits,
|
|
805
|
+
commitMessageWidth,
|
|
806
|
+
commitLine,
|
|
807
|
+
commitText,
|
|
808
|
+
splitMessage,
|
|
809
|
+
};
|