@incremark/core 0.2.6 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +132 -23
- package/dist/MarkedAstBuildter-BsjxZko_.d.ts +72 -0
- 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/engines/marked/index.d.ts +29 -0
- package/dist/engines/marked/index.js +1541 -0
- package/dist/engines/marked/index.js.map +1 -0
- package/dist/engines/micromark/index.d.ts +106 -0
- package/dist/engines/micromark/index.js +1161 -0
- package/dist/engines/micromark/index.js.map +1 -0
- package/dist/index-mZ7yCqNH.d.ts +225 -0
- package/dist/index.d.ts +68 -54
- package/dist/index.js +1908 -1198
- package/dist/index.js.map +1 -1
- package/dist/types-C_EW5vfp.d.ts +123 -0
- 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 +18 -3
- package/dist/index-BMUkM7mT.d.ts +0 -422
package/dist/index.js
CHANGED
|
@@ -1,21 +1,758 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { gfmFromMarkdown } from 'mdast-util-gfm';
|
|
3
|
-
import { gfm } from 'micromark-extension-gfm';
|
|
4
|
-
import { gfmFootnoteFromMarkdown } from 'mdast-util-gfm-footnote';
|
|
5
|
-
import { math } from 'micromark-extension-math';
|
|
6
|
-
import { mathFromMarkdown } from 'mdast-util-math';
|
|
7
|
-
import { codes, constants, types } from 'micromark-util-symbol';
|
|
8
|
-
import { markdownLineEndingOrSpace } from 'micromark-util-character';
|
|
9
|
-
import { factoryDestination } from 'micromark-factory-destination';
|
|
10
|
-
import { factoryTitle } from 'micromark-factory-title';
|
|
11
|
-
import { factoryLabel } from 'micromark-factory-label';
|
|
12
|
-
import { factoryWhitespace } from 'micromark-factory-whitespace';
|
|
13
|
-
import { gfmFootnote } from 'micromark-extension-gfm-footnote';
|
|
14
|
-
import { normalizeIdentifier } from 'micromark-util-normalize-identifier';
|
|
15
|
-
import { directive } from 'micromark-extension-directive';
|
|
16
|
-
import { directiveFromMarkdown } from 'mdast-util-directive';
|
|
1
|
+
import { Lexer, lexer } from 'marked';
|
|
17
2
|
|
|
18
|
-
// src/
|
|
3
|
+
// src/detector/index.ts
|
|
4
|
+
var RE_FENCE_START = /^(\s*)((`{3,})|(~{3,}))/;
|
|
5
|
+
var RE_EMPTY_LINE = /^\s*$/;
|
|
6
|
+
var RE_HEADING = /^#{1,6}\s/;
|
|
7
|
+
var RE_THEMATIC_BREAK = /^(\*{3,}|-{3,}|_{3,})\s*$/;
|
|
8
|
+
var RE_BLOCKQUOTE = /^\s{0,3}>/;
|
|
9
|
+
var RE_ESCAPE_SPECIAL = /[.*+?^${}()|[\]\\]/g;
|
|
10
|
+
var RE_FOOTNOTE_DEFINITION = /^\[\^([^\]]+)\]:\s/;
|
|
11
|
+
var RE_FOOTNOTE_CONTINUATION = /^(?: |\t)/;
|
|
12
|
+
var fenceEndPatternCache = /* @__PURE__ */ new Map();
|
|
13
|
+
var containerPatternCache = /* @__PURE__ */ new Map();
|
|
14
|
+
function detectFenceStart(line) {
|
|
15
|
+
const match = line.match(RE_FENCE_START);
|
|
16
|
+
if (match) {
|
|
17
|
+
const fence = match[2];
|
|
18
|
+
const char = fence[0];
|
|
19
|
+
return { char, length: fence.length };
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
function detectFenceEnd(line, context) {
|
|
24
|
+
if (!context.inFencedCode || !context.fenceChar || !context.fenceLength) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
const cacheKey = `${context.fenceChar}-${context.fenceLength}`;
|
|
28
|
+
let pattern = fenceEndPatternCache.get(cacheKey);
|
|
29
|
+
if (!pattern) {
|
|
30
|
+
pattern = new RegExp(`^\\s{0,3}${context.fenceChar}{${context.fenceLength},}\\s*$`);
|
|
31
|
+
fenceEndPatternCache.set(cacheKey, pattern);
|
|
32
|
+
}
|
|
33
|
+
return pattern.test(line);
|
|
34
|
+
}
|
|
35
|
+
function isEmptyLine(line) {
|
|
36
|
+
return RE_EMPTY_LINE.test(line);
|
|
37
|
+
}
|
|
38
|
+
function isSetextHeadingUnderline(line, prevLine) {
|
|
39
|
+
const trimmed = line.trim();
|
|
40
|
+
if (!/^={3,}$|^-{3,}$/.test(trimmed)) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
if (!prevLine) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
const trimmedPrev = prevLine.trim();
|
|
47
|
+
if (trimmedPrev === "") {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
if (/^#{1,6}\s/.test(trimmedPrev) || /^(\*{3,}|-{3,}|_{3,})\s*$/.test(trimmedPrev)) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
if (/^(\s*)([-*+])\s/.test(trimmedPrev) || /^(\s*)(\d{1,9})[.)]\s/.test(trimmedPrev)) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
if (/^\s{0,3}>/.test(trimmedPrev)) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
if (/^(\s*)(`{3,}|~{3,})/.test(trimmedPrev)) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
const underlineIndent = line.match(/^(\s*)/)?.[1].length ?? 0;
|
|
63
|
+
if (underlineIndent > 3) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
const contentIndent = prevLine.match(/^(\s*)/)?.[1].length ?? 0;
|
|
67
|
+
if (contentIndent > 3) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
function isHeading(line) {
|
|
73
|
+
return RE_HEADING.test(line);
|
|
74
|
+
}
|
|
75
|
+
function isThematicBreak(line) {
|
|
76
|
+
return RE_THEMATIC_BREAK.test(line.trim());
|
|
77
|
+
}
|
|
78
|
+
function isListItemStart(line) {
|
|
79
|
+
const hasListMarker = /^(\s*)([-*+]|\d{1,9}[.)])/.test(line);
|
|
80
|
+
if (!hasListMarker) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const match = line.match(/^(\s*)([-*+]|\d{1,9}[.)])(.*)/);
|
|
84
|
+
if (match) {
|
|
85
|
+
const indent = match[1].length;
|
|
86
|
+
const marker = match[2];
|
|
87
|
+
const rest = match[3];
|
|
88
|
+
if (rest.trim()) {
|
|
89
|
+
const isOrdered = /^\d{1,9}[.)]/.test(marker);
|
|
90
|
+
return { ordered: isOrdered, indent };
|
|
91
|
+
}
|
|
92
|
+
if (/^\s+$/.test(rest)) {
|
|
93
|
+
const isOrdered = /^\d{1,9}[.)]/.test(marker);
|
|
94
|
+
return { ordered: isOrdered, indent };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
function isBlockquoteStart(line) {
|
|
100
|
+
return RE_BLOCKQUOTE.test(line);
|
|
101
|
+
}
|
|
102
|
+
function isFootnoteDefinitionStart(line) {
|
|
103
|
+
return RE_FOOTNOTE_DEFINITION.test(line);
|
|
104
|
+
}
|
|
105
|
+
function isFootnoteContinuation(line) {
|
|
106
|
+
return RE_FOOTNOTE_CONTINUATION.test(line);
|
|
107
|
+
}
|
|
108
|
+
function detectContainer(line, config) {
|
|
109
|
+
const marker = config?.marker || ":";
|
|
110
|
+
const minLength = config?.minMarkerLength || 3;
|
|
111
|
+
const cacheKey = `${marker}-${minLength}`;
|
|
112
|
+
let pattern = containerPatternCache.get(cacheKey);
|
|
113
|
+
if (!pattern) {
|
|
114
|
+
const escapedMarker = marker.replace(RE_ESCAPE_SPECIAL, "\\$&");
|
|
115
|
+
pattern = new RegExp(
|
|
116
|
+
`^(\\s*)(${escapedMarker}{${minLength},})(?:\\s*(\\w[\\w-]*))?(?:\\{[^}]*\\})?(?:\\s+(.*))?\\s*$`
|
|
117
|
+
);
|
|
118
|
+
containerPatternCache.set(cacheKey, pattern);
|
|
119
|
+
}
|
|
120
|
+
const match = line.match(pattern);
|
|
121
|
+
if (!match) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
const markerLength = match[2].length;
|
|
125
|
+
const name = match[3] || "";
|
|
126
|
+
const isEnd = !name && !match[4];
|
|
127
|
+
if (!isEnd && config?.allowedNames && config.allowedNames.length > 0) {
|
|
128
|
+
if (!config.allowedNames.includes(name)) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return { name, markerLength, isEnd };
|
|
133
|
+
}
|
|
134
|
+
function detectContainerEnd(line, context, config) {
|
|
135
|
+
if (!context.inContainer || !context.containerMarkerLength) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
const result = detectContainer(line, config);
|
|
139
|
+
if (!result) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
return result.isEnd && result.markerLength >= context.containerMarkerLength;
|
|
143
|
+
}
|
|
144
|
+
var CodeContextUpdater = class {
|
|
145
|
+
update(line, context) {
|
|
146
|
+
const newContext = { ...context };
|
|
147
|
+
if (context.inFencedCode) {
|
|
148
|
+
if (detectFenceEnd(line, context)) {
|
|
149
|
+
newContext.inFencedCode = false;
|
|
150
|
+
newContext.fenceChar = void 0;
|
|
151
|
+
newContext.fenceLength = void 0;
|
|
152
|
+
return newContext;
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const fence = detectFenceStart(line);
|
|
157
|
+
if (fence) {
|
|
158
|
+
newContext.inFencedCode = true;
|
|
159
|
+
newContext.fenceChar = fence.char;
|
|
160
|
+
newContext.fenceLength = fence.length;
|
|
161
|
+
return newContext;
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
var ContainerContextUpdater = class {
|
|
167
|
+
update(line, context, config) {
|
|
168
|
+
if (config === void 0) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
const newContext = { ...context };
|
|
172
|
+
if (context.inContainer) {
|
|
173
|
+
if (detectContainerEnd(line, context, config)) {
|
|
174
|
+
newContext.containerDepth = context.containerDepth - 1;
|
|
175
|
+
if (newContext.containerDepth === 0) {
|
|
176
|
+
newContext.inContainer = false;
|
|
177
|
+
newContext.containerMarkerLength = void 0;
|
|
178
|
+
newContext.containerName = void 0;
|
|
179
|
+
}
|
|
180
|
+
return newContext;
|
|
181
|
+
}
|
|
182
|
+
const nested = detectContainer(line, config);
|
|
183
|
+
if (nested && !nested.isEnd) {
|
|
184
|
+
newContext.containerDepth = context.containerDepth + 1;
|
|
185
|
+
return newContext;
|
|
186
|
+
}
|
|
187
|
+
return newContext;
|
|
188
|
+
} else {
|
|
189
|
+
const container = detectContainer(line, config);
|
|
190
|
+
if (container && !container.isEnd) {
|
|
191
|
+
newContext.inContainer = true;
|
|
192
|
+
newContext.containerMarkerLength = container.markerLength;
|
|
193
|
+
newContext.containerName = container.name;
|
|
194
|
+
newContext.containerDepth = 1;
|
|
195
|
+
return newContext;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
var FootnoteContextUpdater = class {
|
|
202
|
+
update(line, context) {
|
|
203
|
+
const newContext = { ...context };
|
|
204
|
+
if (!context.inFootnote && isFootnoteDefinitionStart(line)) {
|
|
205
|
+
const identifier = line.match(RE_FOOTNOTE_DEFINITION)?.[1];
|
|
206
|
+
newContext.inFootnote = true;
|
|
207
|
+
newContext.footnoteIdentifier = identifier;
|
|
208
|
+
return newContext;
|
|
209
|
+
}
|
|
210
|
+
if (context.inFootnote) {
|
|
211
|
+
if (isFootnoteDefinitionStart(line)) {
|
|
212
|
+
const identifier = line.match(RE_FOOTNOTE_DEFINITION)?.[1];
|
|
213
|
+
newContext.footnoteIdentifier = identifier;
|
|
214
|
+
return newContext;
|
|
215
|
+
}
|
|
216
|
+
if (isEmptyLine(line)) {
|
|
217
|
+
return { ...context };
|
|
218
|
+
}
|
|
219
|
+
const listItem = isListItemStart(line);
|
|
220
|
+
if (listItem) {
|
|
221
|
+
if (listItem.indent === 0) {
|
|
222
|
+
newContext.inFootnote = false;
|
|
223
|
+
newContext.footnoteIdentifier = void 0;
|
|
224
|
+
} else {
|
|
225
|
+
return { ...context };
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
if (isHeading(line) || detectFenceStart(line) || isBlockquoteStart(line)) {
|
|
230
|
+
newContext.inFootnote = false;
|
|
231
|
+
newContext.footnoteIdentifier = void 0;
|
|
232
|
+
return newContext;
|
|
233
|
+
}
|
|
234
|
+
if (isFootnoteContinuation(line)) {
|
|
235
|
+
return { ...context };
|
|
236
|
+
}
|
|
237
|
+
newContext.inFootnote = false;
|
|
238
|
+
newContext.footnoteIdentifier = void 0;
|
|
239
|
+
return newContext;
|
|
240
|
+
}
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
var ListContextUpdater = class {
|
|
245
|
+
/**
|
|
246
|
+
* 检测是否是列表项的延续内容(缩进内容或空行)
|
|
247
|
+
*/
|
|
248
|
+
isListContinuation(line, listIndent) {
|
|
249
|
+
if (isEmptyLine(line)) {
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
const contentIndent = line.match(/^(\s*)/)?.[1].length ?? 0;
|
|
253
|
+
return contentIndent > listIndent;
|
|
254
|
+
}
|
|
255
|
+
update(line, context) {
|
|
256
|
+
const newContext = { ...context };
|
|
257
|
+
const listItem = isListItemStart(line);
|
|
258
|
+
if (context.inList) {
|
|
259
|
+
if (context.listMayEnd) {
|
|
260
|
+
if (listItem) {
|
|
261
|
+
if (listItem.ordered === context.listOrdered && listItem.indent === context.listIndent) {
|
|
262
|
+
newContext.listMayEnd = false;
|
|
263
|
+
return newContext;
|
|
264
|
+
}
|
|
265
|
+
newContext.listOrdered = listItem.ordered;
|
|
266
|
+
newContext.listIndent = listItem.indent;
|
|
267
|
+
newContext.listMayEnd = false;
|
|
268
|
+
return newContext;
|
|
269
|
+
} else if (this.isListContinuation(line, context.listIndent ?? 0)) {
|
|
270
|
+
newContext.listMayEnd = isEmptyLine(line);
|
|
271
|
+
return newContext;
|
|
272
|
+
} else {
|
|
273
|
+
newContext.inList = false;
|
|
274
|
+
newContext.listOrdered = void 0;
|
|
275
|
+
newContext.listIndent = void 0;
|
|
276
|
+
newContext.listMayEnd = false;
|
|
277
|
+
return newContext;
|
|
278
|
+
}
|
|
279
|
+
} else {
|
|
280
|
+
if (listItem) {
|
|
281
|
+
return null;
|
|
282
|
+
} else if (isEmptyLine(line)) {
|
|
283
|
+
newContext.listMayEnd = true;
|
|
284
|
+
return newContext;
|
|
285
|
+
} else if (this.isListContinuation(line, context.listIndent ?? 0)) {
|
|
286
|
+
return null;
|
|
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
|
+
}
|
|
295
|
+
} else {
|
|
296
|
+
if (listItem) {
|
|
297
|
+
newContext.inList = true;
|
|
298
|
+
newContext.listOrdered = listItem.ordered;
|
|
299
|
+
newContext.listIndent = listItem.indent;
|
|
300
|
+
newContext.listMayEnd = false;
|
|
301
|
+
return newContext;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
function createInitialContext() {
|
|
308
|
+
return {
|
|
309
|
+
inFencedCode: false,
|
|
310
|
+
listDepth: 0,
|
|
311
|
+
blockquoteDepth: 0,
|
|
312
|
+
inContainer: false,
|
|
313
|
+
containerDepth: 0,
|
|
314
|
+
inList: false,
|
|
315
|
+
inFootnote: false,
|
|
316
|
+
footnoteIdentifier: void 0
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
var ContextManager = class {
|
|
320
|
+
updaters = [
|
|
321
|
+
new CodeContextUpdater(),
|
|
322
|
+
new ContainerContextUpdater(),
|
|
323
|
+
new FootnoteContextUpdater(),
|
|
324
|
+
new ListContextUpdater()
|
|
325
|
+
];
|
|
326
|
+
/**
|
|
327
|
+
* 更新上下文(处理一行后)
|
|
328
|
+
*
|
|
329
|
+
* @param line 当前行
|
|
330
|
+
* @param context 当前上下文
|
|
331
|
+
* @param containerConfig 容器配置
|
|
332
|
+
* @returns 更新后的上下文
|
|
333
|
+
*/
|
|
334
|
+
update(line, context, containerConfig) {
|
|
335
|
+
const config = containerConfig === true ? {} : containerConfig === false ? void 0 : containerConfig;
|
|
336
|
+
for (const updater of this.updaters) {
|
|
337
|
+
const result = updater.update(line, context, config);
|
|
338
|
+
if (result !== null) {
|
|
339
|
+
return result;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return { ...context };
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
var contextManager = new ContextManager();
|
|
346
|
+
function updateContext(line, context, containerConfig) {
|
|
347
|
+
return contextManager.update(line, context, containerConfig);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// src/parser/boundary/BoundaryDetector.ts
|
|
351
|
+
var ContainerBoundaryChecker = class {
|
|
352
|
+
constructor(containerConfig) {
|
|
353
|
+
this.containerConfig = containerConfig;
|
|
354
|
+
}
|
|
355
|
+
check(lineIndex, context, lines) {
|
|
356
|
+
const line = lines[lineIndex];
|
|
357
|
+
if (!context.inContainer) {
|
|
358
|
+
return -1;
|
|
359
|
+
}
|
|
360
|
+
if (this.containerConfig !== void 0) {
|
|
361
|
+
const containerEnd = detectContainerEnd(line, context, this.containerConfig);
|
|
362
|
+
if (containerEnd) {
|
|
363
|
+
return lineIndex - 1;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return -1;
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
var ListBoundaryChecker = class {
|
|
370
|
+
check(lineIndex, context, lines) {
|
|
371
|
+
if (!context.inList) {
|
|
372
|
+
return -1;
|
|
373
|
+
}
|
|
374
|
+
if (!context.listMayEnd) {
|
|
375
|
+
return -1;
|
|
376
|
+
}
|
|
377
|
+
const line = lines[lineIndex];
|
|
378
|
+
const listItem = isListItemStart(line);
|
|
379
|
+
const contentIndent = line.match(/^(\s*)/)?.[1].length ?? 0;
|
|
380
|
+
const isListContent = contentIndent > (context.listIndent ?? 0);
|
|
381
|
+
if (!listItem && !isListContent && !isEmptyLine(line)) {
|
|
382
|
+
return lineIndex - 1;
|
|
383
|
+
}
|
|
384
|
+
return -1;
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
var FootnoteBoundaryChecker = class {
|
|
388
|
+
check(lineIndex, context, lines) {
|
|
389
|
+
const line = lines[lineIndex];
|
|
390
|
+
const prevLine = lines[lineIndex - 1];
|
|
391
|
+
if (isFootnoteDefinitionStart(prevLine)) {
|
|
392
|
+
if (isEmptyLine(line) || isFootnoteContinuation(line)) {
|
|
393
|
+
return -1;
|
|
394
|
+
}
|
|
395
|
+
if (isFootnoteDefinitionStart(line)) {
|
|
396
|
+
return lineIndex - 1;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (!isEmptyLine(prevLine) && isFootnoteContinuation(prevLine)) {
|
|
400
|
+
return -1;
|
|
401
|
+
}
|
|
402
|
+
if (!isEmptyLine(prevLine) && isFootnoteDefinitionStart(line) && !isFootnoteDefinitionStart(prevLine)) {
|
|
403
|
+
return lineIndex - 1;
|
|
404
|
+
}
|
|
405
|
+
return -1;
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
var NewBlockBoundaryChecker = class {
|
|
409
|
+
check(lineIndex, context, lines) {
|
|
410
|
+
const line = lines[lineIndex];
|
|
411
|
+
const prevLine = lines[lineIndex - 1];
|
|
412
|
+
if (isEmptyLine(prevLine)) {
|
|
413
|
+
return -1;
|
|
414
|
+
}
|
|
415
|
+
if (isSetextHeadingUnderline(line, prevLine)) {
|
|
416
|
+
return lineIndex - 1;
|
|
417
|
+
}
|
|
418
|
+
if (isHeading(line)) {
|
|
419
|
+
return lineIndex - 1;
|
|
420
|
+
}
|
|
421
|
+
if (detectFenceStart(line)) {
|
|
422
|
+
return lineIndex - 1;
|
|
423
|
+
}
|
|
424
|
+
if (isBlockquoteStart(line) && !isBlockquoteStart(prevLine)) {
|
|
425
|
+
return lineIndex - 1;
|
|
426
|
+
}
|
|
427
|
+
if (!context.inList && isListItemStart(line) && !isListItemStart(prevLine)) {
|
|
428
|
+
return lineIndex - 1;
|
|
429
|
+
}
|
|
430
|
+
return -1;
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
var EmptyLineBoundaryChecker = class {
|
|
434
|
+
check(lineIndex, context, lines) {
|
|
435
|
+
const line = lines[lineIndex];
|
|
436
|
+
const prevLine = lines[lineIndex - 1];
|
|
437
|
+
if (isEmptyLine(line) && !isEmptyLine(prevLine) && !context.inList) {
|
|
438
|
+
return lineIndex;
|
|
439
|
+
}
|
|
440
|
+
return -1;
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
var BoundaryDetector = class {
|
|
444
|
+
containerConfig;
|
|
445
|
+
checkers;
|
|
446
|
+
/** 缓存每一行结束时对应的 Context,避免重复计算 */
|
|
447
|
+
contextCache = /* @__PURE__ */ new Map();
|
|
448
|
+
constructor(config = {}) {
|
|
449
|
+
this.containerConfig = config.containers;
|
|
450
|
+
this.checkers = [
|
|
451
|
+
new ContainerBoundaryChecker(this.containerConfig),
|
|
452
|
+
new ListBoundaryChecker(),
|
|
453
|
+
new FootnoteBoundaryChecker(),
|
|
454
|
+
new NewBlockBoundaryChecker(),
|
|
455
|
+
new EmptyLineBoundaryChecker()
|
|
456
|
+
];
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* 清空上下文缓存
|
|
460
|
+
* 当 pendingStartLine 推进后调用,释放不再需要的缓存
|
|
461
|
+
*/
|
|
462
|
+
clearContextCache(beforeLine) {
|
|
463
|
+
for (const key of this.contextCache.keys()) {
|
|
464
|
+
if (key < beforeLine) {
|
|
465
|
+
this.contextCache.delete(key);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* 查找稳定边界
|
|
471
|
+
* 返回稳定边界行号和该行对应的上下文(用于后续更新,避免重复计算)
|
|
472
|
+
*
|
|
473
|
+
* @param lines 所有行
|
|
474
|
+
* @param startLine 起始行
|
|
475
|
+
* @param context 当前上下文
|
|
476
|
+
* @returns 稳定边界结果
|
|
477
|
+
*/
|
|
478
|
+
findStableBoundary(lines, startLine, context) {
|
|
479
|
+
let stableLine = -1;
|
|
480
|
+
let stableContext = context;
|
|
481
|
+
let tempContext = startLine > 0 && this.contextCache.has(startLine - 1) ? { ...this.contextCache.get(startLine - 1) } : { ...context };
|
|
482
|
+
for (let i = startLine; i < lines.length; i++) {
|
|
483
|
+
const line = lines[i];
|
|
484
|
+
const wasInFencedCode = tempContext.inFencedCode;
|
|
485
|
+
const wasInContainer = tempContext.inContainer;
|
|
486
|
+
const wasContainerDepth = tempContext.containerDepth;
|
|
487
|
+
tempContext = updateContext(line, tempContext, this.containerConfig);
|
|
488
|
+
this.contextCache.set(i, { ...tempContext });
|
|
489
|
+
if (wasInFencedCode && !tempContext.inFencedCode) {
|
|
490
|
+
if (i < lines.length - 1) {
|
|
491
|
+
stableLine = i;
|
|
492
|
+
stableContext = { ...tempContext };
|
|
493
|
+
}
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
if (tempContext.inFencedCode) {
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
if (wasInContainer && wasContainerDepth === 1 && !tempContext.inContainer) {
|
|
500
|
+
if (i < lines.length - 1) {
|
|
501
|
+
stableLine = i;
|
|
502
|
+
stableContext = { ...tempContext };
|
|
503
|
+
}
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
if (tempContext.inContainer) {
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
const stablePoint = this.checkStability(i, tempContext, lines);
|
|
510
|
+
if (stablePoint >= 0) {
|
|
511
|
+
stableLine = stablePoint;
|
|
512
|
+
stableContext = { ...tempContext };
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return { line: stableLine, context: stableContext };
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* 检查指定行是否是稳定边界
|
|
519
|
+
* 使用责任链模式,依次调用各个检查器
|
|
520
|
+
*
|
|
521
|
+
* @param lineIndex 行索引
|
|
522
|
+
* @param context 当前上下文
|
|
523
|
+
* @param lines 所有行
|
|
524
|
+
* @returns 稳定边界行号,如果不是稳定边界返回 -1
|
|
525
|
+
*/
|
|
526
|
+
checkStability(lineIndex, context, lines) {
|
|
527
|
+
if (lineIndex === 0) {
|
|
528
|
+
return -1;
|
|
529
|
+
}
|
|
530
|
+
const line = lines[lineIndex];
|
|
531
|
+
const prevLine = lines[lineIndex - 1];
|
|
532
|
+
if (context.inFootnote) {
|
|
533
|
+
if (isFootnoteDefinitionStart(prevLine) && !isEmptyLine(line)) {
|
|
534
|
+
if (isFootnoteContinuation(line)) {
|
|
535
|
+
return -1;
|
|
536
|
+
}
|
|
537
|
+
return lineIndex - 1;
|
|
538
|
+
}
|
|
539
|
+
if (isEmptyLine(prevLine) && (isEmptyLine(line) || isFootnoteContinuation(line))) {
|
|
540
|
+
return -1;
|
|
541
|
+
}
|
|
542
|
+
return -1;
|
|
543
|
+
}
|
|
544
|
+
if (isHeading(prevLine) || isThematicBreak(prevLine)) {
|
|
545
|
+
return lineIndex - 1;
|
|
546
|
+
}
|
|
547
|
+
if (isSetextHeadingUnderline(prevLine, lines[lineIndex - 2])) {
|
|
548
|
+
return lineIndex - 1;
|
|
549
|
+
}
|
|
550
|
+
if (lineIndex >= lines.length - 1) {
|
|
551
|
+
return -1;
|
|
552
|
+
}
|
|
553
|
+
for (const checker of this.checkers) {
|
|
554
|
+
const stablePoint = checker.check(lineIndex, context, lines);
|
|
555
|
+
if (stablePoint >= 0) {
|
|
556
|
+
return stablePoint;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return -1;
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
// src/utils/index.ts
|
|
564
|
+
function isDefinitionNode(node) {
|
|
565
|
+
return node.type === "definition";
|
|
566
|
+
}
|
|
567
|
+
function isFootnoteDefinitionNode(node) {
|
|
568
|
+
return node.type === "footnoteDefinition";
|
|
569
|
+
}
|
|
570
|
+
function traverseAst(node, visitor) {
|
|
571
|
+
const stopEarly = visitor(node);
|
|
572
|
+
if (stopEarly === true) {
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
if ("children" in node && Array.isArray(node.children)) {
|
|
576
|
+
for (const child of node.children) {
|
|
577
|
+
traverseAst(child, visitor);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
function collectAstNodes(node, predicate) {
|
|
582
|
+
const results = [];
|
|
583
|
+
traverseAst(node, (node2) => {
|
|
584
|
+
if (predicate(node2)) {
|
|
585
|
+
results.push(node2);
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
return results;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// src/parser/manager/DefinitionManager.ts
|
|
592
|
+
var DefinitionManager = class {
|
|
593
|
+
definitions = {};
|
|
594
|
+
/**
|
|
595
|
+
* 从已完成的 blocks 中提取 definitions
|
|
596
|
+
*
|
|
597
|
+
* @param blocks 已完成的块
|
|
598
|
+
*/
|
|
599
|
+
extractFromBlocks(blocks) {
|
|
600
|
+
for (const block of blocks) {
|
|
601
|
+
const newDefinitions = this.findDefinitions(block);
|
|
602
|
+
this.definitions = {
|
|
603
|
+
...this.definitions,
|
|
604
|
+
...newDefinitions
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* 从 block 中提取 definitions
|
|
610
|
+
*
|
|
611
|
+
* @param block 解析块
|
|
612
|
+
* @returns Definition 映射表
|
|
613
|
+
*/
|
|
614
|
+
findDefinitions(block) {
|
|
615
|
+
const definitions = collectAstNodes(block.node, isDefinitionNode);
|
|
616
|
+
return definitions.reduce((acc, node) => {
|
|
617
|
+
acc[node.identifier] = node;
|
|
618
|
+
return acc;
|
|
619
|
+
}, {});
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* 获取所有 definitions
|
|
623
|
+
*
|
|
624
|
+
* @returns Definition 映射表
|
|
625
|
+
*/
|
|
626
|
+
getAll() {
|
|
627
|
+
return this.definitions;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* 清空所有 definitions
|
|
631
|
+
*/
|
|
632
|
+
clear() {
|
|
633
|
+
this.definitions = {};
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
|
|
637
|
+
// src/parser/manager/FootnoteManager.ts
|
|
638
|
+
var FootnoteManager = class {
|
|
639
|
+
definitions = {};
|
|
640
|
+
/** 已完成部分的脚注引用顺序(缓存) */
|
|
641
|
+
completedReferenceOrder = [];
|
|
642
|
+
/** 所有脚注引用顺序(包括 pending 部分) */
|
|
643
|
+
referenceOrder = [];
|
|
644
|
+
/**
|
|
645
|
+
* 从已完成的 blocks 中提取 footnote definitions
|
|
646
|
+
*
|
|
647
|
+
* @param blocks 已完成的块
|
|
648
|
+
*/
|
|
649
|
+
extractDefinitionsFromBlocks(blocks) {
|
|
650
|
+
for (const block of blocks) {
|
|
651
|
+
const newDefinitions = this.findFootnoteDefinitions(block);
|
|
652
|
+
this.definitions = {
|
|
653
|
+
...this.definitions,
|
|
654
|
+
...newDefinitions
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* 从 block 中提取 footnote definitions
|
|
660
|
+
*
|
|
661
|
+
* @param block 解析块
|
|
662
|
+
* @returns Footnote Definition 映射表
|
|
663
|
+
*/
|
|
664
|
+
findFootnoteDefinitions(block) {
|
|
665
|
+
const definitions = collectAstNodes(block.node, isFootnoteDefinitionNode);
|
|
666
|
+
return definitions.reduce((acc, node) => {
|
|
667
|
+
acc[node.identifier] = node;
|
|
668
|
+
return acc;
|
|
669
|
+
}, {});
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* 从已完成的 blocks 中收集脚注引用(增量更新)
|
|
673
|
+
* 只收集新完成的 blocks 中的引用,并缓存结果
|
|
674
|
+
*
|
|
675
|
+
* @param blocks 新完成的 blocks
|
|
676
|
+
*/
|
|
677
|
+
collectReferencesFromCompletedBlocks(blocks) {
|
|
678
|
+
const newReferences = /* @__PURE__ */ new Set();
|
|
679
|
+
blocks.forEach((block) => {
|
|
680
|
+
traverseAst(block.node, (n) => {
|
|
681
|
+
if (n.type === "footnoteReference") {
|
|
682
|
+
const identifier = n.identifier;
|
|
683
|
+
if (!this.completedReferenceOrder.includes(identifier)) {
|
|
684
|
+
newReferences.add(identifier);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
});
|
|
689
|
+
this.completedReferenceOrder.push(...Array.from(newReferences));
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* 收集 pending blocks 中的脚注引用
|
|
693
|
+
* 返回完整的引用顺序(已完成 + pending)
|
|
694
|
+
*
|
|
695
|
+
* @param pendingBlocks pending blocks
|
|
696
|
+
* @returns 完整的脚注引用顺序
|
|
697
|
+
*/
|
|
698
|
+
collectReferencesFromPending(pendingBlocks) {
|
|
699
|
+
const pendingReferences = /* @__PURE__ */ new Set();
|
|
700
|
+
pendingBlocks.forEach((block) => {
|
|
701
|
+
traverseAst(block.node, (n) => {
|
|
702
|
+
if (n.type === "footnoteReference") {
|
|
703
|
+
const identifier = n.identifier;
|
|
704
|
+
if (!this.completedReferenceOrder.includes(identifier)) {
|
|
705
|
+
pendingReferences.add(identifier);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
});
|
|
710
|
+
this.referenceOrder = [...this.completedReferenceOrder, ...Array.from(pendingReferences)];
|
|
711
|
+
return this.referenceOrder;
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* 收集 AST 中的脚注引用(按出现顺序)
|
|
715
|
+
*
|
|
716
|
+
* @deprecated 使用 collectReferencesFromCompletedBlocks 和 collectReferencesFromPending 代替
|
|
717
|
+
* @param nodes AST 节点列表
|
|
718
|
+
*/
|
|
719
|
+
collectReferences(nodes) {
|
|
720
|
+
nodes.forEach((node) => {
|
|
721
|
+
traverseAst(node, (n) => {
|
|
722
|
+
if (n.type === "footnoteReference") {
|
|
723
|
+
const identifier = n.identifier;
|
|
724
|
+
if (!this.referenceOrder.includes(identifier)) {
|
|
725
|
+
this.referenceOrder.push(identifier);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* 获取所有 footnote definitions
|
|
733
|
+
*
|
|
734
|
+
* @returns Footnote Definition 映射表
|
|
735
|
+
*/
|
|
736
|
+
getDefinitions() {
|
|
737
|
+
return this.definitions;
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* 获取脚注引用顺序
|
|
741
|
+
*
|
|
742
|
+
* @returns 脚注引用顺序
|
|
743
|
+
*/
|
|
744
|
+
getReferenceOrder() {
|
|
745
|
+
return this.referenceOrder;
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* 清空所有 footnote definitions 和引用顺序
|
|
749
|
+
*/
|
|
750
|
+
clear() {
|
|
751
|
+
this.definitions = {};
|
|
752
|
+
this.completedReferenceOrder = [];
|
|
753
|
+
this.referenceOrder = [];
|
|
754
|
+
}
|
|
755
|
+
};
|
|
19
756
|
|
|
20
757
|
// src/extensions/html-extension/index.ts
|
|
21
758
|
var DEFAULT_TAG_BLACKLIST = [
|
|
@@ -294,11 +1031,104 @@ function isHtmlNode(node) {
|
|
|
294
1031
|
function hasChildren(node) {
|
|
295
1032
|
return "children" in node && Array.isArray(node.children);
|
|
296
1033
|
}
|
|
297
|
-
function
|
|
1034
|
+
function mergeFragmentedHtmlNodes(nodes) {
|
|
298
1035
|
const result = [];
|
|
299
1036
|
let i = 0;
|
|
300
1037
|
while (i < nodes.length) {
|
|
301
1038
|
const node = nodes[i];
|
|
1039
|
+
if (!isHtmlNode(node)) {
|
|
1040
|
+
result.push(node);
|
|
1041
|
+
i++;
|
|
1042
|
+
continue;
|
|
1043
|
+
}
|
|
1044
|
+
const unclosedTags = findUnclosedTags(node.value);
|
|
1045
|
+
if (unclosedTags.length === 0) {
|
|
1046
|
+
result.push(node);
|
|
1047
|
+
i++;
|
|
1048
|
+
continue;
|
|
1049
|
+
}
|
|
1050
|
+
const mergedParts = [node.value];
|
|
1051
|
+
let j = i + 1;
|
|
1052
|
+
let currentUnclosed = [...unclosedTags];
|
|
1053
|
+
while (j < nodes.length && currentUnclosed.length > 0) {
|
|
1054
|
+
const nextNode = nodes[j];
|
|
1055
|
+
if (isHtmlNode(nextNode)) {
|
|
1056
|
+
const closingInfo = checkClosingTags(nextNode.value, currentUnclosed);
|
|
1057
|
+
if (closingInfo.hasRelevantClosing) {
|
|
1058
|
+
mergedParts.push(nextNode.value);
|
|
1059
|
+
currentUnclosed = closingInfo.remainingUnclosed;
|
|
1060
|
+
if (currentUnclosed.length === 0) {
|
|
1061
|
+
j++;
|
|
1062
|
+
break;
|
|
1063
|
+
}
|
|
1064
|
+
} else {
|
|
1065
|
+
mergedParts.push(nextNode.value);
|
|
1066
|
+
}
|
|
1067
|
+
} else {
|
|
1068
|
+
break;
|
|
1069
|
+
}
|
|
1070
|
+
j++;
|
|
1071
|
+
}
|
|
1072
|
+
if (mergedParts.length > 1) {
|
|
1073
|
+
const mergedValue = mergedParts.join("\n");
|
|
1074
|
+
const mergedNode = {
|
|
1075
|
+
type: "html",
|
|
1076
|
+
value: mergedValue
|
|
1077
|
+
};
|
|
1078
|
+
result.push(mergedNode);
|
|
1079
|
+
i = j;
|
|
1080
|
+
} else {
|
|
1081
|
+
result.push(node);
|
|
1082
|
+
i++;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
return result;
|
|
1086
|
+
}
|
|
1087
|
+
function findUnclosedTags(html) {
|
|
1088
|
+
const tagStack = [];
|
|
1089
|
+
const tagRegex = /<\/?([a-zA-Z][a-zA-Z0-9-]*)[^>]*\/?>/g;
|
|
1090
|
+
let match;
|
|
1091
|
+
while ((match = tagRegex.exec(html)) !== null) {
|
|
1092
|
+
const fullTag = match[0];
|
|
1093
|
+
const tagName = match[1].toLowerCase();
|
|
1094
|
+
if (VOID_ELEMENTS.includes(tagName) || fullTag.endsWith("/>")) {
|
|
1095
|
+
continue;
|
|
1096
|
+
}
|
|
1097
|
+
if (fullTag.startsWith("</")) {
|
|
1098
|
+
const lastIndex = tagStack.lastIndexOf(tagName);
|
|
1099
|
+
if (lastIndex !== -1) {
|
|
1100
|
+
tagStack.splice(lastIndex, 1);
|
|
1101
|
+
}
|
|
1102
|
+
} else {
|
|
1103
|
+
tagStack.push(tagName);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
return tagStack;
|
|
1107
|
+
}
|
|
1108
|
+
function checkClosingTags(html, unclosedTags) {
|
|
1109
|
+
const remaining = [...unclosedTags];
|
|
1110
|
+
let hasRelevant = false;
|
|
1111
|
+
const closeTagRegex = /<\/([a-zA-Z][a-zA-Z0-9-]*)\s*>/g;
|
|
1112
|
+
let match;
|
|
1113
|
+
while ((match = closeTagRegex.exec(html)) !== null) {
|
|
1114
|
+
const tagName = match[1].toLowerCase();
|
|
1115
|
+
const index = remaining.lastIndexOf(tagName);
|
|
1116
|
+
if (index !== -1) {
|
|
1117
|
+
remaining.splice(index, 1);
|
|
1118
|
+
hasRelevant = true;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
return {
|
|
1122
|
+
hasRelevantClosing: hasRelevant,
|
|
1123
|
+
remainingUnclosed: remaining
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
function processHtmlNodesInArray(nodes, options) {
|
|
1127
|
+
const mergedNodes = mergeFragmentedHtmlNodes(nodes);
|
|
1128
|
+
const result = [];
|
|
1129
|
+
let i = 0;
|
|
1130
|
+
while (i < mergedNodes.length) {
|
|
1131
|
+
const node = mergedNodes[i];
|
|
302
1132
|
if (isHtmlNode(node)) {
|
|
303
1133
|
const contentType = detectHtmlContentType(node.value);
|
|
304
1134
|
if (contentType === "fragment") {
|
|
@@ -339,8 +1169,8 @@ function processHtmlNodesInArray(nodes, options) {
|
|
|
339
1169
|
let depth = 1;
|
|
340
1170
|
let j = i + 1;
|
|
341
1171
|
let foundClosing = false;
|
|
342
|
-
while (j <
|
|
343
|
-
const nextNode =
|
|
1172
|
+
while (j < mergedNodes.length && depth > 0) {
|
|
1173
|
+
const nextNode = mergedNodes[j];
|
|
344
1174
|
if (isHtmlNode(nextNode)) {
|
|
345
1175
|
const nextType = detectHtmlContentType(nextNode.value);
|
|
346
1176
|
if (nextType === "closing") {
|
|
@@ -403,792 +1233,1057 @@ function transformHtmlNodes(ast, options = {}) {
|
|
|
403
1233
|
children: processHtmlNodesInArray(ast.children, options)
|
|
404
1234
|
};
|
|
405
1235
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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);
|
|
1236
|
+
|
|
1237
|
+
// src/parser/ast/types.ts
|
|
1238
|
+
function extractMarkedExtensions(plugins) {
|
|
1239
|
+
const extensions = [];
|
|
1240
|
+
for (const plugin of plugins) {
|
|
1241
|
+
if ((plugin.type === "marked" || plugin.type === "both") && plugin.marked) {
|
|
1242
|
+
extensions.push(...plugin.marked.extensions);
|
|
426
1243
|
}
|
|
427
1244
|
}
|
|
1245
|
+
return extensions;
|
|
428
1246
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
1247
|
+
|
|
1248
|
+
// src/extensions/marked-extensions/explicitDefinitionExtension.ts
|
|
1249
|
+
function createExplicitDefinitionExtension() {
|
|
1250
|
+
return {
|
|
1251
|
+
name: "explicitDefinition",
|
|
1252
|
+
level: "block",
|
|
1253
|
+
// 🔑 关键修复:start 必须匹配完整的 definition 模式 [id]:,
|
|
1254
|
+
// 而不能只匹配 [,否则会把 ![alt][id] 中的 [alt] 误认为是 definition 开头
|
|
1255
|
+
// 同时排除脚注定义 [^id]:
|
|
1256
|
+
start(src) {
|
|
1257
|
+
const match = src.match(/^ {0,3}\[(?!\^)[^\]]+\]:/m);
|
|
1258
|
+
return match?.index;
|
|
1259
|
+
},
|
|
1260
|
+
tokenizer(src) {
|
|
1261
|
+
const rule = /^ {0,3}\[(?!\^)[^\]]+\]:.*?(?:\n+|$)/;
|
|
1262
|
+
const match = rule.exec(src);
|
|
1263
|
+
if (match) {
|
|
1264
|
+
const raw = match[0];
|
|
1265
|
+
const contentMatch = raw.match(
|
|
1266
|
+
/^ {0,3}\[([^\]]+)\]:\s*(\S+)(?:\s+["'(](.*?)["')])?/
|
|
1267
|
+
);
|
|
1268
|
+
if (contentMatch) {
|
|
1269
|
+
const identifier = contentMatch[1].toLowerCase();
|
|
1270
|
+
const url = contentMatch[2];
|
|
1271
|
+
const title = contentMatch[3];
|
|
1272
|
+
if (this.lexer?.tokens?.links) {
|
|
1273
|
+
this.lexer.tokens.links[identifier] = { href: url, title };
|
|
1274
|
+
}
|
|
1275
|
+
return {
|
|
1276
|
+
type: "explicitDefinition",
|
|
1277
|
+
raw,
|
|
1278
|
+
identifier,
|
|
1279
|
+
url,
|
|
1280
|
+
title
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
return { type: "explicitDefinition", raw, identifier: "", url: "" };
|
|
1284
|
+
}
|
|
1285
|
+
return void 0;
|
|
1286
|
+
},
|
|
1287
|
+
renderer() {
|
|
1288
|
+
return "";
|
|
434
1289
|
}
|
|
435
|
-
}
|
|
436
|
-
return result;
|
|
1290
|
+
};
|
|
437
1291
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
1292
|
+
|
|
1293
|
+
// src/extensions/marked-extensions/optimisticReferenceExtension.ts
|
|
1294
|
+
function createOptimisticReferenceExtension() {
|
|
1295
|
+
return {
|
|
1296
|
+
name: "optimisticReference",
|
|
1297
|
+
level: "inline",
|
|
1298
|
+
start(src) {
|
|
1299
|
+
return src.match(/!?\[/)?.index;
|
|
1300
|
+
},
|
|
1301
|
+
tokenizer(src) {
|
|
1302
|
+
const rule = /^(!?)\[((?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*)\](?:\s*\[((?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*)\])?/;
|
|
1303
|
+
const match = rule.exec(src);
|
|
1304
|
+
if (match) {
|
|
1305
|
+
const fullMatch = match[0];
|
|
1306
|
+
if (src.length > fullMatch.length && src[fullMatch.length] === "(") {
|
|
1307
|
+
return void 0;
|
|
1308
|
+
}
|
|
1309
|
+
if (src.length > fullMatch.length && src[fullMatch.length] === ":") {
|
|
1310
|
+
return void 0;
|
|
1311
|
+
}
|
|
1312
|
+
const isImage = match[1] === "!";
|
|
1313
|
+
const text = match[2];
|
|
1314
|
+
const refRaw = match[3];
|
|
1315
|
+
if (text.startsWith("^")) {
|
|
1316
|
+
return void 0;
|
|
1317
|
+
}
|
|
1318
|
+
let identifier = "";
|
|
1319
|
+
let referenceType = "shortcut";
|
|
1320
|
+
if (refRaw !== void 0) {
|
|
1321
|
+
if (refRaw === "") {
|
|
1322
|
+
referenceType = "collapsed";
|
|
1323
|
+
identifier = text;
|
|
1324
|
+
} else {
|
|
1325
|
+
referenceType = "full";
|
|
1326
|
+
identifier = refRaw;
|
|
1327
|
+
}
|
|
1328
|
+
} else {
|
|
1329
|
+
referenceType = "shortcut";
|
|
1330
|
+
identifier = text;
|
|
1331
|
+
if (text.match(/^[ xX]$/)) {
|
|
1332
|
+
return void 0;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
return {
|
|
1336
|
+
type: "optimisticReference",
|
|
1337
|
+
raw: fullMatch,
|
|
1338
|
+
isImage,
|
|
1339
|
+
text,
|
|
1340
|
+
identifier: identifier.toLowerCase(),
|
|
1341
|
+
label: identifier,
|
|
1342
|
+
referenceType
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
return void 0;
|
|
1346
|
+
},
|
|
1347
|
+
renderer() {
|
|
1348
|
+
return "";
|
|
454
1349
|
}
|
|
455
|
-
|
|
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());
|
|
1350
|
+
};
|
|
461
1351
|
}
|
|
462
|
-
|
|
463
|
-
|
|
1352
|
+
|
|
1353
|
+
// src/extensions/marked-extensions/mathExtension.ts
|
|
1354
|
+
function createBlockMathExtension() {
|
|
1355
|
+
return {
|
|
1356
|
+
name: "blockMath",
|
|
1357
|
+
level: "block",
|
|
1358
|
+
start(src) {
|
|
1359
|
+
const match = src.match(/^ {0,3}\$\$/m);
|
|
1360
|
+
return match?.index;
|
|
1361
|
+
},
|
|
1362
|
+
tokenizer(src) {
|
|
1363
|
+
const rule = /^ {0,3}\$\$([\s\S]*?)\$\$ *(?:\n+|$)/;
|
|
1364
|
+
const match = rule.exec(src);
|
|
1365
|
+
if (match) {
|
|
1366
|
+
return {
|
|
1367
|
+
type: "blockMath",
|
|
1368
|
+
raw: match[0],
|
|
1369
|
+
text: match[1].trim()
|
|
1370
|
+
};
|
|
1371
|
+
}
|
|
1372
|
+
return void 0;
|
|
1373
|
+
},
|
|
1374
|
+
renderer() {
|
|
1375
|
+
return "";
|
|
1376
|
+
}
|
|
1377
|
+
};
|
|
464
1378
|
}
|
|
465
|
-
function
|
|
1379
|
+
function createInlineMathExtension() {
|
|
466
1380
|
return {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
1381
|
+
name: "inlineMath",
|
|
1382
|
+
level: "inline",
|
|
1383
|
+
start(src) {
|
|
1384
|
+
const index = src.indexOf("$");
|
|
1385
|
+
if (index === -1) return void 0;
|
|
1386
|
+
if (src[index + 1] === "$") return void 0;
|
|
1387
|
+
return index;
|
|
1388
|
+
},
|
|
1389
|
+
tokenizer(src) {
|
|
1390
|
+
const rule = /^\$(?!\$)((?:\\.|[^\\\n$])+?)\$(?!\d)/;
|
|
1391
|
+
const match = rule.exec(src);
|
|
1392
|
+
if (match) {
|
|
1393
|
+
return {
|
|
1394
|
+
type: "inlineMath",
|
|
1395
|
+
raw: match[0],
|
|
1396
|
+
text: match[1].trim()
|
|
1397
|
+
};
|
|
476
1398
|
}
|
|
1399
|
+
return void 0;
|
|
1400
|
+
},
|
|
1401
|
+
renderer() {
|
|
1402
|
+
return "";
|
|
477
1403
|
}
|
|
478
1404
|
};
|
|
479
1405
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
1406
|
+
|
|
1407
|
+
// src/extensions/marked-extensions/footnoteDefinitionExtension.ts
|
|
1408
|
+
function createFootnoteDefinitionExtension() {
|
|
1409
|
+
return {
|
|
1410
|
+
name: "footnoteDefinitionBlock",
|
|
1411
|
+
level: "block",
|
|
1412
|
+
start(src) {
|
|
1413
|
+
const match = src.match(/^ {0,3}\[\^[^\]]+\]:/m);
|
|
1414
|
+
return match?.index;
|
|
1415
|
+
},
|
|
1416
|
+
tokenizer(src) {
|
|
1417
|
+
const firstLineRule = /^ {0,3}\[\^([a-zA-Z0-9_-]+)\]:\s*(.*)/;
|
|
1418
|
+
const firstLineMatch = firstLineRule.exec(src);
|
|
1419
|
+
if (!firstLineMatch) return void 0;
|
|
1420
|
+
const identifier = firstLineMatch[1];
|
|
1421
|
+
let content = firstLineMatch[2];
|
|
1422
|
+
let raw = firstLineMatch[0];
|
|
1423
|
+
const remaining = src.slice(raw.length);
|
|
1424
|
+
const lines = remaining.split("\n");
|
|
1425
|
+
let lineIndex = 0;
|
|
1426
|
+
if (lines[0] === "" && remaining.startsWith("\n")) {
|
|
1427
|
+
lineIndex = 1;
|
|
1428
|
+
raw += "\n";
|
|
1429
|
+
content += "\n";
|
|
1430
|
+
}
|
|
1431
|
+
while (lineIndex < lines.length) {
|
|
1432
|
+
const line = lines[lineIndex];
|
|
1433
|
+
if (line.trim() === "") {
|
|
1434
|
+
let hasIndentedLineAfter = false;
|
|
1435
|
+
for (let j = lineIndex + 1; j < lines.length; j++) {
|
|
1436
|
+
const nextLine = lines[j];
|
|
1437
|
+
if (nextLine.trim() === "") continue;
|
|
1438
|
+
if (nextLine.match(/^( |\t)/)) {
|
|
1439
|
+
hasIndentedLineAfter = true;
|
|
1440
|
+
}
|
|
1441
|
+
break;
|
|
1442
|
+
}
|
|
1443
|
+
if (hasIndentedLineAfter) {
|
|
1444
|
+
raw += line + (lineIndex < lines.length - 1 ? "\n" : "");
|
|
1445
|
+
content += "\n" + line;
|
|
1446
|
+
lineIndex++;
|
|
1447
|
+
continue;
|
|
1448
|
+
} else {
|
|
1449
|
+
break;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
if (line.match(/^( |\t)/)) {
|
|
1453
|
+
raw += line + (lineIndex < lines.length - 1 ? "\n" : "");
|
|
1454
|
+
content += "\n" + line;
|
|
1455
|
+
lineIndex++;
|
|
1456
|
+
continue;
|
|
1457
|
+
}
|
|
1458
|
+
if (line.match(/^ {0,3}\[\^[^\]]+\]:/)) {
|
|
1459
|
+
break;
|
|
1460
|
+
}
|
|
509
1461
|
break;
|
|
510
1462
|
}
|
|
511
|
-
|
|
512
|
-
|
|
1463
|
+
const trimmedContent = content.replace(/\n+$/, "");
|
|
1464
|
+
return {
|
|
1465
|
+
type: "footnoteDefinitionBlock",
|
|
1466
|
+
raw,
|
|
1467
|
+
identifier,
|
|
1468
|
+
content: trimmedContent
|
|
1469
|
+
};
|
|
1470
|
+
},
|
|
1471
|
+
renderer() {
|
|
1472
|
+
return "";
|
|
1473
|
+
}
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
// src/extensions/marked-extensions/inlineHtmlExtension.ts
|
|
1478
|
+
var SELF_CLOSING_TAGS = /* @__PURE__ */ new Set([
|
|
1479
|
+
"area",
|
|
1480
|
+
"base",
|
|
1481
|
+
"br",
|
|
1482
|
+
"col",
|
|
1483
|
+
"embed",
|
|
1484
|
+
"hr",
|
|
1485
|
+
"img",
|
|
1486
|
+
"input",
|
|
1487
|
+
"link",
|
|
1488
|
+
"meta",
|
|
1489
|
+
"param",
|
|
1490
|
+
"source",
|
|
1491
|
+
"track",
|
|
1492
|
+
"wbr"
|
|
1493
|
+
]);
|
|
1494
|
+
function createInlineHtmlExtension() {
|
|
1495
|
+
return {
|
|
1496
|
+
name: "inlineHtml",
|
|
1497
|
+
level: "inline",
|
|
1498
|
+
start(src) {
|
|
1499
|
+
const index = src.indexOf("<");
|
|
1500
|
+
if (index === -1) return void 0;
|
|
1501
|
+
const afterLt = src.slice(index + 1);
|
|
1502
|
+
if (!/^[a-zA-Z\/]/.test(afterLt)) return void 0;
|
|
1503
|
+
return index;
|
|
1504
|
+
},
|
|
1505
|
+
tokenizer(src) {
|
|
1506
|
+
const completeTagMatch = matchCompleteHtmlElement(src);
|
|
1507
|
+
if (completeTagMatch) {
|
|
1508
|
+
return {
|
|
1509
|
+
type: "inlineHtml",
|
|
1510
|
+
raw: completeTagMatch,
|
|
1511
|
+
text: completeTagMatch
|
|
1512
|
+
};
|
|
513
1513
|
}
|
|
514
|
-
|
|
515
|
-
if (
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
}
|
|
1514
|
+
const selfClosingMatch = matchSelfClosingTag(src);
|
|
1515
|
+
if (selfClosingMatch) {
|
|
1516
|
+
return {
|
|
1517
|
+
type: "inlineHtml",
|
|
1518
|
+
raw: selfClosingMatch,
|
|
1519
|
+
text: selfClosingMatch
|
|
1520
|
+
};
|
|
521
1521
|
}
|
|
522
|
-
|
|
523
|
-
|
|
1522
|
+
return void 0;
|
|
1523
|
+
},
|
|
1524
|
+
renderer() {
|
|
1525
|
+
return "";
|
|
1526
|
+
}
|
|
1527
|
+
};
|
|
1528
|
+
}
|
|
1529
|
+
function matchCompleteHtmlElement(src) {
|
|
1530
|
+
const openTagMatch = /^<([a-zA-Z][a-zA-Z0-9]*)((?:\s+[a-zA-Z_:][a-zA-Z0-9_.:-]*(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?)*)\s*>/.exec(src);
|
|
1531
|
+
if (!openTagMatch) return null;
|
|
1532
|
+
const tagName = openTagMatch[1].toLowerCase();
|
|
1533
|
+
const openTag = openTagMatch[0];
|
|
1534
|
+
if (SELF_CLOSING_TAGS.has(tagName)) {
|
|
1535
|
+
return openTag;
|
|
1536
|
+
}
|
|
1537
|
+
const afterOpenTag = src.slice(openTag.length);
|
|
1538
|
+
let depth = 1;
|
|
1539
|
+
let pos = 0;
|
|
1540
|
+
const openPattern = new RegExp(`<${tagName}(?:\\s[^>]*)?>`, "gi");
|
|
1541
|
+
const closePattern = new RegExp(`</${tagName}>`, "gi");
|
|
1542
|
+
while (depth > 0 && pos < afterOpenTag.length) {
|
|
1543
|
+
openPattern.lastIndex = pos;
|
|
1544
|
+
closePattern.lastIndex = pos;
|
|
1545
|
+
const nextOpen = openPattern.exec(afterOpenTag);
|
|
1546
|
+
const nextClose = closePattern.exec(afterOpenTag);
|
|
1547
|
+
if (!nextClose) {
|
|
1548
|
+
return null;
|
|
524
1549
|
}
|
|
1550
|
+
if (nextOpen && nextOpen.index < nextClose.index) {
|
|
1551
|
+
depth++;
|
|
1552
|
+
pos = nextOpen.index + nextOpen[0].length;
|
|
1553
|
+
} else {
|
|
1554
|
+
depth--;
|
|
1555
|
+
pos = nextClose.index + nextClose[0].length;
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
if (depth === 0) {
|
|
1559
|
+
return src.slice(0, openTag.length + pos);
|
|
1560
|
+
}
|
|
1561
|
+
return null;
|
|
1562
|
+
}
|
|
1563
|
+
function matchSelfClosingTag(src) {
|
|
1564
|
+
const explicitSelfClosing = /^<([a-zA-Z][a-zA-Z0-9]*)((?:\s+[a-zA-Z_:][a-zA-Z0-9_.:-]*(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?)*)\s*\/>/.exec(src);
|
|
1565
|
+
if (explicitSelfClosing) {
|
|
1566
|
+
return explicitSelfClosing[0];
|
|
525
1567
|
}
|
|
526
|
-
|
|
527
|
-
|
|
1568
|
+
const implicitSelfClosing = /^<([a-zA-Z][a-zA-Z0-9]*)((?:\s+[a-zA-Z_:][a-zA-Z0-9_.:-]*(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?)*)\s*>/.exec(src);
|
|
1569
|
+
if (implicitSelfClosing && SELF_CLOSING_TAGS.has(implicitSelfClosing[1].toLowerCase())) {
|
|
1570
|
+
return implicitSelfClosing[0];
|
|
528
1571
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
1572
|
+
return null;
|
|
1573
|
+
}
|
|
1574
|
+
function transformBlockMath(token) {
|
|
1575
|
+
return {
|
|
1576
|
+
type: "math",
|
|
1577
|
+
value: token.text,
|
|
1578
|
+
meta: null
|
|
533
1579
|
};
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
1580
|
+
}
|
|
1581
|
+
function transformFootnoteDefinitionBlock(token, ctx) {
|
|
1582
|
+
const children = ctx.parseFootnoteContent(token.content);
|
|
1583
|
+
return {
|
|
1584
|
+
type: "footnoteDefinition",
|
|
1585
|
+
identifier: token.identifier,
|
|
1586
|
+
label: token.identifier,
|
|
1587
|
+
children
|
|
538
1588
|
};
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
1589
|
+
}
|
|
1590
|
+
function transformExplicitDefinition(token) {
|
|
1591
|
+
if (!token.identifier || !token.url) return null;
|
|
1592
|
+
return {
|
|
1593
|
+
type: "definition",
|
|
1594
|
+
identifier: token.identifier,
|
|
1595
|
+
label: token.identifier,
|
|
1596
|
+
url: token.url,
|
|
1597
|
+
title: token.title ?? null
|
|
543
1598
|
};
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
events[close - 2],
|
|
554
|
-
events[close - 1],
|
|
555
|
-
["exit", label, context]
|
|
556
|
-
);
|
|
557
|
-
media.push(...events.slice(close + 1));
|
|
558
|
-
media.push(["exit", group, context]);
|
|
559
|
-
events.splice(open, events.length - open, ...media);
|
|
560
|
-
return events;
|
|
561
|
-
}
|
|
562
|
-
function tokenizeLabelEnd(effects, ok, nok) {
|
|
563
|
-
const self = this;
|
|
564
|
-
let index = self.events.length;
|
|
565
|
-
let labelStart;
|
|
566
|
-
while (index--) {
|
|
567
|
-
if ((self.events[index][1].type === types.labelImage || self.events[index][1].type === types.labelLink) && !self.events[index][1]._balanced) {
|
|
568
|
-
labelStart = self.events[index][1];
|
|
569
|
-
break;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
return start;
|
|
573
|
-
function start(code) {
|
|
574
|
-
if (!labelStart) {
|
|
575
|
-
return nok(code);
|
|
576
|
-
}
|
|
577
|
-
if (labelStart._inactive) {
|
|
578
|
-
return labelEndNok(code);
|
|
579
|
-
}
|
|
580
|
-
if (labelStart.type === types.labelLink) {
|
|
581
|
-
const labelText = self.sliceSerialize({ start: labelStart.end, end: self.now() });
|
|
582
|
-
if (labelText.startsWith("^")) {
|
|
583
|
-
return nok(code);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
effects.enter(types.labelEnd);
|
|
587
|
-
effects.enter(types.labelMarker);
|
|
588
|
-
effects.consume(code);
|
|
589
|
-
effects.exit(types.labelMarker);
|
|
590
|
-
effects.exit(types.labelEnd);
|
|
591
|
-
return after;
|
|
592
|
-
}
|
|
593
|
-
function after(code) {
|
|
594
|
-
if (code === codes.leftParenthesis) {
|
|
595
|
-
return effects.attempt(
|
|
596
|
-
{
|
|
597
|
-
tokenize: tokenizeResource,
|
|
598
|
-
partial: false
|
|
599
|
-
},
|
|
600
|
-
labelEndOk,
|
|
601
|
-
labelEndNok
|
|
602
|
-
// 修复:resource 解析失败时返回 nok
|
|
603
|
-
)(code);
|
|
604
|
-
}
|
|
605
|
-
if (code === codes.leftSquareBracket) {
|
|
606
|
-
return effects.attempt(
|
|
1599
|
+
}
|
|
1600
|
+
function transformDef(token) {
|
|
1601
|
+
if (token.tag.startsWith("^")) {
|
|
1602
|
+
const footnoteId = token.tag.slice(1);
|
|
1603
|
+
return {
|
|
1604
|
+
type: "footnoteDefinition",
|
|
1605
|
+
identifier: footnoteId,
|
|
1606
|
+
label: footnoteId,
|
|
1607
|
+
children: [
|
|
607
1608
|
{
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
1609
|
+
type: "paragraph",
|
|
1610
|
+
children: [{ type: "text", value: token.href }]
|
|
1611
|
+
}
|
|
1612
|
+
]
|
|
1613
|
+
};
|
|
1614
|
+
}
|
|
1615
|
+
return {
|
|
1616
|
+
type: "definition",
|
|
1617
|
+
identifier: token.tag,
|
|
1618
|
+
label: token.tag,
|
|
1619
|
+
url: token.href,
|
|
1620
|
+
title: token.title ?? null
|
|
1621
|
+
};
|
|
1622
|
+
}
|
|
1623
|
+
function transformContainer(token, ctx) {
|
|
1624
|
+
const attributes = {};
|
|
1625
|
+
const attrRegex = /([a-zA-Z0-9_-]+)=?("([^"]*)"|'([^']*)'|([^ ]*))?/g;
|
|
1626
|
+
let match;
|
|
1627
|
+
while ((match = attrRegex.exec(token.attrs)) !== null) {
|
|
1628
|
+
attributes[match[1]] = match[3] || match[4] || match[5] || "";
|
|
1629
|
+
}
|
|
1630
|
+
const children = ctx.transformTokensWithPosition(token.tokens);
|
|
1631
|
+
return {
|
|
1632
|
+
type: "containerDirective",
|
|
1633
|
+
name: token.name,
|
|
1634
|
+
attributes,
|
|
1635
|
+
children
|
|
1636
|
+
};
|
|
1637
|
+
}
|
|
1638
|
+
function transformFootnoteDefToken(token, ctx) {
|
|
1639
|
+
return {
|
|
1640
|
+
type: "footnoteDefinition",
|
|
1641
|
+
identifier: token.identifier,
|
|
1642
|
+
label: token.identifier,
|
|
1643
|
+
children: [
|
|
1644
|
+
{
|
|
1645
|
+
type: "paragraph",
|
|
1646
|
+
children: ctx.transformInline(token.tokens)
|
|
1647
|
+
}
|
|
1648
|
+
]
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
function transformHeading(token, ctx) {
|
|
1652
|
+
return {
|
|
1653
|
+
type: "heading",
|
|
1654
|
+
depth: token.depth,
|
|
1655
|
+
children: ctx.transformInline(token.tokens)
|
|
1656
|
+
};
|
|
1657
|
+
}
|
|
1658
|
+
function transformParagraph(token, ctx) {
|
|
1659
|
+
return {
|
|
1660
|
+
type: "paragraph",
|
|
1661
|
+
children: ctx.transformInline(token.tokens)
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
function transformCode(token) {
|
|
1665
|
+
return {
|
|
1666
|
+
type: "code",
|
|
1667
|
+
lang: token.lang || null,
|
|
1668
|
+
meta: null,
|
|
1669
|
+
// 对齐 micromark 输出
|
|
1670
|
+
value: token.text
|
|
1671
|
+
};
|
|
1672
|
+
}
|
|
1673
|
+
function transformBlockquote(token, ctx) {
|
|
1674
|
+
const children = ctx.transformTokens(token.tokens);
|
|
1675
|
+
return {
|
|
1676
|
+
type: "blockquote",
|
|
1677
|
+
children
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1680
|
+
function transformList(token, ctx) {
|
|
1681
|
+
const children = token.items.map((item) => ({
|
|
1682
|
+
type: "listItem",
|
|
1683
|
+
spread: item.loose,
|
|
1684
|
+
checked: item.checked ?? null,
|
|
1685
|
+
// 对齐 micromark 输出(GFM 任务列表)
|
|
1686
|
+
children: ctx.transformTokens(item.tokens)
|
|
1687
|
+
}));
|
|
1688
|
+
return {
|
|
1689
|
+
type: "list",
|
|
1690
|
+
ordered: token.ordered,
|
|
1691
|
+
start: token.ordered ? token.start || 1 : null,
|
|
1692
|
+
// 对齐 micromark:有序列表有 start,无序列表为 null
|
|
1693
|
+
spread: token.loose,
|
|
1694
|
+
children
|
|
1695
|
+
};
|
|
1696
|
+
}
|
|
1697
|
+
function transformTable(token, ctx) {
|
|
1698
|
+
const headerCells = token.header.map((cell) => ({
|
|
1699
|
+
type: "tableCell",
|
|
1700
|
+
children: ctx.transformInline(cell.tokens)
|
|
1701
|
+
}));
|
|
1702
|
+
const bodyRows = token.rows.map((row) => ({
|
|
1703
|
+
type: "tableRow",
|
|
1704
|
+
children: row.map((cell) => ({
|
|
1705
|
+
type: "tableCell",
|
|
1706
|
+
children: ctx.transformInline(cell.tokens)
|
|
1707
|
+
}))
|
|
1708
|
+
}));
|
|
1709
|
+
return {
|
|
1710
|
+
type: "table",
|
|
1711
|
+
align: token.align,
|
|
1712
|
+
children: [{ type: "tableRow", children: headerCells }, ...bodyRows]
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
function transformHr() {
|
|
1716
|
+
return { type: "thematicBreak" };
|
|
1717
|
+
}
|
|
1718
|
+
function transformHtml(token) {
|
|
1719
|
+
return {
|
|
1720
|
+
type: "html",
|
|
1721
|
+
value: token.text
|
|
1722
|
+
};
|
|
1723
|
+
}
|
|
1724
|
+
function transformTextBlock(token, ctx) {
|
|
1725
|
+
if (token.tokens) {
|
|
1726
|
+
return {
|
|
1727
|
+
type: "paragraph",
|
|
1728
|
+
children: ctx.transformInline(token.tokens)
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
return {
|
|
1732
|
+
type: "paragraph",
|
|
1733
|
+
children: [{ type: "text", value: token.text }]
|
|
1734
|
+
};
|
|
1735
|
+
}
|
|
1736
|
+
function transformInlineMath(token) {
|
|
1737
|
+
return {
|
|
1738
|
+
type: "inlineMath",
|
|
1739
|
+
value: token.text
|
|
1740
|
+
};
|
|
1741
|
+
}
|
|
1742
|
+
function transformOptimisticReference(token, ctx) {
|
|
1743
|
+
if (token.isImage) {
|
|
1744
|
+
return {
|
|
1745
|
+
type: "imageReference",
|
|
1746
|
+
identifier: token.identifier,
|
|
1747
|
+
label: token.label,
|
|
1748
|
+
referenceType: token.referenceType,
|
|
1749
|
+
alt: token.text
|
|
1750
|
+
};
|
|
617
1751
|
}
|
|
618
|
-
|
|
619
|
-
return effects.attempt(
|
|
620
|
-
{
|
|
621
|
-
tokenize: tokenizeReferenceCollapsed,
|
|
622
|
-
partial: false
|
|
623
|
-
},
|
|
624
|
-
labelEndOk,
|
|
625
|
-
labelEndOk
|
|
626
|
-
// 修改:即使失败也返回 ok
|
|
627
|
-
)(code);
|
|
628
|
-
}
|
|
629
|
-
function labelEndOk(code) {
|
|
630
|
-
return ok(code);
|
|
631
|
-
}
|
|
632
|
-
function labelEndNok(code) {
|
|
633
|
-
labelStart._balanced = true;
|
|
634
|
-
return nok(code);
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
function tokenizeResource(effects, ok, nok) {
|
|
638
|
-
return resourceStart;
|
|
639
|
-
function resourceStart(code) {
|
|
640
|
-
if (code !== codes.leftParenthesis) {
|
|
641
|
-
return nok(code);
|
|
642
|
-
}
|
|
643
|
-
effects.enter(types.resource);
|
|
644
|
-
effects.enter(types.resourceMarker);
|
|
645
|
-
effects.consume(code);
|
|
646
|
-
effects.exit(types.resourceMarker);
|
|
647
|
-
return resourceBefore;
|
|
648
|
-
}
|
|
649
|
-
function resourceBefore(code) {
|
|
650
|
-
return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, resourceOpen)(code) : resourceOpen(code);
|
|
651
|
-
}
|
|
652
|
-
function resourceOpen(code) {
|
|
653
|
-
if (code === codes.rightParenthesis) {
|
|
654
|
-
return resourceEnd(code);
|
|
655
|
-
}
|
|
656
|
-
return factoryDestination(
|
|
657
|
-
effects,
|
|
658
|
-
resourceDestinationAfter,
|
|
659
|
-
resourceDestinationMissing,
|
|
660
|
-
types.resourceDestination,
|
|
661
|
-
types.resourceDestinationLiteral,
|
|
662
|
-
types.resourceDestinationLiteralMarker,
|
|
663
|
-
types.resourceDestinationRaw,
|
|
664
|
-
types.resourceDestinationString,
|
|
665
|
-
constants.linkResourceDestinationBalanceMax
|
|
666
|
-
)(code);
|
|
667
|
-
}
|
|
668
|
-
function resourceDestinationAfter(code) {
|
|
669
|
-
return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, resourceBetween)(code) : resourceEnd(code);
|
|
670
|
-
}
|
|
671
|
-
function resourceDestinationMissing(code) {
|
|
672
|
-
return nok(code);
|
|
673
|
-
}
|
|
674
|
-
function resourceBetween(code) {
|
|
675
|
-
if (code === codes.quotationMark || code === codes.apostrophe || code === codes.leftParenthesis) {
|
|
676
|
-
return factoryTitle(
|
|
677
|
-
effects,
|
|
678
|
-
resourceTitleAfter,
|
|
679
|
-
nok,
|
|
680
|
-
types.resourceTitle,
|
|
681
|
-
types.resourceTitleMarker,
|
|
682
|
-
types.resourceTitleString
|
|
683
|
-
)(code);
|
|
684
|
-
}
|
|
685
|
-
return resourceEnd(code);
|
|
686
|
-
}
|
|
687
|
-
function resourceTitleAfter(code) {
|
|
688
|
-
return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, resourceEnd)(code) : resourceEnd(code);
|
|
689
|
-
}
|
|
690
|
-
function resourceEnd(code) {
|
|
691
|
-
if (code === codes.rightParenthesis) {
|
|
692
|
-
effects.enter(types.resourceMarker);
|
|
693
|
-
effects.consume(code);
|
|
694
|
-
effects.exit(types.resourceMarker);
|
|
695
|
-
effects.exit(types.resource);
|
|
696
|
-
return ok;
|
|
697
|
-
}
|
|
698
|
-
return nok(code);
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
function tokenizeReferenceFull(effects, ok, nok) {
|
|
702
|
-
const self = this;
|
|
703
|
-
return referenceFull;
|
|
704
|
-
function referenceFull(code) {
|
|
705
|
-
if (code !== codes.leftSquareBracket) {
|
|
706
|
-
return nok(code);
|
|
707
|
-
}
|
|
708
|
-
return factoryLabel.call(
|
|
709
|
-
self,
|
|
710
|
-
effects,
|
|
711
|
-
referenceFullAfter,
|
|
712
|
-
referenceFullMissing,
|
|
713
|
-
types.reference,
|
|
714
|
-
types.referenceMarker,
|
|
715
|
-
types.referenceString
|
|
716
|
-
)(code);
|
|
717
|
-
}
|
|
718
|
-
function referenceFullAfter(code) {
|
|
719
|
-
return ok(code);
|
|
720
|
-
}
|
|
721
|
-
function referenceFullMissing(code) {
|
|
722
|
-
return nok(code);
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
function tokenizeReferenceCollapsed(effects, ok, nok) {
|
|
726
|
-
return referenceCollapsedStart;
|
|
727
|
-
function referenceCollapsedStart(code) {
|
|
728
|
-
if (code !== codes.leftSquareBracket) {
|
|
729
|
-
return nok(code);
|
|
730
|
-
}
|
|
731
|
-
effects.enter(types.reference);
|
|
732
|
-
effects.enter(types.referenceMarker);
|
|
733
|
-
effects.consume(code);
|
|
734
|
-
effects.exit(types.referenceMarker);
|
|
735
|
-
return referenceCollapsedOpen;
|
|
736
|
-
}
|
|
737
|
-
function referenceCollapsedOpen(code) {
|
|
738
|
-
if (code === codes.rightSquareBracket) {
|
|
739
|
-
effects.enter(types.referenceMarker);
|
|
740
|
-
effects.consume(code);
|
|
741
|
-
effects.exit(types.referenceMarker);
|
|
742
|
-
effects.exit(types.reference);
|
|
743
|
-
return ok;
|
|
744
|
-
}
|
|
745
|
-
return nok(code);
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
function gfmFootnoteIncremental() {
|
|
749
|
-
const original = gfmFootnote();
|
|
1752
|
+
const labelChildren = ctx.transformInline(new Lexer().inlineTokens(token.text));
|
|
750
1753
|
return {
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
...original.text[codes.leftSquareBracket],
|
|
757
|
-
tokenize: tokenizeGfmFootnoteCallIncremental
|
|
758
|
-
},
|
|
759
|
-
// 覆盖 text[93] (`]` 的处理) - 用于处理 ![^1] 这样的情况
|
|
760
|
-
[codes.rightSquareBracket]: {
|
|
761
|
-
...original.text[codes.rightSquareBracket],
|
|
762
|
-
tokenize: tokenizePotentialGfmFootnoteCallIncremental
|
|
763
|
-
}
|
|
764
|
-
}
|
|
1754
|
+
type: "linkReference",
|
|
1755
|
+
identifier: token.identifier,
|
|
1756
|
+
label: token.label,
|
|
1757
|
+
referenceType: token.referenceType,
|
|
1758
|
+
children: labelChildren.length ? labelChildren : [{ type: "text", value: token.text }]
|
|
765
1759
|
};
|
|
766
1760
|
}
|
|
767
|
-
function
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
}
|
|
775
|
-
effects.enter("gfmFootnoteCall");
|
|
776
|
-
effects.enter("gfmFootnoteCallLabelMarker");
|
|
777
|
-
effects.consume(code);
|
|
778
|
-
effects.exit("gfmFootnoteCallLabelMarker");
|
|
779
|
-
return callStart;
|
|
780
|
-
}
|
|
781
|
-
function callStart(code) {
|
|
782
|
-
if (code !== codes.caret) {
|
|
783
|
-
return nok(code);
|
|
784
|
-
}
|
|
785
|
-
effects.enter("gfmFootnoteCallMarker");
|
|
786
|
-
effects.consume(code);
|
|
787
|
-
effects.exit("gfmFootnoteCallMarker");
|
|
788
|
-
effects.enter("gfmFootnoteCallString");
|
|
789
|
-
const token = effects.enter("chunkString");
|
|
790
|
-
token.contentType = "string";
|
|
791
|
-
return callData;
|
|
792
|
-
}
|
|
793
|
-
function callData(code) {
|
|
794
|
-
if (
|
|
795
|
-
// 太长
|
|
796
|
-
size > constants.linkReferenceSizeMax || // 右括号但没有数据
|
|
797
|
-
code === codes.rightSquareBracket && !data || // EOF、换行、空格、制表符、左括号不支持
|
|
798
|
-
code === codes.eof || code === codes.leftSquareBracket || markdownLineEndingOrSpace(code)
|
|
799
|
-
) {
|
|
800
|
-
return nok(code);
|
|
801
|
-
}
|
|
802
|
-
if (code === codes.rightSquareBracket) {
|
|
803
|
-
effects.exit("chunkString");
|
|
804
|
-
effects.exit("gfmFootnoteCallString");
|
|
805
|
-
effects.enter("gfmFootnoteCallLabelMarker");
|
|
806
|
-
effects.consume(code);
|
|
807
|
-
effects.exit("gfmFootnoteCallLabelMarker");
|
|
808
|
-
effects.exit("gfmFootnoteCall");
|
|
809
|
-
return ok;
|
|
810
|
-
}
|
|
811
|
-
if (!markdownLineEndingOrSpace(code)) {
|
|
812
|
-
data = true;
|
|
813
|
-
}
|
|
814
|
-
size++;
|
|
815
|
-
effects.consume(code);
|
|
816
|
-
return code === codes.backslash ? callEscape : callData;
|
|
817
|
-
}
|
|
818
|
-
function callEscape(code) {
|
|
819
|
-
if (code === codes.leftSquareBracket || code === codes.backslash || code === codes.rightSquareBracket) {
|
|
820
|
-
effects.consume(code);
|
|
821
|
-
size++;
|
|
822
|
-
return callData;
|
|
823
|
-
}
|
|
824
|
-
return callData(code);
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
function tokenizePotentialGfmFootnoteCallIncremental(effects, ok, nok) {
|
|
828
|
-
const self = this;
|
|
829
|
-
let index = self.events.length;
|
|
830
|
-
let labelStart;
|
|
831
|
-
while (index--) {
|
|
832
|
-
const token = self.events[index][1];
|
|
833
|
-
if (token.type === "labelImage") {
|
|
834
|
-
labelStart = token;
|
|
835
|
-
break;
|
|
836
|
-
}
|
|
837
|
-
if (token.type === "gfmFootnoteCall" || token.type === "labelLink" || token.type === "label" || token.type === "image" || token.type === "link") {
|
|
838
|
-
break;
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
return start;
|
|
842
|
-
function start(code) {
|
|
843
|
-
if (code !== codes.rightSquareBracket) {
|
|
844
|
-
return nok(code);
|
|
845
|
-
}
|
|
846
|
-
if (!labelStart || !labelStart._balanced) {
|
|
847
|
-
return nok(code);
|
|
848
|
-
}
|
|
849
|
-
const id = normalizeIdentifier(
|
|
850
|
-
self.sliceSerialize({
|
|
851
|
-
start: labelStart.end,
|
|
852
|
-
end: self.now()
|
|
853
|
-
})
|
|
854
|
-
);
|
|
855
|
-
if (id.codePointAt(0) !== codes.caret) {
|
|
856
|
-
return nok(code);
|
|
857
|
-
}
|
|
858
|
-
effects.enter("gfmFootnoteCallLabelMarker");
|
|
859
|
-
effects.consume(code);
|
|
860
|
-
effects.exit("gfmFootnoteCallLabelMarker");
|
|
861
|
-
return ok(code);
|
|
1761
|
+
function transformLink(token, ctx) {
|
|
1762
|
+
if (token.text.startsWith("^") && token.text.length > 1) {
|
|
1763
|
+
const footnoteId = token.text.slice(1);
|
|
1764
|
+
return {
|
|
1765
|
+
type: "footnoteReference",
|
|
1766
|
+
identifier: footnoteId,
|
|
1767
|
+
label: footnoteId
|
|
1768
|
+
};
|
|
862
1769
|
}
|
|
1770
|
+
return {
|
|
1771
|
+
type: "link",
|
|
1772
|
+
url: token.href,
|
|
1773
|
+
title: token.title || null,
|
|
1774
|
+
// 对齐 micromark 输出
|
|
1775
|
+
children: ctx.transformInline(token.tokens)
|
|
1776
|
+
};
|
|
863
1777
|
}
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
var RE_HTML_BLOCK_2 = /^\s{0,3}<\/?[a-zA-Z][a-zA-Z0-9-]*(\s|>|$)/;
|
|
873
|
-
var RE_TABLE_DELIMITER = /^\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)*\|?$/;
|
|
874
|
-
var RE_ESCAPE_SPECIAL = /[.*+?^${}()|[\]\\]/g;
|
|
875
|
-
var RE_FOOTNOTE_DEFINITION = /^\[\^([^\]]+)\]:\s/;
|
|
876
|
-
var RE_FOOTNOTE_CONTINUATION = /^(?: |\t)/;
|
|
877
|
-
var fenceEndPatternCache = /* @__PURE__ */ new Map();
|
|
878
|
-
var containerPatternCache = /* @__PURE__ */ new Map();
|
|
879
|
-
function detectFenceStart(line) {
|
|
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;
|
|
1778
|
+
function transformImage(token) {
|
|
1779
|
+
return {
|
|
1780
|
+
type: "image",
|
|
1781
|
+
url: token.href,
|
|
1782
|
+
title: token.title || null,
|
|
1783
|
+
// 对齐 micromark 输出
|
|
1784
|
+
alt: token.text
|
|
1785
|
+
};
|
|
887
1786
|
}
|
|
888
|
-
function
|
|
889
|
-
|
|
890
|
-
|
|
1787
|
+
function transformText(token) {
|
|
1788
|
+
const results = [];
|
|
1789
|
+
const text = token.text;
|
|
1790
|
+
const footnoteRegex = /\[\^([a-zA-Z0-9_-]+)\]/g;
|
|
1791
|
+
let lastIndex = 0;
|
|
1792
|
+
let match;
|
|
1793
|
+
while ((match = footnoteRegex.exec(text)) !== null) {
|
|
1794
|
+
if (match.index > lastIndex) {
|
|
1795
|
+
results.push({
|
|
1796
|
+
type: "text",
|
|
1797
|
+
value: text.substring(lastIndex, match.index)
|
|
1798
|
+
});
|
|
1799
|
+
}
|
|
1800
|
+
results.push({
|
|
1801
|
+
type: "footnoteReference",
|
|
1802
|
+
identifier: match[1],
|
|
1803
|
+
label: match[1]
|
|
1804
|
+
});
|
|
1805
|
+
lastIndex = match.index + match[0].length;
|
|
891
1806
|
}
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
1807
|
+
if (lastIndex < text.length) {
|
|
1808
|
+
results.push({
|
|
1809
|
+
type: "text",
|
|
1810
|
+
value: text.substring(lastIndex)
|
|
1811
|
+
});
|
|
897
1812
|
}
|
|
898
|
-
return
|
|
899
|
-
}
|
|
900
|
-
function isEmptyLine(line) {
|
|
901
|
-
return RE_EMPTY_LINE.test(line);
|
|
902
|
-
}
|
|
903
|
-
function isHeading(line) {
|
|
904
|
-
return RE_HEADING.test(line);
|
|
1813
|
+
return results;
|
|
905
1814
|
}
|
|
906
|
-
function
|
|
907
|
-
return
|
|
1815
|
+
function transformStrong(token, ctx) {
|
|
1816
|
+
return {
|
|
1817
|
+
type: "strong",
|
|
1818
|
+
children: ctx.transformInline(token.tokens)
|
|
1819
|
+
};
|
|
908
1820
|
}
|
|
909
|
-
function
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
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;
|
|
1821
|
+
function transformEmphasis(token, ctx) {
|
|
1822
|
+
return {
|
|
1823
|
+
type: "emphasis",
|
|
1824
|
+
children: ctx.transformInline(token.tokens)
|
|
1825
|
+
};
|
|
929
1826
|
}
|
|
930
|
-
function
|
|
931
|
-
return
|
|
1827
|
+
function transformCodespan(token) {
|
|
1828
|
+
return {
|
|
1829
|
+
type: "inlineCode",
|
|
1830
|
+
value: token.text
|
|
1831
|
+
};
|
|
932
1832
|
}
|
|
933
|
-
function
|
|
934
|
-
return
|
|
1833
|
+
function transformBreak() {
|
|
1834
|
+
return { type: "break" };
|
|
935
1835
|
}
|
|
936
|
-
function
|
|
937
|
-
return
|
|
1836
|
+
function transformDelete(token, ctx) {
|
|
1837
|
+
return {
|
|
1838
|
+
type: "delete",
|
|
1839
|
+
children: ctx.transformInline(token.tokens)
|
|
1840
|
+
};
|
|
938
1841
|
}
|
|
939
|
-
function
|
|
940
|
-
|
|
1842
|
+
function transformInlineHtml(token) {
|
|
1843
|
+
const parsed = parseHtmlFragment(token.text);
|
|
1844
|
+
if (parsed.length > 0) {
|
|
1845
|
+
return parsed;
|
|
1846
|
+
}
|
|
1847
|
+
return { type: "text", value: token.text };
|
|
941
1848
|
}
|
|
942
|
-
function
|
|
943
|
-
return
|
|
1849
|
+
function isTokenType(token, type) {
|
|
1850
|
+
return token.type === type;
|
|
944
1851
|
}
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
1852
|
+
var builtinBlockTransformers = {
|
|
1853
|
+
blockMath: (token) => {
|
|
1854
|
+
if (isTokenType(token, "blockMath")) return transformBlockMath(token);
|
|
1855
|
+
return null;
|
|
1856
|
+
},
|
|
1857
|
+
footnoteDefinitionBlock: (token, ctx) => {
|
|
1858
|
+
if (isTokenType(token, "footnoteDefinitionBlock"))
|
|
1859
|
+
return transformFootnoteDefinitionBlock(token, ctx);
|
|
1860
|
+
return null;
|
|
1861
|
+
},
|
|
1862
|
+
explicitDefinition: (token) => {
|
|
1863
|
+
if (isTokenType(token, "explicitDefinition"))
|
|
1864
|
+
return transformExplicitDefinition(token);
|
|
1865
|
+
return null;
|
|
1866
|
+
},
|
|
1867
|
+
def: (token) => {
|
|
1868
|
+
if (isTokenType(token, "def")) return transformDef(token);
|
|
1869
|
+
return null;
|
|
1870
|
+
},
|
|
1871
|
+
container: (token, ctx) => {
|
|
1872
|
+
if (isTokenType(token, "container")) return transformContainer(token, ctx);
|
|
1873
|
+
return null;
|
|
1874
|
+
},
|
|
1875
|
+
footnoteDefinition: (token, ctx) => {
|
|
1876
|
+
if (isTokenType(token, "footnoteDefinition"))
|
|
1877
|
+
return transformFootnoteDefToken(token, ctx);
|
|
1878
|
+
return null;
|
|
1879
|
+
},
|
|
1880
|
+
heading: (token, ctx) => {
|
|
1881
|
+
if (isTokenType(token, "heading")) return transformHeading(token, ctx);
|
|
1882
|
+
return null;
|
|
1883
|
+
},
|
|
1884
|
+
paragraph: (token, ctx) => {
|
|
1885
|
+
if (isTokenType(token, "paragraph")) return transformParagraph(token, ctx);
|
|
1886
|
+
return null;
|
|
1887
|
+
},
|
|
1888
|
+
code: (token) => {
|
|
1889
|
+
if (isTokenType(token, "code")) return transformCode(token);
|
|
1890
|
+
return null;
|
|
1891
|
+
},
|
|
1892
|
+
blockquote: (token, ctx) => {
|
|
1893
|
+
if (isTokenType(token, "blockquote")) return transformBlockquote(token, ctx);
|
|
1894
|
+
return null;
|
|
1895
|
+
},
|
|
1896
|
+
list: (token, ctx) => {
|
|
1897
|
+
if (isTokenType(token, "list")) return transformList(token, ctx);
|
|
1898
|
+
return null;
|
|
1899
|
+
},
|
|
1900
|
+
table: (token, ctx) => {
|
|
1901
|
+
if (isTokenType(token, "table")) return transformTable(token, ctx);
|
|
1902
|
+
return null;
|
|
1903
|
+
},
|
|
1904
|
+
hr: () => transformHr(),
|
|
1905
|
+
html: (token) => {
|
|
1906
|
+
if (isTokenType(token, "html")) return transformHtml(token);
|
|
1907
|
+
return null;
|
|
1908
|
+
},
|
|
1909
|
+
space: () => null,
|
|
1910
|
+
text: (token, ctx) => {
|
|
1911
|
+
if (isTokenType(token, "text")) return transformTextBlock(token, ctx);
|
|
959
1912
|
return null;
|
|
960
1913
|
}
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
return
|
|
1914
|
+
};
|
|
1915
|
+
var builtinInlineTransformers = {
|
|
1916
|
+
inlineMath: (token) => {
|
|
1917
|
+
if (isTokenType(token, "inlineMath")) return transformInlineMath(token);
|
|
1918
|
+
return null;
|
|
1919
|
+
},
|
|
1920
|
+
optimisticReference: (token, ctx) => {
|
|
1921
|
+
if (isTokenType(token, "optimisticReference"))
|
|
1922
|
+
return transformOptimisticReference(token, ctx);
|
|
1923
|
+
return null;
|
|
1924
|
+
},
|
|
1925
|
+
link: (token, ctx) => {
|
|
1926
|
+
if (isTokenType(token, "link")) return transformLink(token, ctx);
|
|
1927
|
+
return null;
|
|
1928
|
+
},
|
|
1929
|
+
image: (token) => {
|
|
1930
|
+
if (isTokenType(token, "image")) return transformImage(token);
|
|
1931
|
+
return null;
|
|
1932
|
+
},
|
|
1933
|
+
text: (token) => {
|
|
1934
|
+
if (isTokenType(token, "text")) return transformText(token);
|
|
1935
|
+
return null;
|
|
1936
|
+
},
|
|
1937
|
+
escape: (token) => {
|
|
1938
|
+
if (isTokenType(token, "escape")) return transformText(token);
|
|
1939
|
+
return null;
|
|
1940
|
+
},
|
|
1941
|
+
strong: (token, ctx) => {
|
|
1942
|
+
if (isTokenType(token, "strong")) return transformStrong(token, ctx);
|
|
1943
|
+
return null;
|
|
1944
|
+
},
|
|
1945
|
+
em: (token, ctx) => {
|
|
1946
|
+
if (isTokenType(token, "em")) return transformEmphasis(token, ctx);
|
|
1947
|
+
return null;
|
|
1948
|
+
},
|
|
1949
|
+
codespan: (token) => {
|
|
1950
|
+
if (isTokenType(token, "codespan")) return transformCodespan(token);
|
|
1951
|
+
return null;
|
|
1952
|
+
},
|
|
1953
|
+
br: () => transformBreak(),
|
|
1954
|
+
del: (token, ctx) => {
|
|
1955
|
+
if (isTokenType(token, "del")) return transformDelete(token, ctx);
|
|
1956
|
+
return null;
|
|
1957
|
+
},
|
|
1958
|
+
inlineHtml: (token) => {
|
|
1959
|
+
if (isTokenType(token, "inlineHtml")) return transformInlineHtml(token);
|
|
1960
|
+
return null;
|
|
974
1961
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1962
|
+
};
|
|
1963
|
+
function transformBlockToken(token, ctx) {
|
|
1964
|
+
const tokenType = token.type;
|
|
1965
|
+
if (ctx.customBlockTransformers?.[tokenType]) {
|
|
1966
|
+
const result = ctx.customBlockTransformers[tokenType](token, ctx);
|
|
1967
|
+
if (result !== void 0) return result;
|
|
1968
|
+
}
|
|
1969
|
+
if (builtinBlockTransformers[tokenType]) {
|
|
1970
|
+
const result = builtinBlockTransformers[tokenType](token, ctx);
|
|
1971
|
+
if (result !== void 0) return result;
|
|
1972
|
+
}
|
|
1973
|
+
if ("text" in token && typeof token.text === "string") {
|
|
1974
|
+
const paragraph = {
|
|
1975
|
+
type: "paragraph",
|
|
1976
|
+
children: [{ type: "text", value: token.text }]
|
|
1977
|
+
};
|
|
1978
|
+
return paragraph;
|
|
978
1979
|
}
|
|
979
|
-
return
|
|
1980
|
+
return null;
|
|
980
1981
|
}
|
|
981
|
-
function
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
return true;
|
|
1982
|
+
function transformInlineToken(token, ctx) {
|
|
1983
|
+
const tokenType = token.type;
|
|
1984
|
+
if (ctx.customInlineTransformers?.[tokenType]) {
|
|
1985
|
+
const result = ctx.customInlineTransformers[tokenType](token, ctx);
|
|
1986
|
+
if (result !== void 0) return result;
|
|
987
1987
|
}
|
|
988
|
-
if (
|
|
989
|
-
|
|
1988
|
+
if (builtinInlineTransformers[tokenType]) {
|
|
1989
|
+
const result = builtinInlineTransformers[tokenType](token, ctx);
|
|
1990
|
+
if (result !== void 0) return result;
|
|
990
1991
|
}
|
|
991
|
-
if (
|
|
992
|
-
|
|
1992
|
+
if ("text" in token && typeof token.text === "string") {
|
|
1993
|
+
const text = { type: "text", value: token.text };
|
|
1994
|
+
return text;
|
|
993
1995
|
}
|
|
994
|
-
|
|
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
|
-
};
|
|
1996
|
+
return null;
|
|
1010
1997
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1998
|
+
|
|
1999
|
+
// src/parser/ast/MarkedAstBuildter.ts
|
|
2000
|
+
var MarkedAstBuilder = class {
|
|
2001
|
+
constructor(options = {}) {
|
|
2002
|
+
this.options = options;
|
|
2003
|
+
this.containerConfig = typeof options.containers === "object" ? options.containers : options.containers === true ? {} : void 0;
|
|
2004
|
+
this.htmlTreeOptions = typeof options.htmlTree === "object" ? options.htmlTree : options.htmlTree === true ? {} : void 0;
|
|
2005
|
+
if (options.plugins) {
|
|
2006
|
+
this.userExtensions.push(...extractMarkedExtensions(options.plugins));
|
|
2007
|
+
}
|
|
2008
|
+
if (options.markedExtensions) {
|
|
2009
|
+
this.userExtensions.push(...options.markedExtensions);
|
|
2010
|
+
}
|
|
2011
|
+
this.transformContext = {
|
|
2012
|
+
transformTokens: this.transformTokens.bind(this),
|
|
2013
|
+
transformTokensWithPosition: this.transformTokensWithPosition.bind(this),
|
|
2014
|
+
transformInline: this.transformInline.bind(this),
|
|
2015
|
+
parseFootnoteContent: this.parseFootnoteContent.bind(this)
|
|
2016
|
+
};
|
|
1014
2017
|
}
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
const
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
2018
|
+
containerConfig;
|
|
2019
|
+
htmlTreeOptions;
|
|
2020
|
+
globalLinks = {};
|
|
2021
|
+
/** 用户传入的 marked 扩展 */
|
|
2022
|
+
userExtensions = [];
|
|
2023
|
+
/** 转换上下文(用于递归转换) */
|
|
2024
|
+
transformContext;
|
|
2025
|
+
parse(text) {
|
|
2026
|
+
const normalizedText = text.replace(/[\u00A0\u200b\u202f]/g, " ");
|
|
2027
|
+
const optimisticRefExt = createOptimisticReferenceExtension();
|
|
2028
|
+
const explicitDefExt = createExplicitDefinitionExtension();
|
|
2029
|
+
const footnoteDefExt = createFootnoteDefinitionExtension();
|
|
2030
|
+
const userBlockExts = [];
|
|
2031
|
+
const userBlockStartExts = [];
|
|
2032
|
+
const userInlineExts = [];
|
|
2033
|
+
const userInlineStartExts = [];
|
|
2034
|
+
for (const ext of this.userExtensions) {
|
|
2035
|
+
if (ext.level === "block") {
|
|
2036
|
+
if (ext.tokenizer) userBlockExts.push(ext.tokenizer);
|
|
2037
|
+
if (ext.start) userBlockStartExts.push(ext.start);
|
|
2038
|
+
} else if (ext.level === "inline") {
|
|
2039
|
+
if (ext.tokenizer) userInlineExts.push(ext.tokenizer);
|
|
2040
|
+
if (ext.start) userInlineStartExts.push(ext.start);
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
const blockExts = [
|
|
2044
|
+
footnoteDefExt.tokenizer,
|
|
2045
|
+
explicitDefExt.tokenizer,
|
|
2046
|
+
...userBlockExts
|
|
2047
|
+
];
|
|
2048
|
+
const blockStartExts = [
|
|
2049
|
+
footnoteDefExt.start,
|
|
2050
|
+
explicitDefExt.start,
|
|
2051
|
+
...userBlockStartExts
|
|
2052
|
+
];
|
|
2053
|
+
const inlineExts = [optimisticRefExt.tokenizer, ...userInlineExts];
|
|
2054
|
+
const inlineStartExts = [optimisticRefExt.start, ...userInlineStartExts];
|
|
2055
|
+
if (this.options.math) {
|
|
2056
|
+
const blockMathExt = createBlockMathExtension();
|
|
2057
|
+
const inlineMathExt = createInlineMathExtension();
|
|
2058
|
+
blockExts.unshift(blockMathExt.tokenizer);
|
|
2059
|
+
blockStartExts.unshift(blockMathExt.start);
|
|
2060
|
+
inlineExts.unshift(inlineMathExt.tokenizer);
|
|
2061
|
+
inlineStartExts.unshift(inlineMathExt.start);
|
|
2062
|
+
}
|
|
2063
|
+
if (this.htmlTreeOptions) {
|
|
2064
|
+
const inlineHtmlExt = createInlineHtmlExtension();
|
|
2065
|
+
inlineExts.unshift(inlineHtmlExt.tokenizer);
|
|
2066
|
+
inlineStartExts.unshift(inlineHtmlExt.start);
|
|
2067
|
+
}
|
|
2068
|
+
const lexerOptions = {
|
|
2069
|
+
gfm: true,
|
|
2070
|
+
breaks: false,
|
|
2071
|
+
// 关闭软换行转 break,与 Micromark 保持一致
|
|
2072
|
+
...this.options,
|
|
2073
|
+
extensions: {
|
|
2074
|
+
inline: inlineExts,
|
|
2075
|
+
startInline: inlineStartExts,
|
|
2076
|
+
block: blockExts,
|
|
2077
|
+
startBlock: blockStartExts
|
|
1061
2078
|
}
|
|
2079
|
+
};
|
|
2080
|
+
const lexerInstance = new Lexer(lexerOptions);
|
|
2081
|
+
if (lexerInstance.tokens && lexerInstance.tokens.links) {
|
|
2082
|
+
Object.assign(lexerInstance.tokens.links, this.globalLinks);
|
|
1062
2083
|
}
|
|
2084
|
+
let tokens = lexerInstance.lex(normalizedText);
|
|
2085
|
+
if (lexerInstance.tokens && lexerInstance.tokens.links) {
|
|
2086
|
+
Object.assign(this.globalLinks, lexerInstance.tokens.links);
|
|
2087
|
+
}
|
|
2088
|
+
tokens = this.preprocessTokens(tokens);
|
|
2089
|
+
let children = this.transformTokensWithPosition(tokens);
|
|
2090
|
+
if (this.htmlTreeOptions) {
|
|
2091
|
+
children = this.processHtmlNodes(children);
|
|
2092
|
+
}
|
|
2093
|
+
return {
|
|
2094
|
+
type: "root",
|
|
2095
|
+
children
|
|
2096
|
+
};
|
|
1063
2097
|
}
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
const
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
2098
|
+
/**
|
|
2099
|
+
* 预处理 tokens
|
|
2100
|
+
*
|
|
2101
|
+
* 处理容器指令和遗留的脚注定义(从 paragraph 中提取)
|
|
2102
|
+
*/
|
|
2103
|
+
preprocessTokens(tokens) {
|
|
2104
|
+
const result = [];
|
|
2105
|
+
let i = 0;
|
|
2106
|
+
while (i < tokens.length) {
|
|
2107
|
+
const token = tokens[i];
|
|
2108
|
+
if (token.type === "paragraph") {
|
|
2109
|
+
const text = token.text;
|
|
2110
|
+
const footnoteMatch = text.match(/^\[\^([a-zA-Z0-9_-]+)\]:\s+([\s\S]*)$/);
|
|
2111
|
+
if (footnoteMatch) {
|
|
2112
|
+
const defToken = {
|
|
2113
|
+
type: "footnoteDefinition",
|
|
2114
|
+
identifier: footnoteMatch[1],
|
|
2115
|
+
text: footnoteMatch[2],
|
|
2116
|
+
tokens: new Lexer().inlineTokens(footnoteMatch[2]),
|
|
2117
|
+
raw: token.raw
|
|
2118
|
+
};
|
|
2119
|
+
result.push(defToken);
|
|
2120
|
+
i++;
|
|
2121
|
+
continue;
|
|
2122
|
+
}
|
|
2123
|
+
const containerStartMatch = text.match(/^:::(\s*)([a-zA-Z0-9_-]+)(.*?)(\n|$)/);
|
|
2124
|
+
if (containerStartMatch) {
|
|
2125
|
+
const name = containerStartMatch[2];
|
|
2126
|
+
const attrs = containerStartMatch[3].trim();
|
|
2127
|
+
let rawAccumulator = "";
|
|
2128
|
+
let j = i;
|
|
2129
|
+
let depth = 0;
|
|
2130
|
+
let foundEnd = false;
|
|
2131
|
+
let contentRaw = "";
|
|
2132
|
+
while (j < tokens.length) {
|
|
2133
|
+
const currentToken = tokens[j];
|
|
2134
|
+
rawAccumulator += currentToken.raw;
|
|
2135
|
+
const lines = rawAccumulator.split("\n");
|
|
2136
|
+
depth = 0;
|
|
2137
|
+
let startLineIndex = -1;
|
|
2138
|
+
let endLineIndex = -1;
|
|
2139
|
+
for (let k = 0; k < lines.length; k++) {
|
|
2140
|
+
const line = lines[k];
|
|
2141
|
+
if (line.match(/^:::(\s*)([a-zA-Z0-9_-]+)/)) {
|
|
2142
|
+
if (depth === 0 && startLineIndex === -1) startLineIndex = k;
|
|
2143
|
+
depth++;
|
|
2144
|
+
} else if (line.trim() === ":::") {
|
|
2145
|
+
depth--;
|
|
2146
|
+
if (depth === 0) {
|
|
2147
|
+
endLineIndex = k;
|
|
2148
|
+
foundEnd = true;
|
|
2149
|
+
break;
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
if (foundEnd) {
|
|
2154
|
+
const contentLines = lines.slice(startLineIndex + 1, endLineIndex);
|
|
2155
|
+
contentRaw = contentLines.join("\n");
|
|
2156
|
+
const remainingLines = lines.slice(endLineIndex + 1);
|
|
2157
|
+
const remainingText = remainingLines.join("\n");
|
|
2158
|
+
const containerToken = {
|
|
2159
|
+
type: "container",
|
|
2160
|
+
name,
|
|
2161
|
+
attrs,
|
|
2162
|
+
tokens: this.preprocessTokens(lexer(contentRaw)),
|
|
2163
|
+
raw: rawAccumulator
|
|
2164
|
+
};
|
|
2165
|
+
result.push(containerToken);
|
|
2166
|
+
if (remainingText.trim()) {
|
|
2167
|
+
const remainingTokens = this.preprocessTokens(lexer(remainingText));
|
|
2168
|
+
result.push(...remainingTokens);
|
|
2169
|
+
}
|
|
2170
|
+
i = j + 1;
|
|
2171
|
+
break;
|
|
2172
|
+
}
|
|
2173
|
+
j++;
|
|
2174
|
+
}
|
|
2175
|
+
if (foundEnd) continue;
|
|
1082
2176
|
}
|
|
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
2177
|
}
|
|
2178
|
+
result.push(token);
|
|
2179
|
+
i++;
|
|
1113
2180
|
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
2181
|
+
return result;
|
|
2182
|
+
}
|
|
2183
|
+
/**
|
|
2184
|
+
* 转换 tokens 为 MDAST 节点(带位置信息)
|
|
2185
|
+
*/
|
|
2186
|
+
transformTokensWithPosition(tokens) {
|
|
2187
|
+
if (!tokens) return [];
|
|
2188
|
+
const results = [];
|
|
2189
|
+
let currentOffset = 0;
|
|
2190
|
+
for (const token of tokens) {
|
|
2191
|
+
const rawLength = token.raw?.length ?? 0;
|
|
2192
|
+
const node = transformBlockToken(token, this.transformContext);
|
|
2193
|
+
if (node) {
|
|
2194
|
+
node.position = {
|
|
2195
|
+
start: { line: 0, column: 0, offset: currentOffset },
|
|
2196
|
+
end: { line: 0, column: 0, offset: currentOffset + rawLength }
|
|
2197
|
+
};
|
|
2198
|
+
results.push(node);
|
|
2199
|
+
}
|
|
2200
|
+
currentOffset += rawLength;
|
|
1121
2201
|
}
|
|
2202
|
+
return results;
|
|
1122
2203
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
2204
|
+
/**
|
|
2205
|
+
* 转换 tokens 为 MDAST 节点(不带位置信息)
|
|
2206
|
+
*/
|
|
2207
|
+
transformTokens(tokens) {
|
|
2208
|
+
if (!tokens) return [];
|
|
2209
|
+
return tokens.map((t) => transformBlockToken(t, this.transformContext)).filter(Boolean);
|
|
2210
|
+
}
|
|
2211
|
+
/**
|
|
2212
|
+
* 转换行内 tokens
|
|
2213
|
+
*/
|
|
2214
|
+
transformInline(tokens) {
|
|
2215
|
+
if (!tokens) return [];
|
|
2216
|
+
const results = [];
|
|
2217
|
+
for (const token of tokens) {
|
|
2218
|
+
const result = transformInlineToken(token, this.transformContext);
|
|
2219
|
+
if (result) {
|
|
2220
|
+
if (Array.isArray(result)) {
|
|
2221
|
+
results.push(...result);
|
|
2222
|
+
} else {
|
|
2223
|
+
results.push(result);
|
|
2224
|
+
}
|
|
1133
2225
|
}
|
|
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
2226
|
}
|
|
2227
|
+
return results;
|
|
1157
2228
|
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
2229
|
+
/**
|
|
2230
|
+
* 解析脚注内容为 AST 节点
|
|
2231
|
+
*/
|
|
2232
|
+
parseFootnoteContent(content) {
|
|
2233
|
+
if (!content.trim()) {
|
|
2234
|
+
return [];
|
|
2235
|
+
}
|
|
2236
|
+
const normalizedContent = content.split("\n").map((line, index) => {
|
|
2237
|
+
if (index === 0) return line;
|
|
2238
|
+
if (line.startsWith(" ")) return line.slice(4);
|
|
2239
|
+
if (line.startsWith(" ")) return line.slice(1);
|
|
2240
|
+
return line;
|
|
2241
|
+
}).join("\n");
|
|
2242
|
+
const contentLexer = new Lexer({ gfm: true, breaks: true });
|
|
2243
|
+
const tokens = contentLexer.lex(normalizedContent);
|
|
2244
|
+
return this.transformTokens(tokens);
|
|
1173
2245
|
}
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
2246
|
+
/**
|
|
2247
|
+
* 处理 HTML 节点
|
|
2248
|
+
*
|
|
2249
|
+
* 使用 html-extension 的 transformHtmlNodes 来处理:
|
|
2250
|
+
* - 合并被空行分割的 HTML 节点
|
|
2251
|
+
* - 将 HTML 解析为 HtmlElementNode 树结构
|
|
2252
|
+
*/
|
|
2253
|
+
processHtmlNodes(nodes) {
|
|
2254
|
+
const tempRoot = {
|
|
2255
|
+
type: "root",
|
|
2256
|
+
children: nodes
|
|
2257
|
+
};
|
|
2258
|
+
const transformed = transformHtmlNodes(tempRoot, this.htmlTreeOptions);
|
|
2259
|
+
return transformed.children;
|
|
2260
|
+
}
|
|
2261
|
+
/**
|
|
2262
|
+
* 将 AST 节点转换为 ParsedBlock
|
|
2263
|
+
*/
|
|
2264
|
+
nodesToBlocks(nodes, startOffset, rawText, status, generateBlockId) {
|
|
2265
|
+
const blocks = [];
|
|
2266
|
+
for (const node of nodes) {
|
|
2267
|
+
const relativeStart = node.position?.start?.offset ?? 0;
|
|
2268
|
+
const relativeEnd = node.position?.end?.offset ?? rawText.length;
|
|
2269
|
+
const nodeText = rawText.substring(relativeStart, relativeEnd);
|
|
2270
|
+
const absoluteStart = startOffset + relativeStart;
|
|
2271
|
+
const absoluteEnd = startOffset + relativeEnd;
|
|
2272
|
+
blocks.push({
|
|
2273
|
+
id: generateBlockId(),
|
|
2274
|
+
status,
|
|
2275
|
+
node,
|
|
2276
|
+
startOffset: absoluteStart,
|
|
2277
|
+
endOffset: absoluteEnd,
|
|
2278
|
+
rawText: nodeText
|
|
2279
|
+
});
|
|
2280
|
+
}
|
|
2281
|
+
return blocks;
|
|
2282
|
+
}
|
|
2283
|
+
};
|
|
1188
2284
|
|
|
1189
2285
|
// src/parser/IncremarkParser.ts
|
|
1190
2286
|
var IncremarkParser = class {
|
|
1191
|
-
buffer = "";
|
|
1192
2287
|
lines = [];
|
|
1193
2288
|
/** 行偏移量前缀和:lineOffsets[i] = 第i行起始位置的偏移量 */
|
|
1194
2289
|
lineOffsets = [0];
|
|
@@ -1197,223 +2292,62 @@ var IncremarkParser = class {
|
|
|
1197
2292
|
blockIdCounter = 0;
|
|
1198
2293
|
context;
|
|
1199
2294
|
options;
|
|
1200
|
-
/**
|
|
1201
|
-
|
|
1202
|
-
/**
|
|
1203
|
-
|
|
2295
|
+
/** 边界检测器 */
|
|
2296
|
+
boundaryDetector;
|
|
2297
|
+
/** AST 构建器 */
|
|
2298
|
+
astBuilder;
|
|
2299
|
+
/** Definition 管理器 */
|
|
2300
|
+
definitionManager;
|
|
2301
|
+
/** Footnote 管理器 */
|
|
2302
|
+
footnoteManager;
|
|
1204
2303
|
/** 上次 append 返回的 pending blocks,用于 getAst 复用 */
|
|
1205
2304
|
lastPendingBlocks = [];
|
|
1206
|
-
/** Definition 映射表(用于引用式图片和链接) */
|
|
1207
|
-
definitionMap = {};
|
|
1208
|
-
/** Footnote Definition 映射表 */
|
|
1209
|
-
footnoteDefinitionMap = {};
|
|
1210
|
-
/** Footnote Reference 出现顺序(按引用在文档中的顺序) */
|
|
1211
|
-
footnoteReferenceOrder = [];
|
|
1212
2305
|
constructor(options = {}) {
|
|
1213
2306
|
this.options = {
|
|
1214
2307
|
gfm: true,
|
|
1215
2308
|
...options
|
|
1216
2309
|
};
|
|
1217
2310
|
this.context = createInitialContext();
|
|
1218
|
-
|
|
1219
|
-
this.
|
|
2311
|
+
const BuilderClass = options.astBuilder || MarkedAstBuilder;
|
|
2312
|
+
this.astBuilder = new BuilderClass(this.options);
|
|
2313
|
+
this.boundaryDetector = new BoundaryDetector({ containers: this.astBuilder.containerConfig });
|
|
2314
|
+
this.definitionManager = new DefinitionManager();
|
|
2315
|
+
this.footnoteManager = new FootnoteManager();
|
|
1220
2316
|
}
|
|
1221
2317
|
generateBlockId() {
|
|
1222
2318
|
return `block-${++this.blockIdCounter}`;
|
|
1223
2319
|
}
|
|
1224
|
-
computeContainerConfig() {
|
|
1225
|
-
const containers = this.options.containers;
|
|
1226
|
-
if (!containers) return void 0;
|
|
1227
|
-
return containers === true ? {} : containers;
|
|
1228
|
-
}
|
|
1229
|
-
computeHtmlTreeConfig() {
|
|
1230
|
-
const htmlTree = this.options.htmlTree;
|
|
1231
|
-
if (!htmlTree) return void 0;
|
|
1232
|
-
return htmlTree === true ? {} : htmlTree;
|
|
1233
|
-
}
|
|
1234
2320
|
/**
|
|
1235
|
-
*
|
|
1236
|
-
* 递归处理 AST 中所有 html 类型的节点
|
|
1237
|
-
* - 块级 HTML 节点 → 转换为 paragraph 包含 text
|
|
1238
|
-
* - 内联 HTML 节点(在段落内部)→ 转换为 text 节点
|
|
2321
|
+
* 更新已完成的 blocks 中的 definitions 和 footnote definitions
|
|
1239
2322
|
*/
|
|
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
|
-
parse(text) {
|
|
1300
|
-
const extensions = [];
|
|
1301
|
-
const mdastExtensions = [];
|
|
1302
|
-
if (this.options.gfm) {
|
|
1303
|
-
extensions.push(gfm());
|
|
1304
|
-
mdastExtensions.push(...gfmFromMarkdown(), gfmFootnoteFromMarkdown());
|
|
1305
|
-
}
|
|
1306
|
-
if (this.options.math) {
|
|
1307
|
-
extensions.push(math());
|
|
1308
|
-
mdastExtensions.push(mathFromMarkdown());
|
|
1309
|
-
}
|
|
1310
|
-
if (this.containerConfig !== void 0) {
|
|
1311
|
-
extensions.push(directive());
|
|
1312
|
-
mdastExtensions.push(directiveFromMarkdown());
|
|
1313
|
-
}
|
|
1314
|
-
if (this.options.extensions) {
|
|
1315
|
-
extensions.push(...this.options.extensions);
|
|
1316
|
-
}
|
|
1317
|
-
if (this.options.mdastExtensions) {
|
|
1318
|
-
mdastExtensions.push(...this.options.mdastExtensions);
|
|
1319
|
-
}
|
|
1320
|
-
if (this.options.gfm) {
|
|
1321
|
-
extensions.push(gfmFootnoteIncremental());
|
|
1322
|
-
}
|
|
1323
|
-
extensions.push(micromarkReferenceExtension());
|
|
1324
|
-
let ast = fromMarkdown(text, { extensions, mdastExtensions });
|
|
1325
|
-
if (this.htmlTreeConfig) {
|
|
1326
|
-
ast = transformHtmlNodes(ast, this.htmlTreeConfig);
|
|
1327
|
-
} else {
|
|
1328
|
-
ast = this.convertHtmlToText(ast);
|
|
1329
|
-
}
|
|
1330
|
-
return ast;
|
|
1331
|
-
}
|
|
1332
2323
|
updateDefinitionsFromCompletedBlocks(blocks) {
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
...this.definitionMap,
|
|
1336
|
-
...this.findDefinition(block)
|
|
1337
|
-
};
|
|
1338
|
-
this.footnoteDefinitionMap = {
|
|
1339
|
-
...this.footnoteDefinitionMap,
|
|
1340
|
-
...this.findFootnoteDefinition(block)
|
|
1341
|
-
};
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
findDefinition(block) {
|
|
1345
|
-
const definitions = [];
|
|
1346
|
-
function findDefinitionRecursive(node) {
|
|
1347
|
-
if (isDefinitionNode(node)) {
|
|
1348
|
-
definitions.push(node);
|
|
1349
|
-
}
|
|
1350
|
-
if ("children" in node && Array.isArray(node.children)) {
|
|
1351
|
-
for (const child of node.children) {
|
|
1352
|
-
findDefinitionRecursive(child);
|
|
1353
|
-
}
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
findDefinitionRecursive(block.node);
|
|
1357
|
-
return definitions.reduce((acc, node) => {
|
|
1358
|
-
acc[node.identifier] = node;
|
|
1359
|
-
return acc;
|
|
1360
|
-
}, {});
|
|
1361
|
-
}
|
|
1362
|
-
findFootnoteDefinition(block) {
|
|
1363
|
-
const footnoteDefinitions = [];
|
|
1364
|
-
function findFootnoteDefinition(node) {
|
|
1365
|
-
if (isFootnoteDefinitionNode(node)) {
|
|
1366
|
-
footnoteDefinitions.push(node);
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
|
-
findFootnoteDefinition(block.node);
|
|
1370
|
-
return footnoteDefinitions.reduce((acc, node) => {
|
|
1371
|
-
acc[node.identifier] = node;
|
|
1372
|
-
return acc;
|
|
1373
|
-
}, {});
|
|
1374
|
-
}
|
|
1375
|
-
/**
|
|
1376
|
-
* 收集 AST 中的脚注引用(按出现顺序)
|
|
1377
|
-
* 用于确定脚注的显示顺序
|
|
1378
|
-
*/
|
|
1379
|
-
collectFootnoteReferences(nodes) {
|
|
1380
|
-
const visitNode = (node) => {
|
|
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);
|
|
2324
|
+
this.definitionManager.extractFromBlocks(blocks);
|
|
2325
|
+
this.footnoteManager.extractDefinitionsFromBlocks(blocks);
|
|
1393
2326
|
}
|
|
1394
2327
|
/**
|
|
1395
2328
|
* 增量更新 lines 和 lineOffsets
|
|
1396
|
-
*
|
|
2329
|
+
* 优化策略:只 split 新增的 chunk,不拼接旧字符串,避免长行性能劣化
|
|
1397
2330
|
*/
|
|
1398
|
-
updateLines() {
|
|
2331
|
+
updateLines(chunk) {
|
|
1399
2332
|
const prevLineCount = this.lines.length;
|
|
1400
2333
|
if (prevLineCount === 0) {
|
|
1401
|
-
this.lines =
|
|
2334
|
+
this.lines = chunk.split("\n");
|
|
1402
2335
|
this.lineOffsets = [0];
|
|
1403
|
-
for (let i = 0; i < this.lines.length; i++) {
|
|
2336
|
+
for (let i = 0; i < this.lines.length - 1; i++) {
|
|
1404
2337
|
this.lineOffsets.push(this.lineOffsets[i] + this.lines[i].length + 1);
|
|
1405
2338
|
}
|
|
1406
2339
|
return;
|
|
1407
2340
|
}
|
|
1408
|
-
const
|
|
1409
|
-
const
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
this.lines
|
|
1415
|
-
const
|
|
1416
|
-
this.lineOffsets.push(
|
|
2341
|
+
const chunkLines = chunk.split("\n");
|
|
2342
|
+
const lastLineIndex = prevLineCount - 1;
|
|
2343
|
+
this.lines[lastLineIndex] += chunkLines[0];
|
|
2344
|
+
for (let i = 1; i < chunkLines.length; i++) {
|
|
2345
|
+
const prevLineIndex = this.lines.length - 1;
|
|
2346
|
+
const prevLineStart = this.lineOffsets[prevLineIndex];
|
|
2347
|
+
const prevLineLength = this.lines[prevLineIndex].length;
|
|
2348
|
+
const newOneOffset = prevLineStart + prevLineLength + 1;
|
|
2349
|
+
this.lineOffsets.push(newOneOffset);
|
|
2350
|
+
this.lines.push(chunkLines[i]);
|
|
1417
2351
|
}
|
|
1418
2352
|
}
|
|
1419
2353
|
/**
|
|
@@ -1427,176 +2361,18 @@ var IncremarkParser = class {
|
|
|
1427
2361
|
* 返回稳定边界行号和该行对应的上下文(用于后续更新,避免重复计算)
|
|
1428
2362
|
*/
|
|
1429
2363
|
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;
|
|
2364
|
+
const result = this.boundaryDetector.findStableBoundary(
|
|
2365
|
+
this.lines,
|
|
2366
|
+
this.pendingStartLine,
|
|
2367
|
+
this.context
|
|
2368
|
+
);
|
|
2369
|
+
return { line: result.line, contextAtLine: result.context };
|
|
1593
2370
|
}
|
|
1594
2371
|
/**
|
|
1595
2372
|
* 追加新的 chunk 并返回增量更新
|
|
1596
2373
|
*/
|
|
1597
2374
|
append(chunk) {
|
|
1598
|
-
this.
|
|
1599
|
-
this.updateLines();
|
|
2375
|
+
this.updateLines(chunk);
|
|
1600
2376
|
const { line: stableBoundary, contextAtLine } = this.findStableBoundary();
|
|
1601
2377
|
const update = {
|
|
1602
2378
|
completed: [],
|
|
@@ -1610,11 +2386,13 @@ var IncremarkParser = class {
|
|
|
1610
2386
|
if (stableBoundary >= this.pendingStartLine && stableBoundary >= 0) {
|
|
1611
2387
|
const stableText = this.lines.slice(this.pendingStartLine, stableBoundary + 1).join("\n");
|
|
1612
2388
|
const stableOffset = this.getLineOffset(this.pendingStartLine);
|
|
1613
|
-
const ast = this.parse(stableText);
|
|
1614
|
-
const newBlocks = this.nodesToBlocks(ast.children, stableOffset, stableText, "completed");
|
|
2389
|
+
const ast = this.astBuilder.parse(stableText);
|
|
2390
|
+
const newBlocks = this.astBuilder.nodesToBlocks(ast.children, stableOffset, stableText, "completed", () => this.generateBlockId());
|
|
1615
2391
|
this.completedBlocks.push(...newBlocks);
|
|
1616
2392
|
update.completed = newBlocks;
|
|
1617
2393
|
this.updateDefinitionsFromCompletedBlocks(newBlocks);
|
|
2394
|
+
this.footnoteManager.collectReferencesFromCompletedBlocks(newBlocks);
|
|
2395
|
+
this.boundaryDetector.clearContextCache(this.pendingStartLine);
|
|
1618
2396
|
this.context = contextAtLine;
|
|
1619
2397
|
this.pendingStartLine = stableBoundary + 1;
|
|
1620
2398
|
}
|
|
@@ -1622,8 +2400,8 @@ var IncremarkParser = class {
|
|
|
1622
2400
|
const pendingText = this.lines.slice(this.pendingStartLine).join("\n");
|
|
1623
2401
|
if (pendingText.trim()) {
|
|
1624
2402
|
const pendingOffset = this.getLineOffset(this.pendingStartLine);
|
|
1625
|
-
const ast = this.parse(pendingText);
|
|
1626
|
-
update.pending = this.nodesToBlocks(ast.children, pendingOffset, pendingText, "pending");
|
|
2403
|
+
const ast = this.astBuilder.parse(pendingText);
|
|
2404
|
+
update.pending = this.astBuilder.nodesToBlocks(ast.children, pendingOffset, pendingText, "pending", () => this.generateBlockId());
|
|
1627
2405
|
}
|
|
1628
2406
|
}
|
|
1629
2407
|
this.lastPendingBlocks = update.pending;
|
|
@@ -1631,10 +2409,9 @@ var IncremarkParser = class {
|
|
|
1631
2409
|
type: "root",
|
|
1632
2410
|
children: [...this.completedBlocks.map((b) => b.node), ...update.pending.map((b) => b.node)]
|
|
1633
2411
|
};
|
|
1634
|
-
this.
|
|
2412
|
+
update.footnoteReferenceOrder = this.footnoteManager.collectReferencesFromPending(update.pending);
|
|
1635
2413
|
update.definitions = this.getDefinitionMap();
|
|
1636
2414
|
update.footnoteDefinitions = this.getFootnoteDefinitionMap();
|
|
1637
|
-
update.footnoteReferenceOrder = this.getFootnoteReferenceOrder();
|
|
1638
2415
|
this.emitChange(update.pending);
|
|
1639
2416
|
return update;
|
|
1640
2417
|
}
|
|
@@ -1646,7 +2423,7 @@ var IncremarkParser = class {
|
|
|
1646
2423
|
const state = {
|
|
1647
2424
|
completedBlocks: this.completedBlocks,
|
|
1648
2425
|
pendingBlocks,
|
|
1649
|
-
markdown: this.
|
|
2426
|
+
markdown: this.lines.join("\n"),
|
|
1650
2427
|
ast: {
|
|
1651
2428
|
type: "root",
|
|
1652
2429
|
children: [
|
|
@@ -1654,8 +2431,8 @@ var IncremarkParser = class {
|
|
|
1654
2431
|
...pendingBlocks.map((b) => b.node)
|
|
1655
2432
|
]
|
|
1656
2433
|
},
|
|
1657
|
-
definitions: { ...this.
|
|
1658
|
-
footnoteDefinitions: { ...this.
|
|
2434
|
+
definitions: { ...this.getDefinitionMap() },
|
|
2435
|
+
footnoteDefinitions: { ...this.getFootnoteDefinitionMap() }
|
|
1659
2436
|
};
|
|
1660
2437
|
this.options.onChange(state);
|
|
1661
2438
|
}
|
|
@@ -1670,24 +2447,27 @@ var IncremarkParser = class {
|
|
|
1670
2447
|
updated: [],
|
|
1671
2448
|
pending: [],
|
|
1672
2449
|
ast: { type: "root", children: [] },
|
|
1673
|
-
definitions:
|
|
1674
|
-
footnoteDefinitions:
|
|
1675
|
-
footnoteReferenceOrder:
|
|
2450
|
+
definitions: this.getDefinitionMap(),
|
|
2451
|
+
footnoteDefinitions: this.getFootnoteDefinitionMap(),
|
|
2452
|
+
footnoteReferenceOrder: this.getFootnoteReferenceOrder()
|
|
1676
2453
|
};
|
|
1677
2454
|
if (this.pendingStartLine < this.lines.length) {
|
|
1678
2455
|
const remainingText = this.lines.slice(this.pendingStartLine).join("\n");
|
|
1679
2456
|
if (remainingText.trim()) {
|
|
1680
2457
|
const remainingOffset = this.getLineOffset(this.pendingStartLine);
|
|
1681
|
-
const ast = this.parse(remainingText);
|
|
1682
|
-
const finalBlocks = this.nodesToBlocks(
|
|
2458
|
+
const ast = this.astBuilder.parse(remainingText);
|
|
2459
|
+
const finalBlocks = this.astBuilder.nodesToBlocks(
|
|
1683
2460
|
ast.children,
|
|
1684
2461
|
remainingOffset,
|
|
1685
2462
|
remainingText,
|
|
1686
|
-
"completed"
|
|
2463
|
+
"completed",
|
|
2464
|
+
() => this.generateBlockId()
|
|
1687
2465
|
);
|
|
1688
2466
|
this.completedBlocks.push(...finalBlocks);
|
|
1689
2467
|
update.completed = finalBlocks;
|
|
1690
2468
|
this.updateDefinitionsFromCompletedBlocks(finalBlocks);
|
|
2469
|
+
this.footnoteManager.collectReferencesFromCompletedBlocks(finalBlocks);
|
|
2470
|
+
this.boundaryDetector.clearContextCache(this.pendingStartLine);
|
|
1691
2471
|
}
|
|
1692
2472
|
}
|
|
1693
2473
|
this.lastPendingBlocks = [];
|
|
@@ -1696,7 +2476,6 @@ var IncremarkParser = class {
|
|
|
1696
2476
|
type: "root",
|
|
1697
2477
|
children: this.completedBlocks.map((b) => b.node)
|
|
1698
2478
|
};
|
|
1699
|
-
this.collectFootnoteReferences(update.ast.children);
|
|
1700
2479
|
update.definitions = this.getDefinitionMap();
|
|
1701
2480
|
update.footnoteDefinitions = this.getFootnoteDefinitionMap();
|
|
1702
2481
|
update.footnoteReferenceOrder = this.getFootnoteReferenceOrder();
|
|
@@ -1719,7 +2498,7 @@ var IncremarkParser = class {
|
|
|
1719
2498
|
...this.completedBlocks.map((b) => b.node),
|
|
1720
2499
|
...this.lastPendingBlocks.map((b) => b.node)
|
|
1721
2500
|
];
|
|
1722
|
-
this.
|
|
2501
|
+
this.footnoteManager.collectReferencesFromPending(this.lastPendingBlocks);
|
|
1723
2502
|
return {
|
|
1724
2503
|
type: "root",
|
|
1725
2504
|
children
|
|
@@ -1735,25 +2514,25 @@ var IncremarkParser = class {
|
|
|
1735
2514
|
* 获取当前缓冲区内容
|
|
1736
2515
|
*/
|
|
1737
2516
|
getBuffer() {
|
|
1738
|
-
return this.
|
|
2517
|
+
return this.lines.join("\n");
|
|
1739
2518
|
}
|
|
1740
2519
|
/**
|
|
1741
2520
|
* 获取 Definition 映射表(用于引用式图片和链接)
|
|
1742
2521
|
*/
|
|
1743
2522
|
getDefinitionMap() {
|
|
1744
|
-
return
|
|
2523
|
+
return this.definitionManager.getAll();
|
|
1745
2524
|
}
|
|
1746
2525
|
/**
|
|
1747
2526
|
* 获取 Footnote Definition 映射表
|
|
1748
2527
|
*/
|
|
1749
2528
|
getFootnoteDefinitionMap() {
|
|
1750
|
-
return
|
|
2529
|
+
return this.footnoteManager.getDefinitions();
|
|
1751
2530
|
}
|
|
1752
2531
|
/**
|
|
1753
2532
|
* 获取脚注引用的出现顺序
|
|
1754
2533
|
*/
|
|
1755
2534
|
getFootnoteReferenceOrder() {
|
|
1756
|
-
return
|
|
2535
|
+
return this.footnoteManager.getReferenceOrder();
|
|
1757
2536
|
}
|
|
1758
2537
|
/**
|
|
1759
2538
|
* 设置状态变化回调(用于 DevTools 等)
|
|
@@ -1769,7 +2548,6 @@ var IncremarkParser = class {
|
|
|
1769
2548
|
* 重置解析器状态
|
|
1770
2549
|
*/
|
|
1771
2550
|
reset() {
|
|
1772
|
-
this.buffer = "";
|
|
1773
2551
|
this.lines = [];
|
|
1774
2552
|
this.lineOffsets = [0];
|
|
1775
2553
|
this.completedBlocks = [];
|
|
@@ -1777,9 +2555,8 @@ var IncremarkParser = class {
|
|
|
1777
2555
|
this.blockIdCounter = 0;
|
|
1778
2556
|
this.context = createInitialContext();
|
|
1779
2557
|
this.lastPendingBlocks = [];
|
|
1780
|
-
this.
|
|
1781
|
-
this.
|
|
1782
|
-
this.footnoteReferenceOrder = [];
|
|
2558
|
+
this.definitionManager.clear();
|
|
2559
|
+
this.footnoteManager.clear();
|
|
1783
2560
|
this.emitChange([]);
|
|
1784
2561
|
}
|
|
1785
2562
|
/**
|
|
@@ -1814,10 +2591,9 @@ function countCharsInNode(n) {
|
|
|
1814
2591
|
}
|
|
1815
2592
|
return 1;
|
|
1816
2593
|
}
|
|
1817
|
-
function sliceAst(node, maxChars, accumulatedChunks
|
|
2594
|
+
function sliceAst(node, maxChars, accumulatedChunks) {
|
|
1818
2595
|
if (maxChars <= 0) return null;
|
|
1819
|
-
|
|
1820
|
-
let remaining = maxChars - skipChars;
|
|
2596
|
+
let remaining = maxChars;
|
|
1821
2597
|
let charIndex = 0;
|
|
1822
2598
|
const chunkRanges = [];
|
|
1823
2599
|
if (accumulatedChunks && accumulatedChunks.chunks.length > 0) {
|
|
@@ -1836,16 +2612,10 @@ function sliceAst(node, maxChars, accumulatedChunks, skipChars = 0) {
|
|
|
1836
2612
|
if (n.value && typeof n.value === "string") {
|
|
1837
2613
|
const nodeStart = charIndex;
|
|
1838
2614
|
const nodeEnd = charIndex + n.value.length;
|
|
1839
|
-
|
|
1840
|
-
charIndex = nodeEnd;
|
|
1841
|
-
return null;
|
|
1842
|
-
}
|
|
1843
|
-
const skipInNode = Math.max(0, skipChars - nodeStart);
|
|
1844
|
-
const take = Math.min(n.value.length - skipInNode, remaining);
|
|
2615
|
+
const take = Math.min(n.value.length, remaining);
|
|
1845
2616
|
remaining -= take;
|
|
1846
|
-
if (take === 0) return null;
|
|
1847
|
-
const slicedValue = n.value.slice(skipInNode, skipInNode + take);
|
|
1848
2617
|
charIndex = nodeEnd;
|
|
2618
|
+
const slicedValue = n.value.slice(0, take);
|
|
1849
2619
|
const result = {
|
|
1850
2620
|
...n,
|
|
1851
2621
|
value: slicedValue
|
|
@@ -1854,11 +2624,11 @@ function sliceAst(node, maxChars, accumulatedChunks, skipChars = 0) {
|
|
|
1854
2624
|
const nodeChunks = [];
|
|
1855
2625
|
let firstChunkLocalStart = take;
|
|
1856
2626
|
for (const range of chunkRanges) {
|
|
1857
|
-
const overlapStart = Math.max(range.start, nodeStart
|
|
1858
|
-
const overlapEnd = Math.min(range.end, nodeStart +
|
|
2627
|
+
const overlapStart = Math.max(range.start, nodeStart);
|
|
2628
|
+
const overlapEnd = Math.min(range.end, nodeStart + take);
|
|
1859
2629
|
if (overlapStart < overlapEnd) {
|
|
1860
|
-
const localStart = overlapStart -
|
|
1861
|
-
const localEnd = overlapEnd -
|
|
2630
|
+
const localStart = overlapStart - nodeStart;
|
|
2631
|
+
const localEnd = overlapEnd - nodeStart;
|
|
1862
2632
|
const chunkText = slicedValue.slice(localStart, localEnd);
|
|
1863
2633
|
if (chunkText.length > 0) {
|
|
1864
2634
|
if (nodeChunks.length === 0) {
|
|
@@ -1880,24 +2650,12 @@ function sliceAst(node, maxChars, accumulatedChunks, skipChars = 0) {
|
|
|
1880
2650
|
}
|
|
1881
2651
|
if (n.children && Array.isArray(n.children)) {
|
|
1882
2652
|
const newChildren = [];
|
|
1883
|
-
let childCharIndex = charIndex;
|
|
1884
2653
|
for (const child of n.children) {
|
|
1885
2654
|
if (remaining <= 0) break;
|
|
1886
|
-
const childChars = countCharsInNode(child);
|
|
1887
|
-
const childStart = childCharIndex;
|
|
1888
|
-
const childEnd = childCharIndex + childChars;
|
|
1889
|
-
if (childEnd <= skipChars) {
|
|
1890
|
-
childCharIndex = childEnd;
|
|
1891
|
-
continue;
|
|
1892
|
-
}
|
|
1893
|
-
const savedCharIndex = charIndex;
|
|
1894
|
-
charIndex = childStart;
|
|
1895
2655
|
const processed = process(child);
|
|
1896
|
-
charIndex = savedCharIndex;
|
|
1897
2656
|
if (processed) {
|
|
1898
2657
|
newChildren.push(processed);
|
|
1899
2658
|
}
|
|
1900
|
-
childCharIndex = childEnd;
|
|
1901
2659
|
}
|
|
1902
2660
|
if (newChildren.length === 0) {
|
|
1903
2661
|
return null;
|
|
@@ -1910,10 +2668,7 @@ function sliceAst(node, maxChars, accumulatedChunks, skipChars = 0) {
|
|
|
1910
2668
|
}
|
|
1911
2669
|
return process(node);
|
|
1912
2670
|
}
|
|
1913
|
-
function appendToAst(baseNode, sourceNode,
|
|
1914
|
-
if (endChars <= startChars) {
|
|
1915
|
-
return baseNode;
|
|
1916
|
-
}
|
|
2671
|
+
function appendToAst(baseNode, sourceNode, endChars, accumulatedChunks) {
|
|
1917
2672
|
const fullSlice = sliceAst(sourceNode, endChars, accumulatedChunks);
|
|
1918
2673
|
if (!fullSlice) {
|
|
1919
2674
|
return baseNode;
|
|
@@ -2438,7 +3193,6 @@ var BlockTransformer = class {
|
|
|
2438
3193
|
this.cachedDisplayNode = appendToAst(
|
|
2439
3194
|
this.cachedDisplayNode,
|
|
2440
3195
|
block.node,
|
|
2441
|
-
this.cachedProgress,
|
|
2442
3196
|
currentProgress,
|
|
2443
3197
|
this.getAccumulatedChunks()
|
|
2444
3198
|
);
|
|
@@ -2554,51 +3308,7 @@ function createPlugin(name, matcher, options = {}) {
|
|
|
2554
3308
|
...options
|
|
2555
3309
|
};
|
|
2556
3310
|
}
|
|
2557
|
-
/**
|
|
2558
|
-
* @file Micromark 扩展:支持增量解析的 Reference 语法
|
|
2559
|
-
*
|
|
2560
|
-
* @description
|
|
2561
|
-
* 在增量解析场景中,引用式图片/链接(如 `![Alt][id]`)可能在定义(`[id]: url`)之前出现。
|
|
2562
|
-
* 标准 micromark 会检查 parser.defined,如果 id 未定义就解析为文本。
|
|
2563
|
-
*
|
|
2564
|
-
* 本扩展通过覆盖 labelEnd 构造,移除 parser.defined 检查,
|
|
2565
|
-
* 使得 reference 语法总是被解析为 reference token,
|
|
2566
|
-
* 由渲染层根据实际的 definitionMap 决定如何渲染。
|
|
2567
|
-
*
|
|
2568
|
-
* @module micromark-reference-extension
|
|
2569
|
-
*
|
|
2570
|
-
* @features
|
|
2571
|
-
* - ✅ 支持所有 resource 语法(带 title 的图片/链接)
|
|
2572
|
-
* - ✅ 支持所有 reference 语法(full, collapsed, shortcut)
|
|
2573
|
-
* - ✅ 延迟验证:解析时不检查定义是否存在
|
|
2574
|
-
* - ✅ 使用官方 factory 函数,保证与 CommonMark 标准一致
|
|
2575
|
-
*
|
|
2576
|
-
* @dependencies
|
|
2577
|
-
* - micromark-factory-destination: 解析 URL(支持尖括号、括号平衡)
|
|
2578
|
-
* - micromark-factory-title: 解析 title(支持三种引号,支持多行)
|
|
2579
|
-
* - micromark-factory-label: 解析 label(支持转义、长度限制)
|
|
2580
|
-
* - micromark-factory-whitespace: 解析空白符(正确生成 lineEnding/linePrefix token)
|
|
2581
|
-
* - micromark-util-character: 字符判断工具
|
|
2582
|
-
* - micromark-util-symbol: 常量(codes, types, constants)
|
|
2583
|
-
* - micromark-util-types: TypeScript 类型定义
|
|
2584
|
-
*
|
|
2585
|
-
* @see {@link https://github.com/micromark/micromark} - micromark 官方文档
|
|
2586
|
-
* @see {@link https://spec.commonmark.org/0.30/#images} - CommonMark 图片规范
|
|
2587
|
-
* @see {@link https://spec.commonmark.org/0.30/#links} - CommonMark 链接规范
|
|
2588
|
-
*
|
|
2589
|
-
* @example
|
|
2590
|
-
* ```typescript
|
|
2591
|
-
* import { micromarkReferenceExtension } from './micromark-reference-extension'
|
|
2592
|
-
* import { fromMarkdown } from 'mdast-util-from-markdown'
|
|
2593
|
-
*
|
|
2594
|
-
* const extensions = [micromarkReferenceExtension()]
|
|
2595
|
-
* const ast = fromMarkdown(text, { extensions })
|
|
2596
|
-
* ```
|
|
2597
|
-
*
|
|
2598
|
-
* @author Incremark Team
|
|
2599
|
-
* @license MIT
|
|
2600
|
-
*/
|
|
2601
3311
|
|
|
2602
|
-
export { BlockTransformer,
|
|
3312
|
+
export { BlockTransformer, IncremarkParser, MarkedAstBuilder, allPlugins, cloneNode, codeBlockPlugin, countChars, createBlockTransformer, createIncremarkParser, createPlugin, defaultPlugins, imagePlugin, mathPlugin, mermaidPlugin, sliceAst, thematicBreakPlugin };
|
|
2603
3313
|
//# sourceMappingURL=index.js.map
|
|
2604
3314
|
//# sourceMappingURL=index.js.map
|