reslop 0.2.6 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -1
- package/README.md +16 -14
- package/bin/reslop.js +8 -5
- package/lib/agents/agents.js +504 -0
- package/lib/agents/allow.js +71 -0
- package/lib/agents/allow.json +16 -0
- package/lib/agents/models.js +460 -0
- package/lib/api.js +15 -12
- package/lib/cli.js +61 -66
- package/lib/{files.js → common/files.js} +51 -52
- package/lib/common/find.js +61 -0
- package/lib/common/format.js +101 -0
- package/lib/{utilities.js → common/process.js} +7 -95
- package/lib/common/tasks.json +27 -0
- package/lib/common/utilities.js +112 -0
- package/lib/dashboard/git.js +9 -16
- package/lib/dashboard/model.js +117 -51
- package/lib/dashboard/npm.js +133 -32
- package/lib/dashboard/tree.js +27 -30
- package/lib/{session → dashboard}/watch.js +41 -31
- package/lib/dashboard/workspace.js +151 -0
- package/lib/{deps.js → deps/deps.js} +31 -45
- package/lib/{deps-diff.js → deps/diff.js} +35 -25
- package/lib/{manifest.js → deps/manifest.js} +47 -30
- package/lib/diff/align.js +1 -2
- package/lib/diff/diff.js +2 -2
- package/lib/diff/display.js +29 -5
- package/lib/diff/inline.js +10 -14
- package/lib/diff/items.js +6 -4
- package/lib/diff/patch.js +39 -3
- package/lib/diff/unit.js +45 -16
- package/lib/{git-branches.js → git/branches.js} +53 -48
- package/lib/{git-deps.js → git/deps.js} +123 -95
- package/lib/git/git.js +71 -0
- package/lib/git/worktree.js +475 -0
- package/lib/highlight/bash.js +1 -2
- package/lib/highlight/csv.js +1 -2
- package/lib/highlight/dot.js +1 -2
- package/lib/{highlight.js → highlight/highlight.js} +42 -11
- package/lib/highlight/html.js +20 -18
- package/lib/highlight/json.js +1 -2
- package/lib/highlight/log.js +1 -2
- package/lib/highlight/txt.js +1 -2
- package/lib/{session → input}/actions.js +240 -71
- package/lib/{editor.js → input/editor.js} +7 -8
- package/lib/{keys.js → input/keys.js} +36 -39
- package/lib/input/line-edit.js +77 -0
- package/lib/input/menu-pick.js +70 -0
- package/lib/render/agents.js +640 -0
- package/lib/render/branches.js +4 -5
- package/lib/render/commits.js +54 -64
- package/lib/render/{dash-activity.js → dashboard/activity.js} +25 -40
- package/lib/render/dashboard/blocks.js +490 -0
- package/lib/render/{dashboard.js → dashboard/dashboard.js} +25 -22
- package/lib/render/diff.js +166 -45
- package/lib/render/field.js +131 -0
- package/lib/render/files.js +22 -8
- package/lib/render/footer.js +383 -0
- package/lib/render/frame.js +127 -25
- package/lib/render/header.js +176 -0
- package/lib/render/log.js +319 -0
- package/lib/render/menu.js +60 -0
- package/lib/render/notes.js +29 -13
- package/lib/render/npm.js +322 -196
- package/lib/render/packages.js +89 -0
- package/lib/render/primitives.js +109 -16
- package/lib/render/render.js +7 -11
- package/lib/render/repos.js +214 -0
- package/lib/render/status.js +370 -0
- package/lib/render/status.json +85 -0
- package/lib/render/{dash-table.js → table.js} +19 -61
- package/lib/render/tiles.js +203 -47
- package/lib/render/unit.js +32 -13
- package/lib/{report-model.js → report/model.js} +4 -6
- package/lib/{report-parse.js → report/parse.js} +9 -14
- package/lib/{report-render.js → report/render.js} +46 -14
- package/lib/{report-run.js → report/run.js} +42 -30
- package/lib/{report-test.js → report/test.js} +56 -55
- package/lib/{report-tools.js → report/tools.js} +24 -33
- package/lib/review/agent-instructions.md +9 -0
- package/lib/review/format.js +426 -0
- package/lib/{review-merge.js → review/merge.js} +9 -8
- package/lib/review/review.js +190 -0
- package/lib/review/store.js +281 -0
- package/lib/review/templates.js +83 -0
- package/lib/runs/commands.js +14 -0
- package/lib/runs/logs.js +299 -0
- package/lib/runs/output.js +174 -0
- package/lib/runs/process.js +113 -0
- package/lib/{runs.js → runs/runs.js} +84 -36
- package/lib/runs/scripts.js +150 -0
- package/lib/session/agents/agents.js +958 -0
- package/lib/session/agents/job.js +320 -0
- package/lib/session/agents/jobs.js +355 -0
- package/lib/session/agents/options.js +153 -0
- package/lib/session/agents/plans.js +83 -0
- package/lib/session/branches.js +36 -44
- package/lib/session/change-actions.js +34 -100
- package/lib/session/commit.js +122 -56
- package/lib/session/compose.js +197 -146
- package/lib/session/dashboard.js +183 -37
- package/lib/session/dispatch.js +120 -22
- package/lib/session/files-pane.js +60 -24
- package/lib/session/find.js +81 -0
- package/lib/session/import.js +82 -0
- package/lib/session/items.js +8 -17
- package/lib/session/lifecycle.js +96 -66
- package/lib/session/load.js +2 -4
- package/lib/session/navigation.js +8 -3
- package/lib/session/npm/history.js +249 -0
- package/lib/session/npm/npm.js +815 -0
- package/lib/session/npm/packages.js +150 -0
- package/lib/session/ops.js +125 -34
- package/lib/session/pointer.js +216 -10
- package/lib/session/prefs.js +112 -0
- package/lib/session/reads.js +109 -0
- package/lib/session/review.js +28 -10
- package/lib/session/run-panel.js +40 -0
- package/lib/{session.js → session/session.js} +166 -59
- package/lib/session/tasks.js +321 -66
- package/lib/session/templates.js +1 -1
- package/lib/session/terminal.js +94 -12
- package/lib/session/unit.js +35 -30
- package/lib/session/update.js +194 -6
- package/lib/session/view.js +72 -59
- package/lib/session/workspace.js +416 -0
- package/lib/{capabilities.js → source/capabilities.js} +3 -12
- package/lib/{github.js → source/github.js} +102 -32
- package/lib/{gitlab.js → source/gitlab.js} +87 -10
- package/lib/{remote.js → source/remote.js} +144 -40
- package/lib/source/review-threads.graphql +20 -0
- package/lib/source/source.js +74 -0
- package/lib/term/ansi.js +396 -0
- package/lib/{clipboard.js → term/clipboard.js} +3 -6
- package/lib/{select.js → term/select.js} +2 -2
- package/lib/term/themes/dark.json +122 -0
- package/lib/term/themes/light.json +122 -0
- package/lib/term/wide.txt +21 -0
- package/lib/{wrap.js → term/wrap.js} +4 -4
- package/package.json +2 -2
- package/lib/ansi.js +0 -505
- package/lib/detect.js +0 -34
- package/lib/diff/edit.js +0 -33
- package/lib/git-worktree.js +0 -437
- package/lib/git.js +0 -79
- package/lib/npm-commands.js +0 -522
- package/lib/render/chrome.js +0 -628
- package/lib/render/dash-blocks.js +0 -245
- package/lib/render/present.js +0 -19
- package/lib/render/report.js +0 -43
- package/lib/review.js +0 -744
- package/lib/session/accessors.js +0 -26
- package/lib/session/edit-click.js +0 -57
- package/lib/session/npm.js +0 -452
- package/lib/session/progress.js +0 -41
- package/lib/source.js +0 -36
- package/lib/update.js +0 -192
- /package/lib/{git-reword-todo.js → git/reword-todo.js} +0 -0
package/lib/render/diff.js
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
3
|
+
const { clamp } = require('../common/utilities.js');
|
|
4
|
+
const ansi = require('../term/ansi.js');
|
|
5
|
+
const { displayLines } = require('../diff/diff.js');
|
|
6
|
+
const highlight = require('../highlight/highlight.js');
|
|
7
|
+
const { tokenize, overlayTokens, detectLang } = highlight;
|
|
8
|
+
const { itemPath } = require('../common/files.js');
|
|
9
|
+
const { wrapPlain } = require('../term/wrap.js');
|
|
9
10
|
const primitives = require('./primitives.js');
|
|
10
11
|
|
|
11
|
-
const { displayLines } = diff;
|
|
12
|
-
const { detectLang } = detect;
|
|
13
|
-
const { itemPath } = files;
|
|
14
|
-
const { tokenize, overlayTokens } = highlight;
|
|
15
|
-
const { wrapPlain } = wrap;
|
|
16
12
|
const { THEME, CODE_FG, codeFg, paint, visibleWidth, truncateVisible } = ansi;
|
|
17
13
|
const { GUTTER_W, fill, splitWidths, paintSplitRule } = primitives;
|
|
18
|
-
const { paneResult, sliceVisible } = primitives;
|
|
14
|
+
const { paneResult, lazyRows, sliceVisible } = primitives;
|
|
19
15
|
|
|
20
16
|
const lineStyle = (baseFg, baseBg, chBg = baseBg, mark = ' ') => ({
|
|
21
17
|
baseFg,
|
|
@@ -54,9 +50,80 @@ const STAGED_STYLES = {
|
|
|
54
50
|
),
|
|
55
51
|
};
|
|
56
52
|
|
|
57
|
-
const
|
|
53
|
+
const numberCols = (digits) => (digits > 0 ? digits + 1 : 0);
|
|
58
54
|
|
|
59
|
-
const
|
|
55
|
+
const paintNumberFill = (width, color, digits, split) => {
|
|
56
|
+
const half = (lineW) => {
|
|
57
|
+
const cols = clamp(lineW, 0, numberCols(digits));
|
|
58
|
+
const rest = Math.max(0, lineW - cols);
|
|
59
|
+
const gutter = fill(cols, THEME.lineNoFg, THEME.lineNoBg, color);
|
|
60
|
+
const code = fill(rest, THEME.ctxFg, THEME.ctxBg, color);
|
|
61
|
+
return gutter + code;
|
|
62
|
+
};
|
|
63
|
+
if (!split) return half(width);
|
|
64
|
+
const { leftW, rightW } = splitWidths(width);
|
|
65
|
+
const rule = paintSplitRule(color);
|
|
66
|
+
return half(leftW) + rule + half(rightW);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const gutterWidth = (width, digits = 0) => {
|
|
70
|
+
const want = GUTTER_W + numberCols(digits);
|
|
71
|
+
return Math.min(want, Math.max(0, width));
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const textWidth = (width, digits = 0) =>
|
|
75
|
+
Math.max(1, width - gutterWidth(width, digits));
|
|
76
|
+
|
|
77
|
+
const digitsForMax = (max) => {
|
|
78
|
+
const n = Math.max(1, max || 1);
|
|
79
|
+
return Math.max(2, String(n).length);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const hunkDigits = (hunk) => {
|
|
83
|
+
if (!hunk) return digitsForMax(1);
|
|
84
|
+
const end = (start, count) => {
|
|
85
|
+
if (!start || start < 1) return 0;
|
|
86
|
+
return start + Math.max(count || 1, 1) - 1;
|
|
87
|
+
};
|
|
88
|
+
const oldEnd = end(hunk.oldStart, hunk.oldCount);
|
|
89
|
+
const newEnd = end(hunk.newStart, hunk.newCount);
|
|
90
|
+
return digitsForMax(Math.max(oldEnd, newEnd, 1));
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const overlayDigits = (view, hunk, fromHunk) => {
|
|
94
|
+
const text = view.codeOverlay && view.codeOverlay.text;
|
|
95
|
+
if (typeof text !== 'string') return fromHunk;
|
|
96
|
+
const count = text ? text.split('\n').length : 1;
|
|
97
|
+
const start = hunk && hunk.newStart > 0 ? hunk.newStart : 1;
|
|
98
|
+
return Math.max(fromHunk, digitsForMax(start + count - 1));
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const viewDigits = (view) => {
|
|
102
|
+
if (!view || view.lineNumbers !== true) return 0;
|
|
103
|
+
if (view.pane === 'unit') {
|
|
104
|
+
let max = 1;
|
|
105
|
+
for (const line of view.unitLines ?? []) {
|
|
106
|
+
max = Math.max(max, line.newNo || 0);
|
|
107
|
+
}
|
|
108
|
+
return digitsForMax(max);
|
|
109
|
+
}
|
|
110
|
+
const hunk = view.item && view.item.hunk;
|
|
111
|
+
return overlayDigits(view, hunk, hunkDigits(hunk));
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const activeDigits = (ui) => {
|
|
115
|
+
if (!ui || ui.lineNumbers !== true) return 0;
|
|
116
|
+
if (typeof ui.view !== 'function') return 0;
|
|
117
|
+
return viewDigits(ui.view());
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const lineNumber = (line, side) => {
|
|
121
|
+
if (!line) return 0;
|
|
122
|
+
if (side === 'left') return line.oldNo || 0;
|
|
123
|
+
if (side === 'right') return line.newNo || 0;
|
|
124
|
+
if (line.type === 'del') return line.oldNo || 0;
|
|
125
|
+
return line.newNo || 0;
|
|
126
|
+
};
|
|
60
127
|
|
|
61
128
|
const clipPieces = (pieces, skip) => {
|
|
62
129
|
if (skip <= 0) return pieces;
|
|
@@ -91,45 +158,79 @@ const paintSpans = (pieces, style, budget, color) => {
|
|
|
91
158
|
return out + fill(budget - used, CODE_FG.plain, style.baseBg, color);
|
|
92
159
|
};
|
|
93
160
|
|
|
94
|
-
const
|
|
161
|
+
const paintNumber = (line, digits, side, color) => {
|
|
162
|
+
if (!digits) return '';
|
|
163
|
+
const value = lineNumber(line, side);
|
|
164
|
+
const label = value ? String(value) : '';
|
|
165
|
+
const text = `${label.padStart(digits, ' ')} `;
|
|
166
|
+
return paint(text, THEME.lineNoFg, THEME.lineNoBg, color);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const paintAuditNote = (line, width, color, digits = 0) => {
|
|
95
170
|
const { baseFg, baseBg, mark } = STYLES.note;
|
|
96
|
-
const gutterW = gutterWidth(width);
|
|
97
|
-
const
|
|
171
|
+
const gutterW = gutterWidth(width, digits);
|
|
172
|
+
const numbers = paintNumber(line, digits, '', color);
|
|
173
|
+
const markW = Math.max(0, gutterW - numberCols(digits));
|
|
174
|
+
const label = `${mark} `.slice(0, markW);
|
|
98
175
|
const budget = Math.max(0, width - gutterW);
|
|
99
176
|
const text = truncateVisible(line.text ?? '', budget);
|
|
100
|
-
let out = paint(label, baseFg, baseBg, color);
|
|
177
|
+
let out = numbers + paint(label, baseFg, baseBg, color);
|
|
101
178
|
out += paint(text, baseFg, baseBg, color);
|
|
102
179
|
return out + fill(budget - visibleWidth(text), baseFg, baseBg, color);
|
|
103
180
|
};
|
|
104
181
|
|
|
105
|
-
const paintAuditRule = (width, color) => {
|
|
182
|
+
const paintAuditRule = (width, color, digits = 0) => {
|
|
106
183
|
const { baseFg, baseBg } = STYLES.noteSep;
|
|
107
|
-
|
|
184
|
+
const cols = clamp(width, 0, numberCols(digits));
|
|
185
|
+
const gutter = fill(cols, THEME.lineNoFg, THEME.lineNoBg, color);
|
|
186
|
+
const rule = '─'.repeat(Math.max(0, width - cols));
|
|
187
|
+
return gutter + paint(rule, baseFg, baseBg, color);
|
|
108
188
|
};
|
|
109
189
|
|
|
110
|
-
const paintDiffLine = (line, width, color, origin, lang,
|
|
111
|
-
|
|
112
|
-
|
|
190
|
+
const paintDiffLine = (line, width, color, origin, lang, col, places, edge) => {
|
|
191
|
+
const scrollCol = col ?? 0;
|
|
192
|
+
const digits = places ?? 0;
|
|
193
|
+
const side = edge ?? '';
|
|
194
|
+
if (line.type === 'noteSep') return paintAuditRule(width, color, digits);
|
|
195
|
+
if (line.type === 'note') return paintAuditNote(line, width, color, digits);
|
|
113
196
|
const styles = origin === 'staged' ? STAGED_STYLES : STYLES;
|
|
114
197
|
const style = styles[line.type] ?? styles.ctx;
|
|
115
198
|
const diffSpans = line.spans ?? [{ text: line.text, changed: false }];
|
|
116
199
|
const tokens = tokenize(lang, line.text ?? '');
|
|
117
200
|
const pieces = clipPieces(overlayTokens(tokens, diffSpans), scrollCol);
|
|
118
|
-
const gutterW = gutterWidth(width);
|
|
119
|
-
const
|
|
201
|
+
const gutterW = gutterWidth(width, digits);
|
|
202
|
+
const numbers = paintNumber(line, digits, side, color);
|
|
203
|
+
const markW = Math.max(0, gutterW - numberCols(digits));
|
|
204
|
+
const label = `${line.mark ?? style.mark} `.slice(0, markW);
|
|
120
205
|
const gutter = paint(label, CODE_FG.punct, style.baseBg, color);
|
|
121
206
|
const budget = Math.max(0, width - gutterW);
|
|
122
|
-
return gutter + paintSpans(pieces, style, budget, color);
|
|
207
|
+
return numbers + gutter + paintSpans(pieces, style, budget, color);
|
|
123
208
|
};
|
|
124
209
|
|
|
125
|
-
const
|
|
210
|
+
const emptySide = () => ({
|
|
211
|
+
type: 'ctx',
|
|
212
|
+
text: '',
|
|
213
|
+
spans: [{ text: '', changed: false }],
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const paintSplitRow = (row, width, color, origin, lang, scrollCol, digits) => {
|
|
126
217
|
const { leftW, rightW } = splitWidths(width);
|
|
127
|
-
const half = (line, halfW) => {
|
|
128
|
-
|
|
129
|
-
return
|
|
218
|
+
const half = (line, halfW, side) => {
|
|
219
|
+
const shown = line || (digits ? emptySide() : null);
|
|
220
|
+
if (!shown) return fill(halfW, THEME.ctxFg, THEME.ctxBg, color);
|
|
221
|
+
return paintDiffLine(
|
|
222
|
+
shown,
|
|
223
|
+
halfW,
|
|
224
|
+
color,
|
|
225
|
+
origin,
|
|
226
|
+
lang,
|
|
227
|
+
scrollCol,
|
|
228
|
+
digits,
|
|
229
|
+
side,
|
|
230
|
+
);
|
|
130
231
|
};
|
|
131
|
-
const left = half(row.left, leftW);
|
|
132
|
-
const right = half(row.right, rightW);
|
|
232
|
+
const left = half(row.left, leftW, 'left');
|
|
233
|
+
const right = half(row.right, rightW, 'right');
|
|
133
234
|
return `${left}${paintSplitRule(color)}${right}`;
|
|
134
235
|
};
|
|
135
236
|
|
|
@@ -181,8 +282,8 @@ const cutSpans = (spans, count) => {
|
|
|
181
282
|
return { taken, rest };
|
|
182
283
|
};
|
|
183
284
|
|
|
184
|
-
const wrapSoftLine = (line, width) => {
|
|
185
|
-
const rows = wrapPlain(line.text ?? '', textWidth(width));
|
|
285
|
+
const wrapSoftLine = (line, width, digits = 0) => {
|
|
286
|
+
const rows = wrapPlain(line.text ?? '', textWidth(width, digits));
|
|
186
287
|
const changed = line.wrap === true;
|
|
187
288
|
let used = 0;
|
|
188
289
|
let spans = line.spans ?? [{ text: line.text ?? '', changed }];
|
|
@@ -192,6 +293,10 @@ const wrapSoftLine = (line, width) => {
|
|
|
192
293
|
spans = cut.rest;
|
|
193
294
|
const nextSpans = cut.taken.length ? cut.taken : [{ text, changed }];
|
|
194
295
|
const next = { ...line, text, spans: nextSpans, editLast: last };
|
|
296
|
+
if (index > 0) {
|
|
297
|
+
next.oldNo = 0;
|
|
298
|
+
next.newNo = 0;
|
|
299
|
+
}
|
|
195
300
|
if (line.editStart !== undefined) {
|
|
196
301
|
next.editStart = line.editStart + used;
|
|
197
302
|
next.editEnd = last ? line.editEnd : next.editStart + text.length;
|
|
@@ -201,12 +306,13 @@ const wrapSoftLine = (line, width) => {
|
|
|
201
306
|
});
|
|
202
307
|
};
|
|
203
308
|
|
|
204
|
-
const expandSoftRows = (lines, width, split, wrapView) => {
|
|
309
|
+
const expandSoftRows = (lines, width, split, wrapView, digits = 0) => {
|
|
205
310
|
const { leftW, rightW } = splitWidths(width);
|
|
206
311
|
const partsOf = (line, lineW) => {
|
|
207
312
|
if (!wrapView || !line || line.type === 'noteSep') return [line];
|
|
208
|
-
|
|
209
|
-
return
|
|
313
|
+
const fits = visibleWidth(line.text ?? '') <= textWidth(lineW, digits);
|
|
314
|
+
if (fits) return [line];
|
|
315
|
+
return wrapSoftLine(line, lineW, digits);
|
|
210
316
|
};
|
|
211
317
|
if (!split) return lines.flatMap((line) => partsOf(line, width));
|
|
212
318
|
const out = [];
|
|
@@ -221,11 +327,11 @@ const expandSoftRows = (lines, width, split, wrapView) => {
|
|
|
221
327
|
return out;
|
|
222
328
|
};
|
|
223
329
|
|
|
224
|
-
const codeHits = (rows, split, width, overlay, editing, scrollCol) => {
|
|
330
|
+
const codeHits = (rows, split, width, overlay, editing, scrollCol, digits) => {
|
|
225
331
|
const { leftW, rightW } = splitWidths(width);
|
|
226
332
|
const lineW = split ? rightW : width;
|
|
227
333
|
const x0 = split ? leftW + 1 : 0;
|
|
228
|
-
const textX = x0 + gutterWidth(lineW) + 1;
|
|
334
|
+
const textX = x0 + gutterWidth(lineW, digits) + 1;
|
|
229
335
|
const at = overlay ? overlay.cursor : null;
|
|
230
336
|
const hasCursor = at !== null && at !== undefined;
|
|
231
337
|
const editHits = [];
|
|
@@ -256,8 +362,10 @@ const codeHits = (rows, split, width, overlay, editing, scrollCol) => {
|
|
|
256
362
|
return { cursor, editHits };
|
|
257
363
|
};
|
|
258
364
|
|
|
259
|
-
const codeInnerWidth = (width, layout = 'unified') =>
|
|
260
|
-
|
|
365
|
+
const codeInnerWidth = (width, layout = 'unified', digits = 0) => {
|
|
366
|
+
const lineW = layout === 'side' ? splitWidths(width).rightW : width;
|
|
367
|
+
return textWidth(lineW, digits);
|
|
368
|
+
};
|
|
261
369
|
|
|
262
370
|
const paintBodyDiff = (view, width, color) => {
|
|
263
371
|
const { item, codeOverlay: overlay } = view;
|
|
@@ -267,18 +375,31 @@ const paintBodyDiff = (view, width, color) => {
|
|
|
267
375
|
const editing = typeof overlay?.cursor === 'number';
|
|
268
376
|
const scrollCol = (editing ? overlay.scrollCol : 0) ?? 0;
|
|
269
377
|
const raw = dropLeadingEmpty(bodyLines(item, layout, overlay), splitBody);
|
|
270
|
-
const
|
|
378
|
+
const digits = viewDigits(view);
|
|
379
|
+
const lines = expandSoftRows(raw, width, splitBody, !editing, digits);
|
|
271
380
|
const paintRow = splitBody ? paintSplitRow : paintDiffLine;
|
|
272
|
-
const body = lines.
|
|
273
|
-
|
|
381
|
+
const body = lazyRows(lines.length, (index) => {
|
|
382
|
+
const line = lines[index];
|
|
383
|
+
return paintRow(line, width, color, item.origin, lang, scrollCol, digits);
|
|
384
|
+
});
|
|
385
|
+
const hits = codeHits(
|
|
386
|
+
lines,
|
|
387
|
+
splitBody,
|
|
388
|
+
width,
|
|
389
|
+
overlay,
|
|
390
|
+
editing,
|
|
391
|
+
scrollCol,
|
|
392
|
+
digits,
|
|
274
393
|
);
|
|
275
|
-
const hits = codeHits(lines, splitBody, width, overlay, editing, scrollCol);
|
|
276
394
|
return paneResult({ body, splitBody, ...hits });
|
|
277
395
|
};
|
|
278
396
|
|
|
279
397
|
module.exports = {
|
|
280
398
|
paintDiffLine,
|
|
281
399
|
codeInnerWidth,
|
|
400
|
+
activeDigits,
|
|
401
|
+
viewDigits,
|
|
402
|
+
paintNumberFill,
|
|
282
403
|
paintBodyDiff,
|
|
283
404
|
expandSoftRows,
|
|
284
405
|
codeHits,
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const ansi = require('../term/ansi.js');
|
|
4
|
+
|
|
5
|
+
const { THEME, paint, graphemes, graphemeWidth, visibleWidth } = ansi;
|
|
6
|
+
const { fileMatches } = require('../common/find.js');
|
|
7
|
+
|
|
8
|
+
const pieceKind = (index, cursor, lo, hi) => {
|
|
9
|
+
if (index === cursor) return 'caret';
|
|
10
|
+
if (lo >= 0 && index >= lo && index < hi) return 'select';
|
|
11
|
+
return 'plain';
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const pushPiece = (pieces, kind, ch) => {
|
|
15
|
+
const last = pieces[pieces.length - 1];
|
|
16
|
+
if (last && last.kind === kind) last.text += ch;
|
|
17
|
+
else pieces.push({ kind, text: ch });
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const fieldPieces = (text, cursor, anchor, scroll, room) => {
|
|
21
|
+
const raw = `${text ?? ''}`;
|
|
22
|
+
const selected = typeof anchor === 'number' && anchor !== cursor;
|
|
23
|
+
const lo = selected ? Math.min(anchor, cursor) : -1;
|
|
24
|
+
const hi = selected ? Math.max(anchor, cursor) : -1;
|
|
25
|
+
const pieces = [];
|
|
26
|
+
let index = 0;
|
|
27
|
+
let col = 0;
|
|
28
|
+
for (const ch of graphemes(raw)) {
|
|
29
|
+
const width = graphemeWidth(ch, col);
|
|
30
|
+
const startCol = col;
|
|
31
|
+
const start = index;
|
|
32
|
+
index += ch.length;
|
|
33
|
+
col += width;
|
|
34
|
+
if (col <= scroll) continue;
|
|
35
|
+
if (startCol >= scroll + room) break;
|
|
36
|
+
const kind = pieceKind(start, cursor, lo, hi);
|
|
37
|
+
pushPiece(pieces, kind, ch);
|
|
38
|
+
}
|
|
39
|
+
const caretAt = visibleWidth(raw.slice(0, Math.min(cursor, raw.length)));
|
|
40
|
+
const atEnd = cursor >= raw.length;
|
|
41
|
+
const caretFits = caretAt >= scroll && caretAt < scroll + room;
|
|
42
|
+
if (atEnd && caretFits) pushPiece(pieces, 'caret', ' ');
|
|
43
|
+
return pieces;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const paintField = (text, editor, room, color, tones) => {
|
|
47
|
+
const cursor = editor ? editor.cursor : `${text ?? ''}`.length;
|
|
48
|
+
const anchor = editor ? editor.anchor : null;
|
|
49
|
+
const scroll = editor ? editor.scrollCol : 0;
|
|
50
|
+
const pieces = fieldPieces(text, cursor, anchor, scroll, room);
|
|
51
|
+
let used = 0;
|
|
52
|
+
let out = '';
|
|
53
|
+
for (const piece of pieces) {
|
|
54
|
+
used += visibleWidth(piece.text);
|
|
55
|
+
const tone = tones[piece.kind] ?? tones.plain;
|
|
56
|
+
const painted = paint(piece.text, tone.fg, tone.bg, color);
|
|
57
|
+
out += color ? painted : piece.text;
|
|
58
|
+
}
|
|
59
|
+
const pad = ' '.repeat(Math.max(0, room - used));
|
|
60
|
+
const plain = tones.plain;
|
|
61
|
+
if (!color) return out + pad;
|
|
62
|
+
return out + paint(pad, plain.fg, plain.bg, color);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const paintPlanFace = (view, face, fgRgb, bgRgb, color) => {
|
|
66
|
+
const editor = view.planEditor;
|
|
67
|
+
if (!editor || !view.planQuery) {
|
|
68
|
+
return paint(face, fgRgb, bgRgb, color, true);
|
|
69
|
+
}
|
|
70
|
+
const room = visibleWidth(face);
|
|
71
|
+
if (room) editor.reveal(room);
|
|
72
|
+
const tones = {
|
|
73
|
+
plain: { fg: THEME.searchFg, bg: THEME.searchBg },
|
|
74
|
+
select: { fg: THEME.chromeFg, bg: THEME.checkBg },
|
|
75
|
+
caret: { fg: THEME.searchOnFg, bg: THEME.searchOnBg },
|
|
76
|
+
};
|
|
77
|
+
return paintField(view.planQuery, editor, room, color, tones);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const fieldTones = (fgRgb, bgRgb) => ({
|
|
81
|
+
plain: { fg: fgRgb, bg: bgRgb },
|
|
82
|
+
select: { fg: THEME.searchFg, bg: THEME.searchBg },
|
|
83
|
+
caret: { fg: THEME.searchOnFg, bg: THEME.searchOnBg },
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const paintPromptLine = (lead, leadFg, text, editor, width, color, fgRgb) => {
|
|
87
|
+
const room = Math.max(0, width - lead.length);
|
|
88
|
+
if (editor && room) editor.reveal(room);
|
|
89
|
+
const bgRgb = THEME.chromeBg;
|
|
90
|
+
const body = paintField(text, editor, room, color, fieldTones(fgRgb, bgRgb));
|
|
91
|
+
if (!color) return `${lead}${body}`;
|
|
92
|
+
return paint(lead, leadFg, bgRgb, color, true) + body;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const paintImportStatus = (view, width, color) => {
|
|
96
|
+
const state = view.import ?? {};
|
|
97
|
+
const query = state.url ?? '';
|
|
98
|
+
return paintPromptLine(
|
|
99
|
+
' url ',
|
|
100
|
+
THEME.warnFg,
|
|
101
|
+
query,
|
|
102
|
+
state.editor,
|
|
103
|
+
width,
|
|
104
|
+
color,
|
|
105
|
+
THEME.chromeFg,
|
|
106
|
+
);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const paintFindStatus = (view, width, color) => {
|
|
110
|
+
const find = view.find ?? {};
|
|
111
|
+
const query = find.query ?? '';
|
|
112
|
+
const hits = fileMatches(view.files ?? [], query);
|
|
113
|
+
const miss = query.length > 0 && hits.length === 0;
|
|
114
|
+
const tone = miss ? THEME.errorFg : THEME.chromeFg;
|
|
115
|
+
return paintPromptLine(
|
|
116
|
+
' /',
|
|
117
|
+
THEME.warnFg,
|
|
118
|
+
query,
|
|
119
|
+
find.editor,
|
|
120
|
+
width,
|
|
121
|
+
color,
|
|
122
|
+
tone,
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
module.exports = {
|
|
127
|
+
paintField,
|
|
128
|
+
paintPlanFace,
|
|
129
|
+
paintImportStatus,
|
|
130
|
+
paintFindStatus,
|
|
131
|
+
};
|
package/lib/render/files.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const ansi = require('../ansi.js');
|
|
4
|
-
const
|
|
3
|
+
const ansi = require('../term/ansi.js');
|
|
4
|
+
const { isTasksEntry } = require('../common/files.js');
|
|
5
|
+
const { shortAge } = require('../common/format.js');
|
|
5
6
|
const primitives = require('./primitives.js');
|
|
6
7
|
|
|
7
|
-
const { isTasksEntry, shortAge } = files;
|
|
8
8
|
const { THEME, paint, visibleWidth, trimVisible, seq, EL } = ansi;
|
|
9
9
|
const { fill, markPrefix, paintCursorList } = primitives;
|
|
10
|
-
const { measureColumns, padColumns } = primitives;
|
|
10
|
+
const { measureColumns, padColumns, paintPathText } = primitives;
|
|
11
|
+
const { activeHit, spansFor } = require('../common/find.js');
|
|
11
12
|
|
|
12
13
|
const FILE_STATUS_FG = {
|
|
13
14
|
unstaged: THEME.delLineFg,
|
|
@@ -56,7 +57,13 @@ const paintFileMeta = (parts, fgRgb, bgRgb, color) => {
|
|
|
56
57
|
return out + paint(parts.m, fgRgb, bgRgb, color);
|
|
57
58
|
};
|
|
58
59
|
|
|
59
|
-
const
|
|
60
|
+
const rowSpans = (entry, path, marks) => {
|
|
61
|
+
if (!marks || !marks.query) return [];
|
|
62
|
+
const hit = marks.hit && marks.hit.index === marks.index ? marks.hit : null;
|
|
63
|
+
return spansFor(path, entry.path ?? '', marks.query, hit);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const paintFileRow = (entry, width, color, selected, cols, marks) => {
|
|
60
67
|
const tasks = isTasksEntry(entry);
|
|
61
68
|
const inner = Math.max(1, width - 1);
|
|
62
69
|
const prefix = markPrefix(selected);
|
|
@@ -72,8 +79,11 @@ const paintFileRow = (entry, width, color, selected, cols) => {
|
|
|
72
79
|
const bgRgb = selected ? THEME.buttonBg : THEME.ctxBg;
|
|
73
80
|
const edge = fill(1, fgRgb, bgRgb, color);
|
|
74
81
|
if (!color) return `${left}${meta}${date}${edge}`;
|
|
82
|
+
const spans = rowSpans(entry, path, marks);
|
|
75
83
|
let out = `${seq(fgRgb, bgRgb)}${EL}`;
|
|
76
|
-
out += paint(
|
|
84
|
+
out += paint(prefix, fgRgb, bgRgb, color, tasks);
|
|
85
|
+
out += paintPathText(path, fgRgb, bgRgb, color, tasks, spans);
|
|
86
|
+
out += paint(pad, fgRgb, bgRgb, color, tasks);
|
|
77
87
|
if (tasks) out += paint(meta, fgRgb, bgRgb, color, true);
|
|
78
88
|
else out += paintFileMeta(parts, fgRgb, bgRgb, color);
|
|
79
89
|
if (date) {
|
|
@@ -88,8 +98,12 @@ const paintBodyFiles = (view, width, color, bodyH, headerLines) => {
|
|
|
88
98
|
const cols = measureColumns(entries, fileRowFields, FILE_COL_ALIGN);
|
|
89
99
|
const cursor = view.fileCursor ?? 0;
|
|
90
100
|
const pane = { width, color, bodyH, headerLines, offset: view.listScroll };
|
|
91
|
-
const
|
|
92
|
-
|
|
101
|
+
const hit = activeHit(entries, view.find);
|
|
102
|
+
const query = view.find && view.find.query ? view.find.query : '';
|
|
103
|
+
const paintRow = (entry, selected, index) => {
|
|
104
|
+
const marks = { query, hit, index };
|
|
105
|
+
return paintFileRow(entry, width, color, selected, cols, marks);
|
|
106
|
+
};
|
|
93
107
|
return paintCursorList(entries, cursor, pane, paintRow);
|
|
94
108
|
};
|
|
95
109
|
|