@incremark/core 0.2.6 → 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/dist/detector/index.d.ts +118 -1
- package/dist/detector/index.js +196 -118
- package/dist/detector/index.js.map +1 -1
- package/dist/index-CfgnWMWh.d.ts +225 -0
- package/dist/index.d.ts +11 -40
- package/dist/index.js +892 -713
- package/dist/index.js.map +1 -1
- package/dist/utils/index.d.ts +17 -1
- package/dist/utils/index.js +21 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +2 -1
- package/dist/index-BMUkM7mT.d.ts +0 -422
package/dist/index.js
CHANGED
|
@@ -4,6 +4,8 @@ import { gfm } from 'micromark-extension-gfm';
|
|
|
4
4
|
import { gfmFootnoteFromMarkdown } from 'mdast-util-gfm-footnote';
|
|
5
5
|
import { math } from 'micromark-extension-math';
|
|
6
6
|
import { mathFromMarkdown } from 'mdast-util-math';
|
|
7
|
+
import { directive } from 'micromark-extension-directive';
|
|
8
|
+
import { directiveFromMarkdown } from 'mdast-util-directive';
|
|
7
9
|
import { codes, constants, types } from 'micromark-util-symbol';
|
|
8
10
|
import { markdownLineEndingOrSpace } from 'micromark-util-character';
|
|
9
11
|
import { factoryDestination } from 'micromark-factory-destination';
|
|
@@ -12,10 +14,699 @@ import { factoryLabel } from 'micromark-factory-label';
|
|
|
12
14
|
import { factoryWhitespace } from 'micromark-factory-whitespace';
|
|
13
15
|
import { gfmFootnote } from 'micromark-extension-gfm-footnote';
|
|
14
16
|
import { normalizeIdentifier } from 'micromark-util-normalize-identifier';
|
|
15
|
-
import { directive } from 'micromark-extension-directive';
|
|
16
|
-
import { directiveFromMarkdown } from 'mdast-util-directive';
|
|
17
17
|
|
|
18
|
-
// src/
|
|
18
|
+
// src/detector/index.ts
|
|
19
|
+
var RE_FENCE_START = /^(\s*)((`{3,})|(~{3,}))/;
|
|
20
|
+
var RE_EMPTY_LINE = /^\s*$/;
|
|
21
|
+
var RE_HEADING = /^#{1,6}\s/;
|
|
22
|
+
var RE_THEMATIC_BREAK = /^(\*{3,}|-{3,}|_{3,})\s*$/;
|
|
23
|
+
var RE_BLOCKQUOTE = /^\s{0,3}>/;
|
|
24
|
+
var RE_ESCAPE_SPECIAL = /[.*+?^${}()|[\]\\]/g;
|
|
25
|
+
var RE_FOOTNOTE_DEFINITION = /^\[\^([^\]]+)\]:\s/;
|
|
26
|
+
var RE_FOOTNOTE_CONTINUATION = /^(?: |\t)/;
|
|
27
|
+
var fenceEndPatternCache = /* @__PURE__ */ new Map();
|
|
28
|
+
var containerPatternCache = /* @__PURE__ */ new Map();
|
|
29
|
+
function detectFenceStart(line) {
|
|
30
|
+
const match = line.match(RE_FENCE_START);
|
|
31
|
+
if (match) {
|
|
32
|
+
const fence = match[2];
|
|
33
|
+
const char = fence[0];
|
|
34
|
+
return { char, length: fence.length };
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
function detectFenceEnd(line, context) {
|
|
39
|
+
if (!context.inFencedCode || !context.fenceChar || !context.fenceLength) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const cacheKey = `${context.fenceChar}-${context.fenceLength}`;
|
|
43
|
+
let pattern = fenceEndPatternCache.get(cacheKey);
|
|
44
|
+
if (!pattern) {
|
|
45
|
+
pattern = new RegExp(`^\\s{0,3}${context.fenceChar}{${context.fenceLength},}\\s*$`);
|
|
46
|
+
fenceEndPatternCache.set(cacheKey, pattern);
|
|
47
|
+
}
|
|
48
|
+
return pattern.test(line);
|
|
49
|
+
}
|
|
50
|
+
function isEmptyLine(line) {
|
|
51
|
+
return RE_EMPTY_LINE.test(line);
|
|
52
|
+
}
|
|
53
|
+
function isSetextHeadingUnderline(line, prevLine) {
|
|
54
|
+
const trimmed = line.trim();
|
|
55
|
+
if (!/^={3,}$|^-{3,}$/.test(trimmed)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
if (!prevLine) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
const trimmedPrev = prevLine.trim();
|
|
62
|
+
if (trimmedPrev === "") {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
if (/^#{1,6}\s/.test(trimmedPrev) || /^(\*{3,}|-{3,}|_{3,})\s*$/.test(trimmedPrev)) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (/^(\s*)([-*+])\s/.test(trimmedPrev) || /^(\s*)(\d{1,9})[.)]\s/.test(trimmedPrev)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
if (/^\s{0,3}>/.test(trimmedPrev)) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
if (/^(\s*)(`{3,}|~{3,})/.test(trimmedPrev)) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
const underlineIndent = line.match(/^(\s*)/)?.[1].length ?? 0;
|
|
78
|
+
if (underlineIndent > 3) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
const contentIndent = prevLine.match(/^(\s*)/)?.[1].length ?? 0;
|
|
82
|
+
if (contentIndent > 3) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
function isHeading(line) {
|
|
88
|
+
return RE_HEADING.test(line);
|
|
89
|
+
}
|
|
90
|
+
function isThematicBreak(line) {
|
|
91
|
+
return RE_THEMATIC_BREAK.test(line.trim());
|
|
92
|
+
}
|
|
93
|
+
function isListItemStart(line) {
|
|
94
|
+
const hasListMarker = /^(\s*)([-*+]|\d{1,9}[.)])/.test(line);
|
|
95
|
+
if (!hasListMarker) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
const match = line.match(/^(\s*)([-*+]|\d{1,9}[.)])(.*)/);
|
|
99
|
+
if (match) {
|
|
100
|
+
const indent = match[1].length;
|
|
101
|
+
const marker = match[2];
|
|
102
|
+
const rest = match[3];
|
|
103
|
+
if (rest.trim()) {
|
|
104
|
+
const isOrdered = /^\d{1,9}[.)]/.test(marker);
|
|
105
|
+
return { ordered: isOrdered, indent };
|
|
106
|
+
}
|
|
107
|
+
if (/^\s+$/.test(rest)) {
|
|
108
|
+
const isOrdered = /^\d{1,9}[.)]/.test(marker);
|
|
109
|
+
return { ordered: isOrdered, indent };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
function isBlockquoteStart(line) {
|
|
115
|
+
return RE_BLOCKQUOTE.test(line);
|
|
116
|
+
}
|
|
117
|
+
function isFootnoteDefinitionStart(line) {
|
|
118
|
+
return RE_FOOTNOTE_DEFINITION.test(line);
|
|
119
|
+
}
|
|
120
|
+
function isFootnoteContinuation(line) {
|
|
121
|
+
return RE_FOOTNOTE_CONTINUATION.test(line);
|
|
122
|
+
}
|
|
123
|
+
function detectContainer(line, config) {
|
|
124
|
+
const marker = config?.marker || ":";
|
|
125
|
+
const minLength = config?.minMarkerLength || 3;
|
|
126
|
+
const cacheKey = `${marker}-${minLength}`;
|
|
127
|
+
let pattern = containerPatternCache.get(cacheKey);
|
|
128
|
+
if (!pattern) {
|
|
129
|
+
const escapedMarker = marker.replace(RE_ESCAPE_SPECIAL, "\\$&");
|
|
130
|
+
pattern = new RegExp(
|
|
131
|
+
`^(\\s*)(${escapedMarker}{${minLength},})(?:\\s*(\\w[\\w-]*))?(?:\\{[^}]*\\})?(?:\\s+(.*))?\\s*$`
|
|
132
|
+
);
|
|
133
|
+
containerPatternCache.set(cacheKey, pattern);
|
|
134
|
+
}
|
|
135
|
+
const match = line.match(pattern);
|
|
136
|
+
if (!match) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
const markerLength = match[2].length;
|
|
140
|
+
const name = match[3] || "";
|
|
141
|
+
const isEnd = !name && !match[4];
|
|
142
|
+
if (!isEnd && config?.allowedNames && config.allowedNames.length > 0) {
|
|
143
|
+
if (!config.allowedNames.includes(name)) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return { name, markerLength, isEnd };
|
|
148
|
+
}
|
|
149
|
+
function detectContainerEnd(line, context, config) {
|
|
150
|
+
if (!context.inContainer || !context.containerMarkerLength) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
const result = detectContainer(line, config);
|
|
154
|
+
if (!result) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
return result.isEnd && result.markerLength >= context.containerMarkerLength;
|
|
158
|
+
}
|
|
159
|
+
var CodeContextUpdater = class {
|
|
160
|
+
update(line, context) {
|
|
161
|
+
const newContext = { ...context };
|
|
162
|
+
if (context.inFencedCode) {
|
|
163
|
+
if (detectFenceEnd(line, context)) {
|
|
164
|
+
newContext.inFencedCode = false;
|
|
165
|
+
newContext.fenceChar = void 0;
|
|
166
|
+
newContext.fenceLength = void 0;
|
|
167
|
+
return newContext;
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
const fence = detectFenceStart(line);
|
|
172
|
+
if (fence) {
|
|
173
|
+
newContext.inFencedCode = true;
|
|
174
|
+
newContext.fenceChar = fence.char;
|
|
175
|
+
newContext.fenceLength = fence.length;
|
|
176
|
+
return newContext;
|
|
177
|
+
}
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
var ContainerContextUpdater = class {
|
|
182
|
+
update(line, context, config) {
|
|
183
|
+
if (config === void 0) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
const newContext = { ...context };
|
|
187
|
+
if (context.inContainer) {
|
|
188
|
+
if (detectContainerEnd(line, context, config)) {
|
|
189
|
+
newContext.containerDepth = context.containerDepth - 1;
|
|
190
|
+
if (newContext.containerDepth === 0) {
|
|
191
|
+
newContext.inContainer = false;
|
|
192
|
+
newContext.containerMarkerLength = void 0;
|
|
193
|
+
newContext.containerName = void 0;
|
|
194
|
+
}
|
|
195
|
+
return newContext;
|
|
196
|
+
}
|
|
197
|
+
const nested = detectContainer(line, config);
|
|
198
|
+
if (nested && !nested.isEnd) {
|
|
199
|
+
newContext.containerDepth = context.containerDepth + 1;
|
|
200
|
+
return newContext;
|
|
201
|
+
}
|
|
202
|
+
return newContext;
|
|
203
|
+
} else {
|
|
204
|
+
const container = detectContainer(line, config);
|
|
205
|
+
if (container && !container.isEnd) {
|
|
206
|
+
newContext.inContainer = true;
|
|
207
|
+
newContext.containerMarkerLength = container.markerLength;
|
|
208
|
+
newContext.containerName = container.name;
|
|
209
|
+
newContext.containerDepth = 1;
|
|
210
|
+
return newContext;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
var FootnoteContextUpdater = class {
|
|
217
|
+
update(line, context) {
|
|
218
|
+
const newContext = { ...context };
|
|
219
|
+
if (!context.inFootnote && isFootnoteDefinitionStart(line)) {
|
|
220
|
+
const identifier = line.match(RE_FOOTNOTE_DEFINITION)?.[1];
|
|
221
|
+
newContext.inFootnote = true;
|
|
222
|
+
newContext.footnoteIdentifier = identifier;
|
|
223
|
+
return newContext;
|
|
224
|
+
}
|
|
225
|
+
if (context.inFootnote) {
|
|
226
|
+
if (isFootnoteDefinitionStart(line)) {
|
|
227
|
+
const identifier = line.match(RE_FOOTNOTE_DEFINITION)?.[1];
|
|
228
|
+
newContext.footnoteIdentifier = identifier;
|
|
229
|
+
return newContext;
|
|
230
|
+
}
|
|
231
|
+
if (isEmptyLine(line)) {
|
|
232
|
+
return { ...context };
|
|
233
|
+
}
|
|
234
|
+
const listItem = isListItemStart(line);
|
|
235
|
+
if (listItem) {
|
|
236
|
+
if (listItem.indent === 0) {
|
|
237
|
+
newContext.inFootnote = false;
|
|
238
|
+
newContext.footnoteIdentifier = void 0;
|
|
239
|
+
} else {
|
|
240
|
+
return { ...context };
|
|
241
|
+
}
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
if (isHeading(line) || detectFenceStart(line) || isBlockquoteStart(line)) {
|
|
245
|
+
newContext.inFootnote = false;
|
|
246
|
+
newContext.footnoteIdentifier = void 0;
|
|
247
|
+
return newContext;
|
|
248
|
+
}
|
|
249
|
+
if (isFootnoteContinuation(line)) {
|
|
250
|
+
return { ...context };
|
|
251
|
+
}
|
|
252
|
+
newContext.inFootnote = false;
|
|
253
|
+
newContext.footnoteIdentifier = void 0;
|
|
254
|
+
return newContext;
|
|
255
|
+
}
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
var ListContextUpdater = class {
|
|
260
|
+
/**
|
|
261
|
+
* 检测是否是列表项的延续内容(缩进内容或空行)
|
|
262
|
+
*/
|
|
263
|
+
isListContinuation(line, listIndent) {
|
|
264
|
+
if (isEmptyLine(line)) {
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
const contentIndent = line.match(/^(\s*)/)?.[1].length ?? 0;
|
|
268
|
+
return contentIndent > listIndent;
|
|
269
|
+
}
|
|
270
|
+
update(line, context) {
|
|
271
|
+
const newContext = { ...context };
|
|
272
|
+
const listItem = isListItemStart(line);
|
|
273
|
+
if (context.inList) {
|
|
274
|
+
if (context.listMayEnd) {
|
|
275
|
+
if (listItem) {
|
|
276
|
+
if (listItem.ordered === context.listOrdered && listItem.indent === context.listIndent) {
|
|
277
|
+
newContext.listMayEnd = false;
|
|
278
|
+
return newContext;
|
|
279
|
+
}
|
|
280
|
+
newContext.listOrdered = listItem.ordered;
|
|
281
|
+
newContext.listIndent = listItem.indent;
|
|
282
|
+
newContext.listMayEnd = false;
|
|
283
|
+
return newContext;
|
|
284
|
+
} else if (this.isListContinuation(line, context.listIndent ?? 0)) {
|
|
285
|
+
newContext.listMayEnd = isEmptyLine(line);
|
|
286
|
+
return newContext;
|
|
287
|
+
} else {
|
|
288
|
+
newContext.inList = false;
|
|
289
|
+
newContext.listOrdered = void 0;
|
|
290
|
+
newContext.listIndent = void 0;
|
|
291
|
+
newContext.listMayEnd = false;
|
|
292
|
+
return newContext;
|
|
293
|
+
}
|
|
294
|
+
} else {
|
|
295
|
+
if (listItem) {
|
|
296
|
+
return null;
|
|
297
|
+
} else if (isEmptyLine(line)) {
|
|
298
|
+
newContext.listMayEnd = true;
|
|
299
|
+
return newContext;
|
|
300
|
+
} else if (this.isListContinuation(line, context.listIndent ?? 0)) {
|
|
301
|
+
return null;
|
|
302
|
+
} else {
|
|
303
|
+
newContext.inList = false;
|
|
304
|
+
newContext.listOrdered = void 0;
|
|
305
|
+
newContext.listIndent = void 0;
|
|
306
|
+
newContext.listMayEnd = false;
|
|
307
|
+
return newContext;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
} else {
|
|
311
|
+
if (listItem) {
|
|
312
|
+
newContext.inList = true;
|
|
313
|
+
newContext.listOrdered = listItem.ordered;
|
|
314
|
+
newContext.listIndent = listItem.indent;
|
|
315
|
+
newContext.listMayEnd = false;
|
|
316
|
+
return newContext;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
function createInitialContext() {
|
|
323
|
+
return {
|
|
324
|
+
inFencedCode: false,
|
|
325
|
+
listDepth: 0,
|
|
326
|
+
blockquoteDepth: 0,
|
|
327
|
+
inContainer: false,
|
|
328
|
+
containerDepth: 0,
|
|
329
|
+
inList: false,
|
|
330
|
+
inFootnote: false,
|
|
331
|
+
footnoteIdentifier: void 0
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
var ContextManager = class {
|
|
335
|
+
updaters = [
|
|
336
|
+
new CodeContextUpdater(),
|
|
337
|
+
new ContainerContextUpdater(),
|
|
338
|
+
new FootnoteContextUpdater(),
|
|
339
|
+
new ListContextUpdater()
|
|
340
|
+
];
|
|
341
|
+
/**
|
|
342
|
+
* 更新上下文(处理一行后)
|
|
343
|
+
*
|
|
344
|
+
* @param line 当前行
|
|
345
|
+
* @param context 当前上下文
|
|
346
|
+
* @param containerConfig 容器配置
|
|
347
|
+
* @returns 更新后的上下文
|
|
348
|
+
*/
|
|
349
|
+
update(line, context, containerConfig) {
|
|
350
|
+
const config = containerConfig === true ? {} : containerConfig === false ? void 0 : containerConfig;
|
|
351
|
+
for (const updater of this.updaters) {
|
|
352
|
+
const result = updater.update(line, context, config);
|
|
353
|
+
if (result !== null) {
|
|
354
|
+
return result;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return { ...context };
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
var contextManager = new ContextManager();
|
|
361
|
+
function updateContext(line, context, containerConfig) {
|
|
362
|
+
return contextManager.update(line, context, containerConfig);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// src/parser/boundary/BoundaryDetector.ts
|
|
366
|
+
var ContainerBoundaryChecker = class {
|
|
367
|
+
constructor(containerConfig) {
|
|
368
|
+
this.containerConfig = containerConfig;
|
|
369
|
+
}
|
|
370
|
+
check(lineIndex, context, lines) {
|
|
371
|
+
const line = lines[lineIndex];
|
|
372
|
+
if (!context.inContainer) {
|
|
373
|
+
return -1;
|
|
374
|
+
}
|
|
375
|
+
if (this.containerConfig !== void 0) {
|
|
376
|
+
const containerEnd = detectContainerEnd(line, context, this.containerConfig);
|
|
377
|
+
if (containerEnd) {
|
|
378
|
+
return lineIndex - 1;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return -1;
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
var ListBoundaryChecker = class {
|
|
385
|
+
check(lineIndex, context, lines) {
|
|
386
|
+
if (!context.inList) {
|
|
387
|
+
return -1;
|
|
388
|
+
}
|
|
389
|
+
if (!context.listMayEnd) {
|
|
390
|
+
return -1;
|
|
391
|
+
}
|
|
392
|
+
const line = lines[lineIndex];
|
|
393
|
+
const listItem = isListItemStart(line);
|
|
394
|
+
const contentIndent = line.match(/^(\s*)/)?.[1].length ?? 0;
|
|
395
|
+
const isListContent = contentIndent > (context.listIndent ?? 0);
|
|
396
|
+
if (!listItem && !isListContent && !isEmptyLine(line)) {
|
|
397
|
+
return lineIndex - 1;
|
|
398
|
+
}
|
|
399
|
+
return -1;
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
var FootnoteBoundaryChecker = class {
|
|
403
|
+
check(lineIndex, context, lines) {
|
|
404
|
+
const line = lines[lineIndex];
|
|
405
|
+
const prevLine = lines[lineIndex - 1];
|
|
406
|
+
if (isFootnoteDefinitionStart(prevLine)) {
|
|
407
|
+
if (isEmptyLine(line) || isFootnoteContinuation(line)) {
|
|
408
|
+
return -1;
|
|
409
|
+
}
|
|
410
|
+
if (isFootnoteDefinitionStart(line)) {
|
|
411
|
+
return lineIndex - 1;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
if (!isEmptyLine(prevLine) && isFootnoteContinuation(prevLine)) {
|
|
415
|
+
return -1;
|
|
416
|
+
}
|
|
417
|
+
if (!isEmptyLine(prevLine) && isFootnoteDefinitionStart(line) && !isFootnoteDefinitionStart(prevLine)) {
|
|
418
|
+
return lineIndex - 1;
|
|
419
|
+
}
|
|
420
|
+
return -1;
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
var NewBlockBoundaryChecker = class {
|
|
424
|
+
check(lineIndex, context, lines) {
|
|
425
|
+
const line = lines[lineIndex];
|
|
426
|
+
const prevLine = lines[lineIndex - 1];
|
|
427
|
+
if (isEmptyLine(prevLine)) {
|
|
428
|
+
return -1;
|
|
429
|
+
}
|
|
430
|
+
if (isSetextHeadingUnderline(line, prevLine)) {
|
|
431
|
+
return lineIndex - 1;
|
|
432
|
+
}
|
|
433
|
+
if (isHeading(line)) {
|
|
434
|
+
return lineIndex - 1;
|
|
435
|
+
}
|
|
436
|
+
if (detectFenceStart(line)) {
|
|
437
|
+
return lineIndex - 1;
|
|
438
|
+
}
|
|
439
|
+
if (isBlockquoteStart(line) && !isBlockquoteStart(prevLine)) {
|
|
440
|
+
return lineIndex - 1;
|
|
441
|
+
}
|
|
442
|
+
if (!context.inList && isListItemStart(line) && !isListItemStart(prevLine)) {
|
|
443
|
+
return lineIndex - 1;
|
|
444
|
+
}
|
|
445
|
+
return -1;
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
var EmptyLineBoundaryChecker = class {
|
|
449
|
+
check(lineIndex, context, lines) {
|
|
450
|
+
const line = lines[lineIndex];
|
|
451
|
+
const prevLine = lines[lineIndex - 1];
|
|
452
|
+
if (isEmptyLine(line) && !isEmptyLine(prevLine) && !context.inList) {
|
|
453
|
+
return lineIndex;
|
|
454
|
+
}
|
|
455
|
+
return -1;
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
var BoundaryDetector = class {
|
|
459
|
+
containerConfig;
|
|
460
|
+
checkers;
|
|
461
|
+
constructor(config = {}) {
|
|
462
|
+
this.containerConfig = config.containers;
|
|
463
|
+
this.checkers = [
|
|
464
|
+
new ContainerBoundaryChecker(this.containerConfig),
|
|
465
|
+
new ListBoundaryChecker(),
|
|
466
|
+
new FootnoteBoundaryChecker(),
|
|
467
|
+
new NewBlockBoundaryChecker(),
|
|
468
|
+
new EmptyLineBoundaryChecker()
|
|
469
|
+
];
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* 查找稳定边界
|
|
473
|
+
* 返回稳定边界行号和该行对应的上下文(用于后续更新,避免重复计算)
|
|
474
|
+
*
|
|
475
|
+
* @param lines 所有行
|
|
476
|
+
* @param startLine 起始行
|
|
477
|
+
* @param context 当前上下文
|
|
478
|
+
* @returns 稳定边界结果
|
|
479
|
+
*/
|
|
480
|
+
findStableBoundary(lines, startLine, context) {
|
|
481
|
+
let stableLine = -1;
|
|
482
|
+
let stableContext = context;
|
|
483
|
+
let tempContext = { ...context };
|
|
484
|
+
for (let i = startLine; i < lines.length; i++) {
|
|
485
|
+
const line = lines[i];
|
|
486
|
+
const wasInFencedCode = tempContext.inFencedCode;
|
|
487
|
+
const wasInContainer = tempContext.inContainer;
|
|
488
|
+
const wasContainerDepth = tempContext.containerDepth;
|
|
489
|
+
tempContext = updateContext(line, tempContext, this.containerConfig);
|
|
490
|
+
if (wasInFencedCode && !tempContext.inFencedCode) {
|
|
491
|
+
if (i < lines.length - 1) {
|
|
492
|
+
stableLine = i;
|
|
493
|
+
stableContext = { ...tempContext };
|
|
494
|
+
}
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
if (tempContext.inFencedCode) {
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
if (wasInContainer && wasContainerDepth === 1 && !tempContext.inContainer) {
|
|
501
|
+
if (i < lines.length - 1) {
|
|
502
|
+
stableLine = i;
|
|
503
|
+
stableContext = { ...tempContext };
|
|
504
|
+
}
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
if (tempContext.inContainer) {
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
const stablePoint = this.checkStability(i, tempContext, lines);
|
|
511
|
+
if (stablePoint >= 0) {
|
|
512
|
+
stableLine = stablePoint;
|
|
513
|
+
stableContext = { ...tempContext };
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return { line: stableLine, context: stableContext };
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* 检查指定行是否是稳定边界
|
|
520
|
+
* 使用责任链模式,依次调用各个检查器
|
|
521
|
+
*
|
|
522
|
+
* @param lineIndex 行索引
|
|
523
|
+
* @param context 当前上下文
|
|
524
|
+
* @param lines 所有行
|
|
525
|
+
* @returns 稳定边界行号,如果不是稳定边界返回 -1
|
|
526
|
+
*/
|
|
527
|
+
checkStability(lineIndex, context, lines) {
|
|
528
|
+
if (lineIndex === 0) {
|
|
529
|
+
return -1;
|
|
530
|
+
}
|
|
531
|
+
const line = lines[lineIndex];
|
|
532
|
+
const prevLine = lines[lineIndex - 1];
|
|
533
|
+
if (context.inFootnote) {
|
|
534
|
+
if (isFootnoteDefinitionStart(prevLine) && !isEmptyLine(line)) {
|
|
535
|
+
if (isFootnoteContinuation(line)) {
|
|
536
|
+
return -1;
|
|
537
|
+
}
|
|
538
|
+
return lineIndex - 1;
|
|
539
|
+
}
|
|
540
|
+
if (isEmptyLine(prevLine) && (isEmptyLine(line) || isFootnoteContinuation(line))) {
|
|
541
|
+
return -1;
|
|
542
|
+
}
|
|
543
|
+
return -1;
|
|
544
|
+
}
|
|
545
|
+
if (isHeading(prevLine) || isThematicBreak(prevLine)) {
|
|
546
|
+
return lineIndex - 1;
|
|
547
|
+
}
|
|
548
|
+
if (isSetextHeadingUnderline(prevLine, lines[lineIndex - 2])) {
|
|
549
|
+
return lineIndex - 1;
|
|
550
|
+
}
|
|
551
|
+
if (lineIndex >= lines.length - 1) {
|
|
552
|
+
return -1;
|
|
553
|
+
}
|
|
554
|
+
for (const checker of this.checkers) {
|
|
555
|
+
const stablePoint = checker.check(lineIndex, context, lines);
|
|
556
|
+
if (stablePoint >= 0) {
|
|
557
|
+
return stablePoint;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
return -1;
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
// src/utils/index.ts
|
|
565
|
+
function isDefinitionNode(node) {
|
|
566
|
+
return node.type === "definition";
|
|
567
|
+
}
|
|
568
|
+
function isFootnoteDefinitionNode(node) {
|
|
569
|
+
return node.type === "footnoteDefinition";
|
|
570
|
+
}
|
|
571
|
+
function traverseAst(node, visitor) {
|
|
572
|
+
const stopEarly = visitor(node);
|
|
573
|
+
if (stopEarly === true) {
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
if ("children" in node && Array.isArray(node.children)) {
|
|
577
|
+
for (const child of node.children) {
|
|
578
|
+
traverseAst(child, visitor);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
function collectAstNodes(node, predicate) {
|
|
583
|
+
const results = [];
|
|
584
|
+
traverseAst(node, (node2) => {
|
|
585
|
+
if (predicate(node2)) {
|
|
586
|
+
results.push(node2);
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
return results;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// src/parser/manager/DefinitionManager.ts
|
|
593
|
+
var DefinitionManager = class {
|
|
594
|
+
definitions = {};
|
|
595
|
+
/**
|
|
596
|
+
* 从已完成的 blocks 中提取 definitions
|
|
597
|
+
*
|
|
598
|
+
* @param blocks 已完成的块
|
|
599
|
+
*/
|
|
600
|
+
extractFromBlocks(blocks) {
|
|
601
|
+
for (const block of blocks) {
|
|
602
|
+
const newDefinitions = this.findDefinitions(block);
|
|
603
|
+
this.definitions = {
|
|
604
|
+
...this.definitions,
|
|
605
|
+
...newDefinitions
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* 从 block 中提取 definitions
|
|
611
|
+
*
|
|
612
|
+
* @param block 解析块
|
|
613
|
+
* @returns Definition 映射表
|
|
614
|
+
*/
|
|
615
|
+
findDefinitions(block) {
|
|
616
|
+
const definitions = collectAstNodes(block.node, isDefinitionNode);
|
|
617
|
+
return definitions.reduce((acc, node) => {
|
|
618
|
+
acc[node.identifier] = node;
|
|
619
|
+
return acc;
|
|
620
|
+
}, {});
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* 获取所有 definitions
|
|
624
|
+
*
|
|
625
|
+
* @returns Definition 映射表
|
|
626
|
+
*/
|
|
627
|
+
getAll() {
|
|
628
|
+
return this.definitions;
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* 清空所有 definitions
|
|
632
|
+
*/
|
|
633
|
+
clear() {
|
|
634
|
+
this.definitions = {};
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
// src/parser/manager/FootnoteManager.ts
|
|
639
|
+
var FootnoteManager = class {
|
|
640
|
+
definitions = {};
|
|
641
|
+
referenceOrder = [];
|
|
642
|
+
/**
|
|
643
|
+
* 从已完成的 blocks 中提取 footnote definitions
|
|
644
|
+
*
|
|
645
|
+
* @param blocks 已完成的块
|
|
646
|
+
*/
|
|
647
|
+
extractDefinitionsFromBlocks(blocks) {
|
|
648
|
+
for (const block of blocks) {
|
|
649
|
+
const newDefinitions = this.findFootnoteDefinitions(block);
|
|
650
|
+
this.definitions = {
|
|
651
|
+
...this.definitions,
|
|
652
|
+
...newDefinitions
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* 从 block 中提取 footnote definitions
|
|
658
|
+
*
|
|
659
|
+
* @param block 解析块
|
|
660
|
+
* @returns Footnote Definition 映射表
|
|
661
|
+
*/
|
|
662
|
+
findFootnoteDefinitions(block) {
|
|
663
|
+
const definitions = collectAstNodes(block.node, isFootnoteDefinitionNode);
|
|
664
|
+
return definitions.reduce((acc, node) => {
|
|
665
|
+
acc[node.identifier] = node;
|
|
666
|
+
return acc;
|
|
667
|
+
}, {});
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* 收集 AST 中的脚注引用(按出现顺序)
|
|
671
|
+
*
|
|
672
|
+
* @param nodes AST 节点列表
|
|
673
|
+
*/
|
|
674
|
+
collectReferences(nodes) {
|
|
675
|
+
nodes.forEach((node) => {
|
|
676
|
+
traverseAst(node, (n) => {
|
|
677
|
+
if (n.type === "footnoteReference") {
|
|
678
|
+
const identifier = n.identifier;
|
|
679
|
+
if (!this.referenceOrder.includes(identifier)) {
|
|
680
|
+
this.referenceOrder.push(identifier);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* 获取所有 footnote definitions
|
|
688
|
+
*
|
|
689
|
+
* @returns Footnote Definition 映射表
|
|
690
|
+
*/
|
|
691
|
+
getDefinitions() {
|
|
692
|
+
return this.definitions;
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* 获取脚注引用顺序
|
|
696
|
+
*
|
|
697
|
+
* @returns 脚注引用顺序
|
|
698
|
+
*/
|
|
699
|
+
getReferenceOrder() {
|
|
700
|
+
return this.referenceOrder;
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* 清空所有 footnote definitions 和引用顺序
|
|
704
|
+
*/
|
|
705
|
+
clear() {
|
|
706
|
+
this.definitions = {};
|
|
707
|
+
this.referenceOrder = [];
|
|
708
|
+
}
|
|
709
|
+
};
|
|
19
710
|
|
|
20
711
|
// src/extensions/html-extension/index.ts
|
|
21
712
|
var DEFAULT_TAG_BLACKLIST = [
|
|
@@ -403,65 +1094,6 @@ function transformHtmlNodes(ast, options = {}) {
|
|
|
403
1094
|
children: processHtmlNodesInArray(ast.children, options)
|
|
404
1095
|
};
|
|
405
1096
|
}
|
|
406
|
-
function createHtmlTreeTransformer(options = {}) {
|
|
407
|
-
return function transformer(tree) {
|
|
408
|
-
return transformHtmlNodes(tree, options);
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
var htmlTreeExtension = {
|
|
412
|
-
enter: {},
|
|
413
|
-
exit: {}
|
|
414
|
-
};
|
|
415
|
-
function isHtmlElementNode(node) {
|
|
416
|
-
return node.type === "htmlElement";
|
|
417
|
-
}
|
|
418
|
-
function walkHtmlElements(node, callback, parent = null) {
|
|
419
|
-
if (isHtmlElementNode(node)) {
|
|
420
|
-
callback(node, parent);
|
|
421
|
-
}
|
|
422
|
-
if (hasChildren(node) || node.type === "root") {
|
|
423
|
-
const children = node.children;
|
|
424
|
-
for (const child of children) {
|
|
425
|
-
walkHtmlElements(child, callback, node);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
function findHtmlElementsByTag(root, tagName) {
|
|
430
|
-
const result = [];
|
|
431
|
-
walkHtmlElements(root, (node) => {
|
|
432
|
-
if (node.tagName === tagName.toLowerCase()) {
|
|
433
|
-
result.push(node);
|
|
434
|
-
}
|
|
435
|
-
});
|
|
436
|
-
return result;
|
|
437
|
-
}
|
|
438
|
-
function htmlElementToString(node) {
|
|
439
|
-
const { tagName, attrs, children } = node;
|
|
440
|
-
const attrsStr = Object.entries(attrs).map(([name, value]) => {
|
|
441
|
-
if (value === "") return name;
|
|
442
|
-
return `${name}="${escapeHtml(value)}"`;
|
|
443
|
-
}).join(" ");
|
|
444
|
-
const openTag = attrsStr ? `<${tagName} ${attrsStr}>` : `<${tagName}>`;
|
|
445
|
-
if (children.length === 0 && isSelfClosingTag(tagName)) {
|
|
446
|
-
return attrsStr ? `<${tagName} ${attrsStr} />` : `<${tagName} />`;
|
|
447
|
-
}
|
|
448
|
-
const childrenStr = children.map((child) => {
|
|
449
|
-
if (child.type === "text") {
|
|
450
|
-
return child.value;
|
|
451
|
-
}
|
|
452
|
-
if (isHtmlElementNode(child)) {
|
|
453
|
-
return htmlElementToString(child);
|
|
454
|
-
}
|
|
455
|
-
return "";
|
|
456
|
-
}).join("");
|
|
457
|
-
return `${openTag}${childrenStr}</${tagName}>`;
|
|
458
|
-
}
|
|
459
|
-
function isSelfClosingTag(tagName) {
|
|
460
|
-
return ["br", "hr", "img", "input", "meta", "link", "area", "base", "col", "embed", "source", "track", "wbr"].includes(tagName.toLowerCase());
|
|
461
|
-
}
|
|
462
|
-
function escapeHtml(text) {
|
|
463
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
464
|
-
}
|
|
465
1097
|
function micromarkReferenceExtension() {
|
|
466
1098
|
return {
|
|
467
1099
|
// 在 text 中使用 codes.rightSquareBracket 键覆盖 labelEnd
|
|
@@ -862,440 +1494,51 @@ function tokenizePotentialGfmFootnoteCallIncremental(effects, ok, nok) {
|
|
|
862
1494
|
}
|
|
863
1495
|
}
|
|
864
1496
|
|
|
865
|
-
// src/
|
|
866
|
-
var
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
const match = line.match(RE_FENCE_START);
|
|
881
|
-
if (match) {
|
|
882
|
-
const fence = match[2];
|
|
883
|
-
const char = fence[0];
|
|
884
|
-
return { char, length: fence.length };
|
|
885
|
-
}
|
|
886
|
-
return null;
|
|
887
|
-
}
|
|
888
|
-
function detectFenceEnd(line, context) {
|
|
889
|
-
if (!context.inFencedCode || !context.fenceChar || !context.fenceLength) {
|
|
890
|
-
return false;
|
|
891
|
-
}
|
|
892
|
-
const cacheKey = `${context.fenceChar}-${context.fenceLength}`;
|
|
893
|
-
let pattern = fenceEndPatternCache.get(cacheKey);
|
|
894
|
-
if (!pattern) {
|
|
895
|
-
pattern = new RegExp(`^\\s{0,3}${context.fenceChar}{${context.fenceLength},}\\s*$`);
|
|
896
|
-
fenceEndPatternCache.set(cacheKey, pattern);
|
|
897
|
-
}
|
|
898
|
-
return pattern.test(line);
|
|
899
|
-
}
|
|
900
|
-
function isEmptyLine(line) {
|
|
901
|
-
return RE_EMPTY_LINE.test(line);
|
|
902
|
-
}
|
|
903
|
-
function isHeading(line) {
|
|
904
|
-
return RE_HEADING.test(line);
|
|
905
|
-
}
|
|
906
|
-
function isThematicBreak(line) {
|
|
907
|
-
return RE_THEMATIC_BREAK.test(line.trim());
|
|
908
|
-
}
|
|
909
|
-
function isListItemStart(line) {
|
|
910
|
-
const hasListMarker = /^(\s*)([-*+]|\d{1,9}[.)])/.test(line);
|
|
911
|
-
if (!hasListMarker) {
|
|
912
|
-
return null;
|
|
913
|
-
}
|
|
914
|
-
const match = line.match(/^(\s*)([-*+]|\d{1,9}[.)])(.*)/);
|
|
915
|
-
if (match) {
|
|
916
|
-
const indent = match[1].length;
|
|
917
|
-
const marker = match[2];
|
|
918
|
-
const rest = match[3];
|
|
919
|
-
if (rest.trim()) {
|
|
920
|
-
const isOrdered = /^\d{1,9}[.)]/.test(marker);
|
|
921
|
-
return { ordered: isOrdered, indent };
|
|
922
|
-
}
|
|
923
|
-
if (/^\s+$/.test(rest)) {
|
|
924
|
-
const isOrdered = /^\d{1,9}[.)]/.test(marker);
|
|
925
|
-
return { ordered: isOrdered, indent };
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
return null;
|
|
929
|
-
}
|
|
930
|
-
function isBlockquoteStart(line) {
|
|
931
|
-
return RE_BLOCKQUOTE.test(line);
|
|
932
|
-
}
|
|
933
|
-
function isHtmlBlock(line) {
|
|
934
|
-
return RE_HTML_BLOCK_1.test(line) || RE_HTML_BLOCK_2.test(line);
|
|
935
|
-
}
|
|
936
|
-
function isTableDelimiter(line) {
|
|
937
|
-
return RE_TABLE_DELIMITER.test(line.trim());
|
|
938
|
-
}
|
|
939
|
-
function isFootnoteDefinitionStart(line) {
|
|
940
|
-
return RE_FOOTNOTE_DEFINITION.test(line);
|
|
941
|
-
}
|
|
942
|
-
function isFootnoteContinuation(line) {
|
|
943
|
-
return RE_FOOTNOTE_CONTINUATION.test(line);
|
|
944
|
-
}
|
|
945
|
-
function detectContainer(line, config) {
|
|
946
|
-
const marker = config?.marker || ":";
|
|
947
|
-
const minLength = config?.minMarkerLength || 3;
|
|
948
|
-
const cacheKey = `${marker}-${minLength}`;
|
|
949
|
-
let pattern = containerPatternCache.get(cacheKey);
|
|
950
|
-
if (!pattern) {
|
|
951
|
-
const escapedMarker = marker.replace(RE_ESCAPE_SPECIAL, "\\$&");
|
|
952
|
-
pattern = new RegExp(
|
|
953
|
-
`^(\\s*)(${escapedMarker}{${minLength},})(?:\\s*(\\w[\\w-]*))?(?:\\{[^}]*\\})?(?:\\s+(.*))?\\s*$`
|
|
954
|
-
);
|
|
955
|
-
containerPatternCache.set(cacheKey, pattern);
|
|
956
|
-
}
|
|
957
|
-
const match = line.match(pattern);
|
|
958
|
-
if (!match) {
|
|
959
|
-
return null;
|
|
960
|
-
}
|
|
961
|
-
const markerLength = match[2].length;
|
|
962
|
-
const name = match[3] || "";
|
|
963
|
-
const isEnd = !name && !match[4];
|
|
964
|
-
if (!isEnd && config?.allowedNames && config.allowedNames.length > 0) {
|
|
965
|
-
if (!config.allowedNames.includes(name)) {
|
|
966
|
-
return null;
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
return { name, markerLength, isEnd };
|
|
970
|
-
}
|
|
971
|
-
function detectContainerEnd(line, context, config) {
|
|
972
|
-
if (!context.inContainer || !context.containerMarkerLength) {
|
|
973
|
-
return false;
|
|
974
|
-
}
|
|
975
|
-
const result = detectContainer(line, config);
|
|
976
|
-
if (!result) {
|
|
977
|
-
return false;
|
|
978
|
-
}
|
|
979
|
-
return result.isEnd && result.markerLength >= context.containerMarkerLength;
|
|
980
|
-
}
|
|
981
|
-
function isBlockBoundary(prevLine, currentLine, context) {
|
|
982
|
-
if (context.inFencedCode) {
|
|
983
|
-
return detectFenceEnd(currentLine, context);
|
|
984
|
-
}
|
|
985
|
-
if (isEmptyLine(prevLine) && !isEmptyLine(currentLine)) {
|
|
986
|
-
return true;
|
|
987
|
-
}
|
|
988
|
-
if (isHeading(currentLine) && !isEmptyLine(prevLine)) {
|
|
989
|
-
return true;
|
|
990
|
-
}
|
|
991
|
-
if (isThematicBreak(currentLine)) {
|
|
992
|
-
return true;
|
|
993
|
-
}
|
|
994
|
-
if (detectFenceStart(currentLine)) {
|
|
995
|
-
return true;
|
|
996
|
-
}
|
|
997
|
-
return false;
|
|
998
|
-
}
|
|
999
|
-
function createInitialContext() {
|
|
1000
|
-
return {
|
|
1001
|
-
inFencedCode: false,
|
|
1002
|
-
listDepth: 0,
|
|
1003
|
-
blockquoteDepth: 0,
|
|
1004
|
-
inContainer: false,
|
|
1005
|
-
containerDepth: 0,
|
|
1006
|
-
inList: false,
|
|
1007
|
-
inFootnote: false,
|
|
1008
|
-
footnoteIdentifier: void 0
|
|
1009
|
-
};
|
|
1010
|
-
}
|
|
1011
|
-
function isListContinuation(line, listIndent) {
|
|
1012
|
-
if (isEmptyLine(line)) {
|
|
1013
|
-
return true;
|
|
1014
|
-
}
|
|
1015
|
-
const contentIndent = line.match(/^(\s*)/)?.[1].length ?? 0;
|
|
1016
|
-
return contentIndent > listIndent;
|
|
1017
|
-
}
|
|
1018
|
-
function updateContext(line, context, containerConfig) {
|
|
1019
|
-
const newContext = { ...context };
|
|
1020
|
-
const containerCfg = containerConfig === true ? {} : containerConfig === false ? void 0 : containerConfig;
|
|
1021
|
-
if (context.inFencedCode) {
|
|
1022
|
-
if (detectFenceEnd(line, context)) {
|
|
1023
|
-
newContext.inFencedCode = false;
|
|
1024
|
-
newContext.fenceChar = void 0;
|
|
1025
|
-
newContext.fenceLength = void 0;
|
|
1026
|
-
}
|
|
1027
|
-
return newContext;
|
|
1028
|
-
}
|
|
1029
|
-
const fence = detectFenceStart(line);
|
|
1030
|
-
if (fence) {
|
|
1031
|
-
newContext.inFencedCode = true;
|
|
1032
|
-
newContext.fenceChar = fence.char;
|
|
1033
|
-
newContext.fenceLength = fence.length;
|
|
1034
|
-
return newContext;
|
|
1035
|
-
}
|
|
1036
|
-
if (containerCfg !== void 0) {
|
|
1037
|
-
if (context.inContainer) {
|
|
1038
|
-
if (detectContainerEnd(line, context, containerCfg)) {
|
|
1039
|
-
newContext.containerDepth = context.containerDepth - 1;
|
|
1040
|
-
if (newContext.containerDepth === 0) {
|
|
1041
|
-
newContext.inContainer = false;
|
|
1042
|
-
newContext.containerMarkerLength = void 0;
|
|
1043
|
-
newContext.containerName = void 0;
|
|
1044
|
-
}
|
|
1045
|
-
return newContext;
|
|
1046
|
-
}
|
|
1047
|
-
const nested = detectContainer(line, containerCfg);
|
|
1048
|
-
if (nested && !nested.isEnd) {
|
|
1049
|
-
newContext.containerDepth = context.containerDepth + 1;
|
|
1050
|
-
return newContext;
|
|
1051
|
-
}
|
|
1052
|
-
return newContext;
|
|
1053
|
-
} else {
|
|
1054
|
-
const container = detectContainer(line, containerCfg);
|
|
1055
|
-
if (container && !container.isEnd) {
|
|
1056
|
-
newContext.inContainer = true;
|
|
1057
|
-
newContext.containerMarkerLength = container.markerLength;
|
|
1058
|
-
newContext.containerName = container.name;
|
|
1059
|
-
newContext.containerDepth = 1;
|
|
1060
|
-
return newContext;
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
if (context.inFootnote && isFootnoteDefinitionStart(line)) {
|
|
1065
|
-
const identifier = line.match(RE_FOOTNOTE_DEFINITION)?.[1];
|
|
1066
|
-
newContext.footnoteIdentifier = identifier;
|
|
1067
|
-
return newContext;
|
|
1068
|
-
}
|
|
1069
|
-
if (!context.inFootnote && isFootnoteDefinitionStart(line)) {
|
|
1070
|
-
const identifier = line.match(RE_FOOTNOTE_DEFINITION)?.[1];
|
|
1071
|
-
newContext.inFootnote = true;
|
|
1072
|
-
newContext.footnoteIdentifier = identifier;
|
|
1073
|
-
return newContext;
|
|
1074
|
-
}
|
|
1075
|
-
const listItem = isListItemStart(line);
|
|
1076
|
-
if (context.inList) {
|
|
1077
|
-
if (context.listMayEnd) {
|
|
1078
|
-
if (listItem) {
|
|
1079
|
-
if (listItem.ordered === context.listOrdered && listItem.indent === context.listIndent) {
|
|
1080
|
-
newContext.listMayEnd = false;
|
|
1081
|
-
return newContext;
|
|
1082
|
-
}
|
|
1083
|
-
newContext.inList = true;
|
|
1084
|
-
newContext.listOrdered = listItem.ordered;
|
|
1085
|
-
newContext.listIndent = listItem.indent;
|
|
1086
|
-
newContext.listMayEnd = false;
|
|
1087
|
-
return newContext;
|
|
1088
|
-
} else if (isListContinuation(line, context.listIndent ?? 0)) {
|
|
1089
|
-
newContext.listMayEnd = isEmptyLine(line);
|
|
1090
|
-
return newContext;
|
|
1091
|
-
} else {
|
|
1092
|
-
newContext.inList = false;
|
|
1093
|
-
newContext.listOrdered = void 0;
|
|
1094
|
-
newContext.listIndent = void 0;
|
|
1095
|
-
newContext.listMayEnd = false;
|
|
1096
|
-
return newContext;
|
|
1097
|
-
}
|
|
1098
|
-
} else {
|
|
1099
|
-
if (listItem) {
|
|
1100
|
-
return newContext;
|
|
1101
|
-
} else if (isEmptyLine(line)) {
|
|
1102
|
-
newContext.listMayEnd = true;
|
|
1103
|
-
return newContext;
|
|
1104
|
-
} else if (isListContinuation(line, context.listIndent ?? 0)) {
|
|
1105
|
-
return newContext;
|
|
1106
|
-
} else {
|
|
1107
|
-
newContext.inList = false;
|
|
1108
|
-
newContext.listOrdered = void 0;
|
|
1109
|
-
newContext.listIndent = void 0;
|
|
1110
|
-
newContext.listMayEnd = false;
|
|
1111
|
-
return newContext;
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
} else {
|
|
1115
|
-
if (listItem) {
|
|
1116
|
-
newContext.inList = true;
|
|
1117
|
-
newContext.listOrdered = listItem.ordered;
|
|
1118
|
-
newContext.listIndent = listItem.indent;
|
|
1119
|
-
newContext.listMayEnd = false;
|
|
1120
|
-
return newContext;
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
if (context.inFootnote) {
|
|
1124
|
-
if (isEmptyLine(line)) {
|
|
1125
|
-
return newContext;
|
|
1126
|
-
} else if (isListItemStart(line)) {
|
|
1127
|
-
const listItemInfo = isListItemStart(line);
|
|
1128
|
-
if (listItemInfo.indent === 0) {
|
|
1129
|
-
newContext.inFootnote = false;
|
|
1130
|
-
newContext.footnoteIdentifier = void 0;
|
|
1131
|
-
} else {
|
|
1132
|
-
return newContext;
|
|
1133
|
-
}
|
|
1134
|
-
} else if (isHeading(line)) {
|
|
1135
|
-
newContext.inFootnote = false;
|
|
1136
|
-
newContext.footnoteIdentifier = void 0;
|
|
1137
|
-
return newContext;
|
|
1138
|
-
} else if (detectFenceStart(line)) {
|
|
1139
|
-
newContext.inFootnote = false;
|
|
1140
|
-
newContext.footnoteIdentifier = void 0;
|
|
1141
|
-
return newContext;
|
|
1142
|
-
} else if (isBlockquoteStart(line)) {
|
|
1143
|
-
newContext.inFootnote = false;
|
|
1144
|
-
newContext.footnoteIdentifier = void 0;
|
|
1145
|
-
return newContext;
|
|
1146
|
-
} else if (isFootnoteContinuation(line)) {
|
|
1147
|
-
return newContext;
|
|
1148
|
-
} else if (isFootnoteDefinitionStart(line)) {
|
|
1149
|
-
const identifier = line.match(RE_FOOTNOTE_DEFINITION)?.[1];
|
|
1150
|
-
newContext.footnoteIdentifier = identifier;
|
|
1151
|
-
return newContext;
|
|
1152
|
-
} else {
|
|
1153
|
-
newContext.inFootnote = false;
|
|
1154
|
-
newContext.footnoteIdentifier = void 0;
|
|
1155
|
-
return newContext;
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
return newContext;
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
// src/utils/index.ts
|
|
1162
|
-
var idCounter = 0;
|
|
1163
|
-
function generateId(prefix = "block") {
|
|
1164
|
-
return `${prefix}-${++idCounter}`;
|
|
1165
|
-
}
|
|
1166
|
-
function resetIdCounter() {
|
|
1167
|
-
idCounter = 0;
|
|
1168
|
-
}
|
|
1169
|
-
function calculateLineOffset(lines, lineIndex) {
|
|
1170
|
-
let offset = 0;
|
|
1171
|
-
for (let i = 0; i < lineIndex && i < lines.length; i++) {
|
|
1172
|
-
offset += lines[i].length + 1;
|
|
1173
|
-
}
|
|
1174
|
-
return offset;
|
|
1175
|
-
}
|
|
1176
|
-
function splitLines(text) {
|
|
1177
|
-
return text.split("\n");
|
|
1178
|
-
}
|
|
1179
|
-
function joinLines(lines, start, end) {
|
|
1180
|
-
return lines.slice(start, end + 1).join("\n");
|
|
1181
|
-
}
|
|
1182
|
-
function isDefinitionNode(node) {
|
|
1183
|
-
return node.type === "definition";
|
|
1184
|
-
}
|
|
1185
|
-
function isFootnoteDefinitionNode(node) {
|
|
1186
|
-
return node.type === "footnoteDefinition";
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
// src/parser/IncremarkParser.ts
|
|
1190
|
-
var IncremarkParser = class {
|
|
1191
|
-
buffer = "";
|
|
1192
|
-
lines = [];
|
|
1193
|
-
/** 行偏移量前缀和:lineOffsets[i] = 第i行起始位置的偏移量 */
|
|
1194
|
-
lineOffsets = [0];
|
|
1195
|
-
completedBlocks = [];
|
|
1196
|
-
pendingStartLine = 0;
|
|
1197
|
-
blockIdCounter = 0;
|
|
1198
|
-
context;
|
|
1497
|
+
// src/parser/ast/AstBuilder.ts
|
|
1498
|
+
var INLINE_CONTAINER_TYPES = [
|
|
1499
|
+
"paragraph",
|
|
1500
|
+
"heading",
|
|
1501
|
+
"tableCell",
|
|
1502
|
+
"delete",
|
|
1503
|
+
"emphasis",
|
|
1504
|
+
"strong",
|
|
1505
|
+
"link",
|
|
1506
|
+
"linkReference"
|
|
1507
|
+
];
|
|
1508
|
+
function isInlineContainer(node) {
|
|
1509
|
+
return INLINE_CONTAINER_TYPES.includes(node.type);
|
|
1510
|
+
}
|
|
1511
|
+
var AstBuilder = class {
|
|
1199
1512
|
options;
|
|
1200
|
-
/** 缓存的容器配置,避免重复计算 */
|
|
1201
1513
|
containerConfig;
|
|
1202
|
-
/** 缓存的 HTML 树配置,避免重复计算 */
|
|
1203
1514
|
htmlTreeConfig;
|
|
1204
|
-
/** 上次 append 返回的 pending blocks,用于 getAst 复用 */
|
|
1205
|
-
lastPendingBlocks = [];
|
|
1206
|
-
/** Definition 映射表(用于引用式图片和链接) */
|
|
1207
|
-
definitionMap = {};
|
|
1208
|
-
/** Footnote Definition 映射表 */
|
|
1209
|
-
footnoteDefinitionMap = {};
|
|
1210
|
-
/** Footnote Reference 出现顺序(按引用在文档中的顺序) */
|
|
1211
|
-
footnoteReferenceOrder = [];
|
|
1212
1515
|
constructor(options = {}) {
|
|
1213
|
-
this.options =
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
};
|
|
1217
|
-
this.context = createInitialContext();
|
|
1218
|
-
this.containerConfig = this.computeContainerConfig();
|
|
1219
|
-
this.htmlTreeConfig = this.computeHtmlTreeConfig();
|
|
1516
|
+
this.options = options;
|
|
1517
|
+
this.containerConfig = this.computeContainerConfig(options);
|
|
1518
|
+
this.htmlTreeConfig = this.computeHtmlTreeConfig(options);
|
|
1220
1519
|
}
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
computeContainerConfig() {
|
|
1225
|
-
const containers =
|
|
1520
|
+
/**
|
|
1521
|
+
* 计算容器配置
|
|
1522
|
+
*/
|
|
1523
|
+
computeContainerConfig(options) {
|
|
1524
|
+
const containers = options.containers;
|
|
1226
1525
|
if (!containers) return void 0;
|
|
1227
1526
|
return containers === true ? {} : containers;
|
|
1228
1527
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1528
|
+
/**
|
|
1529
|
+
* 计算 HTML 树配置
|
|
1530
|
+
*/
|
|
1531
|
+
computeHtmlTreeConfig(options) {
|
|
1532
|
+
const htmlTree = options.htmlTree;
|
|
1231
1533
|
if (!htmlTree) return void 0;
|
|
1232
1534
|
return htmlTree === true ? {} : htmlTree;
|
|
1233
1535
|
}
|
|
1234
1536
|
/**
|
|
1235
|
-
*
|
|
1236
|
-
*
|
|
1237
|
-
*
|
|
1238
|
-
*
|
|
1537
|
+
* 解析文本为 AST
|
|
1538
|
+
*
|
|
1539
|
+
* @param text Markdown 文本
|
|
1540
|
+
* @returns AST
|
|
1239
1541
|
*/
|
|
1240
|
-
convertHtmlToText(ast) {
|
|
1241
|
-
const processInlineChildren = (children) => {
|
|
1242
|
-
return children.map((node) => {
|
|
1243
|
-
const n = node;
|
|
1244
|
-
if (n.type === "html") {
|
|
1245
|
-
const htmlNode = n;
|
|
1246
|
-
const textNode = {
|
|
1247
|
-
type: "text",
|
|
1248
|
-
value: htmlNode.value,
|
|
1249
|
-
position: htmlNode.position
|
|
1250
|
-
};
|
|
1251
|
-
return textNode;
|
|
1252
|
-
}
|
|
1253
|
-
if ("children" in n && Array.isArray(n.children)) {
|
|
1254
|
-
const parent = n;
|
|
1255
|
-
return {
|
|
1256
|
-
...parent,
|
|
1257
|
-
children: processInlineChildren(parent.children)
|
|
1258
|
-
};
|
|
1259
|
-
}
|
|
1260
|
-
return n;
|
|
1261
|
-
});
|
|
1262
|
-
};
|
|
1263
|
-
const processBlockChildren = (children) => {
|
|
1264
|
-
return children.map((node) => {
|
|
1265
|
-
if (node.type === "html") {
|
|
1266
|
-
const htmlNode = node;
|
|
1267
|
-
const textNode = {
|
|
1268
|
-
type: "text",
|
|
1269
|
-
value: htmlNode.value
|
|
1270
|
-
};
|
|
1271
|
-
const paragraphNode = {
|
|
1272
|
-
type: "paragraph",
|
|
1273
|
-
children: [textNode],
|
|
1274
|
-
position: htmlNode.position
|
|
1275
|
-
};
|
|
1276
|
-
return paragraphNode;
|
|
1277
|
-
}
|
|
1278
|
-
if ("children" in node && Array.isArray(node.children)) {
|
|
1279
|
-
const parent = node;
|
|
1280
|
-
if (node.type === "paragraph" || node.type === "heading" || node.type === "tableCell" || node.type === "delete" || node.type === "emphasis" || node.type === "strong" || node.type === "link" || node.type === "linkReference") {
|
|
1281
|
-
return {
|
|
1282
|
-
...parent,
|
|
1283
|
-
children: processInlineChildren(parent.children)
|
|
1284
|
-
};
|
|
1285
|
-
}
|
|
1286
|
-
return {
|
|
1287
|
-
...parent,
|
|
1288
|
-
children: processBlockChildren(parent.children)
|
|
1289
|
-
};
|
|
1290
|
-
}
|
|
1291
|
-
return node;
|
|
1292
|
-
});
|
|
1293
|
-
};
|
|
1294
|
-
return {
|
|
1295
|
-
...ast,
|
|
1296
|
-
children: processBlockChildren(ast.children)
|
|
1297
|
-
};
|
|
1298
|
-
}
|
|
1299
1542
|
parse(text) {
|
|
1300
1543
|
const extensions = [];
|
|
1301
1544
|
const mdastExtensions = [];
|
|
@@ -1329,67 +1572,160 @@ var IncremarkParser = class {
|
|
|
1329
1572
|
}
|
|
1330
1573
|
return ast;
|
|
1331
1574
|
}
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
}
|
|
1575
|
+
/**
|
|
1576
|
+
* 将 HTML 节点转换为纯文本(当未启用 HTML 树转换时)
|
|
1577
|
+
*
|
|
1578
|
+
* @param ast AST
|
|
1579
|
+
* @returns 转换后的 AST
|
|
1580
|
+
*/
|
|
1581
|
+
convertHtmlToText(ast) {
|
|
1582
|
+
return {
|
|
1583
|
+
...ast,
|
|
1584
|
+
children: this.processBlockChildren(ast.children)
|
|
1585
|
+
};
|
|
1343
1586
|
}
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1587
|
+
/**
|
|
1588
|
+
* 处理块级节点
|
|
1589
|
+
*/
|
|
1590
|
+
processBlockChildren(children) {
|
|
1591
|
+
return children.map((node) => {
|
|
1592
|
+
if (node.type === "html") {
|
|
1593
|
+
return this.convertBlockHtmlToParagraph(node);
|
|
1349
1594
|
}
|
|
1350
1595
|
if ("children" in node && Array.isArray(node.children)) {
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1596
|
+
const parent = node;
|
|
1597
|
+
const children2 = isInlineContainer(node) ? this.processInlineChildren(parent.children) : this.processBlockChildren(parent.children);
|
|
1598
|
+
return {
|
|
1599
|
+
...parent,
|
|
1600
|
+
children: children2
|
|
1601
|
+
};
|
|
1354
1602
|
}
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
return definitions.reduce((acc, node) => {
|
|
1358
|
-
acc[node.identifier] = node;
|
|
1359
|
-
return acc;
|
|
1360
|
-
}, {});
|
|
1603
|
+
return node;
|
|
1604
|
+
});
|
|
1361
1605
|
}
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1606
|
+
/**
|
|
1607
|
+
* 处理内联节点
|
|
1608
|
+
*/
|
|
1609
|
+
processInlineChildren(children) {
|
|
1610
|
+
return children.map((node) => {
|
|
1611
|
+
const n = node;
|
|
1612
|
+
if (n.type === "html") {
|
|
1613
|
+
return this.convertInlineHtmlToText(n);
|
|
1614
|
+
}
|
|
1615
|
+
if ("children" in n && Array.isArray(n.children)) {
|
|
1616
|
+
const parent = n;
|
|
1617
|
+
return {
|
|
1618
|
+
...parent,
|
|
1619
|
+
children: this.processInlineChildren(parent.children)
|
|
1620
|
+
};
|
|
1367
1621
|
}
|
|
1622
|
+
return n;
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* 将块级 HTML 节点转换为段落
|
|
1627
|
+
*/
|
|
1628
|
+
convertBlockHtmlToParagraph(htmlNode) {
|
|
1629
|
+
const textNode = {
|
|
1630
|
+
type: "text",
|
|
1631
|
+
value: htmlNode.value
|
|
1632
|
+
};
|
|
1633
|
+
const paragraphNode = {
|
|
1634
|
+
type: "paragraph",
|
|
1635
|
+
children: [textNode],
|
|
1636
|
+
position: htmlNode.position
|
|
1637
|
+
};
|
|
1638
|
+
return paragraphNode;
|
|
1639
|
+
}
|
|
1640
|
+
/**
|
|
1641
|
+
* 将内联 HTML 节点转换为纯文本节点
|
|
1642
|
+
*/
|
|
1643
|
+
convertInlineHtmlToText(htmlNode) {
|
|
1644
|
+
return {
|
|
1645
|
+
type: "text",
|
|
1646
|
+
value: htmlNode.value,
|
|
1647
|
+
position: htmlNode.position
|
|
1648
|
+
};
|
|
1649
|
+
}
|
|
1650
|
+
/**
|
|
1651
|
+
* 将 AST 节点转换为 ParsedBlock
|
|
1652
|
+
*
|
|
1653
|
+
* @param nodes AST 节点列表
|
|
1654
|
+
* @param startOffset 起始偏移量
|
|
1655
|
+
* @param rawText 原始文本
|
|
1656
|
+
* @param status 块状态
|
|
1657
|
+
* @param generateBlockId 生成块 ID 的函数
|
|
1658
|
+
* @returns ParsedBlock 列表
|
|
1659
|
+
*/
|
|
1660
|
+
nodesToBlocks(nodes, startOffset, rawText, status, generateBlockId) {
|
|
1661
|
+
const blocks = [];
|
|
1662
|
+
let currentOffset = startOffset;
|
|
1663
|
+
for (const node of nodes) {
|
|
1664
|
+
const nodeStart = node.position?.start?.offset ?? currentOffset;
|
|
1665
|
+
const nodeEnd = node.position?.end?.offset ?? currentOffset + 1;
|
|
1666
|
+
const nodeText = rawText.substring(nodeStart - startOffset, nodeEnd - startOffset);
|
|
1667
|
+
blocks.push({
|
|
1668
|
+
id: generateBlockId(),
|
|
1669
|
+
status,
|
|
1670
|
+
node,
|
|
1671
|
+
startOffset: nodeStart,
|
|
1672
|
+
endOffset: nodeEnd,
|
|
1673
|
+
rawText: nodeText
|
|
1674
|
+
});
|
|
1675
|
+
currentOffset = nodeEnd;
|
|
1368
1676
|
}
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1677
|
+
return blocks;
|
|
1678
|
+
}
|
|
1679
|
+
};
|
|
1680
|
+
|
|
1681
|
+
// src/parser/IncremarkParser.ts
|
|
1682
|
+
var IncremarkParser = class {
|
|
1683
|
+
buffer = "";
|
|
1684
|
+
lines = [];
|
|
1685
|
+
/** 行偏移量前缀和:lineOffsets[i] = 第i行起始位置的偏移量 */
|
|
1686
|
+
lineOffsets = [0];
|
|
1687
|
+
completedBlocks = [];
|
|
1688
|
+
pendingStartLine = 0;
|
|
1689
|
+
blockIdCounter = 0;
|
|
1690
|
+
context;
|
|
1691
|
+
options;
|
|
1692
|
+
/** 边界检测器 */
|
|
1693
|
+
boundaryDetector;
|
|
1694
|
+
/** AST 构建器 */
|
|
1695
|
+
astBuilder;
|
|
1696
|
+
/** Definition 管理器 */
|
|
1697
|
+
definitionManager;
|
|
1698
|
+
/** Footnote 管理器 */
|
|
1699
|
+
footnoteManager;
|
|
1700
|
+
/** 上次 append 返回的 pending blocks,用于 getAst 复用 */
|
|
1701
|
+
lastPendingBlocks = [];
|
|
1702
|
+
constructor(options = {}) {
|
|
1703
|
+
this.options = {
|
|
1704
|
+
gfm: true,
|
|
1705
|
+
...options
|
|
1706
|
+
};
|
|
1707
|
+
this.context = createInitialContext();
|
|
1708
|
+
this.astBuilder = new AstBuilder(this.options);
|
|
1709
|
+
this.boundaryDetector = new BoundaryDetector({ containers: this.astBuilder.containerConfig });
|
|
1710
|
+
this.definitionManager = new DefinitionManager();
|
|
1711
|
+
this.footnoteManager = new FootnoteManager();
|
|
1712
|
+
}
|
|
1713
|
+
generateBlockId() {
|
|
1714
|
+
return `block-${++this.blockIdCounter}`;
|
|
1715
|
+
}
|
|
1716
|
+
/**
|
|
1717
|
+
* 更新已完成的 blocks 中的 definitions 和 footnote definitions
|
|
1718
|
+
*/
|
|
1719
|
+
updateDefinitionsFromCompletedBlocks(blocks) {
|
|
1720
|
+
this.definitionManager.extractFromBlocks(blocks);
|
|
1721
|
+
this.footnoteManager.extractDefinitionsFromBlocks(blocks);
|
|
1374
1722
|
}
|
|
1375
1723
|
/**
|
|
1376
1724
|
* 收集 AST 中的脚注引用(按出现顺序)
|
|
1377
1725
|
* 用于确定脚注的显示顺序
|
|
1378
1726
|
*/
|
|
1379
1727
|
collectFootnoteReferences(nodes) {
|
|
1380
|
-
|
|
1381
|
-
if (!node) return;
|
|
1382
|
-
if (node.type === "footnoteReference") {
|
|
1383
|
-
const identifier = node.identifier;
|
|
1384
|
-
if (!this.footnoteReferenceOrder.includes(identifier)) {
|
|
1385
|
-
this.footnoteReferenceOrder.push(identifier);
|
|
1386
|
-
}
|
|
1387
|
-
}
|
|
1388
|
-
if (node.children && Array.isArray(node.children)) {
|
|
1389
|
-
node.children.forEach(visitNode);
|
|
1390
|
-
}
|
|
1391
|
-
};
|
|
1392
|
-
nodes.forEach(visitNode);
|
|
1728
|
+
this.footnoteManager.collectReferences(nodes);
|
|
1393
1729
|
}
|
|
1394
1730
|
/**
|
|
1395
1731
|
* 增量更新 lines 和 lineOffsets
|
|
@@ -1427,169 +1763,12 @@ var IncremarkParser = class {
|
|
|
1427
1763
|
* 返回稳定边界行号和该行对应的上下文(用于后续更新,避免重复计算)
|
|
1428
1764
|
*/
|
|
1429
1765
|
findStableBoundary() {
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
const wasInContainer = tempContext.inContainer;
|
|
1437
|
-
const wasContainerDepth = tempContext.containerDepth;
|
|
1438
|
-
tempContext = updateContext(line, tempContext, this.containerConfig);
|
|
1439
|
-
if (wasInFencedCode && !tempContext.inFencedCode) {
|
|
1440
|
-
if (i < this.lines.length - 1) {
|
|
1441
|
-
stableLine = i;
|
|
1442
|
-
stableContext = { ...tempContext };
|
|
1443
|
-
}
|
|
1444
|
-
continue;
|
|
1445
|
-
}
|
|
1446
|
-
if (tempContext.inFencedCode) {
|
|
1447
|
-
continue;
|
|
1448
|
-
}
|
|
1449
|
-
if (wasInContainer && wasContainerDepth === 1 && !tempContext.inContainer) {
|
|
1450
|
-
if (i < this.lines.length - 1) {
|
|
1451
|
-
stableLine = i;
|
|
1452
|
-
stableContext = { ...tempContext };
|
|
1453
|
-
}
|
|
1454
|
-
continue;
|
|
1455
|
-
}
|
|
1456
|
-
if (tempContext.inContainer) {
|
|
1457
|
-
continue;
|
|
1458
|
-
}
|
|
1459
|
-
const stablePoint = this.checkStability(i, tempContext);
|
|
1460
|
-
if (stablePoint >= 0) {
|
|
1461
|
-
stableLine = stablePoint;
|
|
1462
|
-
stableContext = { ...tempContext };
|
|
1463
|
-
}
|
|
1464
|
-
}
|
|
1465
|
-
return { line: stableLine, contextAtLine: stableContext };
|
|
1466
|
-
}
|
|
1467
|
-
checkStability(lineIndex, context) {
|
|
1468
|
-
if (lineIndex === 0) {
|
|
1469
|
-
return -1;
|
|
1470
|
-
}
|
|
1471
|
-
const line = this.lines[lineIndex];
|
|
1472
|
-
const prevLine = this.lines[lineIndex - 1];
|
|
1473
|
-
if (context.inContainer) {
|
|
1474
|
-
if (this.containerConfig !== void 0) {
|
|
1475
|
-
const containerEnd = detectContainerEnd(line, context, this.containerConfig);
|
|
1476
|
-
if (containerEnd) {
|
|
1477
|
-
return lineIndex - 1;
|
|
1478
|
-
}
|
|
1479
|
-
}
|
|
1480
|
-
return -1;
|
|
1481
|
-
}
|
|
1482
|
-
if (context.inList) {
|
|
1483
|
-
if (!context.listMayEnd) {
|
|
1484
|
-
return -1;
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
if (isHeading(prevLine) || isThematicBreak(prevLine)) {
|
|
1488
|
-
return lineIndex - 1;
|
|
1489
|
-
}
|
|
1490
|
-
if (lineIndex >= this.lines.length - 1) {
|
|
1491
|
-
return -1;
|
|
1492
|
-
}
|
|
1493
|
-
if (isFootnoteDefinitionStart(prevLine)) {
|
|
1494
|
-
if (isEmptyLine(line) || isFootnoteContinuation(line)) {
|
|
1495
|
-
return -1;
|
|
1496
|
-
}
|
|
1497
|
-
if (isFootnoteDefinitionStart(line)) {
|
|
1498
|
-
return lineIndex - 1;
|
|
1499
|
-
}
|
|
1500
|
-
}
|
|
1501
|
-
if (!isEmptyLine(prevLine) && isFootnoteContinuation(prevLine)) {
|
|
1502
|
-
const footnoteStartLine = this.findFootnoteStart(lineIndex - 1);
|
|
1503
|
-
if (footnoteStartLine >= 0) {
|
|
1504
|
-
if (isEmptyLine(line) || isFootnoteContinuation(line)) {
|
|
1505
|
-
return -1;
|
|
1506
|
-
}
|
|
1507
|
-
if (isFootnoteDefinitionStart(line)) {
|
|
1508
|
-
return lineIndex - 1;
|
|
1509
|
-
}
|
|
1510
|
-
return lineIndex - 1;
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1513
|
-
if (!isEmptyLine(prevLine)) {
|
|
1514
|
-
if (isFootnoteDefinitionStart(line) && !isFootnoteDefinitionStart(prevLine)) {
|
|
1515
|
-
return lineIndex - 1;
|
|
1516
|
-
}
|
|
1517
|
-
if (isHeading(line)) {
|
|
1518
|
-
return lineIndex - 1;
|
|
1519
|
-
}
|
|
1520
|
-
if (detectFenceStart(line)) {
|
|
1521
|
-
return lineIndex - 1;
|
|
1522
|
-
}
|
|
1523
|
-
if (isBlockquoteStart(line) && !isBlockquoteStart(prevLine)) {
|
|
1524
|
-
return lineIndex - 1;
|
|
1525
|
-
}
|
|
1526
|
-
if (!context.inList && isListItemStart(line) && !isListItemStart(prevLine)) {
|
|
1527
|
-
return lineIndex - 1;
|
|
1528
|
-
}
|
|
1529
|
-
if (this.containerConfig !== void 0) {
|
|
1530
|
-
const container = detectContainer(line, this.containerConfig);
|
|
1531
|
-
if (container && !container.isEnd) {
|
|
1532
|
-
const prevContainer = detectContainer(prevLine, this.containerConfig);
|
|
1533
|
-
if (!prevContainer || prevContainer.isEnd) {
|
|
1534
|
-
return lineIndex - 1;
|
|
1535
|
-
}
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
if (isEmptyLine(line) && !isEmptyLine(prevLine) && !context.inList) {
|
|
1540
|
-
return lineIndex;
|
|
1541
|
-
}
|
|
1542
|
-
return -1;
|
|
1543
|
-
}
|
|
1544
|
-
/**
|
|
1545
|
-
* 从指定行向上查找脚注定义的起始行
|
|
1546
|
-
*
|
|
1547
|
-
* @param fromLine 开始查找的行索引
|
|
1548
|
-
* @returns 脚注起始行索引,如果不属于脚注返回 -1
|
|
1549
|
-
*
|
|
1550
|
-
* @example
|
|
1551
|
-
* // 假设 lines 为:
|
|
1552
|
-
* // 0: "[^1]: 第一行"
|
|
1553
|
-
* // 1: " 第二行"
|
|
1554
|
-
* // 2: " 第三行"
|
|
1555
|
-
* findFootnoteStart(2) // 返回 0
|
|
1556
|
-
* findFootnoteStart(1) // 返回 0
|
|
1557
|
-
*/
|
|
1558
|
-
findFootnoteStart(fromLine) {
|
|
1559
|
-
const maxLookback = 20;
|
|
1560
|
-
const startLine = Math.max(0, fromLine - maxLookback);
|
|
1561
|
-
for (let i = fromLine; i >= startLine; i--) {
|
|
1562
|
-
const line = this.lines[i];
|
|
1563
|
-
if (isFootnoteDefinitionStart(line)) {
|
|
1564
|
-
return i;
|
|
1565
|
-
}
|
|
1566
|
-
if (isEmptyLine(line)) {
|
|
1567
|
-
continue;
|
|
1568
|
-
}
|
|
1569
|
-
if (!isFootnoteContinuation(line)) {
|
|
1570
|
-
return -1;
|
|
1571
|
-
}
|
|
1572
|
-
}
|
|
1573
|
-
return -1;
|
|
1574
|
-
}
|
|
1575
|
-
nodesToBlocks(nodes, startOffset, rawText, status) {
|
|
1576
|
-
const blocks = [];
|
|
1577
|
-
let currentOffset = startOffset;
|
|
1578
|
-
for (const node of nodes) {
|
|
1579
|
-
const nodeStart = node.position?.start?.offset ?? currentOffset;
|
|
1580
|
-
const nodeEnd = node.position?.end?.offset ?? currentOffset + 1;
|
|
1581
|
-
const nodeText = rawText.substring(nodeStart - startOffset, nodeEnd - startOffset);
|
|
1582
|
-
blocks.push({
|
|
1583
|
-
id: this.generateBlockId(),
|
|
1584
|
-
status,
|
|
1585
|
-
node,
|
|
1586
|
-
startOffset: nodeStart,
|
|
1587
|
-
endOffset: nodeEnd,
|
|
1588
|
-
rawText: nodeText
|
|
1589
|
-
});
|
|
1590
|
-
currentOffset = nodeEnd;
|
|
1591
|
-
}
|
|
1592
|
-
return blocks;
|
|
1766
|
+
const result = this.boundaryDetector.findStableBoundary(
|
|
1767
|
+
this.lines,
|
|
1768
|
+
this.pendingStartLine,
|
|
1769
|
+
this.context
|
|
1770
|
+
);
|
|
1771
|
+
return { line: result.line, contextAtLine: result.context };
|
|
1593
1772
|
}
|
|
1594
1773
|
/**
|
|
1595
1774
|
* 追加新的 chunk 并返回增量更新
|
|
@@ -1610,8 +1789,8 @@ var IncremarkParser = class {
|
|
|
1610
1789
|
if (stableBoundary >= this.pendingStartLine && stableBoundary >= 0) {
|
|
1611
1790
|
const stableText = this.lines.slice(this.pendingStartLine, stableBoundary + 1).join("\n");
|
|
1612
1791
|
const stableOffset = this.getLineOffset(this.pendingStartLine);
|
|
1613
|
-
const ast = this.parse(stableText);
|
|
1614
|
-
const newBlocks = this.nodesToBlocks(ast.children, stableOffset, stableText, "completed");
|
|
1792
|
+
const ast = this.astBuilder.parse(stableText);
|
|
1793
|
+
const newBlocks = this.astBuilder.nodesToBlocks(ast.children, stableOffset, stableText, "completed", () => this.generateBlockId());
|
|
1615
1794
|
this.completedBlocks.push(...newBlocks);
|
|
1616
1795
|
update.completed = newBlocks;
|
|
1617
1796
|
this.updateDefinitionsFromCompletedBlocks(newBlocks);
|
|
@@ -1622,8 +1801,8 @@ var IncremarkParser = class {
|
|
|
1622
1801
|
const pendingText = this.lines.slice(this.pendingStartLine).join("\n");
|
|
1623
1802
|
if (pendingText.trim()) {
|
|
1624
1803
|
const pendingOffset = this.getLineOffset(this.pendingStartLine);
|
|
1625
|
-
const ast = this.parse(pendingText);
|
|
1626
|
-
update.pending = this.nodesToBlocks(ast.children, pendingOffset, pendingText, "pending");
|
|
1804
|
+
const ast = this.astBuilder.parse(pendingText);
|
|
1805
|
+
update.pending = this.astBuilder.nodesToBlocks(ast.children, pendingOffset, pendingText, "pending", () => this.generateBlockId());
|
|
1627
1806
|
}
|
|
1628
1807
|
}
|
|
1629
1808
|
this.lastPendingBlocks = update.pending;
|
|
@@ -1654,8 +1833,8 @@ var IncremarkParser = class {
|
|
|
1654
1833
|
...pendingBlocks.map((b) => b.node)
|
|
1655
1834
|
]
|
|
1656
1835
|
},
|
|
1657
|
-
definitions: { ...this.
|
|
1658
|
-
footnoteDefinitions: { ...this.
|
|
1836
|
+
definitions: { ...this.getDefinitionMap() },
|
|
1837
|
+
footnoteDefinitions: { ...this.getFootnoteDefinitionMap() }
|
|
1659
1838
|
};
|
|
1660
1839
|
this.options.onChange(state);
|
|
1661
1840
|
}
|
|
@@ -1670,20 +1849,21 @@ var IncremarkParser = class {
|
|
|
1670
1849
|
updated: [],
|
|
1671
1850
|
pending: [],
|
|
1672
1851
|
ast: { type: "root", children: [] },
|
|
1673
|
-
definitions:
|
|
1674
|
-
footnoteDefinitions:
|
|
1675
|
-
footnoteReferenceOrder:
|
|
1852
|
+
definitions: this.getDefinitionMap(),
|
|
1853
|
+
footnoteDefinitions: this.getFootnoteDefinitionMap(),
|
|
1854
|
+
footnoteReferenceOrder: this.getFootnoteReferenceOrder()
|
|
1676
1855
|
};
|
|
1677
1856
|
if (this.pendingStartLine < this.lines.length) {
|
|
1678
1857
|
const remainingText = this.lines.slice(this.pendingStartLine).join("\n");
|
|
1679
1858
|
if (remainingText.trim()) {
|
|
1680
1859
|
const remainingOffset = this.getLineOffset(this.pendingStartLine);
|
|
1681
|
-
const ast = this.parse(remainingText);
|
|
1682
|
-
const finalBlocks = this.nodesToBlocks(
|
|
1860
|
+
const ast = this.astBuilder.parse(remainingText);
|
|
1861
|
+
const finalBlocks = this.astBuilder.nodesToBlocks(
|
|
1683
1862
|
ast.children,
|
|
1684
1863
|
remainingOffset,
|
|
1685
1864
|
remainingText,
|
|
1686
|
-
"completed"
|
|
1865
|
+
"completed",
|
|
1866
|
+
() => this.generateBlockId()
|
|
1687
1867
|
);
|
|
1688
1868
|
this.completedBlocks.push(...finalBlocks);
|
|
1689
1869
|
update.completed = finalBlocks;
|
|
@@ -1741,19 +1921,19 @@ var IncremarkParser = class {
|
|
|
1741
1921
|
* 获取 Definition 映射表(用于引用式图片和链接)
|
|
1742
1922
|
*/
|
|
1743
1923
|
getDefinitionMap() {
|
|
1744
|
-
return
|
|
1924
|
+
return this.definitionManager.getAll();
|
|
1745
1925
|
}
|
|
1746
1926
|
/**
|
|
1747
1927
|
* 获取 Footnote Definition 映射表
|
|
1748
1928
|
*/
|
|
1749
1929
|
getFootnoteDefinitionMap() {
|
|
1750
|
-
return
|
|
1930
|
+
return this.footnoteManager.getDefinitions();
|
|
1751
1931
|
}
|
|
1752
1932
|
/**
|
|
1753
1933
|
* 获取脚注引用的出现顺序
|
|
1754
1934
|
*/
|
|
1755
1935
|
getFootnoteReferenceOrder() {
|
|
1756
|
-
return
|
|
1936
|
+
return this.footnoteManager.getReferenceOrder();
|
|
1757
1937
|
}
|
|
1758
1938
|
/**
|
|
1759
1939
|
* 设置状态变化回调(用于 DevTools 等)
|
|
@@ -1777,9 +1957,8 @@ var IncremarkParser = class {
|
|
|
1777
1957
|
this.blockIdCounter = 0;
|
|
1778
1958
|
this.context = createInitialContext();
|
|
1779
1959
|
this.lastPendingBlocks = [];
|
|
1780
|
-
this.
|
|
1781
|
-
this.
|
|
1782
|
-
this.footnoteReferenceOrder = [];
|
|
1960
|
+
this.definitionManager.clear();
|
|
1961
|
+
this.footnoteManager.clear();
|
|
1783
1962
|
this.emitChange([]);
|
|
1784
1963
|
}
|
|
1785
1964
|
/**
|
|
@@ -2599,6 +2778,6 @@ function createPlugin(name, matcher, options = {}) {
|
|
|
2599
2778
|
* @license MIT
|
|
2600
2779
|
*/
|
|
2601
2780
|
|
|
2602
|
-
export { BlockTransformer,
|
|
2781
|
+
export { BlockTransformer, IncremarkParser, allPlugins, cloneNode, codeBlockPlugin, countChars, createBlockTransformer, createIncremarkParser, createPlugin, defaultPlugins, imagePlugin, mathPlugin, mermaidPlugin, sliceAst, thematicBreakPlugin };
|
|
2603
2782
|
//# sourceMappingURL=index.js.map
|
|
2604
2783
|
//# sourceMappingURL=index.js.map
|