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/notes.js
CHANGED
|
@@ -2,209 +2,193 @@
|
|
|
2
2
|
|
|
3
3
|
const ansi = require('../ansi.js');
|
|
4
4
|
const wrap = require('../wrap.js');
|
|
5
|
-
const files = require('../files.js');
|
|
6
5
|
const primitives = require('./primitives.js');
|
|
6
|
+
const { wrapMultiline, wrapDoc, cursorInWrap } = wrap;
|
|
7
|
+
const { THEME, paint, visibleWidth, truncateVisible, seq, EL, RESET } = ansi;
|
|
8
|
+
const { isListPane, paneResult, editSpan } = primitives;
|
|
7
9
|
|
|
8
|
-
const { REPO_TODOS_LABEL } = files;
|
|
9
|
-
const { wrapMultiline, cursorInWrap } = wrap;
|
|
10
|
-
const { THEME, truncateVisible } = ansi;
|
|
11
|
-
const { paintBar, paintCheckBar, windowRows } = primitives;
|
|
12
|
-
const { LIST_PANES, CHECK_LEAD, paneResult } = primitives;
|
|
13
|
-
|
|
14
|
-
const NOTE_COMPOSE = ['feedback'];
|
|
15
10
|
const INPUT_MAX = 4;
|
|
16
11
|
const NOTE_MAX = 4;
|
|
17
12
|
const NOTE_PAD = 1;
|
|
13
|
+
const CHECK_DONE = 'xX';
|
|
14
|
+
const CHECK_TOKEN = /\[([ xX])\]/;
|
|
15
|
+
const CHECK_LEAD = /^\[[ xX]\] /;
|
|
16
|
+
const TODO_LEAD_W = '[ ] '.length;
|
|
17
|
+
const NO_NOTE = { rows: [], cursor: null, templateHits: [], editHits: [] };
|
|
18
|
+
|
|
19
|
+
const paintBar = (text, width, fgRgb, bgRgb, color) => {
|
|
20
|
+
const clipped = truncateVisible(text, width);
|
|
21
|
+
const pad = Math.max(0, width - visibleWidth(clipped));
|
|
22
|
+
const filled = clipped + ' '.repeat(pad);
|
|
23
|
+
if (!color) return filled;
|
|
24
|
+
return `${seq(fgRgb, bgRgb)}${EL}${filled}${RESET}`;
|
|
25
|
+
};
|
|
18
26
|
|
|
19
|
-
const
|
|
27
|
+
const checkChip = (mark, fgRgb, bgRgb, plain) => {
|
|
28
|
+
if (plain) return { fgRgb, bgRgb };
|
|
29
|
+
if (CHECK_DONE.includes(mark)) {
|
|
30
|
+
return { fgRgb: THEME.checkDoneFg, bgRgb: THEME.checkDoneBg };
|
|
31
|
+
}
|
|
32
|
+
return { fgRgb: THEME.checkFg, bgRgb: THEME.checkBg };
|
|
33
|
+
};
|
|
20
34
|
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
|
|
35
|
+
const paintCheckBar = (text, width, fgRgb, bgRgb, color, plain = false) => {
|
|
36
|
+
const clipped = truncateVisible(text, width);
|
|
37
|
+
const match = color ? CHECK_TOKEN.exec(clipped) : null;
|
|
38
|
+
if (!match) return paintBar(text, width, fgRgb, bgRgb, color);
|
|
39
|
+
const token = match[0];
|
|
40
|
+
const pad = Math.max(0, width - visibleWidth(clipped));
|
|
41
|
+
const before = clipped.slice(0, match.index);
|
|
42
|
+
const after = clipped.slice(match.index + token.length) + ' '.repeat(pad);
|
|
43
|
+
const chip = checkChip(match[1], fgRgb, bgRgb, plain);
|
|
44
|
+
let out = `${seq(fgRgb, bgRgb)}${EL}`;
|
|
45
|
+
out += paint(before, fgRgb, bgRgb, color);
|
|
46
|
+
out += paint(token, chip.fgRgb, chip.bgRgb, color);
|
|
47
|
+
return out + paint(after, fgRgb, bgRgb, color);
|
|
24
48
|
};
|
|
25
49
|
|
|
26
|
-
const
|
|
50
|
+
const windowRows = (rows, focus, cap) => {
|
|
51
|
+
if (rows.length <= cap) return { rows, offset: 0 };
|
|
52
|
+
const start = Math.min(Math.max(0, focus - cap + 1), rows.length - cap);
|
|
53
|
+
return { rows: rows.slice(start, start + cap), offset: start };
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const todoInnerWidth = (width) => Math.max(1, width - 2);
|
|
27
57
|
|
|
28
58
|
const todoBodyWidth = (width) =>
|
|
29
59
|
Math.max(1, todoInnerWidth(width) - TODO_LEAD_W);
|
|
30
60
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
61
|
+
const noteInnerWidth = (width) => Math.max(1, width - NOTE_PAD * 2);
|
|
62
|
+
|
|
63
|
+
const todoParts = (text, inner) => {
|
|
64
|
+
const match = CHECK_LEAD.exec(text);
|
|
65
|
+
const lead = match ? match[0].length : 0;
|
|
66
|
+
return { lead, body: text.slice(lead), bodyW: Math.max(1, inner - lead) };
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const wrapTodoItem = (text, todo) => {
|
|
70
|
+
const mark = text.slice(0, todo.lead);
|
|
71
|
+
const hang = ' '.repeat(todo.lead);
|
|
72
|
+
const lines = wrapMultiline(todo.body, todo.bodyW);
|
|
73
|
+
return lines.map((line, index) => `${index ? hang : mark}${line}`);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const todoEdit = (todo, edit, rowStart, width) => {
|
|
77
|
+
const pos = cursorInWrap(todo.body, edit.cursor ?? 0, todo.bodyW);
|
|
78
|
+
const cursor = { x: pos.col + todo.lead + 2, row: rowStart + pos.row };
|
|
79
|
+
const x0 = todo.lead + 1;
|
|
80
|
+
const doc = wrapDoc(todo.body, todo.bodyW);
|
|
81
|
+
const hits = doc.map((piece, line) => ({
|
|
82
|
+
row: rowStart + line,
|
|
83
|
+
...editSpan(x0, x0 + 1, width + 1, piece.start, piece.text),
|
|
84
|
+
}));
|
|
85
|
+
return { cursor, hits };
|
|
46
86
|
};
|
|
47
87
|
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
return {
|
|
88
|
+
const taskLists = (view) => {
|
|
89
|
+
const sections = view.taskSections;
|
|
90
|
+
if (sections && sections.length) return sections;
|
|
91
|
+
const tasks = view.tasks ?? [];
|
|
92
|
+
const texts = !tasks.length && view.noteText ? [view.noteText] : tasks;
|
|
93
|
+
return [{ title: '', texts }];
|
|
54
94
|
};
|
|
55
95
|
|
|
56
|
-
const
|
|
96
|
+
const paintSectionHead = (title, width, color) =>
|
|
97
|
+
paintBar(` ${title}`, width, THEME.mutedFg, THEME.ctxBg, color);
|
|
98
|
+
|
|
99
|
+
const paintBodyTodo = (view, width, color) => {
|
|
100
|
+
const focus = view.tasksFocus ?? 0;
|
|
57
101
|
const inner = todoInnerWidth(width);
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
const
|
|
102
|
+
const body = [];
|
|
103
|
+
const taskOwners = [];
|
|
104
|
+
const taskChecks = [];
|
|
105
|
+
let editHits = [];
|
|
61
106
|
let cursor = null;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
for (
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
107
|
+
let index = 0;
|
|
108
|
+
for (const section of taskLists(view)) {
|
|
109
|
+
if (section.title) {
|
|
110
|
+
body.push(paintSectionHead(section.title, width, color));
|
|
111
|
+
taskOwners.push(null);
|
|
112
|
+
taskChecks.push(false);
|
|
113
|
+
}
|
|
114
|
+
for (const text of section.texts) {
|
|
115
|
+
const selected = index === focus;
|
|
116
|
+
const fgRgb = selected ? THEME.chromeFg : THEME.mutedFg;
|
|
117
|
+
const bgRgb = selected ? THEME.buttonBg : THEME.ctxBg;
|
|
118
|
+
const todo = todoParts(text, inner);
|
|
119
|
+
const rowStart = body.length;
|
|
120
|
+
const wrapped = wrapTodoItem(text, todo);
|
|
121
|
+
for (let line = 0; line < wrapped.length; line++) {
|
|
122
|
+
const row = ` ${wrapped[line]}`;
|
|
123
|
+
body.push(paintCheckBar(row, width, fgRgb, bgRgb, color, true));
|
|
124
|
+
taskOwners.push(index);
|
|
125
|
+
taskChecks.push(line === 0);
|
|
126
|
+
}
|
|
127
|
+
if (view.taskEdit && selected) {
|
|
128
|
+
const edit = todoEdit(todo, view.taskEdit, rowStart, width);
|
|
129
|
+
cursor = edit.cursor;
|
|
130
|
+
editHits = edit.hits;
|
|
131
|
+
}
|
|
132
|
+
index += 1;
|
|
81
133
|
}
|
|
82
|
-
if (!edit || !selected) continue;
|
|
83
|
-
cursor = todoCursor(text, edit, inner, rowStart);
|
|
84
134
|
}
|
|
85
|
-
return {
|
|
135
|
+
return paneResult({ body, editHits, taskOwners, taskChecks, cursor });
|
|
86
136
|
};
|
|
87
137
|
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
const paintNoteLine = (text, width, fgRgb, bgRgb, color, withCheck) => {
|
|
91
|
-
const inner = noteInnerWidth(width);
|
|
92
|
-
const clipped = truncateVisible(text, inner);
|
|
93
|
-
const line = `${' '.repeat(NOTE_PAD)}${clipped}`;
|
|
94
|
-
if (withCheck) return paintCheckBar(line, width, fgRgb, bgRgb, color);
|
|
95
|
-
return paintBar(line, width, fgRgb, bgRgb, color);
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const paintTemplateRow = (text, selected, width, color) => {
|
|
99
|
-
const fgRgb = selected ? THEME.chromeFg : THEME.mutedFg;
|
|
100
|
-
const bgRgb = selected ? THEME.buttonBg : THEME.noteBg;
|
|
101
|
-
return paintNoteLine(text, width, fgRgb, bgRgb, color);
|
|
102
|
-
};
|
|
138
|
+
const noteLine = (text, width) =>
|
|
139
|
+
`${' '.repeat(NOTE_PAD)}${truncateVisible(text, noteInnerWidth(width))}`;
|
|
103
140
|
|
|
104
141
|
const paintComposeNote = (view, width, color, cap) => {
|
|
105
142
|
const inner = noteInnerWidth(width);
|
|
106
|
-
const
|
|
107
|
-
const
|
|
108
|
-
const wrapped = wrapMultiline(compose.text, inner);
|
|
143
|
+
const { compose } = view;
|
|
144
|
+
const doc = wrapDoc(compose.text ?? '', inner);
|
|
109
145
|
const { row, col } = cursorInWrap(compose.text, compose.cursor ?? 0, inner);
|
|
110
|
-
const shown = windowRows(
|
|
146
|
+
const shown = windowRows(doc, row, INPUT_MAX);
|
|
147
|
+
const templates = view.templates ?? [];
|
|
148
|
+
const room = Math.max(0, cap - shown.rows.length);
|
|
149
|
+
const count = Math.min(templates.length, room);
|
|
150
|
+
const focus = view.templateIndex ?? -1;
|
|
111
151
|
const rows = [];
|
|
112
152
|
const templateHits = [];
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const text = all[i].text ?? '';
|
|
121
|
-
const selected = i === focus;
|
|
122
|
-
rows.push(paintTemplateRow(text, selected, width, color));
|
|
123
|
-
templateHits.push({ row: i, cursor: i });
|
|
124
|
-
}
|
|
153
|
+
for (let i = 0; i < count; i++) {
|
|
154
|
+
const selected = i === focus;
|
|
155
|
+
const fgRgb = selected ? THEME.chromeFg : THEME.mutedFg;
|
|
156
|
+
const bgRgb = selected ? THEME.buttonBg : THEME.noteBg;
|
|
157
|
+
const line = noteLine(templates[i].text ?? '', width);
|
|
158
|
+
rows.push(paintBar(line, width, fgRgb, bgRgb, color));
|
|
159
|
+
templateHits.push({ row: i, cursor: i });
|
|
125
160
|
}
|
|
126
|
-
const
|
|
127
|
-
for (const
|
|
128
|
-
|
|
161
|
+
const editHits = [];
|
|
162
|
+
for (const piece of shown.rows) {
|
|
163
|
+
const span = editSpan(1, NOTE_PAD + 1, width + 1, piece.start, piece.text);
|
|
164
|
+
editHits.push({ row: rows.length, ...span });
|
|
165
|
+
const line = noteLine(piece.text, width);
|
|
166
|
+
rows.push(paintBar(line, width, THEME.chromeFg, THEME.noteBg, color));
|
|
129
167
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
cursor: {
|
|
133
|
-
x: NOTE_PAD + col + 1,
|
|
134
|
-
row: templateCount + row - shown.offset,
|
|
135
|
-
},
|
|
136
|
-
templateHits,
|
|
137
|
-
};
|
|
168
|
+
const cursor = { x: NOTE_PAD + col + 1, row: count + row - shown.offset };
|
|
169
|
+
return { rows, cursor, templateHits, editHits };
|
|
138
170
|
};
|
|
139
171
|
|
|
140
172
|
const paintIdleNote = (view, width, color) => {
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
line,
|
|
150
|
-
width,
|
|
151
|
-
THEME.chromeFg,
|
|
152
|
-
THEME.noteBg,
|
|
153
|
-
color,
|
|
154
|
-
true,
|
|
155
|
-
);
|
|
156
|
-
rows.push(painted);
|
|
157
|
-
}
|
|
158
|
-
return { rows, cursor: null, templateHits: [] };
|
|
173
|
+
if (!view.noteText) return NO_NOTE;
|
|
174
|
+
const note = `feedback: ${view.noteText}`;
|
|
175
|
+
const wrapped = wrapMultiline(note, noteInnerWidth(width));
|
|
176
|
+
const rows = wrapped.slice(0, NOTE_MAX).map((text) => {
|
|
177
|
+
const line = noteLine(text, width);
|
|
178
|
+
return paintCheckBar(line, width, THEME.chromeFg, THEME.noteBg, color);
|
|
179
|
+
});
|
|
180
|
+
return { rows, cursor: null, templateHits: [], editHits: [] };
|
|
159
181
|
};
|
|
160
182
|
|
|
161
183
|
const paintNotePanel = (view, width, color, cap) => {
|
|
162
|
-
|
|
163
|
-
const compose = view.compose;
|
|
164
|
-
if (compose && NOTE_COMPOSE.includes(compose.kind)) {
|
|
184
|
+
if (view.compose?.kind === 'feedback') {
|
|
165
185
|
return paintComposeNote(view, width, color, cap);
|
|
166
186
|
}
|
|
167
|
-
|
|
168
|
-
if (LIST_PANES.includes(pane)) return empty;
|
|
187
|
+
if (isListPane(view.pane)) return NO_NOTE;
|
|
169
188
|
return paintIdleNote(view, width, color);
|
|
170
189
|
};
|
|
171
190
|
|
|
172
|
-
const paintTodoCaption = (width, color) =>
|
|
173
|
-
paintBar(` ${REPO_TODOS_LABEL}`, width, THEME.mutedFg, THEME.ctxBg, color);
|
|
174
|
-
|
|
175
|
-
const paintBodyTodo = (view, width, color) => {
|
|
176
|
-
const texts = view.todos ?? [];
|
|
177
|
-
let lines = texts;
|
|
178
|
-
if (!lines.length && view.noteText) lines = [view.noteText];
|
|
179
|
-
const painted = paintTodoList(
|
|
180
|
-
lines,
|
|
181
|
-
view.todoFocus ?? 0,
|
|
182
|
-
width,
|
|
183
|
-
color,
|
|
184
|
-
view.todoEdit,
|
|
185
|
-
);
|
|
186
|
-
const caption = paintTodoCaption(width, color);
|
|
187
|
-
const body = [caption, ...painted.rows];
|
|
188
|
-
const todoOwners = [null, ...painted.owners];
|
|
189
|
-
const todoChecks = [false, ...painted.checks];
|
|
190
|
-
let cursor = null;
|
|
191
|
-
if (painted.cursor) {
|
|
192
|
-
cursor = { x: painted.cursor.x, row: painted.cursor.row + 1 };
|
|
193
|
-
}
|
|
194
|
-
const fileHits = [];
|
|
195
|
-
const splitBody = false;
|
|
196
|
-
return paneResult({
|
|
197
|
-
body,
|
|
198
|
-
fileHits,
|
|
199
|
-
todoOwners,
|
|
200
|
-
todoChecks,
|
|
201
|
-
cursor,
|
|
202
|
-
splitBody,
|
|
203
|
-
});
|
|
204
|
-
};
|
|
205
|
-
|
|
206
191
|
module.exports = {
|
|
207
|
-
todoInnerWidth,
|
|
208
192
|
todoBodyWidth,
|
|
209
193
|
noteInnerWidth,
|
|
210
194
|
paintNotePanel,
|