reslop 0.2.4 → 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 +25 -1
- package/LICENSE +1 -1
- package/README.md +12 -13
- package/bin/reslop.js +3 -8
- package/lib/ansi.js +32 -19
- package/lib/capabilities.js +8 -12
- package/lib/cli.js +40 -74
- package/lib/clipboard.js +48 -18
- 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 +196 -404
- package/lib/deps.js +240 -399
- 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 +138 -141
- 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 +90 -206
- package/lib/editor.js +98 -55
- package/lib/files.js +86 -103
- package/lib/git-branches.js +254 -416
- package/lib/git-deps.js +182 -344
- 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 +157 -132
- 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 +16 -26
- 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 +22 -35
- package/lib/highlight/txt.js +9 -22
- package/lib/highlight.js +17 -46
- package/lib/keys.js +62 -85
- package/lib/manifest.js +67 -130
- package/lib/npm-commands.js +119 -189
- package/lib/remote.js +141 -197
- package/lib/render/branches.js +66 -73
- package/lib/render/chrome.js +264 -323
- package/lib/render/commits.js +187 -500
- 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 -228
- package/lib/render/files.js +45 -99
- package/lib/render/frame.js +129 -183
- package/lib/render/notes.js +144 -160
- package/lib/render/npm.js +234 -112
- package/lib/render/present.js +4 -11
- package/lib/render/primitives.js +131 -170
- 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 -28
- package/lib/report-model.js +110 -33
- package/lib/report-parse.js +56 -38
- package/lib/report-render.js +102 -59
- package/lib/report-run.js +87 -78
- package/lib/report-test.js +256 -141
- package/lib/report-tools.js +96 -161
- package/lib/review-merge.js +114 -152
- package/lib/review.js +115 -189
- package/lib/runs.js +225 -0
- package/lib/select.js +89 -75
- package/lib/session/accessors.js +14 -21
- package/lib/session/actions.js +82 -63
- package/lib/session/branches.js +193 -247
- package/lib/session/change-actions.js +171 -214
- package/lib/session/commit.js +280 -342
- package/lib/session/compose.js +559 -674
- package/lib/session/dashboard.js +385 -0
- package/lib/session/dispatch.js +164 -157
- package/lib/session/edit-click.js +57 -0
- package/lib/session/files-pane.js +85 -53
- package/lib/session/items.js +73 -104
- package/lib/session/lifecycle.js +403 -541
- package/lib/session/load.js +48 -68
- package/lib/session/navigation.js +96 -87
- package/lib/session/npm.js +409 -345
- package/lib/session/ops.js +196 -63
- package/lib/session/pointer.js +23 -15
- package/lib/session/progress.js +33 -21
- package/lib/session/review.js +33 -54
- package/lib/session/tasks.js +333 -0
- package/lib/session/templates.js +51 -59
- package/lib/session/terminal.js +85 -136
- package/lib/session/unit.js +3 -4
- package/lib/session/update.js +76 -97
- package/lib/session/view.js +52 -83
- package/lib/session/watch.js +208 -204
- package/lib/session.js +112 -237
- package/lib/update.js +45 -116
- package/lib/utilities.js +67 -89
- package/lib/wrap.js +33 -44
- package/package.json +1 -1
- package/lib/session/todos.js +0 -293
package/lib/render/diff.js
CHANGED
|
@@ -2,102 +2,61 @@
|
|
|
2
2
|
|
|
3
3
|
const ansi = require('../ansi.js');
|
|
4
4
|
const diff = require('../diff/diff.js');
|
|
5
|
-
const { displayLines } = diff;
|
|
6
5
|
const highlight = require('../highlight.js');
|
|
7
6
|
const detect = require('../detect.js');
|
|
8
|
-
const { detectLang } = detect;
|
|
9
7
|
const files = require('../files.js');
|
|
10
8
|
const wrap = require('../wrap.js');
|
|
11
9
|
const primitives = require('./primitives.js');
|
|
12
10
|
|
|
11
|
+
const { displayLines } = diff;
|
|
12
|
+
const { detectLang } = detect;
|
|
13
13
|
const { itemPath } = files;
|
|
14
14
|
const { tokenize, overlayTokens } = highlight;
|
|
15
15
|
const { wrapPlain } = wrap;
|
|
16
|
-
const { THEME, CODE_FG, codeFg, paint, visibleWidth } = ansi;
|
|
17
|
-
const {
|
|
18
|
-
const {
|
|
16
|
+
const { THEME, CODE_FG, codeFg, paint, visibleWidth, truncateVisible } = ansi;
|
|
17
|
+
const { GUTTER_W, fill, splitWidths, paintSplitRule } = primitives;
|
|
18
|
+
const { paneResult, sliceVisible } = primitives;
|
|
19
|
+
|
|
20
|
+
const lineStyle = (baseFg, baseBg, chBg = baseBg, mark = ' ') => ({
|
|
21
|
+
baseFg,
|
|
22
|
+
baseBg,
|
|
23
|
+
chBg,
|
|
24
|
+
mark,
|
|
25
|
+
});
|
|
19
26
|
|
|
20
27
|
const STYLES = {
|
|
21
|
-
del:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
add: {
|
|
28
|
-
baseFg: THEME.addLineFg,
|
|
29
|
-
baseBg: THEME.addLineBg,
|
|
30
|
-
chBg: THEME.addCharBg,
|
|
31
|
-
mark: '+',
|
|
32
|
-
},
|
|
33
|
-
warn: {
|
|
34
|
-
baseFg: THEME.delLineFg,
|
|
35
|
-
baseBg: THEME.delLineBg,
|
|
36
|
-
chBg: THEME.delCharBg,
|
|
37
|
-
mark: ' ',
|
|
38
|
-
},
|
|
39
|
-
note: {
|
|
40
|
-
baseFg: THEME.chromeFg,
|
|
41
|
-
baseBg: THEME.noteBg,
|
|
42
|
-
chBg: THEME.noteBg,
|
|
43
|
-
mark: ' ',
|
|
44
|
-
},
|
|
45
|
-
noteSep: {
|
|
46
|
-
baseFg: THEME.mutedFg,
|
|
47
|
-
baseBg: THEME.chromeBg,
|
|
48
|
-
chBg: THEME.chromeBg,
|
|
49
|
-
mark: ' ',
|
|
50
|
-
},
|
|
51
|
-
ctx: {
|
|
52
|
-
baseFg: THEME.ctxFg,
|
|
53
|
-
baseBg: THEME.ctxBg,
|
|
54
|
-
chBg: THEME.ctxBg,
|
|
55
|
-
mark: ' ',
|
|
56
|
-
},
|
|
28
|
+
del: lineStyle(THEME.delLineFg, THEME.delLineBg, THEME.delCharBg, '-'),
|
|
29
|
+
add: lineStyle(THEME.addLineFg, THEME.addLineBg, THEME.addCharBg, '+'),
|
|
30
|
+
warn: lineStyle(THEME.delLineFg, THEME.delLineBg, THEME.delCharBg),
|
|
31
|
+
note: lineStyle(THEME.chromeFg, THEME.noteBg),
|
|
32
|
+
noteSep: lineStyle(THEME.mutedFg, THEME.chromeBg),
|
|
33
|
+
ctx: lineStyle(THEME.ctxFg, THEME.ctxBg),
|
|
57
34
|
};
|
|
58
35
|
|
|
59
36
|
const STAGED_STYLES = {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
note: STYLES.note,
|
|
79
|
-
noteSep: STYLES.noteSep,
|
|
80
|
-
ctx: STYLES.ctx,
|
|
37
|
+
...STYLES,
|
|
38
|
+
del: lineStyle(
|
|
39
|
+
THEME.stagedDelLineFg,
|
|
40
|
+
THEME.stagedDelLineBg,
|
|
41
|
+
THEME.stagedDelCharBg,
|
|
42
|
+
'-',
|
|
43
|
+
),
|
|
44
|
+
add: lineStyle(
|
|
45
|
+
THEME.stagedAddLineFg,
|
|
46
|
+
THEME.stagedAddLineBg,
|
|
47
|
+
THEME.stagedAddCharBg,
|
|
48
|
+
'+',
|
|
49
|
+
),
|
|
50
|
+
warn: lineStyle(
|
|
51
|
+
THEME.stagedDelLineFg,
|
|
52
|
+
THEME.stagedDelLineBg,
|
|
53
|
+
THEME.stagedDelCharBg,
|
|
54
|
+
),
|
|
81
55
|
};
|
|
82
56
|
|
|
83
|
-
const
|
|
84
|
-
const addCharBg = () => ansi.bg(THEME.addCharBg);
|
|
57
|
+
const gutterWidth = (width) => Math.min(GUTTER_W, Math.max(0, width));
|
|
85
58
|
|
|
86
|
-
const
|
|
87
|
-
if (skip <= 0) return text ?? '';
|
|
88
|
-
const parts = graphemes(text ?? '');
|
|
89
|
-
let used = 0;
|
|
90
|
-
let i = 0;
|
|
91
|
-
while (i < parts.length) {
|
|
92
|
-
const w = graphemeWidth(parts[i], used);
|
|
93
|
-
if (used + w > skip) break;
|
|
94
|
-
used += w;
|
|
95
|
-
i += 1;
|
|
96
|
-
}
|
|
97
|
-
let out = '';
|
|
98
|
-
for (; i < parts.length; i++) out += parts[i];
|
|
99
|
-
return out;
|
|
100
|
-
};
|
|
59
|
+
const textWidth = (width) => Math.max(1, width - gutterWidth(width));
|
|
101
60
|
|
|
102
61
|
const clipPieces = (pieces, skip) => {
|
|
103
62
|
if (skip <= 0) return pieces;
|
|
@@ -110,54 +69,42 @@ const clipPieces = (pieces, skip) => {
|
|
|
110
69
|
drop -= width;
|
|
111
70
|
continue;
|
|
112
71
|
}
|
|
113
|
-
const shown = drop > 0 ?
|
|
72
|
+
const shown = drop > 0 ? sliceVisible(text, drop, Infinity) : text;
|
|
114
73
|
drop = 0;
|
|
115
74
|
if (shown) out.push({ ...piece, text: shown });
|
|
116
75
|
}
|
|
117
76
|
return out;
|
|
118
77
|
};
|
|
119
78
|
|
|
120
|
-
const paintSpans = (pieces,
|
|
79
|
+
const paintSpans = (pieces, style, budget, color) => {
|
|
121
80
|
let out = '';
|
|
122
81
|
let used = 0;
|
|
123
82
|
for (const piece of pieces) {
|
|
124
83
|
if (used >= budget) break;
|
|
125
|
-
const
|
|
126
|
-
const text = truncateVisible(piece.text, room);
|
|
84
|
+
const text = truncateVisible(piece.text, budget - used);
|
|
127
85
|
const w = visibleWidth(text);
|
|
128
86
|
if (w <= 0) continue;
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
out += paint(text, fgRgb, bgRgb, color);
|
|
87
|
+
const bgRgb = piece.changed ? style.chBg : style.baseBg;
|
|
88
|
+
out += paint(text, codeFg(piece.style), bgRgb, color);
|
|
132
89
|
used += w;
|
|
133
90
|
}
|
|
134
|
-
|
|
135
|
-
out += fill(budget - used, CODE_FG.plain, lineStyle.baseBg, color);
|
|
136
|
-
}
|
|
137
|
-
return out;
|
|
91
|
+
return out + fill(budget - used, CODE_FG.plain, style.baseBg, color);
|
|
138
92
|
};
|
|
139
93
|
|
|
140
94
|
const paintAuditNote = (line, width, color) => {
|
|
141
|
-
const
|
|
142
|
-
const gutterW =
|
|
143
|
-
const label = `${
|
|
144
|
-
const gutter = paint(label, style.baseFg, style.baseBg, color);
|
|
95
|
+
const { baseFg, baseBg, mark } = STYLES.note;
|
|
96
|
+
const gutterW = gutterWidth(width);
|
|
97
|
+
const label = `${mark} `.slice(0, gutterW);
|
|
145
98
|
const budget = Math.max(0, width - gutterW);
|
|
146
99
|
const text = truncateVisible(line.text ?? '', budget);
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (used < budget) {
|
|
151
|
-
out += fill(budget - used, style.baseFg, style.baseBg, color);
|
|
152
|
-
}
|
|
153
|
-
return out;
|
|
100
|
+
let out = paint(label, baseFg, baseBg, color);
|
|
101
|
+
out += paint(text, baseFg, baseBg, color);
|
|
102
|
+
return out + fill(budget - visibleWidth(text), baseFg, baseBg, color);
|
|
154
103
|
};
|
|
155
104
|
|
|
156
105
|
const paintAuditRule = (width, color) => {
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
const bar = '─'.repeat(n);
|
|
160
|
-
return paint(bar, style.baseFg, style.baseBg, color);
|
|
106
|
+
const { baseFg, baseBg } = STYLES.noteSep;
|
|
107
|
+
return paint('─'.repeat(Math.max(0, width)), baseFg, baseBg, color);
|
|
161
108
|
};
|
|
162
109
|
|
|
163
110
|
const paintDiffLine = (line, width, color, origin, lang, scrollCol = 0) => {
|
|
@@ -168,32 +115,21 @@ const paintDiffLine = (line, width, color, origin, lang, scrollCol = 0) => {
|
|
|
168
115
|
const diffSpans = line.spans ?? [{ text: line.text, changed: false }];
|
|
169
116
|
const tokens = tokenize(lang, line.text ?? '');
|
|
170
117
|
const pieces = clipPieces(overlayTokens(tokens, diffSpans), scrollCol);
|
|
171
|
-
const gutterW =
|
|
118
|
+
const gutterW = gutterWidth(width);
|
|
172
119
|
const label = `${line.mark ?? style.mark} `.slice(0, gutterW);
|
|
173
120
|
const gutter = paint(label, CODE_FG.punct, style.baseBg, color);
|
|
174
121
|
const budget = Math.max(0, width - gutterW);
|
|
175
|
-
|
|
176
|
-
return gutter + rest;
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
const paintSplitHalf = (line, width, color, origin, lang, scrollCol) => {
|
|
180
|
-
if (!line || width <= 0) {
|
|
181
|
-
return fill(width, THEME.ctxFg, THEME.ctxBg, color);
|
|
182
|
-
}
|
|
183
|
-
return paintDiffLine(line, width, color, origin, lang, scrollCol);
|
|
122
|
+
return gutter + paintSpans(pieces, style, budget, color);
|
|
184
123
|
};
|
|
185
124
|
|
|
186
125
|
const paintSplitRow = (row, width, color, origin, lang, scrollCol) => {
|
|
187
126
|
const { leftW, rightW } = splitWidths(width);
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
lang,
|
|
195
|
-
scrollCol,
|
|
196
|
-
);
|
|
127
|
+
const half = (line, halfW) => {
|
|
128
|
+
if (!line) return fill(halfW, THEME.ctxFg, THEME.ctxBg, color);
|
|
129
|
+
return paintDiffLine(line, halfW, color, origin, lang, scrollCol);
|
|
130
|
+
};
|
|
131
|
+
const left = half(row.left, leftW);
|
|
132
|
+
const right = half(row.right, rightW);
|
|
197
133
|
return `${left}${paintSplitRule(color)}${right}`;
|
|
198
134
|
};
|
|
199
135
|
|
|
@@ -213,178 +149,137 @@ const sourceEmpty = (line) => {
|
|
|
213
149
|
return !(line.text ?? '').trim();
|
|
214
150
|
};
|
|
215
151
|
|
|
216
|
-
const
|
|
152
|
+
const rowEmpty = (row, split) => {
|
|
217
153
|
if (!split) return sourceEmpty(row);
|
|
218
154
|
return sourceEmpty(row.left) && sourceEmpty(row.right);
|
|
219
155
|
};
|
|
220
156
|
|
|
221
157
|
const dropLeadingEmpty = (lines, split) => {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
return i ? lines.slice(i) : lines;
|
|
158
|
+
const at = lines.findIndex((row) => !rowEmpty(row, split));
|
|
159
|
+
if (at < 0) return [];
|
|
160
|
+
return at ? lines.slice(at) : lines;
|
|
227
161
|
};
|
|
228
162
|
|
|
229
163
|
const cutSpans = (spans, count) => {
|
|
230
164
|
const taken = [];
|
|
231
165
|
const rest = [];
|
|
232
166
|
let left = count;
|
|
233
|
-
|
|
234
|
-
while (i < spans.length) {
|
|
235
|
-
const span = spans[i];
|
|
167
|
+
for (const span of spans) {
|
|
236
168
|
const text = span.text ?? '';
|
|
237
169
|
if (left <= 0) {
|
|
238
170
|
rest.push(span);
|
|
239
|
-
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
if (text.length <= left) {
|
|
171
|
+
} else if (text.length <= left) {
|
|
243
172
|
taken.push(span);
|
|
244
173
|
left -= text.length;
|
|
245
|
-
|
|
246
|
-
|
|
174
|
+
} else {
|
|
175
|
+
const changed = span.changed === true;
|
|
176
|
+
taken.push({ text: text.slice(0, left), changed });
|
|
177
|
+
rest.push({ text: text.slice(left), changed });
|
|
178
|
+
left = 0;
|
|
247
179
|
}
|
|
248
|
-
const changed = span.changed === true;
|
|
249
|
-
taken.push({ text: text.slice(0, left), changed });
|
|
250
|
-
rest.push({ text: text.slice(left), changed });
|
|
251
|
-
left = 0;
|
|
252
|
-
i += 1;
|
|
253
180
|
}
|
|
254
181
|
return { taken, rest };
|
|
255
182
|
};
|
|
256
183
|
|
|
257
184
|
const wrapSoftLine = (line, width) => {
|
|
258
|
-
const
|
|
259
|
-
const budget = Math.max(1, width - gutterW);
|
|
260
|
-
const rows = wrapPlain(line.text ?? '', budget);
|
|
185
|
+
const rows = wrapPlain(line.text ?? '', textWidth(width));
|
|
261
186
|
const changed = line.wrap === true;
|
|
262
|
-
const { length } = rows;
|
|
263
|
-
const pieces = new Array(length);
|
|
264
187
|
let used = 0;
|
|
265
188
|
let spans = line.spans ?? [{ text: line.text ?? '', changed }];
|
|
266
|
-
|
|
267
|
-
const
|
|
268
|
-
const last = i === length - 1;
|
|
189
|
+
return rows.map((text, index) => {
|
|
190
|
+
const last = index === rows.length - 1;
|
|
269
191
|
const cut = cutSpans(spans, text.length);
|
|
270
192
|
spans = cut.rest;
|
|
271
193
|
const nextSpans = cut.taken.length ? cut.taken : [{ text, changed }];
|
|
272
194
|
const next = { ...line, text, spans: nextSpans, editLast: last };
|
|
273
195
|
if (line.editStart !== undefined) {
|
|
274
196
|
next.editStart = line.editStart + used;
|
|
275
|
-
|
|
276
|
-
else next.editEnd = next.editStart + text.length;
|
|
197
|
+
next.editEnd = last ? line.editEnd : next.editStart + text.length;
|
|
277
198
|
}
|
|
278
199
|
used += text.length;
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
return pieces;
|
|
200
|
+
return next;
|
|
201
|
+
});
|
|
282
202
|
};
|
|
283
203
|
|
|
284
204
|
const expandSoftRows = (lines, width, split, wrapView) => {
|
|
285
205
|
const { leftW, rightW } = splitWidths(width);
|
|
286
|
-
const out = [];
|
|
287
206
|
const partsOf = (line, lineW) => {
|
|
288
207
|
if (!wrapView || !line || line.type === 'noteSep') return [line];
|
|
289
|
-
|
|
290
|
-
const budget = Math.max(1, lineW - gutterW);
|
|
291
|
-
if (visibleWidth(line.text ?? '') <= budget) return [line];
|
|
208
|
+
if (visibleWidth(line.text ?? '') <= textWidth(lineW)) return [line];
|
|
292
209
|
return wrapSoftLine(line, lineW);
|
|
293
210
|
};
|
|
211
|
+
if (!split) return lines.flatMap((line) => partsOf(line, width));
|
|
212
|
+
const out = [];
|
|
294
213
|
for (const row of lines) {
|
|
295
|
-
if (!split) {
|
|
296
|
-
for (const part of partsOf(row, width)) out.push(part);
|
|
297
|
-
continue;
|
|
298
|
-
}
|
|
299
214
|
const leftParts = partsOf(row.left, leftW);
|
|
300
215
|
const rightParts = partsOf(row.right, rightW);
|
|
301
216
|
const n = Math.max(leftParts.length, rightParts.length);
|
|
302
217
|
for (let i = 0; i < n; i++) {
|
|
303
|
-
out.push({
|
|
304
|
-
left: leftParts[i] ?? null,
|
|
305
|
-
right: rightParts[i] ?? null,
|
|
306
|
-
});
|
|
218
|
+
out.push({ left: leftParts[i] ?? null, right: rightParts[i] ?? null });
|
|
307
219
|
}
|
|
308
220
|
}
|
|
309
221
|
return out;
|
|
310
222
|
};
|
|
311
223
|
|
|
312
|
-
const
|
|
313
|
-
if (!line || line.editStart === undefined) return false;
|
|
314
|
-
if (cursor < line.editStart) return false;
|
|
315
|
-
return cursor <= line.editEnd;
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
const codeCursorInRows = (rows, split, width, cursor, scrollCol = 0) => {
|
|
319
|
-
if (cursor === undefined || cursor === null) return null;
|
|
224
|
+
const codeHits = (rows, split, width, overlay, editing, scrollCol) => {
|
|
320
225
|
const { leftW, rightW } = splitWidths(width);
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
226
|
+
const lineW = split ? rightW : width;
|
|
227
|
+
const x0 = split ? leftW + 1 : 0;
|
|
228
|
+
const textX = x0 + gutterWidth(lineW) + 1;
|
|
229
|
+
const at = overlay ? overlay.cursor : null;
|
|
230
|
+
const hasCursor = at !== null && at !== undefined;
|
|
231
|
+
const editHits = [];
|
|
232
|
+
let cursor = null;
|
|
233
|
+
for (let row = 0; row < rows.length; row++) {
|
|
234
|
+
const line = split ? rows[row].right : rows[row];
|
|
235
|
+
if (!line || line.editStart === undefined) continue;
|
|
236
|
+
if (editing) {
|
|
237
|
+
editHits.push({
|
|
238
|
+
row,
|
|
239
|
+
x0: x0 + 1,
|
|
240
|
+
textX,
|
|
241
|
+
x1: x0 + lineW + 1,
|
|
242
|
+
scroll: scrollCol,
|
|
243
|
+
start: line.editStart,
|
|
244
|
+
text: line.text ?? '',
|
|
245
|
+
limit: line.editEnd,
|
|
246
|
+
live: true,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
if (cursor || !hasCursor) continue;
|
|
250
|
+
const onPiece = at >= line.editStart && at <= line.editEnd;
|
|
251
|
+
if (!onPiece) continue;
|
|
252
|
+
const take = Math.min(at - line.editStart, line.text.length);
|
|
327
253
|
const col = visibleWidth(line.text.slice(0, take));
|
|
328
|
-
|
|
329
|
-
const x0 = split ? leftW + 1 : 0;
|
|
330
|
-
return { x: x0 + gutterW + shown + 1, row: i };
|
|
254
|
+
cursor = { x: textX + Math.max(0, col - scrollCol), row };
|
|
331
255
|
}
|
|
332
|
-
return
|
|
256
|
+
return { cursor, editHits };
|
|
333
257
|
};
|
|
334
258
|
|
|
335
|
-
const codeInnerWidth = (width, layout = 'unified') =>
|
|
336
|
-
|
|
337
|
-
const gutterW = Math.min(GUTTER_W, Math.max(0, lineW));
|
|
338
|
-
return Math.max(1, lineW - gutterW);
|
|
339
|
-
};
|
|
259
|
+
const codeInnerWidth = (width, layout = 'unified') =>
|
|
260
|
+
textWidth(layout === 'side' ? splitWidths(width).rightW : width);
|
|
340
261
|
|
|
341
262
|
const paintBodyDiff = (view, width, color) => {
|
|
342
|
-
const item = view
|
|
263
|
+
const { item, codeOverlay: overlay } = view;
|
|
343
264
|
const layout = view.layout ?? 'unified';
|
|
344
|
-
const overlay = view.codeOverlay;
|
|
345
265
|
const lang = detectLang(itemPath(item));
|
|
346
266
|
const splitBody = layout === 'side' && !item.file.isBinary;
|
|
347
|
-
const editing =
|
|
348
|
-
const
|
|
349
|
-
const scrollCol = picked ?? 0;
|
|
267
|
+
const editing = typeof overlay?.cursor === 'number';
|
|
268
|
+
const scrollCol = (editing ? overlay.scrollCol : 0) ?? 0;
|
|
350
269
|
const raw = dropLeadingEmpty(bodyLines(item, layout, overlay), splitBody);
|
|
351
270
|
const lines = expandSoftRows(raw, width, splitBody, !editing);
|
|
352
|
-
const
|
|
353
|
-
const body =
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
width,
|
|
359
|
-
color,
|
|
360
|
-
item.origin,
|
|
361
|
-
lang,
|
|
362
|
-
scrollCol,
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
} else {
|
|
366
|
-
for (let i = 0; i < length; i++) {
|
|
367
|
-
body[i] = paintDiffLine(
|
|
368
|
-
lines[i],
|
|
369
|
-
width,
|
|
370
|
-
color,
|
|
371
|
-
item.origin,
|
|
372
|
-
lang,
|
|
373
|
-
scrollCol,
|
|
374
|
-
);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
const edit = overlay ? overlay.cursor : null;
|
|
378
|
-
const cursor = codeCursorInRows(lines, splitBody, width, edit, scrollCol);
|
|
379
|
-
return paneResult({ body, cursor, splitBody });
|
|
271
|
+
const paintRow = splitBody ? paintSplitRow : paintDiffLine;
|
|
272
|
+
const body = lines.map((line) =>
|
|
273
|
+
paintRow(line, width, color, item.origin, lang, scrollCol),
|
|
274
|
+
);
|
|
275
|
+
const hits = codeHits(lines, splitBody, width, overlay, editing, scrollCol);
|
|
276
|
+
return paneResult({ body, splitBody, ...hits });
|
|
380
277
|
};
|
|
381
278
|
|
|
382
279
|
module.exports = {
|
|
383
|
-
delCharBg,
|
|
384
|
-
addCharBg,
|
|
385
280
|
paintDiffLine,
|
|
386
281
|
codeInnerWidth,
|
|
387
282
|
paintBodyDiff,
|
|
388
|
-
codeCursorInRows,
|
|
389
283
|
expandSoftRows,
|
|
284
|
+
codeHits,
|
|
390
285
|
};
|
package/lib/render/files.js
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
const ansi = require('../ansi.js');
|
|
4
4
|
const files = require('../files.js');
|
|
5
5
|
const primitives = require('./primitives.js');
|
|
6
|
-
const { measureColumns, padColumns } = primitives;
|
|
7
6
|
|
|
8
|
-
const {
|
|
9
|
-
const { THEME, paint, visibleWidth,
|
|
10
|
-
const { fill,
|
|
7
|
+
const { isTasksEntry, shortAge } = files;
|
|
8
|
+
const { THEME, paint, visibleWidth, trimVisible, seq, EL } = ansi;
|
|
9
|
+
const { fill, markPrefix, paintCursorList } = primitives;
|
|
10
|
+
const { measureColumns, padColumns } = primitives;
|
|
11
11
|
|
|
12
12
|
const FILE_STATUS_FG = {
|
|
13
13
|
unstaged: THEME.delLineFg,
|
|
@@ -27,124 +27,70 @@ const FILE_COL_ALIGN = {
|
|
|
27
27
|
|
|
28
28
|
const FILE_DATE_GAP = ' ';
|
|
29
29
|
|
|
30
|
-
const signedCount = (sign, n) => `${sign}${n ?? 0}`;
|
|
31
|
-
|
|
32
30
|
const fileRowFields = (entry) => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
const n = `${entry.staged ?? 0}`;
|
|
32
|
+
const m = `${entry.remaining ?? 0}`;
|
|
33
|
+
if (isTasksEntry(entry)) {
|
|
36
34
|
return { add: '', del: '', status: '', n, m, date: '' };
|
|
37
35
|
}
|
|
38
|
-
const {
|
|
39
|
-
const
|
|
40
|
-
const del = signedCount('-', removed);
|
|
36
|
+
const add = `+${entry.added ?? 0}`;
|
|
37
|
+
const del = `-${entry.removed ?? 0}`;
|
|
41
38
|
const status = entry.status ?? '';
|
|
42
|
-
|
|
43
|
-
const m = `${remaining ?? 0}`;
|
|
44
|
-
const date = shortAge(entry.date ?? '');
|
|
45
|
-
return { add, del, status, n, m, date };
|
|
39
|
+
return { add, del, status, n, m, date: shortAge(entry.date) };
|
|
46
40
|
};
|
|
47
41
|
|
|
48
|
-
const fileRowFg = (
|
|
49
|
-
|
|
42
|
+
const fileRowFg = (entry, selected) => {
|
|
43
|
+
if (isTasksEntry(entry)) return THEME.buttonHotFg;
|
|
44
|
+
const statusFg = FILE_STATUS_FG[entry.status];
|
|
50
45
|
if (statusFg) return statusFg;
|
|
51
|
-
|
|
52
|
-
return THEME.mutedFg;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const joinFileDate = (parts, cols) => {
|
|
56
|
-
if (!cols?.date) return '';
|
|
57
|
-
return `${FILE_DATE_GAP}${parts.date}`;
|
|
46
|
+
return selected ? THEME.chromeFg : THEME.mutedFg;
|
|
58
47
|
};
|
|
59
48
|
|
|
60
|
-
const
|
|
61
|
-
const { add, del, status, n, m } = parts;
|
|
62
|
-
return ` ${add} ${del} ${status} ${n}/${m}`;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const paintFileSuffix = (parts, cols, fgRgb, bgRgb, color) => {
|
|
66
|
-
const { add, del, status, n, m } = parts;
|
|
49
|
+
const paintFileMeta = (parts, fgRgb, bgRgb, color) => {
|
|
67
50
|
const gap = paint(' ', fgRgb, bgRgb, color);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const counts = addText + gap + delText;
|
|
75
|
-
const meta = statusText + gap + nText + slash + mText;
|
|
76
|
-
if (!cols?.date) return gap + counts + gap + meta;
|
|
77
|
-
const dateGap = paint(FILE_DATE_GAP, fgRgb, bgRgb, color);
|
|
78
|
-
const age = paint(parts.date, THEME.mutedFg, bgRgb, color);
|
|
79
|
-
return gap + counts + gap + meta + dateGap + age;
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const paintTodosRow = (entry, width, color, selected, cols) => {
|
|
83
|
-
const inner = Math.max(1, width - 1);
|
|
84
|
-
const mark = selected ? FILE_MARK : ' ';
|
|
85
|
-
const prefix = ` ${mark} `;
|
|
86
|
-
const parts = padColumns(fileRowFields(entry), cols, FILE_COL_ALIGN);
|
|
87
|
-
const meta = joinFileMeta(parts);
|
|
88
|
-
const dated = joinFileDate(parts, cols);
|
|
89
|
-
const suffix = `${meta}${dated}`;
|
|
90
|
-
const pathW = Math.max(1, inner - visibleWidth(prefix + suffix));
|
|
91
|
-
const clipped = truncateVisible(entry.path, pathW);
|
|
92
|
-
const pad = Math.max(0, pathW - visibleWidth(clipped));
|
|
93
|
-
const left = `${prefix}${clipped}${' '.repeat(pad)}`;
|
|
94
|
-
const fgRgb = THEME.buttonHotFg;
|
|
95
|
-
const bgRgb = selected ? THEME.buttonBg : THEME.ctxBg;
|
|
96
|
-
const edge = fill(1, fgRgb, bgRgb, color);
|
|
97
|
-
if (!color) return `${left}${suffix}${edge}`;
|
|
98
|
-
let out = `${seq(fgRgb, bgRgb)}${EL}`;
|
|
99
|
-
out += paint(left, fgRgb, bgRgb, color, true);
|
|
100
|
-
out += paint(meta, fgRgb, bgRgb, color, true);
|
|
101
|
-
if (!dated) return out + edge;
|
|
102
|
-
out += paint(FILE_DATE_GAP, fgRgb, bgRgb, color);
|
|
103
|
-
out += paint(parts.date, THEME.mutedFg, bgRgb, color);
|
|
104
|
-
return out + edge;
|
|
51
|
+
let out = gap + paint(parts.add, THEME.addLineFg, bgRgb, color);
|
|
52
|
+
out += gap + paint(parts.del, THEME.delLineFg, bgRgb, color);
|
|
53
|
+
out += gap + paint(parts.status, fgRgb, bgRgb, color);
|
|
54
|
+
out += gap + paint(parts.n, fgRgb, bgRgb, color);
|
|
55
|
+
out += paint('/', fgRgb, bgRgb, color);
|
|
56
|
+
return out + paint(parts.m, fgRgb, bgRgb, color);
|
|
105
57
|
};
|
|
106
58
|
|
|
107
59
|
const paintFileRow = (entry, width, color, selected, cols) => {
|
|
108
|
-
|
|
109
|
-
return paintTodosRow(entry, width, color, selected, cols);
|
|
110
|
-
}
|
|
60
|
+
const tasks = isTasksEntry(entry);
|
|
111
61
|
const inner = Math.max(1, width - 1);
|
|
112
|
-
const
|
|
113
|
-
const prefix = ` ${mark} `;
|
|
62
|
+
const prefix = markPrefix(selected);
|
|
114
63
|
const parts = padColumns(fileRowFields(entry), cols, FILE_COL_ALIGN);
|
|
115
|
-
const
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
const
|
|
119
|
-
const
|
|
120
|
-
const
|
|
121
|
-
const
|
|
64
|
+
const { add, del, status, n, m } = parts;
|
|
65
|
+
const meta = ` ${add} ${del} ${status} ${n}/${m}`;
|
|
66
|
+
const date = cols.date ? `${FILE_DATE_GAP}${parts.date}` : '';
|
|
67
|
+
const pathW = Math.max(1, inner - visibleWidth(prefix + meta + date));
|
|
68
|
+
const path = trimVisible(entry.path, pathW);
|
|
69
|
+
const pad = ' '.repeat(Math.max(0, pathW - visibleWidth(path)));
|
|
70
|
+
const left = `${prefix}${path}${pad}`;
|
|
71
|
+
const fgRgb = fileRowFg(entry, selected);
|
|
122
72
|
const bgRgb = selected ? THEME.buttonBg : THEME.ctxBg;
|
|
123
73
|
const edge = fill(1, fgRgb, bgRgb, color);
|
|
124
|
-
if (!color) return `${left}${
|
|
74
|
+
if (!color) return `${left}${meta}${date}${edge}`;
|
|
125
75
|
let out = `${seq(fgRgb, bgRgb)}${EL}`;
|
|
126
|
-
out += paint(left, fgRgb, bgRgb, color);
|
|
127
|
-
out +=
|
|
76
|
+
out += paint(left, fgRgb, bgRgb, color, tasks);
|
|
77
|
+
if (tasks) out += paint(meta, fgRgb, bgRgb, color, true);
|
|
78
|
+
else out += paintFileMeta(parts, fgRgb, bgRgb, color);
|
|
79
|
+
if (date) {
|
|
80
|
+
out += paint(FILE_DATE_GAP, fgRgb, bgRgb, color);
|
|
81
|
+
out += paint(parts.date, THEME.mutedFg, bgRgb, color);
|
|
82
|
+
}
|
|
128
83
|
return out + edge;
|
|
129
84
|
};
|
|
130
85
|
|
|
131
86
|
const paintBodyFiles = (view, width, color, bodyH, headerLines) => {
|
|
132
|
-
const
|
|
133
|
-
const cols = measureColumns(
|
|
87
|
+
const entries = view.files ?? [];
|
|
88
|
+
const cols = measureColumns(entries, fileRowFields, FILE_COL_ALIGN);
|
|
134
89
|
const cursor = view.fileCursor ?? 0;
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
cursor,
|
|
140
|
-
width,
|
|
141
|
-
color,
|
|
142
|
-
bodyH,
|
|
143
|
-
headerLines,
|
|
144
|
-
paintRow,
|
|
145
|
-
null,
|
|
146
|
-
view.listScroll,
|
|
147
|
-
);
|
|
90
|
+
const pane = { width, color, bodyH, headerLines, offset: view.listScroll };
|
|
91
|
+
const paintRow = (entry, selected) =>
|
|
92
|
+
paintFileRow(entry, width, color, selected, cols);
|
|
93
|
+
return paintCursorList(entries, cursor, pane, paintRow);
|
|
148
94
|
};
|
|
149
95
|
|
|
150
96
|
module.exports = { paintBodyFiles };
|