mdrefine 0.2.0-rc.2 → 0.2.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 +577 -0
- package/CODE_OF_CONDUCT.md +30 -0
- package/CONTRIBUTING.md +189 -16
- package/README.md +100 -154
- package/SECURITY.md +15 -13
- package/config.schema.json +91 -0
- package/dist/cli/diff.d.ts +10 -0
- package/dist/cli/diff.js +247 -0
- package/dist/cli/diff.js.map +1 -0
- package/dist/cli/main.js +270 -150
- package/dist/cli/main.js.map +1 -1
- package/dist/config/load.js +67 -6
- package/dist/config/load.js.map +1 -1
- package/dist/config/problems.d.ts +7 -0
- package/dist/config/problems.js +208 -0
- package/dist/config/problems.js.map +1 -0
- package/dist/config/resolve.d.ts +42 -109
- package/dist/config/resolve.js +193 -52
- package/dist/config/resolve.js.map +1 -1
- package/dist/core/directives.d.ts +7 -0
- package/dist/core/directives.js +25 -13
- package/dist/core/directives.js.map +1 -1
- package/dist/core/engine.d.ts +42 -4
- package/dist/core/engine.js +518 -79
- package/dist/core/engine.js.map +1 -1
- package/dist/core/frozen.d.ts +6 -0
- package/dist/core/frozen.js +15 -0
- package/dist/core/frozen.js.map +1 -0
- package/dist/core/scope.d.ts +30 -0
- package/dist/core/scope.js +122 -0
- package/dist/core/scope.js.map +1 -0
- package/dist/core/types.d.ts +202 -23
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +3 -0
- package/dist/node.js +4 -0
- package/dist/node.js.map +1 -0
- package/dist/rules/blocks.d.ts +34 -0
- package/dist/rules/blocks.js +550 -0
- package/dist/rules/blocks.js.map +1 -0
- package/dist/rules/content.d.ts +2 -0
- package/dist/rules/content.js +85 -0
- package/dist/rules/content.js.map +1 -0
- package/dist/rules/dialects.js +10 -6
- package/dist/rules/dialects.js.map +1 -1
- package/dist/rules/links.js +302 -6
- package/dist/rules/links.js.map +1 -1
- package/dist/rules/lists.d.ts +2 -0
- package/dist/rules/lists.js +924 -0
- package/dist/rules/lists.js.map +1 -0
- package/dist/rules/structure.js +127 -1
- package/dist/rules/structure.js.map +1 -1
- package/dist/rules/style.d.ts +8 -2
- package/dist/rules/style.js +1137 -165
- package/dist/rules/style.js.map +1 -1
- package/dist/syntax/block-identifier.d.ts +18 -0
- package/dist/syntax/block-identifier.js +24 -0
- package/dist/syntax/block-identifier.js.map +1 -0
- package/dist/syntax/obsidian.d.ts +29 -11
- package/dist/syntax/obsidian.js +9 -10
- package/dist/syntax/obsidian.js.map +1 -1
- package/dist/syntax/parse.d.ts +87 -3
- package/dist/syntax/parse.js +247 -19
- package/dist/syntax/parse.js.map +1 -1
- package/dist/workspace/access.d.ts +12 -3
- package/dist/workspace/access.js +33 -5
- package/dist/workspace/access.js.map +1 -1
- package/dist/workspace/discovery.d.ts +85 -0
- package/dist/workspace/discovery.js +155 -0
- package/dist/workspace/discovery.js.map +1 -0
- package/dist/workspace/encoding.d.ts +24 -0
- package/dist/workspace/encoding.js +113 -0
- package/dist/workspace/encoding.js.map +1 -0
- package/dist/workspace/files.d.ts +92 -6
- package/dist/workspace/files.js +715 -59
- package/dist/workspace/files.js.map +1 -1
- package/dist/workspace/git.d.ts +54 -0
- package/dist/workspace/git.js +198 -0
- package/dist/workspace/git.js.map +1 -0
- package/dist/workspace/index.d.ts +58 -3
- package/dist/workspace/index.js +136 -37
- package/dist/workspace/index.js.map +1 -1
- package/dist/workspace/selection.d.ts +2 -2
- package/dist/workspace/selection.js +3 -3
- package/dist/workspace/selection.js.map +1 -1
- package/dist/workspace/slug.js +8 -1
- package/dist/workspace/slug.js.map +1 -1
- package/dist/workspace/vault.js +3 -1
- package/dist/workspace/vault.js.map +1 -1
- package/docs/api.md +239 -0
- package/docs/benchmarks.md +140 -200
- package/docs/changelog.md +1 -0
- package/docs/cli.md +281 -89
- package/docs/configuration.md +193 -480
- package/docs/design.md +163 -284
- package/docs/dialects.md +169 -0
- package/docs/examples.md +151 -109
- package/docs/index.md +21 -37
- package/docs/integrations.md +144 -0
- package/docs/obsidian.md +91 -115
- package/docs/plugins.md +147 -212
- package/docs/quick-start.md +68 -74
- package/docs/rules.md +1237 -0
- package/docs/versions.md +56 -0
- package/examples/README.md +17 -14
- package/examples/automation/summarize-report.mjs +5 -2
- package/examples/library/lint-folder.mjs +5 -4
- package/examples/library/lint-workspace.mjs +32 -0
- package/examples/plugin/notes/release-checklist.md +2 -2
- package/examples/plugin/rules.mjs +17 -23
- package/examples/workspaces/obsidian-vault/after/mdtools.config.jsonc +2 -2
- package/examples/workspaces/obsidian-vault/before/mdtools.config.jsonc +2 -2
- package/examples/workspaces/repository-docs/after/README.md +2 -2
- package/examples/workspaces/repository-docs/after/mdtools.config.jsonc +0 -2
- package/examples/workspaces/repository-docs/before/README.md +2 -2
- package/examples/workspaces/repository-docs/before/docs/guide.md +7 -7
- package/examples/workspaces/repository-docs/before/docs/reference.md +3 -1
- package/examples/workspaces/repository-docs/before/mdtools.config.jsonc +0 -2
- package/package.json +38 -12
- package/docs/contributing.md +0 -218
- package/docs/releases/0.1.0-beta.1.md +0 -39
- package/docs/releases/0.1.0-beta.2.md +0 -83
- package/docs/releases/0.1.0-beta.3.md +0 -61
- package/docs/releases/0.1.0-beta.4.md +0 -50
- package/docs/releases/0.1.0-rc.1.md +0 -99
- package/docs/releases/0.2.0-rc.1.md +0 -141
- package/docs/releases/0.2.0-rc.2.md +0 -225
- package/docs/releases.md +0 -144
- package/docs/testing.md +0 -93
package/dist/cli/diff.js
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { diffArrays, formatPatch } from "diff";
|
|
2
|
+
/** Lines of context around each change, as `diff`'s `createTwoFilesPatch` shows them. */
|
|
3
|
+
const context = 4;
|
|
4
|
+
/**
|
|
5
|
+
* The most lines, of both texts together, that one run of the line diff
|
|
6
|
+
* compares. Its cost grows with the lines it compares times the lines that
|
|
7
|
+
* changed, so this limit keeps the cost of the whole diff in proportion to the
|
|
8
|
+
* length of the texts.
|
|
9
|
+
*/
|
|
10
|
+
const limit = 100;
|
|
11
|
+
/** The lines of `text`, each with its line break, split where `diff` splits them. */
|
|
12
|
+
function lines(text) {
|
|
13
|
+
const result = [];
|
|
14
|
+
for (let start = 0; start < text.length;) {
|
|
15
|
+
const end = text.indexOf("\n", start) + 1 || text.length;
|
|
16
|
+
result.push(text.slice(start, end));
|
|
17
|
+
start = end;
|
|
18
|
+
}
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The longest chain of `pairs`, which are in increasing order of their first
|
|
23
|
+
* item, whose second items increase too.
|
|
24
|
+
*/
|
|
25
|
+
function chain(pairs) {
|
|
26
|
+
// The pair that ends the longest chain of each length so far, and each pair's predecessor.
|
|
27
|
+
const ends = [];
|
|
28
|
+
const previous = [];
|
|
29
|
+
pairs.forEach(([, place], index) => {
|
|
30
|
+
let low = 0;
|
|
31
|
+
for (let high = ends.length; low < high;) {
|
|
32
|
+
const middle = (low + high) >> 1;
|
|
33
|
+
if (pairs[ends[middle]][1] < place)
|
|
34
|
+
low = middle + 1;
|
|
35
|
+
else
|
|
36
|
+
high = middle;
|
|
37
|
+
}
|
|
38
|
+
previous[index] = low > 0 ? ends[low - 1] : -1;
|
|
39
|
+
ends[low] = index;
|
|
40
|
+
});
|
|
41
|
+
const result = [];
|
|
42
|
+
for (let index = ends.at(-1) ?? -1; index >= 0; index = previous[index])
|
|
43
|
+
result.push(pairs[index]);
|
|
44
|
+
return result.reverse();
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The regions where `after` differs from `before`, in order, each followed by
|
|
48
|
+
* a line that both texts share, unless it reaches their ends.
|
|
49
|
+
*
|
|
50
|
+
* Lines that occur once in each text, in the same order in both, are anchors
|
|
51
|
+
* that the texts keep, as in patience diff, so that a region never reaches past
|
|
52
|
+
* an unchanged paragraph or heading. Between anchors, at a difference, the
|
|
53
|
+
* texts agree again at the nearest pair of equal lines: the one that skips the
|
|
54
|
+
* fewest lines of both texts together. An index of where each line occurs in
|
|
55
|
+
* `after` finds that pair in time proportional to the lines it skips, so the
|
|
56
|
+
* scan takes time in proportion to the length of the texts, however much
|
|
57
|
+
* changed. No line of a region equals a line of the other text's part of it,
|
|
58
|
+
* since that pair would have been nearer.
|
|
59
|
+
*/
|
|
60
|
+
function regions(before, after) {
|
|
61
|
+
const occurrences = new Map();
|
|
62
|
+
after.forEach((line, index) => {
|
|
63
|
+
const entry = occurrences.get(line);
|
|
64
|
+
if (entry)
|
|
65
|
+
entry.places.push(index);
|
|
66
|
+
else
|
|
67
|
+
occurrences.set(line, { places: [index], passed: 0, old: 0 });
|
|
68
|
+
});
|
|
69
|
+
for (const line of before) {
|
|
70
|
+
const entry = occurrences.get(line);
|
|
71
|
+
if (entry)
|
|
72
|
+
entry.old++;
|
|
73
|
+
}
|
|
74
|
+
const anchors = chain(before.flatMap((line, index) => {
|
|
75
|
+
const entry = occurrences.get(line);
|
|
76
|
+
return entry?.old === 1 && entry.places.length === 1 ? [[index, entry.places[0]]] : [];
|
|
77
|
+
}));
|
|
78
|
+
/** The first place of `line` in `after` at or after `from`, which never decreases. */
|
|
79
|
+
const next = (line, from) => {
|
|
80
|
+
const entry = occurrences.get(line);
|
|
81
|
+
if (!entry)
|
|
82
|
+
return undefined;
|
|
83
|
+
while ((entry.places[entry.passed] ?? Infinity) < from)
|
|
84
|
+
entry.passed++;
|
|
85
|
+
return entry.places[entry.passed];
|
|
86
|
+
};
|
|
87
|
+
const found = [];
|
|
88
|
+
let anchor = 0;
|
|
89
|
+
for (let oldLine = 0, newLine = 0; oldLine < before.length || newLine < after.length;) {
|
|
90
|
+
if (before[oldLine] === after[newLine]) {
|
|
91
|
+
oldLine++;
|
|
92
|
+
newLine++;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
while ((anchors[anchor]?.[0] ?? Infinity) < oldLine)
|
|
96
|
+
anchor++;
|
|
97
|
+
// The region ends at the next anchor, or at the ends of the texts, unless a pair is nearer.
|
|
98
|
+
const [oldLimit, newLimit] = anchors[anchor] ?? [before.length, after.length];
|
|
99
|
+
let oldEnd = oldLimit;
|
|
100
|
+
let newEnd = newLimit;
|
|
101
|
+
let skipped = oldEnd - oldLine + newEnd - newLine;
|
|
102
|
+
for (let line = oldLine; line < oldLimit && line - oldLine < skipped; line++) {
|
|
103
|
+
const place = next(before[line], newLine);
|
|
104
|
+
if (place !== undefined && place < newLimit && line - oldLine + place - newLine < skipped) {
|
|
105
|
+
skipped = line - oldLine + place - newLine;
|
|
106
|
+
oldEnd = line;
|
|
107
|
+
newEnd = place;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
found.push({ oldStart: oldLine, oldEnd, newStart: newLine, newEnd });
|
|
111
|
+
oldLine = oldEnd;
|
|
112
|
+
newLine = newEnd;
|
|
113
|
+
}
|
|
114
|
+
return found;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The runs of lines that turn `before` into `after`. The line diff, which
|
|
118
|
+
* finds the fewest lines to remove and add, compares each group of nearby
|
|
119
|
+
* regions that fits within the limit, so that a pair of equal lines that only
|
|
120
|
+
* happens to be the nearest, such as two blank lines, does not decide how a
|
|
121
|
+
* change is shown. A group's lines run on to the next group, or to the ends,
|
|
122
|
+
* so that texts with one group get the diff of the whole texts. A region
|
|
123
|
+
* larger than the limit has no line in common with the other text's part of
|
|
124
|
+
* it, so the line diff would remove and add it whole, as this does.
|
|
125
|
+
*/
|
|
126
|
+
function runs(before, after) {
|
|
127
|
+
const result = [];
|
|
128
|
+
const add = (kind, count) => {
|
|
129
|
+
const last = result.at(-1);
|
|
130
|
+
if (count === 0)
|
|
131
|
+
return;
|
|
132
|
+
if (last?.kind === kind)
|
|
133
|
+
last.count += count;
|
|
134
|
+
else
|
|
135
|
+
result.push({ kind, count });
|
|
136
|
+
};
|
|
137
|
+
const found = regions(before, after);
|
|
138
|
+
add(" ", found[0]?.oldStart ?? before.length);
|
|
139
|
+
for (let index = 0; index < found.length;) {
|
|
140
|
+
const first = found[index];
|
|
141
|
+
const size = (region) => region.oldEnd - first.oldStart + region.newEnd - first.newStart;
|
|
142
|
+
let end = index + 1;
|
|
143
|
+
while (end < found.length && size(found[end]) <= limit)
|
|
144
|
+
end++;
|
|
145
|
+
const last = found[end - 1];
|
|
146
|
+
const oldEnd = found[end]?.oldStart ?? before.length;
|
|
147
|
+
const newEnd = found[end]?.newStart ?? after.length;
|
|
148
|
+
if (size(last) > limit) {
|
|
149
|
+
add("-", last.oldEnd - first.oldStart);
|
|
150
|
+
add("+", last.newEnd - first.newStart);
|
|
151
|
+
add(" ", oldEnd - last.oldEnd);
|
|
152
|
+
}
|
|
153
|
+
else
|
|
154
|
+
for (const change of diffArrays(before.slice(first.oldStart, oldEnd), after.slice(first.newStart, newEnd)))
|
|
155
|
+
add(change.added ? "+" : change.removed ? "-" : " ", change.count);
|
|
156
|
+
index = end;
|
|
157
|
+
}
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* The hunks of `changes`, as `diff`'s `structuredPatch` makes them: up to
|
|
162
|
+
* `context` kept lines on each side of a change, and one hunk for changes
|
|
163
|
+
* whose contexts touch.
|
|
164
|
+
*/
|
|
165
|
+
function hunks(before, after, changes) {
|
|
166
|
+
const result = [];
|
|
167
|
+
let hunk;
|
|
168
|
+
/** Adds a line to the hunk as a patch shows it, and marks a last line without a line break. */
|
|
169
|
+
const show = (prefix, line) => {
|
|
170
|
+
if (line.endsWith("\n"))
|
|
171
|
+
hunk.lines.push(prefix + line.slice(0, -1));
|
|
172
|
+
else
|
|
173
|
+
hunk.lines.push(prefix + line, "\");
|
|
174
|
+
if (prefix !== "+")
|
|
175
|
+
hunk.oldLines++;
|
|
176
|
+
if (prefix !== "-")
|
|
177
|
+
hunk.newLines++;
|
|
178
|
+
};
|
|
179
|
+
let oldLine = 0;
|
|
180
|
+
let newLine = 0;
|
|
181
|
+
changes.forEach((run, index) => {
|
|
182
|
+
if (run.kind === " ") {
|
|
183
|
+
if (hunk) {
|
|
184
|
+
// When another change follows within twice the context, the hunk takes it in.
|
|
185
|
+
const shown = run.count <= 2 * context && index < changes.length - 1
|
|
186
|
+
? run.count
|
|
187
|
+
: Math.min(run.count, context);
|
|
188
|
+
for (const line of before.slice(oldLine, oldLine + shown))
|
|
189
|
+
show(" ", line);
|
|
190
|
+
if (shown < run.count || index === changes.length - 1) {
|
|
191
|
+
result.push(hunk);
|
|
192
|
+
hunk = undefined;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
oldLine += run.count;
|
|
196
|
+
newLine += run.count;
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (!hunk) {
|
|
200
|
+
// The kept lines before the change, which the previous run holds.
|
|
201
|
+
const leading = Math.min(changes[index - 1]?.count ?? 0, context);
|
|
202
|
+
hunk = {
|
|
203
|
+
oldStart: oldLine - leading + 1,
|
|
204
|
+
oldLines: 0,
|
|
205
|
+
newStart: newLine - leading + 1,
|
|
206
|
+
newLines: 0,
|
|
207
|
+
lines: [],
|
|
208
|
+
};
|
|
209
|
+
for (const line of before.slice(oldLine - leading, oldLine))
|
|
210
|
+
show(" ", line);
|
|
211
|
+
}
|
|
212
|
+
if (run.kind === "-") {
|
|
213
|
+
for (const line of before.slice(oldLine, oldLine + run.count))
|
|
214
|
+
show("-", line);
|
|
215
|
+
oldLine += run.count;
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
for (const line of after.slice(newLine, newLine + run.count))
|
|
219
|
+
show("+", line);
|
|
220
|
+
newLine += run.count;
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
if (hunk)
|
|
224
|
+
result.push(hunk);
|
|
225
|
+
return result;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* A unified diff that turns `before` into `after`, with the headers and hunks
|
|
229
|
+
* of `diff`'s `createTwoFilesPatch`. That function compares the whole texts,
|
|
230
|
+
* at a cost that grows with their length times the number of changed lines, so
|
|
231
|
+
* that rewrapping a long document took minutes to show. This one compares only
|
|
232
|
+
* the regions that changed and their neighbours. Where it removes and adds
|
|
233
|
+
* other lines than a comparison of the whole texts would, both diffs turn
|
|
234
|
+
* `before` into `after`.
|
|
235
|
+
*/
|
|
236
|
+
export function unifiedDiff(oldFileName, newFileName, before, after) {
|
|
237
|
+
const oldLines = lines(before);
|
|
238
|
+
const newLines = lines(after);
|
|
239
|
+
return formatPatch({
|
|
240
|
+
oldFileName,
|
|
241
|
+
newFileName,
|
|
242
|
+
oldHeader: undefined,
|
|
243
|
+
newHeader: undefined,
|
|
244
|
+
hunks: hunks(oldLines, newLines, runs(oldLines, newLines)),
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/cli/diff.ts"],"names":[],"mappings":"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","sourcesContent":["import { diffArrays, formatPatch, type StructuredPatchHunk } from \"diff\";\n\n/** Lines of context around each change, as `diff`'s `createTwoFilesPatch` shows them. */\nconst context = 4;\n/**\n * The most lines, of both texts together, that one run of the line diff\n * compares. Its cost grows with the lines it compares times the lines that\n * changed, so this limit keeps the cost of the whole diff in proportion to the\n * length of the texts.\n */\nconst limit = 100;\n\n/** The lines of `text`, each with its line break, split where `diff` splits them. */\nfunction lines(text: string): string[] {\n const result: string[] = [];\n for (let start = 0; start < text.length;) {\n const end = text.indexOf(\"\\n\", start) + 1 || text.length;\n result.push(text.slice(start, end));\n start = end;\n }\n return result;\n}\n\n/** Lines `oldStart` to `oldEnd` of the old text, which became `newStart` to `newEnd` of the new. */\ninterface Region {\n oldStart: number;\n oldEnd: number;\n newStart: number;\n newEnd: number;\n}\n\n/**\n * Where a line occurs: its places in the new text, how many of them the scan\n * has passed, and how often it occurs in the old text.\n */\ninterface Occurrences {\n places: number[];\n passed: number;\n old: number;\n}\n\n/**\n * The longest chain of `pairs`, which are in increasing order of their first\n * item, whose second items increase too.\n */\nfunction chain(pairs: [number, number][]): [number, number][] {\n // The pair that ends the longest chain of each length so far, and each pair's predecessor.\n const ends: number[] = [];\n const previous: number[] = [];\n pairs.forEach(([, place], index) => {\n let low = 0;\n for (let high = ends.length; low < high;) {\n const middle = (low + high) >> 1;\n if (pairs[ends[middle]!]![1] < place) low = middle + 1;\n else high = middle;\n }\n previous[index] = low > 0 ? ends[low - 1]! : -1;\n ends[low] = index;\n });\n const result: [number, number][] = [];\n for (let index = ends.at(-1) ?? -1; index >= 0; index = previous[index]!)\n result.push(pairs[index]!);\n return result.reverse();\n}\n\n/**\n * The regions where `after` differs from `before`, in order, each followed by\n * a line that both texts share, unless it reaches their ends.\n *\n * Lines that occur once in each text, in the same order in both, are anchors\n * that the texts keep, as in patience diff, so that a region never reaches past\n * an unchanged paragraph or heading. Between anchors, at a difference, the\n * texts agree again at the nearest pair of equal lines: the one that skips the\n * fewest lines of both texts together. An index of where each line occurs in\n * `after` finds that pair in time proportional to the lines it skips, so the\n * scan takes time in proportion to the length of the texts, however much\n * changed. No line of a region equals a line of the other text's part of it,\n * since that pair would have been nearer.\n */\nfunction regions(before: string[], after: string[]): Region[] {\n const occurrences = new Map<string, Occurrences>();\n after.forEach((line, index) => {\n const entry = occurrences.get(line);\n if (entry) entry.places.push(index);\n else occurrences.set(line, { places: [index], passed: 0, old: 0 });\n });\n for (const line of before) {\n const entry = occurrences.get(line);\n if (entry) entry.old++;\n }\n const anchors = chain(\n before.flatMap((line, index): [number, number][] => {\n const entry = occurrences.get(line);\n return entry?.old === 1 && entry.places.length === 1 ? [[index, entry.places[0]!]] : [];\n }),\n );\n /** The first place of `line` in `after` at or after `from`, which never decreases. */\n const next = (line: string, from: number) => {\n const entry = occurrences.get(line);\n if (!entry) return undefined;\n while ((entry.places[entry.passed] ?? Infinity) < from) entry.passed++;\n return entry.places[entry.passed];\n };\n const found: Region[] = [];\n let anchor = 0;\n for (let oldLine = 0, newLine = 0; oldLine < before.length || newLine < after.length;) {\n if (before[oldLine] === after[newLine]) {\n oldLine++;\n newLine++;\n continue;\n }\n while ((anchors[anchor]?.[0] ?? Infinity) < oldLine) anchor++;\n // The region ends at the next anchor, or at the ends of the texts, unless a pair is nearer.\n const [oldLimit, newLimit] = anchors[anchor] ?? [before.length, after.length];\n let oldEnd = oldLimit;\n let newEnd = newLimit;\n let skipped = oldEnd - oldLine + newEnd - newLine;\n for (let line = oldLine; line < oldLimit && line - oldLine < skipped; line++) {\n const place = next(before[line]!, newLine);\n if (place !== undefined && place < newLimit && line - oldLine + place - newLine < skipped) {\n skipped = line - oldLine + place - newLine;\n oldEnd = line;\n newEnd = place;\n }\n }\n found.push({ oldStart: oldLine, oldEnd, newStart: newLine, newEnd });\n oldLine = oldEnd;\n newLine = newEnd;\n }\n return found;\n}\n\n/** Lines that both texts keep (\" \"), that the old text loses (\"-\"), or that the new gains (\"+\"). */\ninterface Run {\n kind: \" \" | \"-\" | \"+\";\n count: number;\n}\n\n/**\n * The runs of lines that turn `before` into `after`. The line diff, which\n * finds the fewest lines to remove and add, compares each group of nearby\n * regions that fits within the limit, so that a pair of equal lines that only\n * happens to be the nearest, such as two blank lines, does not decide how a\n * change is shown. A group's lines run on to the next group, or to the ends,\n * so that texts with one group get the diff of the whole texts. A region\n * larger than the limit has no line in common with the other text's part of\n * it, so the line diff would remove and add it whole, as this does.\n */\nfunction runs(before: string[], after: string[]): Run[] {\n const result: Run[] = [];\n const add = (kind: Run[\"kind\"], count: number) => {\n const last = result.at(-1);\n if (count === 0) return;\n if (last?.kind === kind) last.count += count;\n else result.push({ kind, count });\n };\n const found = regions(before, after);\n add(\" \", found[0]?.oldStart ?? before.length);\n for (let index = 0; index < found.length;) {\n const first = found[index]!;\n const size = (region: Region) =>\n region.oldEnd - first.oldStart + region.newEnd - first.newStart;\n let end = index + 1;\n while (end < found.length && size(found[end]!) <= limit) end++;\n const last = found[end - 1]!;\n const oldEnd = found[end]?.oldStart ?? before.length;\n const newEnd = found[end]?.newStart ?? after.length;\n if (size(last) > limit) {\n add(\"-\", last.oldEnd - first.oldStart);\n add(\"+\", last.newEnd - first.newStart);\n add(\" \", oldEnd - last.oldEnd);\n } else\n for (const change of diffArrays(\n before.slice(first.oldStart, oldEnd),\n after.slice(first.newStart, newEnd),\n ))\n add(change.added ? \"+\" : change.removed ? \"-\" : \" \", change.count);\n index = end;\n }\n return result;\n}\n\n/**\n * The hunks of `changes`, as `diff`'s `structuredPatch` makes them: up to\n * `context` kept lines on each side of a change, and one hunk for changes\n * whose contexts touch.\n */\nfunction hunks(before: string[], after: string[], changes: Run[]): StructuredPatchHunk[] {\n const result: StructuredPatchHunk[] = [];\n let hunk: StructuredPatchHunk | undefined;\n /** Adds a line to the hunk as a patch shows it, and marks a last line without a line break. */\n const show = (prefix: Run[\"kind\"], line: string) => {\n if (line.endsWith(\"\\n\")) hunk!.lines.push(prefix + line.slice(0, -1));\n else hunk!.lines.push(prefix + line, \"\\\\");\n if (prefix !== \"+\") hunk!.oldLines++;\n if (prefix !== \"-\") hunk!.newLines++;\n };\n let oldLine = 0;\n let newLine = 0;\n changes.forEach((run, index) => {\n if (run.kind === \" \") {\n if (hunk) {\n // When another change follows within twice the context, the hunk takes it in.\n const shown =\n run.count <= 2 * context && index < changes.length - 1\n ? run.count\n : Math.min(run.count, context);\n for (const line of before.slice(oldLine, oldLine + shown)) show(\" \", line);\n if (shown < run.count || index === changes.length - 1) {\n result.push(hunk);\n hunk = undefined;\n }\n }\n oldLine += run.count;\n newLine += run.count;\n return;\n }\n if (!hunk) {\n // The kept lines before the change, which the previous run holds.\n const leading = Math.min(changes[index - 1]?.count ?? 0, context);\n hunk = {\n oldStart: oldLine - leading + 1,\n oldLines: 0,\n newStart: newLine - leading + 1,\n newLines: 0,\n lines: [],\n };\n for (const line of before.slice(oldLine - leading, oldLine)) show(\" \", line);\n }\n if (run.kind === \"-\") {\n for (const line of before.slice(oldLine, oldLine + run.count)) show(\"-\", line);\n oldLine += run.count;\n } else {\n for (const line of after.slice(newLine, newLine + run.count)) show(\"+\", line);\n newLine += run.count;\n }\n });\n if (hunk) result.push(hunk);\n return result;\n}\n\n/**\n * A unified diff that turns `before` into `after`, with the headers and hunks\n * of `diff`'s `createTwoFilesPatch`. That function compares the whole texts,\n * at a cost that grows with their length times the number of changed lines, so\n * that rewrapping a long document took minutes to show. This one compares only\n * the regions that changed and their neighbours. Where it removes and adds\n * other lines than a comparison of the whole texts would, both diffs turn\n * `before` into `after`.\n */\nexport function unifiedDiff(\n oldFileName: string,\n newFileName: string,\n before: string,\n after: string,\n): string {\n const oldLines = lines(before);\n const newLines = lines(after);\n return formatPatch({\n oldFileName,\n newFileName,\n oldHeader: undefined,\n newHeader: undefined,\n hunks: hunks(oldLines, newLines, runs(oldLines, newLines)),\n });\n}\n"]}
|