reslop 0.2.5 → 0.2.7
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 +22 -1
- package/LICENSE +1 -1
- package/README.md +11 -11
- package/bin/reslop.js +3 -8
- package/lib/ansi.js +72 -19
- package/lib/api.js +1 -1
- package/lib/capabilities.js +8 -8
- package/lib/cli.js +61 -75
- package/lib/clipboard.js +39 -47
- package/lib/dashboard/git.js +128 -0
- package/lib/dashboard/model.js +357 -0
- package/lib/dashboard/npm.js +290 -0
- package/lib/dashboard/tree.js +278 -0
- package/lib/deps-diff.js +197 -402
- package/lib/deps.js +113 -240
- package/lib/detect.js +8 -18
- package/lib/diff/align.js +41 -113
- package/lib/diff/diff.js +12 -43
- package/lib/diff/display.js +25 -36
- package/lib/diff/edit.js +6 -20
- package/lib/diff/inline.js +76 -144
- package/lib/diff/items.js +33 -47
- package/lib/diff/parse.js +28 -56
- package/lib/diff/patch.js +12 -33
- package/lib/diff/unit.js +79 -178
- package/lib/editor.js +52 -69
- package/lib/files.js +79 -91
- package/lib/find.js +61 -0
- package/lib/git-branches.js +254 -416
- package/lib/git-deps.js +148 -272
- package/lib/git-reword-todo.js +5 -13
- package/lib/git-worktree.js +161 -214
- package/lib/git.js +21 -49
- package/lib/github.js +67 -181
- package/lib/gitlab.js +40 -90
- package/lib/highlight/bash.js +55 -177
- package/lib/highlight/core.js +155 -141
- package/lib/highlight/css.js +25 -58
- package/lib/highlight/csv.js +19 -37
- package/lib/highlight/dart.js +72 -213
- package/lib/highlight/dot.js +14 -14
- package/lib/highlight/html.js +42 -63
- package/lib/highlight/js.js +177 -506
- package/lib/highlight/json.js +17 -37
- package/lib/highlight/log.js +19 -25
- package/lib/highlight/txt.js +7 -10
- package/lib/highlight.js +17 -46
- package/lib/keys.js +75 -83
- package/lib/manifest.js +67 -126
- package/lib/npm-commands.js +120 -169
- package/lib/remote.js +140 -190
- package/lib/render/branches.js +63 -61
- package/lib/render/chrome.js +412 -347
- package/lib/render/commits.js +160 -361
- package/lib/render/dash-activity.js +212 -0
- package/lib/render/dash-blocks.js +496 -0
- package/lib/render/dash-table.js +295 -0
- package/lib/render/dashboard.js +62 -0
- package/lib/render/diff.js +123 -258
- package/lib/render/files.js +60 -86
- package/lib/render/frame.js +82 -117
- package/lib/render/notes.js +150 -210
- package/lib/render/npm.js +213 -177
- package/lib/render/packages.js +93 -0
- package/lib/render/present.js +4 -11
- package/lib/render/primitives.js +77 -172
- package/lib/render/render.js +5 -14
- package/lib/render/report.js +43 -0
- package/lib/render/repos.js +226 -0
- package/lib/render/search.js +25 -0
- package/lib/render/tiles.js +355 -0
- package/lib/render/unit.js +15 -31
- package/lib/report-model.js +110 -33
- package/lib/report-parse.js +56 -37
- package/lib/report-render.js +102 -59
- package/lib/report-run.js +87 -77
- package/lib/report-test.js +221 -99
- package/lib/report-tools.js +96 -154
- package/lib/review-merge.js +33 -41
- package/lib/review.js +69 -129
- package/lib/runs.js +225 -0
- package/lib/select.js +53 -91
- package/lib/session/accessors.js +14 -21
- package/lib/session/actions.js +189 -65
- package/lib/session/branches.js +164 -222
- package/lib/session/change-actions.js +49 -119
- package/lib/session/commit.js +298 -352
- package/lib/session/compose.js +572 -715
- package/lib/session/dashboard.js +530 -0
- package/lib/session/dispatch.js +197 -171
- package/lib/session/files-pane.js +92 -45
- package/lib/session/find.js +94 -0
- package/lib/session/items.js +6 -17
- package/lib/session/lifecycle.js +420 -466
- package/lib/session/load.js +1 -14
- package/lib/session/navigation.js +4 -2
- package/lib/session/npm.js +403 -420
- package/lib/session/ops.js +5 -13
- package/lib/session/packages.js +143 -0
- package/lib/session/pointer.js +30 -14
- package/lib/session/progress.js +39 -30
- package/lib/session/review.js +1 -3
- package/lib/session/tasks.js +277 -236
- package/lib/session/templates.js +51 -59
- package/lib/session/terminal.js +85 -132
- package/lib/session/view.js +59 -80
- package/lib/session/watch.js +201 -212
- package/lib/session/workspace.js +455 -0
- package/lib/session.js +95 -161
- package/lib/update.js +44 -111
- package/lib/utilities.js +46 -88
- package/lib/workspace.js +166 -0
- package/lib/wrap.js +29 -55
- package/package.json +1 -1
package/lib/report-tools.js
CHANGED
|
@@ -6,41 +6,52 @@ const model = require('./report-model.js');
|
|
|
6
6
|
|
|
7
7
|
const { summary, problem, report, userTrace } = model;
|
|
8
8
|
|
|
9
|
+
const PRETTIER_SUMMARY = [
|
|
10
|
+
'Code style issues',
|
|
11
|
+
'All matched files use Prettier',
|
|
12
|
+
];
|
|
13
|
+
|
|
9
14
|
const place = (file, line, column, root) =>
|
|
10
15
|
userTrace(`${file}:${line}:${column}`, root);
|
|
11
16
|
|
|
17
|
+
const errorReport = (tool, problems) => {
|
|
18
|
+
const stats = summary();
|
|
19
|
+
stats.errors = problems.length;
|
|
20
|
+
return report(tool, { summary: stats, problems });
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const eslintReport = (problems) => {
|
|
24
|
+
const stats = summary();
|
|
25
|
+
const isWarning = (item) => item.severity === 'warning';
|
|
26
|
+
stats.warnings = problems.filter(isWarning).length;
|
|
27
|
+
stats.errors = problems.length - stats.warnings;
|
|
28
|
+
return report('eslint', { summary: stats, problems });
|
|
29
|
+
};
|
|
30
|
+
|
|
12
31
|
const parseEslintJson = (data, root) => {
|
|
13
32
|
const rows = Array.isArray(data) ? data : data && data.results;
|
|
14
33
|
if (!Array.isArray(rows) || !rows.length) return null;
|
|
15
34
|
if (!rows[0] || !Array.isArray(rows[0].messages)) return null;
|
|
16
|
-
const stats = summary();
|
|
17
|
-
let errors = 0;
|
|
18
|
-
let warnings = 0;
|
|
19
35
|
const problems = [];
|
|
20
36
|
for (const file of rows) {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
37
|
+
for (const item of file.messages || []) {
|
|
38
|
+
const trace = place(
|
|
39
|
+
file.filePath || '',
|
|
40
|
+
item.line || 0,
|
|
41
|
+
item.column || 0,
|
|
42
|
+
root,
|
|
43
|
+
);
|
|
26
44
|
problems.push(
|
|
27
45
|
problem({
|
|
28
|
-
severity,
|
|
46
|
+
severity: item.severity === 1 ? 'warning' : 'error',
|
|
29
47
|
message: item.message || '',
|
|
30
48
|
code: item.ruleId || '',
|
|
31
|
-
trace
|
|
32
|
-
file.filePath || '',
|
|
33
|
-
item.line || 0,
|
|
34
|
-
item.column || 0,
|
|
35
|
-
root,
|
|
36
|
-
),
|
|
49
|
+
trace,
|
|
37
50
|
}),
|
|
38
51
|
);
|
|
39
52
|
}
|
|
40
53
|
}
|
|
41
|
-
|
|
42
|
-
stats.warnings = warnings;
|
|
43
|
-
return report('eslint', { summary: stats, problems });
|
|
54
|
+
return eslintReport(problems);
|
|
44
55
|
};
|
|
45
56
|
|
|
46
57
|
const eslintLine = (line) => {
|
|
@@ -67,69 +78,35 @@ const isPathLine = (line) => {
|
|
|
67
78
|
};
|
|
68
79
|
|
|
69
80
|
const parseEslintStylish = (text, root) => {
|
|
70
|
-
const lines = `${text ?? ''}`.split(/\r?\n/);
|
|
71
81
|
const problems = [];
|
|
72
82
|
let file = '';
|
|
73
|
-
let errors = 0;
|
|
74
|
-
let warnings = 0;
|
|
75
83
|
let seen = false;
|
|
76
|
-
for (const line of
|
|
84
|
+
for (const line of `${text ?? ''}`.split(/\r?\n/)) {
|
|
77
85
|
const item = eslintLine(line);
|
|
78
86
|
if (item) {
|
|
79
87
|
seen = true;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
problems.push(
|
|
83
|
-
problem({
|
|
84
|
-
severity: item.severity,
|
|
85
|
-
message: item.message,
|
|
86
|
-
code: item.code,
|
|
87
|
-
trace: place(file, item.line, item.column, root),
|
|
88
|
-
}),
|
|
89
|
-
);
|
|
88
|
+
const { severity, message, code } = item;
|
|
89
|
+
const trace = place(file, item.line, item.column, root);
|
|
90
|
+
problems.push(problem({ severity, message, code, trace }));
|
|
90
91
|
continue;
|
|
91
92
|
}
|
|
92
93
|
if (/✖\s+\d+\s+problems?\b/.test(line)) seen = true;
|
|
93
94
|
if (isPathLine(line)) file = line.trim();
|
|
94
95
|
}
|
|
95
|
-
|
|
96
|
-
const stats = summary();
|
|
97
|
-
stats.errors = errors;
|
|
98
|
-
stats.warnings = warnings;
|
|
99
|
-
return report('eslint', { summary: stats, problems });
|
|
96
|
+
return seen ? eslintReport(problems) : null;
|
|
100
97
|
};
|
|
101
98
|
|
|
102
|
-
const
|
|
103
|
-
const match = /^(.*)\((\d+),(\d+)\):\s+error\s+(TS\d+):\s+(.*)$/.exec(
|
|
104
|
-
line.trim(),
|
|
105
|
-
);
|
|
106
|
-
if (!match) return null;
|
|
107
|
-
return {
|
|
108
|
-
file: match[1],
|
|
109
|
-
line: match[2],
|
|
110
|
-
column: match[3],
|
|
111
|
-
code: match[4],
|
|
112
|
-
message: match[5],
|
|
113
|
-
};
|
|
114
|
-
};
|
|
99
|
+
const TSC_LINE = /^(.*)\((\d+),(\d+)\):\s+error\s+(TS\d+):\s+(.*)$/;
|
|
115
100
|
|
|
116
101
|
const parseTsc = (text, root) => {
|
|
117
102
|
const problems = [];
|
|
118
103
|
for (const line of `${text ?? ''}`.split(/\r?\n/)) {
|
|
119
|
-
const
|
|
120
|
-
if (!
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
message: item.message,
|
|
124
|
-
code: item.code,
|
|
125
|
-
trace: place(item.file, item.line, item.column, root),
|
|
126
|
-
}),
|
|
127
|
-
);
|
|
104
|
+
const match = TSC_LINE.exec(line.trim());
|
|
105
|
+
if (!match) continue;
|
|
106
|
+
const trace = place(match[1], match[2], match[3], root);
|
|
107
|
+
problems.push(problem({ message: match[5], code: match[4], trace }));
|
|
128
108
|
}
|
|
129
|
-
|
|
130
|
-
const stats = summary();
|
|
131
|
-
stats.errors = problems.length;
|
|
132
|
-
return report('tsc', { summary: stats, problems });
|
|
109
|
+
return problems.length ? errorReport('tsc', problems) : null;
|
|
133
110
|
};
|
|
134
111
|
|
|
135
112
|
const prettierFile = (line) => {
|
|
@@ -138,76 +115,53 @@ const prettierFile = (line) => {
|
|
|
138
115
|
return '';
|
|
139
116
|
};
|
|
140
117
|
|
|
118
|
+
const isPrettierSummary = (line) =>
|
|
119
|
+
PRETTIER_SUMMARY.some((text) => line.includes(text));
|
|
120
|
+
|
|
141
121
|
const parsePrettier = (text, root) => {
|
|
142
|
-
const lines = `${text ?? ''}`.split(/\r?\n/);
|
|
143
122
|
const problems = [];
|
|
144
123
|
let seen = false;
|
|
145
|
-
for (const line of
|
|
146
|
-
if (line
|
|
147
|
-
if (line.includes('All matched files use Prettier')) seen = true;
|
|
124
|
+
for (const line of `${text ?? ''}`.split(/\r?\n/)) {
|
|
125
|
+
if (isPrettierSummary(line)) seen = true;
|
|
148
126
|
const file = prettierFile(line);
|
|
149
127
|
if (!file) continue;
|
|
150
128
|
seen = true;
|
|
151
|
-
problems.push(
|
|
152
|
-
problem({
|
|
153
|
-
message: 'format',
|
|
154
|
-
trace: userTrace(file, root),
|
|
155
|
-
}),
|
|
156
|
-
);
|
|
129
|
+
problems.push(problem({ message: 'format', trace: userTrace(file, root) }));
|
|
157
130
|
}
|
|
158
|
-
|
|
159
|
-
const stats = summary();
|
|
160
|
-
stats.errors = problems.length;
|
|
161
|
-
return report('prettier', { summary: stats, problems });
|
|
131
|
+
return seen ? errorReport('prettier', problems) : null;
|
|
162
132
|
};
|
|
163
133
|
|
|
164
134
|
const auditExtra = (item) => {
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
if (item.range) {
|
|
169
|
-
extra[index] = ['range', item.range];
|
|
170
|
-
index += 1;
|
|
171
|
-
}
|
|
172
|
-
if (item.fix) extra[index] = ['fix', item.fix];
|
|
135
|
+
const extra = [];
|
|
136
|
+
if (item.range) extra.push(['range', item.range]);
|
|
137
|
+
if (item.fix) extra.push(['fix', item.fix]);
|
|
173
138
|
return extra;
|
|
174
139
|
};
|
|
175
140
|
|
|
176
|
-
const auditProblems = (text) => {
|
|
177
|
-
const audit = parseAuditReport(text);
|
|
178
|
-
const problems = new Array(audit.size);
|
|
179
|
-
let index = 0;
|
|
180
|
-
for (const item of audit.values()) {
|
|
181
|
-
problems[index] = problem({
|
|
182
|
-
severity: item.severity || 'error',
|
|
183
|
-
name: item.name,
|
|
184
|
-
message: item.title || '',
|
|
185
|
-
extra: auditExtra(item),
|
|
186
|
-
});
|
|
187
|
-
index += 1;
|
|
188
|
-
}
|
|
189
|
-
return problems;
|
|
190
|
-
};
|
|
191
|
-
|
|
192
141
|
const parseAudit = (text) => {
|
|
193
|
-
const problems =
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
142
|
+
const problems = [];
|
|
143
|
+
for (const item of parseAuditReport(text).values()) {
|
|
144
|
+
problems.push(
|
|
145
|
+
problem({
|
|
146
|
+
severity: item.severity || 'error',
|
|
147
|
+
name: item.name,
|
|
148
|
+
message: item.title || '',
|
|
149
|
+
extra: auditExtra(item),
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
return problems.length ? errorReport('audit', problems) : null;
|
|
198
154
|
};
|
|
199
155
|
|
|
200
156
|
const behind = (item) => {
|
|
201
157
|
if (!item.current) return true;
|
|
202
158
|
if (item.wanted && item.wanted !== item.current) return true;
|
|
203
|
-
|
|
204
|
-
return false;
|
|
159
|
+
return !!item.latest && item.latest !== item.current;
|
|
205
160
|
};
|
|
206
161
|
|
|
207
162
|
const parseOutdated = (text) => {
|
|
208
|
-
const outdated = parseOutdatedReport(text);
|
|
209
163
|
const problems = [];
|
|
210
|
-
for (const [name, item] of
|
|
164
|
+
for (const [name, item] of parseOutdatedReport(text)) {
|
|
211
165
|
if (!behind(item)) continue;
|
|
212
166
|
problems.push(
|
|
213
167
|
problem({
|
|
@@ -230,8 +184,7 @@ const parseOutdated = (text) => {
|
|
|
230
184
|
|
|
231
185
|
const pushLs = (problems, text) => {
|
|
232
186
|
const message = `${text ?? ''}`.trim();
|
|
233
|
-
if (
|
|
234
|
-
problems.push(problem({ message }));
|
|
187
|
+
if (message) problems.push(problem({ message }));
|
|
235
188
|
};
|
|
236
189
|
|
|
237
190
|
const walkLs = (node, problems) => {
|
|
@@ -251,13 +204,12 @@ const walkLs = (node, problems) => {
|
|
|
251
204
|
|
|
252
205
|
const parseLs = (data) => {
|
|
253
206
|
if (!data || typeof data !== 'object' || Array.isArray(data)) return null;
|
|
254
|
-
|
|
207
|
+
const listed = Array.isArray(data.problems);
|
|
208
|
+
if (!data.dependencies && !listed) return null;
|
|
255
209
|
const problems = [];
|
|
256
210
|
walkLs(data, problems);
|
|
257
|
-
if (!problems.length && !
|
|
258
|
-
|
|
259
|
-
stats.errors = problems.length;
|
|
260
|
-
return report('ls', { summary: stats, problems });
|
|
211
|
+
if (!problems.length && !listed) return null;
|
|
212
|
+
return errorReport('ls', problems);
|
|
261
213
|
};
|
|
262
214
|
|
|
263
215
|
const parseToolJson = (data, root) => {
|
|
@@ -281,50 +233,40 @@ const parseAuditText = (text) => {
|
|
|
281
233
|
const match = /^Severity:\s+(\w+)\s*$/.exec(lines[index].trim());
|
|
282
234
|
if (!match) continue;
|
|
283
235
|
const name = (lines[index - 1] || '').trim();
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
severity: match[1].toLowerCase(),
|
|
287
|
-
name,
|
|
288
|
-
message: name,
|
|
289
|
-
}),
|
|
290
|
-
);
|
|
236
|
+
const severity = match[1].toLowerCase();
|
|
237
|
+
problems.push(problem({ severity, name, message: name }));
|
|
291
238
|
}
|
|
292
|
-
|
|
293
|
-
const stats = summary();
|
|
294
|
-
stats.errors = problems.length;
|
|
295
|
-
return report('audit', { summary: stats, problems });
|
|
239
|
+
return problems.length ? errorReport('audit', problems) : null;
|
|
296
240
|
};
|
|
297
241
|
|
|
298
|
-
const TOOL_SLOTS = 4;
|
|
299
|
-
|
|
300
242
|
const parseToolText = (text, root) => {
|
|
301
|
-
const reports =
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
return reports;
|
|
243
|
+
const reports = [
|
|
244
|
+
parseEslintStylish(text, root),
|
|
245
|
+
parseTsc(text, root),
|
|
246
|
+
parsePrettier(text, root),
|
|
247
|
+
parseAuditText(text),
|
|
248
|
+
];
|
|
249
|
+
return reports.filter(Boolean);
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const isPrettierLine = (line) => {
|
|
253
|
+
const text = line.trim();
|
|
254
|
+
if (!text) return false;
|
|
255
|
+
if (prettierFile(text) || isPrettierSummary(text)) return true;
|
|
256
|
+
return text.startsWith('Checking formatting');
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const stderrRest = (text, reports) => {
|
|
260
|
+
if (!reports.some((item) => item.tool === 'prettier')) return text;
|
|
261
|
+
const lines = `${text ?? ''}`.split(/\r?\n/);
|
|
262
|
+
return lines
|
|
263
|
+
.filter((line) => !isPrettierLine(line))
|
|
264
|
+
.join('\n')
|
|
265
|
+
.trim();
|
|
325
266
|
};
|
|
326
267
|
|
|
327
268
|
module.exports = {
|
|
328
269
|
parseToolJson,
|
|
329
270
|
parseToolText,
|
|
271
|
+
stderrRest,
|
|
330
272
|
};
|
package/lib/review-merge.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const files = require('./files.js');
|
|
4
|
-
const { TASKS_FILE } = files;
|
|
4
|
+
const { TASKS_FILE, taskKind } = files;
|
|
5
5
|
|
|
6
6
|
const flatText = (text) => `${text ?? ''}`.trim().replace(/\n/g, ' ');
|
|
7
7
|
|
|
@@ -9,21 +9,13 @@ const snapTodo = (todo) => ({
|
|
|
9
9
|
file: todo.file ?? '',
|
|
10
10
|
text: flatText(todo.text),
|
|
11
11
|
done: todo.done === true,
|
|
12
|
+
kind: taskKind(todo),
|
|
12
13
|
});
|
|
13
14
|
|
|
14
|
-
const snapList = (todos) =>
|
|
15
|
-
const list = [];
|
|
16
|
-
for (const todo of todos ?? []) {
|
|
17
|
-
const snap = snapTodo(todo);
|
|
18
|
-
if (!snap.text) continue;
|
|
19
|
-
list.push(snap);
|
|
20
|
-
}
|
|
21
|
-
return list;
|
|
22
|
-
};
|
|
15
|
+
const snapList = (todos) => todos.map(snapTodo).filter((snap) => snap.text);
|
|
23
16
|
|
|
24
17
|
const copyMap = (map) => {
|
|
25
18
|
const next = new Map();
|
|
26
|
-
if (!map) return next;
|
|
27
19
|
for (const [key, note] of map) next.set(key, { ...note });
|
|
28
20
|
return next;
|
|
29
21
|
};
|
|
@@ -58,27 +50,31 @@ const pickDone = (baseDone, ourDone, theirDone) => {
|
|
|
58
50
|
|
|
59
51
|
const withDone = (todo, done) => {
|
|
60
52
|
if (todo.done === done) return todo;
|
|
61
|
-
return {
|
|
53
|
+
return { ...todo, done };
|
|
62
54
|
};
|
|
63
55
|
|
|
64
56
|
const liveText = (todo) => flatText(todo.text);
|
|
65
57
|
|
|
66
58
|
const snapText = (snap) => snap.text;
|
|
67
59
|
|
|
60
|
+
const liveKey = (todo) => `${taskKind(todo)}\0${liveText(todo)}`;
|
|
61
|
+
|
|
62
|
+
const snapKey = (snap) => `${taskKind(snap)}\0${snapText(snap)}`;
|
|
63
|
+
|
|
68
64
|
const flags = (n) => new Array(n).fill(false);
|
|
69
65
|
|
|
70
66
|
const adoptTodo = (store, snap) => {
|
|
71
67
|
const id = store.nextTaskId;
|
|
72
68
|
store.nextTaskId += 1;
|
|
73
69
|
const file = snap.file || TASKS_FILE;
|
|
74
|
-
return { id, file, text: snap.text, done: snap.done };
|
|
70
|
+
return { id, file, text: snap.text, done: snap.done, kind: taskKind(snap) };
|
|
75
71
|
};
|
|
76
72
|
|
|
77
73
|
const replaceTodo = (todo, snap) => ({
|
|
78
|
-
|
|
79
|
-
file: todo.file,
|
|
74
|
+
...todo,
|
|
80
75
|
text: snap.text,
|
|
81
76
|
done: snap.done,
|
|
77
|
+
kind: taskKind(snap),
|
|
82
78
|
});
|
|
83
79
|
|
|
84
80
|
class TodoMerge {
|
|
@@ -94,23 +90,23 @@ class TodoMerge {
|
|
|
94
90
|
this.alias = new Map();
|
|
95
91
|
}
|
|
96
92
|
|
|
97
|
-
keepOurs(index
|
|
93
|
+
keepOurs(index) {
|
|
98
94
|
this.kept[index] = true;
|
|
99
|
-
this.alias.set(
|
|
95
|
+
this.alias.set(liveKey(this.ours[index]), this.ours[index]);
|
|
100
96
|
}
|
|
101
97
|
|
|
102
98
|
mergeBase(base) {
|
|
103
99
|
const { ours, theirs } = this;
|
|
104
|
-
const
|
|
105
|
-
const oi = claim(ours, this.usedOurs,
|
|
106
|
-
const ti = claim(theirs, this.usedTheirs,
|
|
100
|
+
const key = snapKey(base);
|
|
101
|
+
const oi = claim(ours, this.usedOurs, key, liveKey);
|
|
102
|
+
const ti = claim(theirs, this.usedTheirs, key, snapKey);
|
|
107
103
|
if (oi >= 0 && ti >= 0) {
|
|
108
104
|
const done = pickDone(base.done, ours[oi].done === true, theirs[ti].done);
|
|
109
105
|
ours[oi] = withDone(ours[oi], done);
|
|
110
|
-
this.keepOurs(oi
|
|
106
|
+
this.keepOurs(oi);
|
|
111
107
|
} else if (oi >= 0) {
|
|
112
108
|
if (ours[oi].done === base.done) this.dropped[oi] = true;
|
|
113
|
-
else this.keepOurs(oi
|
|
109
|
+
else this.keepOurs(oi);
|
|
114
110
|
} else if (ti >= 0 && theirs[ti].done !== base.done) {
|
|
115
111
|
this.theirKeep[ti] = true;
|
|
116
112
|
}
|
|
@@ -118,32 +114,33 @@ class TodoMerge {
|
|
|
118
114
|
|
|
119
115
|
mergeFresh(index) {
|
|
120
116
|
const { ours, theirs } = this;
|
|
121
|
-
const
|
|
122
|
-
const ti = claim(theirs, this.usedTheirs,
|
|
117
|
+
const key = liveKey(ours[index]);
|
|
118
|
+
const ti = claim(theirs, this.usedTheirs, key, snapKey);
|
|
123
119
|
if (ti < 0) {
|
|
124
120
|
this.kept[index] = true;
|
|
125
121
|
return;
|
|
126
122
|
}
|
|
127
123
|
const done = ours[index].done === true || theirs[ti].done === true;
|
|
128
124
|
ours[index] = withDone(ours[index], done);
|
|
129
|
-
this.keepOurs(index
|
|
125
|
+
this.keepOurs(index);
|
|
130
126
|
}
|
|
131
127
|
|
|
132
|
-
findMerged(merged,
|
|
133
|
-
const
|
|
128
|
+
findMerged(merged, snap) {
|
|
129
|
+
const key = snapKey(snap);
|
|
130
|
+
const linked = this.alias.get(key);
|
|
134
131
|
const at = linked ? merged.indexOf(linked) : -1;
|
|
135
132
|
if (at >= 0) return at;
|
|
136
|
-
return merged.findIndex((todo) =>
|
|
133
|
+
return merged.findIndex((todo) => liveKey(todo) === key);
|
|
137
134
|
}
|
|
138
135
|
|
|
139
136
|
theirPlace(merged, index) {
|
|
140
137
|
const { theirs } = this;
|
|
141
138
|
for (let j = index - 1; j >= 0; j--) {
|
|
142
|
-
const prev = this.findMerged(merged, theirs[j]
|
|
139
|
+
const prev = this.findMerged(merged, theirs[j]);
|
|
143
140
|
if (prev >= 0) return prev + 1;
|
|
144
141
|
}
|
|
145
142
|
for (let j = index + 1; j < theirs.length; j++) {
|
|
146
|
-
const next = this.findMerged(merged, theirs[j]
|
|
143
|
+
const next = this.findMerged(merged, theirs[j]);
|
|
147
144
|
if (next >= 0) return next;
|
|
148
145
|
}
|
|
149
146
|
return merged.length;
|
|
@@ -151,7 +148,7 @@ class TodoMerge {
|
|
|
151
148
|
|
|
152
149
|
place(merged, at, index, item) {
|
|
153
150
|
merged.splice(at, 0, item);
|
|
154
|
-
this.alias.set(this.theirs[index]
|
|
151
|
+
this.alias.set(snapKey(this.theirs[index]), item);
|
|
155
152
|
this.theirKeep[index] = false;
|
|
156
153
|
}
|
|
157
154
|
|
|
@@ -214,10 +211,7 @@ const pickNote = (ours, base, theirs) => {
|
|
|
214
211
|
};
|
|
215
212
|
|
|
216
213
|
const mergeMap = (ours, base, theirs) => {
|
|
217
|
-
const keys = new Set();
|
|
218
|
-
for (const key of ours.keys()) keys.add(key);
|
|
219
|
-
for (const key of base.keys()) keys.add(key);
|
|
220
|
-
for (const key of theirs.keys()) keys.add(key);
|
|
214
|
+
const keys = new Set([...ours.keys(), ...base.keys(), ...theirs.keys()]);
|
|
221
215
|
for (const key of keys) {
|
|
222
216
|
const next = pickNote(ours.get(key), base.get(key), theirs.get(key));
|
|
223
217
|
if (!next) ours.delete(key);
|
|
@@ -227,13 +221,11 @@ const mergeMap = (ours, base, theirs) => {
|
|
|
227
221
|
|
|
228
222
|
const mergeReview = (store, disk) => {
|
|
229
223
|
const base = store.baseline;
|
|
230
|
-
const baseTodos = base?.tasks ?? base?.todos ?? [];
|
|
231
224
|
const baseStatus = base ? base.status : store.status;
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
mergeMap(store.
|
|
236
|
-
mergeMap(store.code, code, disk.code);
|
|
225
|
+
const theirs = snapList(disk.tasks);
|
|
226
|
+
store.tasks = new TodoMerge(store, theirs).merge(base?.tasks ?? []);
|
|
227
|
+
mergeMap(store.feedback, base?.feedback ?? new Map(), disk.feedback);
|
|
228
|
+
mergeMap(store.code, base?.code ?? new Map(), disk.code);
|
|
237
229
|
if (store.status === baseStatus && disk.status) store.status = disk.status;
|
|
238
230
|
store.baseline = snapshotSide(disk);
|
|
239
231
|
};
|