@incremark/core 0.2.7 → 0.3.1
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-DDP1An5M.d.ts +77 -0
- package/dist/detector/index.d.ts +1 -1
- package/dist/engines/marked/index.d.ts +29 -0
- package/dist/engines/marked/index.js +1608 -0
- package/dist/engines/marked/index.js.map +1 -0
- package/dist/engines/micromark/index.d.ts +115 -0
- package/dist/engines/micromark/index.js +1667 -0
- package/dist/engines/micromark/index.js.map +1 -0
- package/dist/{index-CfgnWMWh.d.ts → index-CWuosVAK.d.ts} +29 -4
- package/dist/index.d.ts +81 -17
- package/dist/index.js +1271 -639
- package/dist/index.js.map +1 -1
- package/dist/types-N1b99kYB.d.ts +128 -0
- package/package.json +18 -3
package/dist/index.js
CHANGED
|
@@ -1,19 +1,4 @@
|
|
|
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 { directive } from 'micromark-extension-directive';
|
|
8
|
-
import { directiveFromMarkdown } from 'mdast-util-directive';
|
|
9
|
-
import { codes, constants, types } from 'micromark-util-symbol';
|
|
10
|
-
import { markdownLineEndingOrSpace } from 'micromark-util-character';
|
|
11
|
-
import { factoryDestination } from 'micromark-factory-destination';
|
|
12
|
-
import { factoryTitle } from 'micromark-factory-title';
|
|
13
|
-
import { factoryLabel } from 'micromark-factory-label';
|
|
14
|
-
import { factoryWhitespace } from 'micromark-factory-whitespace';
|
|
15
|
-
import { gfmFootnote } from 'micromark-extension-gfm-footnote';
|
|
16
|
-
import { normalizeIdentifier } from 'micromark-util-normalize-identifier';
|
|
1
|
+
import { Lexer, lexer } from 'marked';
|
|
17
2
|
|
|
18
3
|
// src/detector/index.ts
|
|
19
4
|
var RE_FENCE_START = /^(\s*)((`{3,})|(~{3,}))/;
|
|
@@ -458,6 +443,8 @@ var EmptyLineBoundaryChecker = class {
|
|
|
458
443
|
var BoundaryDetector = class {
|
|
459
444
|
containerConfig;
|
|
460
445
|
checkers;
|
|
446
|
+
/** 缓存每一行结束时对应的 Context,避免重复计算 */
|
|
447
|
+
contextCache = /* @__PURE__ */ new Map();
|
|
461
448
|
constructor(config = {}) {
|
|
462
449
|
this.containerConfig = config.containers;
|
|
463
450
|
this.checkers = [
|
|
@@ -468,6 +455,17 @@ var BoundaryDetector = class {
|
|
|
468
455
|
new EmptyLineBoundaryChecker()
|
|
469
456
|
];
|
|
470
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
|
+
}
|
|
471
469
|
/**
|
|
472
470
|
* 查找稳定边界
|
|
473
471
|
* 返回稳定边界行号和该行对应的上下文(用于后续更新,避免重复计算)
|
|
@@ -480,13 +478,14 @@ var BoundaryDetector = class {
|
|
|
480
478
|
findStableBoundary(lines, startLine, context) {
|
|
481
479
|
let stableLine = -1;
|
|
482
480
|
let stableContext = context;
|
|
483
|
-
let tempContext = { ...context };
|
|
481
|
+
let tempContext = startLine > 0 && this.contextCache.has(startLine - 1) ? { ...this.contextCache.get(startLine - 1) } : { ...context };
|
|
484
482
|
for (let i = startLine; i < lines.length; i++) {
|
|
485
483
|
const line = lines[i];
|
|
486
484
|
const wasInFencedCode = tempContext.inFencedCode;
|
|
487
485
|
const wasInContainer = tempContext.inContainer;
|
|
488
486
|
const wasContainerDepth = tempContext.containerDepth;
|
|
489
487
|
tempContext = updateContext(line, tempContext, this.containerConfig);
|
|
488
|
+
this.contextCache.set(i, { ...tempContext });
|
|
490
489
|
if (wasInFencedCode && !tempContext.inFencedCode) {
|
|
491
490
|
if (i < lines.length - 1) {
|
|
492
491
|
stableLine = i;
|
|
@@ -638,6 +637,9 @@ var DefinitionManager = class {
|
|
|
638
637
|
// src/parser/manager/FootnoteManager.ts
|
|
639
638
|
var FootnoteManager = class {
|
|
640
639
|
definitions = {};
|
|
640
|
+
/** 已完成部分的脚注引用顺序(缓存) */
|
|
641
|
+
completedReferenceOrder = [];
|
|
642
|
+
/** 所有脚注引用顺序(包括 pending 部分) */
|
|
641
643
|
referenceOrder = [];
|
|
642
644
|
/**
|
|
643
645
|
* 从已完成的 blocks 中提取 footnote definitions
|
|
@@ -666,9 +668,52 @@ var FootnoteManager = class {
|
|
|
666
668
|
return acc;
|
|
667
669
|
}, {});
|
|
668
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
|
+
}
|
|
669
713
|
/**
|
|
670
714
|
* 收集 AST 中的脚注引用(按出现顺序)
|
|
671
715
|
*
|
|
716
|
+
* @deprecated 使用 collectReferencesFromCompletedBlocks 和 collectReferencesFromPending 代替
|
|
672
717
|
* @param nodes AST 节点列表
|
|
673
718
|
*/
|
|
674
719
|
collectReferences(nodes) {
|
|
@@ -704,6 +749,7 @@ var FootnoteManager = class {
|
|
|
704
749
|
*/
|
|
705
750
|
clear() {
|
|
706
751
|
this.definitions = {};
|
|
752
|
+
this.completedReferenceOrder = [];
|
|
707
753
|
this.referenceOrder = [];
|
|
708
754
|
}
|
|
709
755
|
};
|
|
@@ -985,11 +1031,104 @@ function isHtmlNode(node) {
|
|
|
985
1031
|
function hasChildren(node) {
|
|
986
1032
|
return "children" in node && Array.isArray(node.children);
|
|
987
1033
|
}
|
|
988
|
-
function
|
|
1034
|
+
function mergeFragmentedHtmlNodes(nodes) {
|
|
989
1035
|
const result = [];
|
|
990
1036
|
let i = 0;
|
|
991
1037
|
while (i < nodes.length) {
|
|
992
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];
|
|
993
1132
|
if (isHtmlNode(node)) {
|
|
994
1133
|
const contentType = detectHtmlContentType(node.value);
|
|
995
1134
|
if (contentType === "fragment") {
|
|
@@ -1030,8 +1169,8 @@ function processHtmlNodesInArray(nodes, options) {
|
|
|
1030
1169
|
let depth = 1;
|
|
1031
1170
|
let j = i + 1;
|
|
1032
1171
|
let foundClosing = false;
|
|
1033
|
-
while (j <
|
|
1034
|
-
const nextNode =
|
|
1172
|
+
while (j < mergedNodes.length && depth > 0) {
|
|
1173
|
+
const nextNode = mergedNodes[j];
|
|
1035
1174
|
if (isHtmlNode(nextNode)) {
|
|
1036
1175
|
const nextType = detectHtmlContentType(nextNode.value);
|
|
1037
1176
|
if (nextType === "closing") {
|
|
@@ -1094,593 +1233,1124 @@ function transformHtmlNodes(ast, options = {}) {
|
|
|
1094
1233
|
children: processHtmlNodesInArray(ast.children, options)
|
|
1095
1234
|
};
|
|
1096
1235
|
}
|
|
1097
|
-
|
|
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);
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
return extensions;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
// src/extensions/marked-extensions/explicitDefinitionExtension.ts
|
|
1249
|
+
function createExplicitDefinitionExtension() {
|
|
1098
1250
|
return {
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
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: "" };
|
|
1108
1284
|
}
|
|
1285
|
+
return void 0;
|
|
1286
|
+
},
|
|
1287
|
+
renderer() {
|
|
1288
|
+
return "";
|
|
1109
1289
|
}
|
|
1110
1290
|
};
|
|
1111
1291
|
}
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
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 "";
|
|
1349
|
+
}
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
// src/extensions/marked-extensions/mathExtension.ts
|
|
1354
|
+
function resolveOptions(options) {
|
|
1355
|
+
return {
|
|
1356
|
+
tex: options?.tex ?? false
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
function createBlockMathExtension(options) {
|
|
1360
|
+
const resolved = resolveOptions(options);
|
|
1361
|
+
return {
|
|
1362
|
+
name: "blockMath",
|
|
1363
|
+
level: "block",
|
|
1364
|
+
start(src) {
|
|
1365
|
+
const dollarMatch = src.match(/^ {0,3}\$\$/m);
|
|
1366
|
+
let bracketMatch = null;
|
|
1367
|
+
if (resolved.tex) {
|
|
1368
|
+
bracketMatch = src.match(/^ {0,3}\\\[/m);
|
|
1369
|
+
}
|
|
1370
|
+
if (dollarMatch && bracketMatch) {
|
|
1371
|
+
return Math.min(dollarMatch.index, bracketMatch.index);
|
|
1372
|
+
}
|
|
1373
|
+
return dollarMatch?.index ?? bracketMatch?.index;
|
|
1374
|
+
},
|
|
1375
|
+
tokenizer(src) {
|
|
1376
|
+
const dollarRule = /^ {0,3}\$\$([\s\S]*?)\$\$ *(?:\n+|$)/;
|
|
1377
|
+
const dollarMatch = dollarRule.exec(src);
|
|
1378
|
+
if (dollarMatch) {
|
|
1379
|
+
return {
|
|
1380
|
+
type: "blockMath",
|
|
1381
|
+
raw: dollarMatch[0],
|
|
1382
|
+
text: dollarMatch[1].trim()
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
if (resolved.tex) {
|
|
1386
|
+
const bracketRule = /^ {0,3}\\\[([\s\S]*?)\\\] *(?:\n+|$)/;
|
|
1387
|
+
const bracketMatch = bracketRule.exec(src);
|
|
1388
|
+
if (bracketMatch) {
|
|
1389
|
+
return {
|
|
1390
|
+
type: "blockMath",
|
|
1391
|
+
raw: bracketMatch[0],
|
|
1392
|
+
text: bracketMatch[1].trim()
|
|
1393
|
+
};
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
return void 0;
|
|
1397
|
+
},
|
|
1398
|
+
renderer() {
|
|
1399
|
+
return "";
|
|
1400
|
+
}
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
function createInlineMathExtension(options) {
|
|
1404
|
+
const resolved = resolveOptions(options);
|
|
1405
|
+
return {
|
|
1406
|
+
name: "inlineMath",
|
|
1407
|
+
level: "inline",
|
|
1408
|
+
start(src) {
|
|
1409
|
+
const dollarIndex = src.indexOf("$");
|
|
1410
|
+
const validDollarIndex = dollarIndex !== -1 && src[dollarIndex + 1] !== "$" ? dollarIndex : -1;
|
|
1411
|
+
let parenIndex = -1;
|
|
1412
|
+
if (resolved.tex) {
|
|
1413
|
+
parenIndex = src.indexOf("\\(");
|
|
1142
1414
|
}
|
|
1143
|
-
if (
|
|
1144
|
-
|
|
1415
|
+
if (validDollarIndex !== -1 && parenIndex !== -1) {
|
|
1416
|
+
return Math.min(validDollarIndex, parenIndex);
|
|
1145
1417
|
}
|
|
1146
|
-
|
|
1147
|
-
if (
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1418
|
+
if (validDollarIndex !== -1) return validDollarIndex;
|
|
1419
|
+
if (parenIndex !== -1) return parenIndex;
|
|
1420
|
+
return void 0;
|
|
1421
|
+
},
|
|
1422
|
+
tokenizer(src) {
|
|
1423
|
+
const dollarRule = /^\$(?!\$)((?:\\.|[^\\\n$])+?)\$(?!\d)/;
|
|
1424
|
+
const dollarMatch = dollarRule.exec(src);
|
|
1425
|
+
if (dollarMatch) {
|
|
1426
|
+
return {
|
|
1427
|
+
type: "inlineMath",
|
|
1428
|
+
raw: dollarMatch[0],
|
|
1429
|
+
text: dollarMatch[1].trim()
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
if (resolved.tex) {
|
|
1433
|
+
const parenRule = /^\\\(([\s\S]*?)\\\)/;
|
|
1434
|
+
const parenMatch = parenRule.exec(src);
|
|
1435
|
+
if (parenMatch) {
|
|
1436
|
+
return {
|
|
1437
|
+
type: "inlineMath",
|
|
1438
|
+
raw: parenMatch[0],
|
|
1439
|
+
text: parenMatch[1].trim()
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
return void 0;
|
|
1444
|
+
},
|
|
1445
|
+
renderer() {
|
|
1446
|
+
return "";
|
|
1447
|
+
}
|
|
1448
|
+
};
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
// src/extensions/marked-extensions/footnoteDefinitionExtension.ts
|
|
1452
|
+
function createFootnoteDefinitionExtension() {
|
|
1453
|
+
return {
|
|
1454
|
+
name: "footnoteDefinitionBlock",
|
|
1455
|
+
level: "block",
|
|
1456
|
+
start(src) {
|
|
1457
|
+
const match = src.match(/^ {0,3}\[\^[^\]]+\]:/m);
|
|
1458
|
+
return match?.index;
|
|
1459
|
+
},
|
|
1460
|
+
tokenizer(src) {
|
|
1461
|
+
const firstLineRule = /^ {0,3}\[\^([a-zA-Z0-9_-]+)\]:\s*(.*)/;
|
|
1462
|
+
const firstLineMatch = firstLineRule.exec(src);
|
|
1463
|
+
if (!firstLineMatch) return void 0;
|
|
1464
|
+
const identifier = firstLineMatch[1];
|
|
1465
|
+
let content = firstLineMatch[2];
|
|
1466
|
+
let raw = firstLineMatch[0];
|
|
1467
|
+
const remaining = src.slice(raw.length);
|
|
1468
|
+
const lines = remaining.split("\n");
|
|
1469
|
+
let lineIndex = 0;
|
|
1470
|
+
if (lines[0] === "" && remaining.startsWith("\n")) {
|
|
1471
|
+
lineIndex = 1;
|
|
1472
|
+
raw += "\n";
|
|
1473
|
+
content += "\n";
|
|
1474
|
+
}
|
|
1475
|
+
while (lineIndex < lines.length) {
|
|
1476
|
+
const line = lines[lineIndex];
|
|
1477
|
+
if (line.trim() === "") {
|
|
1478
|
+
let hasIndentedLineAfter = false;
|
|
1479
|
+
for (let j = lineIndex + 1; j < lines.length; j++) {
|
|
1480
|
+
const nextLine = lines[j];
|
|
1481
|
+
if (nextLine.trim() === "") continue;
|
|
1482
|
+
if (nextLine.match(/^( |\t)/)) {
|
|
1483
|
+
hasIndentedLineAfter = true;
|
|
1484
|
+
}
|
|
1485
|
+
break;
|
|
1486
|
+
}
|
|
1487
|
+
if (hasIndentedLineAfter) {
|
|
1488
|
+
raw += line + (lineIndex < lines.length - 1 ? "\n" : "");
|
|
1489
|
+
content += "\n" + line;
|
|
1490
|
+
lineIndex++;
|
|
1491
|
+
continue;
|
|
1492
|
+
} else {
|
|
1493
|
+
break;
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
if (line.match(/^( |\t)/)) {
|
|
1497
|
+
raw += line + (lineIndex < lines.length - 1 ? "\n" : "");
|
|
1498
|
+
content += "\n" + line;
|
|
1499
|
+
lineIndex++;
|
|
1500
|
+
continue;
|
|
1501
|
+
}
|
|
1502
|
+
if (line.match(/^ {0,3}\[\^[^\]]+\]:/)) {
|
|
1151
1503
|
break;
|
|
1152
1504
|
}
|
|
1505
|
+
break;
|
|
1506
|
+
}
|
|
1507
|
+
const trimmedContent = content.replace(/\n+$/, "");
|
|
1508
|
+
return {
|
|
1509
|
+
type: "footnoteDefinitionBlock",
|
|
1510
|
+
raw,
|
|
1511
|
+
identifier,
|
|
1512
|
+
content: trimmedContent
|
|
1513
|
+
};
|
|
1514
|
+
},
|
|
1515
|
+
renderer() {
|
|
1516
|
+
return "";
|
|
1517
|
+
}
|
|
1518
|
+
};
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
// src/extensions/marked-extensions/inlineHtmlExtension.ts
|
|
1522
|
+
var SELF_CLOSING_TAGS = /* @__PURE__ */ new Set([
|
|
1523
|
+
"area",
|
|
1524
|
+
"base",
|
|
1525
|
+
"br",
|
|
1526
|
+
"col",
|
|
1527
|
+
"embed",
|
|
1528
|
+
"hr",
|
|
1529
|
+
"img",
|
|
1530
|
+
"input",
|
|
1531
|
+
"link",
|
|
1532
|
+
"meta",
|
|
1533
|
+
"param",
|
|
1534
|
+
"source",
|
|
1535
|
+
"track",
|
|
1536
|
+
"wbr"
|
|
1537
|
+
]);
|
|
1538
|
+
function createInlineHtmlExtension() {
|
|
1539
|
+
return {
|
|
1540
|
+
name: "inlineHtml",
|
|
1541
|
+
level: "inline",
|
|
1542
|
+
start(src) {
|
|
1543
|
+
const index = src.indexOf("<");
|
|
1544
|
+
if (index === -1) return void 0;
|
|
1545
|
+
const afterLt = src.slice(index + 1);
|
|
1546
|
+
if (!/^[a-zA-Z\/]/.test(afterLt)) return void 0;
|
|
1547
|
+
return index;
|
|
1548
|
+
},
|
|
1549
|
+
tokenizer(src) {
|
|
1550
|
+
const completeTagMatch = matchCompleteHtmlElement(src);
|
|
1551
|
+
if (completeTagMatch) {
|
|
1552
|
+
return {
|
|
1553
|
+
type: "inlineHtml",
|
|
1554
|
+
raw: completeTagMatch,
|
|
1555
|
+
text: completeTagMatch
|
|
1556
|
+
};
|
|
1153
1557
|
}
|
|
1154
|
-
|
|
1155
|
-
|
|
1558
|
+
const selfClosingMatch = matchSelfClosingTag(src);
|
|
1559
|
+
if (selfClosingMatch) {
|
|
1560
|
+
return {
|
|
1561
|
+
type: "inlineHtml",
|
|
1562
|
+
raw: selfClosingMatch,
|
|
1563
|
+
text: selfClosingMatch
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
return void 0;
|
|
1567
|
+
},
|
|
1568
|
+
renderer() {
|
|
1569
|
+
return "";
|
|
1156
1570
|
}
|
|
1571
|
+
};
|
|
1572
|
+
}
|
|
1573
|
+
function matchCompleteHtmlElement(src) {
|
|
1574
|
+
const openTagMatch = /^<([a-zA-Z][a-zA-Z0-9]*)((?:\s+[a-zA-Z_:][a-zA-Z0-9_.:-]*(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?)*)\s*>/.exec(src);
|
|
1575
|
+
if (!openTagMatch) return null;
|
|
1576
|
+
const tagName = openTagMatch[1].toLowerCase();
|
|
1577
|
+
const openTag = openTagMatch[0];
|
|
1578
|
+
if (SELF_CLOSING_TAGS.has(tagName)) {
|
|
1579
|
+
return openTag;
|
|
1580
|
+
}
|
|
1581
|
+
const afterOpenTag = src.slice(openTag.length);
|
|
1582
|
+
let depth = 1;
|
|
1583
|
+
let pos = 0;
|
|
1584
|
+
const openPattern = new RegExp(`<${tagName}(?:\\s[^>]*)?>`, "gi");
|
|
1585
|
+
const closePattern = new RegExp(`</${tagName}>`, "gi");
|
|
1586
|
+
while (depth > 0 && pos < afterOpenTag.length) {
|
|
1587
|
+
openPattern.lastIndex = pos;
|
|
1588
|
+
closePattern.lastIndex = pos;
|
|
1589
|
+
const nextOpen = openPattern.exec(afterOpenTag);
|
|
1590
|
+
const nextClose = closePattern.exec(afterOpenTag);
|
|
1591
|
+
if (!nextClose) {
|
|
1592
|
+
return null;
|
|
1593
|
+
}
|
|
1594
|
+
if (nextOpen && nextOpen.index < nextClose.index) {
|
|
1595
|
+
depth++;
|
|
1596
|
+
pos = nextOpen.index + nextOpen[0].length;
|
|
1597
|
+
} else {
|
|
1598
|
+
depth--;
|
|
1599
|
+
pos = nextClose.index + nextClose[0].length;
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
if (depth === 0) {
|
|
1603
|
+
return src.slice(0, openTag.length + pos);
|
|
1604
|
+
}
|
|
1605
|
+
return null;
|
|
1606
|
+
}
|
|
1607
|
+
function matchSelfClosingTag(src) {
|
|
1608
|
+
const explicitSelfClosing = /^<([a-zA-Z][a-zA-Z0-9]*)((?:\s+[a-zA-Z_:][a-zA-Z0-9_.:-]*(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?)*)\s*\/>/.exec(src);
|
|
1609
|
+
if (explicitSelfClosing) {
|
|
1610
|
+
return explicitSelfClosing[0];
|
|
1157
1611
|
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1612
|
+
const implicitSelfClosing = /^<([a-zA-Z][a-zA-Z0-9]*)((?:\s+[a-zA-Z_:][a-zA-Z0-9_.:-]*(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?)*)\s*>/.exec(src);
|
|
1613
|
+
if (implicitSelfClosing && SELF_CLOSING_TAGS.has(implicitSelfClosing[1].toLowerCase())) {
|
|
1614
|
+
return implicitSelfClosing[0];
|
|
1160
1615
|
}
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1616
|
+
return null;
|
|
1617
|
+
}
|
|
1618
|
+
function transformBlockMath(token) {
|
|
1619
|
+
return {
|
|
1620
|
+
type: "math",
|
|
1621
|
+
value: token.text,
|
|
1622
|
+
meta: null
|
|
1165
1623
|
};
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1624
|
+
}
|
|
1625
|
+
function transformFootnoteDefinitionBlock(token, ctx) {
|
|
1626
|
+
const children = ctx.parseFootnoteContent(token.content);
|
|
1627
|
+
return {
|
|
1628
|
+
type: "footnoteDefinition",
|
|
1629
|
+
identifier: token.identifier,
|
|
1630
|
+
label: token.identifier,
|
|
1631
|
+
children
|
|
1170
1632
|
};
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1633
|
+
}
|
|
1634
|
+
function transformExplicitDefinition(token) {
|
|
1635
|
+
if (!token.identifier || !token.url) return null;
|
|
1636
|
+
return {
|
|
1637
|
+
type: "definition",
|
|
1638
|
+
identifier: token.identifier,
|
|
1639
|
+
label: token.identifier,
|
|
1640
|
+
url: token.url,
|
|
1641
|
+
title: token.title ?? null
|
|
1175
1642
|
};
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
events[close - 2],
|
|
1186
|
-
events[close - 1],
|
|
1187
|
-
["exit", label, context]
|
|
1188
|
-
);
|
|
1189
|
-
media.push(...events.slice(close + 1));
|
|
1190
|
-
media.push(["exit", group, context]);
|
|
1191
|
-
events.splice(open, events.length - open, ...media);
|
|
1192
|
-
return events;
|
|
1193
|
-
}
|
|
1194
|
-
function tokenizeLabelEnd(effects, ok, nok) {
|
|
1195
|
-
const self = this;
|
|
1196
|
-
let index = self.events.length;
|
|
1197
|
-
let labelStart;
|
|
1198
|
-
while (index--) {
|
|
1199
|
-
if ((self.events[index][1].type === types.labelImage || self.events[index][1].type === types.labelLink) && !self.events[index][1]._balanced) {
|
|
1200
|
-
labelStart = self.events[index][1];
|
|
1201
|
-
break;
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
return start;
|
|
1205
|
-
function start(code) {
|
|
1206
|
-
if (!labelStart) {
|
|
1207
|
-
return nok(code);
|
|
1208
|
-
}
|
|
1209
|
-
if (labelStart._inactive) {
|
|
1210
|
-
return labelEndNok(code);
|
|
1211
|
-
}
|
|
1212
|
-
if (labelStart.type === types.labelLink) {
|
|
1213
|
-
const labelText = self.sliceSerialize({ start: labelStart.end, end: self.now() });
|
|
1214
|
-
if (labelText.startsWith("^")) {
|
|
1215
|
-
return nok(code);
|
|
1216
|
-
}
|
|
1217
|
-
}
|
|
1218
|
-
effects.enter(types.labelEnd);
|
|
1219
|
-
effects.enter(types.labelMarker);
|
|
1220
|
-
effects.consume(code);
|
|
1221
|
-
effects.exit(types.labelMarker);
|
|
1222
|
-
effects.exit(types.labelEnd);
|
|
1223
|
-
return after;
|
|
1224
|
-
}
|
|
1225
|
-
function after(code) {
|
|
1226
|
-
if (code === codes.leftParenthesis) {
|
|
1227
|
-
return effects.attempt(
|
|
1228
|
-
{
|
|
1229
|
-
tokenize: tokenizeResource,
|
|
1230
|
-
partial: false
|
|
1231
|
-
},
|
|
1232
|
-
labelEndOk,
|
|
1233
|
-
labelEndNok
|
|
1234
|
-
// 修复:resource 解析失败时返回 nok
|
|
1235
|
-
)(code);
|
|
1236
|
-
}
|
|
1237
|
-
if (code === codes.leftSquareBracket) {
|
|
1238
|
-
return effects.attempt(
|
|
1643
|
+
}
|
|
1644
|
+
function transformDef(token) {
|
|
1645
|
+
if (token.tag.startsWith("^")) {
|
|
1646
|
+
const footnoteId = token.tag.slice(1);
|
|
1647
|
+
return {
|
|
1648
|
+
type: "footnoteDefinition",
|
|
1649
|
+
identifier: footnoteId,
|
|
1650
|
+
label: footnoteId,
|
|
1651
|
+
children: [
|
|
1239
1652
|
{
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
// 修改:即使不是 full reference,也尝试 collapsed
|
|
1246
|
-
)(code);
|
|
1247
|
-
}
|
|
1248
|
-
return labelEndOk(code);
|
|
1653
|
+
type: "paragraph",
|
|
1654
|
+
children: [{ type: "text", value: token.href }]
|
|
1655
|
+
}
|
|
1656
|
+
]
|
|
1657
|
+
};
|
|
1249
1658
|
}
|
|
1250
|
-
function referenceNotFull(code) {
|
|
1251
|
-
return effects.attempt(
|
|
1252
|
-
{
|
|
1253
|
-
tokenize: tokenizeReferenceCollapsed,
|
|
1254
|
-
partial: false
|
|
1255
|
-
},
|
|
1256
|
-
labelEndOk,
|
|
1257
|
-
labelEndOk
|
|
1258
|
-
// 修改:即使失败也返回 ok
|
|
1259
|
-
)(code);
|
|
1260
|
-
}
|
|
1261
|
-
function labelEndOk(code) {
|
|
1262
|
-
return ok(code);
|
|
1263
|
-
}
|
|
1264
|
-
function labelEndNok(code) {
|
|
1265
|
-
labelStart._balanced = true;
|
|
1266
|
-
return nok(code);
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
function tokenizeResource(effects, ok, nok) {
|
|
1270
|
-
return resourceStart;
|
|
1271
|
-
function resourceStart(code) {
|
|
1272
|
-
if (code !== codes.leftParenthesis) {
|
|
1273
|
-
return nok(code);
|
|
1274
|
-
}
|
|
1275
|
-
effects.enter(types.resource);
|
|
1276
|
-
effects.enter(types.resourceMarker);
|
|
1277
|
-
effects.consume(code);
|
|
1278
|
-
effects.exit(types.resourceMarker);
|
|
1279
|
-
return resourceBefore;
|
|
1280
|
-
}
|
|
1281
|
-
function resourceBefore(code) {
|
|
1282
|
-
return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, resourceOpen)(code) : resourceOpen(code);
|
|
1283
|
-
}
|
|
1284
|
-
function resourceOpen(code) {
|
|
1285
|
-
if (code === codes.rightParenthesis) {
|
|
1286
|
-
return resourceEnd(code);
|
|
1287
|
-
}
|
|
1288
|
-
return factoryDestination(
|
|
1289
|
-
effects,
|
|
1290
|
-
resourceDestinationAfter,
|
|
1291
|
-
resourceDestinationMissing,
|
|
1292
|
-
types.resourceDestination,
|
|
1293
|
-
types.resourceDestinationLiteral,
|
|
1294
|
-
types.resourceDestinationLiteralMarker,
|
|
1295
|
-
types.resourceDestinationRaw,
|
|
1296
|
-
types.resourceDestinationString,
|
|
1297
|
-
constants.linkResourceDestinationBalanceMax
|
|
1298
|
-
)(code);
|
|
1299
|
-
}
|
|
1300
|
-
function resourceDestinationAfter(code) {
|
|
1301
|
-
return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, resourceBetween)(code) : resourceEnd(code);
|
|
1302
|
-
}
|
|
1303
|
-
function resourceDestinationMissing(code) {
|
|
1304
|
-
return nok(code);
|
|
1305
|
-
}
|
|
1306
|
-
function resourceBetween(code) {
|
|
1307
|
-
if (code === codes.quotationMark || code === codes.apostrophe || code === codes.leftParenthesis) {
|
|
1308
|
-
return factoryTitle(
|
|
1309
|
-
effects,
|
|
1310
|
-
resourceTitleAfter,
|
|
1311
|
-
nok,
|
|
1312
|
-
types.resourceTitle,
|
|
1313
|
-
types.resourceTitleMarker,
|
|
1314
|
-
types.resourceTitleString
|
|
1315
|
-
)(code);
|
|
1316
|
-
}
|
|
1317
|
-
return resourceEnd(code);
|
|
1318
|
-
}
|
|
1319
|
-
function resourceTitleAfter(code) {
|
|
1320
|
-
return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, resourceEnd)(code) : resourceEnd(code);
|
|
1321
|
-
}
|
|
1322
|
-
function resourceEnd(code) {
|
|
1323
|
-
if (code === codes.rightParenthesis) {
|
|
1324
|
-
effects.enter(types.resourceMarker);
|
|
1325
|
-
effects.consume(code);
|
|
1326
|
-
effects.exit(types.resourceMarker);
|
|
1327
|
-
effects.exit(types.resource);
|
|
1328
|
-
return ok;
|
|
1329
|
-
}
|
|
1330
|
-
return nok(code);
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1333
|
-
function tokenizeReferenceFull(effects, ok, nok) {
|
|
1334
|
-
const self = this;
|
|
1335
|
-
return referenceFull;
|
|
1336
|
-
function referenceFull(code) {
|
|
1337
|
-
if (code !== codes.leftSquareBracket) {
|
|
1338
|
-
return nok(code);
|
|
1339
|
-
}
|
|
1340
|
-
return factoryLabel.call(
|
|
1341
|
-
self,
|
|
1342
|
-
effects,
|
|
1343
|
-
referenceFullAfter,
|
|
1344
|
-
referenceFullMissing,
|
|
1345
|
-
types.reference,
|
|
1346
|
-
types.referenceMarker,
|
|
1347
|
-
types.referenceString
|
|
1348
|
-
)(code);
|
|
1349
|
-
}
|
|
1350
|
-
function referenceFullAfter(code) {
|
|
1351
|
-
return ok(code);
|
|
1352
|
-
}
|
|
1353
|
-
function referenceFullMissing(code) {
|
|
1354
|
-
return nok(code);
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
function tokenizeReferenceCollapsed(effects, ok, nok) {
|
|
1358
|
-
return referenceCollapsedStart;
|
|
1359
|
-
function referenceCollapsedStart(code) {
|
|
1360
|
-
if (code !== codes.leftSquareBracket) {
|
|
1361
|
-
return nok(code);
|
|
1362
|
-
}
|
|
1363
|
-
effects.enter(types.reference);
|
|
1364
|
-
effects.enter(types.referenceMarker);
|
|
1365
|
-
effects.consume(code);
|
|
1366
|
-
effects.exit(types.referenceMarker);
|
|
1367
|
-
return referenceCollapsedOpen;
|
|
1368
|
-
}
|
|
1369
|
-
function referenceCollapsedOpen(code) {
|
|
1370
|
-
if (code === codes.rightSquareBracket) {
|
|
1371
|
-
effects.enter(types.referenceMarker);
|
|
1372
|
-
effects.consume(code);
|
|
1373
|
-
effects.exit(types.referenceMarker);
|
|
1374
|
-
effects.exit(types.reference);
|
|
1375
|
-
return ok;
|
|
1376
|
-
}
|
|
1377
|
-
return nok(code);
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
function gfmFootnoteIncremental() {
|
|
1381
|
-
const original = gfmFootnote();
|
|
1382
1659
|
return {
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1660
|
+
type: "definition",
|
|
1661
|
+
identifier: token.tag,
|
|
1662
|
+
label: token.tag,
|
|
1663
|
+
url: token.href,
|
|
1664
|
+
title: token.title ?? null
|
|
1665
|
+
};
|
|
1666
|
+
}
|
|
1667
|
+
function transformContainer(token, ctx) {
|
|
1668
|
+
const attributes = {};
|
|
1669
|
+
const attrRegex = /([a-zA-Z0-9_-]+)=?("([^"]*)"|'([^']*)'|([^ ]*))?/g;
|
|
1670
|
+
let match;
|
|
1671
|
+
while ((match = attrRegex.exec(token.attrs)) !== null) {
|
|
1672
|
+
attributes[match[1]] = match[3] || match[4] || match[5] || "";
|
|
1673
|
+
}
|
|
1674
|
+
const children = ctx.transformTokensWithPosition(token.tokens);
|
|
1675
|
+
return {
|
|
1676
|
+
type: "containerDirective",
|
|
1677
|
+
name: token.name,
|
|
1678
|
+
attributes,
|
|
1679
|
+
children
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
function transformFootnoteDefToken(token, ctx) {
|
|
1683
|
+
return {
|
|
1684
|
+
type: "footnoteDefinition",
|
|
1685
|
+
identifier: token.identifier,
|
|
1686
|
+
label: token.identifier,
|
|
1687
|
+
children: [
|
|
1688
|
+
{
|
|
1689
|
+
type: "paragraph",
|
|
1690
|
+
children: ctx.transformInline(token.tokens)
|
|
1395
1691
|
}
|
|
1396
|
-
|
|
1692
|
+
]
|
|
1397
1693
|
};
|
|
1398
1694
|
}
|
|
1399
|
-
function
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
}
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
}
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
}
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1695
|
+
function transformHeading(token, ctx) {
|
|
1696
|
+
return {
|
|
1697
|
+
type: "heading",
|
|
1698
|
+
depth: token.depth,
|
|
1699
|
+
children: ctx.transformInline(token.tokens)
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
function transformParagraph(token, ctx) {
|
|
1703
|
+
return {
|
|
1704
|
+
type: "paragraph",
|
|
1705
|
+
children: ctx.transformInline(token.tokens)
|
|
1706
|
+
};
|
|
1707
|
+
}
|
|
1708
|
+
function transformCode(token) {
|
|
1709
|
+
return {
|
|
1710
|
+
type: "code",
|
|
1711
|
+
lang: token.lang || null,
|
|
1712
|
+
meta: null,
|
|
1713
|
+
// 对齐 micromark 输出
|
|
1714
|
+
value: token.text
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1717
|
+
function transformBlockquote(token, ctx) {
|
|
1718
|
+
const children = ctx.transformTokens(token.tokens);
|
|
1719
|
+
return {
|
|
1720
|
+
type: "blockquote",
|
|
1721
|
+
children
|
|
1722
|
+
};
|
|
1723
|
+
}
|
|
1724
|
+
function transformList(token, ctx) {
|
|
1725
|
+
const children = token.items.map((item) => ({
|
|
1726
|
+
type: "listItem",
|
|
1727
|
+
spread: item.loose,
|
|
1728
|
+
checked: item.checked ?? null,
|
|
1729
|
+
// 对齐 micromark 输出(GFM 任务列表)
|
|
1730
|
+
children: ctx.transformTokens(item.tokens)
|
|
1731
|
+
}));
|
|
1732
|
+
return {
|
|
1733
|
+
type: "list",
|
|
1734
|
+
ordered: token.ordered,
|
|
1735
|
+
start: token.ordered ? token.start || 1 : null,
|
|
1736
|
+
// 对齐 micromark:有序列表有 start,无序列表为 null
|
|
1737
|
+
spread: token.loose,
|
|
1738
|
+
children
|
|
1739
|
+
};
|
|
1740
|
+
}
|
|
1741
|
+
function transformTable(token, ctx) {
|
|
1742
|
+
const headerCells = token.header.map((cell) => ({
|
|
1743
|
+
type: "tableCell",
|
|
1744
|
+
children: ctx.transformInline(cell.tokens)
|
|
1745
|
+
}));
|
|
1746
|
+
const bodyRows = token.rows.map((row) => ({
|
|
1747
|
+
type: "tableRow",
|
|
1748
|
+
children: row.map((cell) => ({
|
|
1749
|
+
type: "tableCell",
|
|
1750
|
+
children: ctx.transformInline(cell.tokens)
|
|
1751
|
+
}))
|
|
1752
|
+
}));
|
|
1753
|
+
return {
|
|
1754
|
+
type: "table",
|
|
1755
|
+
align: token.align,
|
|
1756
|
+
children: [{ type: "tableRow", children: headerCells }, ...bodyRows]
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1759
|
+
function transformHr() {
|
|
1760
|
+
return { type: "thematicBreak" };
|
|
1761
|
+
}
|
|
1762
|
+
function transformHtml(token) {
|
|
1763
|
+
return {
|
|
1764
|
+
type: "html",
|
|
1765
|
+
value: token.text
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
function transformTextBlock(token, ctx) {
|
|
1769
|
+
if (token.tokens) {
|
|
1770
|
+
return {
|
|
1771
|
+
type: "paragraph",
|
|
1772
|
+
children: ctx.transformInline(token.tokens)
|
|
1773
|
+
};
|
|
1774
|
+
}
|
|
1775
|
+
return {
|
|
1776
|
+
type: "paragraph",
|
|
1777
|
+
children: [{ type: "text", value: token.text }]
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1780
|
+
function transformInlineMath(token) {
|
|
1781
|
+
return {
|
|
1782
|
+
type: "inlineMath",
|
|
1783
|
+
value: token.text
|
|
1784
|
+
};
|
|
1785
|
+
}
|
|
1786
|
+
function transformOptimisticReference(token, ctx) {
|
|
1787
|
+
if (token.isImage) {
|
|
1788
|
+
return {
|
|
1789
|
+
type: "imageReference",
|
|
1790
|
+
identifier: token.identifier,
|
|
1791
|
+
label: token.label,
|
|
1792
|
+
referenceType: token.referenceType,
|
|
1793
|
+
alt: token.text
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
const labelChildren = ctx.transformInline(new Lexer().inlineTokens(token.text));
|
|
1797
|
+
return {
|
|
1798
|
+
type: "linkReference",
|
|
1799
|
+
identifier: token.identifier,
|
|
1800
|
+
label: token.label,
|
|
1801
|
+
referenceType: token.referenceType,
|
|
1802
|
+
children: labelChildren.length ? labelChildren : [{ type: "text", value: token.text }]
|
|
1803
|
+
};
|
|
1804
|
+
}
|
|
1805
|
+
function transformLink(token, ctx) {
|
|
1806
|
+
if (token.text.startsWith("^") && token.text.length > 1) {
|
|
1807
|
+
const footnoteId = token.text.slice(1);
|
|
1808
|
+
return {
|
|
1809
|
+
type: "footnoteReference",
|
|
1810
|
+
identifier: footnoteId,
|
|
1811
|
+
label: footnoteId
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1814
|
+
return {
|
|
1815
|
+
type: "link",
|
|
1816
|
+
url: token.href,
|
|
1817
|
+
title: token.title || null,
|
|
1818
|
+
// 对齐 micromark 输出
|
|
1819
|
+
children: ctx.transformInline(token.tokens)
|
|
1820
|
+
};
|
|
1821
|
+
}
|
|
1822
|
+
function transformImage(token) {
|
|
1823
|
+
return {
|
|
1824
|
+
type: "image",
|
|
1825
|
+
url: token.href,
|
|
1826
|
+
title: token.title || null,
|
|
1827
|
+
// 对齐 micromark 输出
|
|
1828
|
+
alt: token.text
|
|
1829
|
+
};
|
|
1830
|
+
}
|
|
1831
|
+
function transformText(token) {
|
|
1832
|
+
const results = [];
|
|
1833
|
+
const text = token.text;
|
|
1834
|
+
const footnoteRegex = /\[\^([a-zA-Z0-9_-]+)\]/g;
|
|
1835
|
+
let lastIndex = 0;
|
|
1836
|
+
let match;
|
|
1837
|
+
while ((match = footnoteRegex.exec(text)) !== null) {
|
|
1838
|
+
if (match.index > lastIndex) {
|
|
1839
|
+
results.push({
|
|
1840
|
+
type: "text",
|
|
1841
|
+
value: text.substring(lastIndex, match.index)
|
|
1842
|
+
});
|
|
1489
1843
|
}
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1844
|
+
results.push({
|
|
1845
|
+
type: "footnoteReference",
|
|
1846
|
+
identifier: match[1],
|
|
1847
|
+
label: match[1]
|
|
1848
|
+
});
|
|
1849
|
+
lastIndex = match.index + match[0].length;
|
|
1850
|
+
}
|
|
1851
|
+
if (lastIndex < text.length) {
|
|
1852
|
+
results.push({
|
|
1853
|
+
type: "text",
|
|
1854
|
+
value: text.substring(lastIndex)
|
|
1855
|
+
});
|
|
1494
1856
|
}
|
|
1857
|
+
return results;
|
|
1495
1858
|
}
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
"tableCell",
|
|
1502
|
-
"delete",
|
|
1503
|
-
"emphasis",
|
|
1504
|
-
"strong",
|
|
1505
|
-
"link",
|
|
1506
|
-
"linkReference"
|
|
1507
|
-
];
|
|
1508
|
-
function isInlineContainer(node) {
|
|
1509
|
-
return INLINE_CONTAINER_TYPES.includes(node.type);
|
|
1859
|
+
function transformStrong(token, ctx) {
|
|
1860
|
+
return {
|
|
1861
|
+
type: "strong",
|
|
1862
|
+
children: ctx.transformInline(token.tokens)
|
|
1863
|
+
};
|
|
1510
1864
|
}
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1865
|
+
function transformEmphasis(token, ctx) {
|
|
1866
|
+
return {
|
|
1867
|
+
type: "emphasis",
|
|
1868
|
+
children: ctx.transformInline(token.tokens)
|
|
1869
|
+
};
|
|
1870
|
+
}
|
|
1871
|
+
function transformCodespan(token) {
|
|
1872
|
+
return {
|
|
1873
|
+
type: "inlineCode",
|
|
1874
|
+
value: token.text
|
|
1875
|
+
};
|
|
1876
|
+
}
|
|
1877
|
+
function transformBreak() {
|
|
1878
|
+
return { type: "break" };
|
|
1879
|
+
}
|
|
1880
|
+
function transformDelete(token, ctx) {
|
|
1881
|
+
return {
|
|
1882
|
+
type: "delete",
|
|
1883
|
+
children: ctx.transformInline(token.tokens)
|
|
1884
|
+
};
|
|
1885
|
+
}
|
|
1886
|
+
function transformInlineHtml(token) {
|
|
1887
|
+
const parsed = parseHtmlFragment(token.text);
|
|
1888
|
+
if (parsed.length > 0) {
|
|
1889
|
+
return parsed;
|
|
1519
1890
|
}
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1891
|
+
return { type: "text", value: token.text };
|
|
1892
|
+
}
|
|
1893
|
+
function isTokenType(token, type) {
|
|
1894
|
+
return token.type === type;
|
|
1895
|
+
}
|
|
1896
|
+
var builtinBlockTransformers = {
|
|
1897
|
+
blockMath: (token) => {
|
|
1898
|
+
if (isTokenType(token, "blockMath")) return transformBlockMath(token);
|
|
1899
|
+
return null;
|
|
1900
|
+
},
|
|
1901
|
+
footnoteDefinitionBlock: (token, ctx) => {
|
|
1902
|
+
if (isTokenType(token, "footnoteDefinitionBlock"))
|
|
1903
|
+
return transformFootnoteDefinitionBlock(token, ctx);
|
|
1904
|
+
return null;
|
|
1905
|
+
},
|
|
1906
|
+
explicitDefinition: (token) => {
|
|
1907
|
+
if (isTokenType(token, "explicitDefinition"))
|
|
1908
|
+
return transformExplicitDefinition(token);
|
|
1909
|
+
return null;
|
|
1910
|
+
},
|
|
1911
|
+
def: (token) => {
|
|
1912
|
+
if (isTokenType(token, "def")) return transformDef(token);
|
|
1913
|
+
return null;
|
|
1914
|
+
},
|
|
1915
|
+
container: (token, ctx) => {
|
|
1916
|
+
if (isTokenType(token, "container")) return transformContainer(token, ctx);
|
|
1917
|
+
return null;
|
|
1918
|
+
},
|
|
1919
|
+
footnoteDefinition: (token, ctx) => {
|
|
1920
|
+
if (isTokenType(token, "footnoteDefinition"))
|
|
1921
|
+
return transformFootnoteDefToken(token, ctx);
|
|
1922
|
+
return null;
|
|
1923
|
+
},
|
|
1924
|
+
heading: (token, ctx) => {
|
|
1925
|
+
if (isTokenType(token, "heading")) return transformHeading(token, ctx);
|
|
1926
|
+
return null;
|
|
1927
|
+
},
|
|
1928
|
+
paragraph: (token, ctx) => {
|
|
1929
|
+
if (isTokenType(token, "paragraph")) return transformParagraph(token, ctx);
|
|
1930
|
+
return null;
|
|
1931
|
+
},
|
|
1932
|
+
code: (token) => {
|
|
1933
|
+
if (isTokenType(token, "code")) return transformCode(token);
|
|
1934
|
+
return null;
|
|
1935
|
+
},
|
|
1936
|
+
blockquote: (token, ctx) => {
|
|
1937
|
+
if (isTokenType(token, "blockquote")) return transformBlockquote(token, ctx);
|
|
1938
|
+
return null;
|
|
1939
|
+
},
|
|
1940
|
+
list: (token, ctx) => {
|
|
1941
|
+
if (isTokenType(token, "list")) return transformList(token, ctx);
|
|
1942
|
+
return null;
|
|
1943
|
+
},
|
|
1944
|
+
table: (token, ctx) => {
|
|
1945
|
+
if (isTokenType(token, "table")) return transformTable(token, ctx);
|
|
1946
|
+
return null;
|
|
1947
|
+
},
|
|
1948
|
+
hr: () => transformHr(),
|
|
1949
|
+
html: (token) => {
|
|
1950
|
+
if (isTokenType(token, "html")) return transformHtml(token);
|
|
1951
|
+
return null;
|
|
1952
|
+
},
|
|
1953
|
+
space: () => null,
|
|
1954
|
+
text: (token, ctx) => {
|
|
1955
|
+
if (isTokenType(token, "text")) return transformTextBlock(token, ctx);
|
|
1956
|
+
return null;
|
|
1527
1957
|
}
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1958
|
+
};
|
|
1959
|
+
var builtinInlineTransformers = {
|
|
1960
|
+
inlineMath: (token) => {
|
|
1961
|
+
if (isTokenType(token, "inlineMath")) return transformInlineMath(token);
|
|
1962
|
+
return null;
|
|
1963
|
+
},
|
|
1964
|
+
optimisticReference: (token, ctx) => {
|
|
1965
|
+
if (isTokenType(token, "optimisticReference"))
|
|
1966
|
+
return transformOptimisticReference(token, ctx);
|
|
1967
|
+
return null;
|
|
1968
|
+
},
|
|
1969
|
+
link: (token, ctx) => {
|
|
1970
|
+
if (isTokenType(token, "link")) return transformLink(token, ctx);
|
|
1971
|
+
return null;
|
|
1972
|
+
},
|
|
1973
|
+
image: (token) => {
|
|
1974
|
+
if (isTokenType(token, "image")) return transformImage(token);
|
|
1975
|
+
return null;
|
|
1976
|
+
},
|
|
1977
|
+
text: (token) => {
|
|
1978
|
+
if (isTokenType(token, "text")) return transformText(token);
|
|
1979
|
+
return null;
|
|
1980
|
+
},
|
|
1981
|
+
escape: (token) => {
|
|
1982
|
+
if (isTokenType(token, "escape")) return transformText(token);
|
|
1983
|
+
return null;
|
|
1984
|
+
},
|
|
1985
|
+
strong: (token, ctx) => {
|
|
1986
|
+
if (isTokenType(token, "strong")) return transformStrong(token, ctx);
|
|
1987
|
+
return null;
|
|
1988
|
+
},
|
|
1989
|
+
em: (token, ctx) => {
|
|
1990
|
+
if (isTokenType(token, "em")) return transformEmphasis(token, ctx);
|
|
1991
|
+
return null;
|
|
1992
|
+
},
|
|
1993
|
+
codespan: (token) => {
|
|
1994
|
+
if (isTokenType(token, "codespan")) return transformCodespan(token);
|
|
1995
|
+
return null;
|
|
1996
|
+
},
|
|
1997
|
+
br: () => transformBreak(),
|
|
1998
|
+
del: (token, ctx) => {
|
|
1999
|
+
if (isTokenType(token, "del")) return transformDelete(token, ctx);
|
|
2000
|
+
return null;
|
|
2001
|
+
},
|
|
2002
|
+
inlineHtml: (token) => {
|
|
2003
|
+
if (isTokenType(token, "inlineHtml")) return transformInlineHtml(token);
|
|
2004
|
+
return null;
|
|
1535
2005
|
}
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
2006
|
+
};
|
|
2007
|
+
function transformBlockToken(token, ctx) {
|
|
2008
|
+
const tokenType = token.type;
|
|
2009
|
+
if (ctx.customBlockTransformers?.[tokenType]) {
|
|
2010
|
+
const result = ctx.customBlockTransformers[tokenType](token, ctx);
|
|
2011
|
+
if (result !== void 0) return result;
|
|
2012
|
+
}
|
|
2013
|
+
if (builtinBlockTransformers[tokenType]) {
|
|
2014
|
+
const result = builtinBlockTransformers[tokenType](token, ctx);
|
|
2015
|
+
if (result !== void 0) return result;
|
|
2016
|
+
}
|
|
2017
|
+
if ("text" in token && typeof token.text === "string") {
|
|
2018
|
+
const paragraph = {
|
|
2019
|
+
type: "paragraph",
|
|
2020
|
+
children: [{ type: "text", value: token.text }]
|
|
2021
|
+
};
|
|
2022
|
+
return paragraph;
|
|
2023
|
+
}
|
|
2024
|
+
return null;
|
|
2025
|
+
}
|
|
2026
|
+
function transformInlineToken(token, ctx) {
|
|
2027
|
+
const tokenType = token.type;
|
|
2028
|
+
if (ctx.customInlineTransformers?.[tokenType]) {
|
|
2029
|
+
const result = ctx.customInlineTransformers[tokenType](token, ctx);
|
|
2030
|
+
if (result !== void 0) return result;
|
|
2031
|
+
}
|
|
2032
|
+
if (builtinInlineTransformers[tokenType]) {
|
|
2033
|
+
const result = builtinInlineTransformers[tokenType](token, ctx);
|
|
2034
|
+
if (result !== void 0) return result;
|
|
2035
|
+
}
|
|
2036
|
+
if ("text" in token && typeof token.text === "string") {
|
|
2037
|
+
const text = { type: "text", value: token.text };
|
|
2038
|
+
return text;
|
|
2039
|
+
}
|
|
2040
|
+
return null;
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
// src/parser/ast/MarkedAstBuildter.ts
|
|
2044
|
+
var MarkedAstBuilder = class {
|
|
2045
|
+
constructor(options = {}) {
|
|
2046
|
+
this.options = options;
|
|
2047
|
+
this.containerConfig = typeof options.containers === "object" ? options.containers : options.containers === true ? {} : void 0;
|
|
2048
|
+
this.htmlTreeOptions = typeof options.htmlTree === "object" ? options.htmlTree : options.htmlTree === true ? {} : void 0;
|
|
2049
|
+
if (options.plugins) {
|
|
2050
|
+
this.userExtensions.push(...extractMarkedExtensions(options.plugins));
|
|
2051
|
+
}
|
|
2052
|
+
if (options.markedExtensions) {
|
|
2053
|
+
this.userExtensions.push(...options.markedExtensions);
|
|
2054
|
+
}
|
|
2055
|
+
this.transformContext = {
|
|
2056
|
+
transformTokens: this.transformTokens.bind(this),
|
|
2057
|
+
transformTokensWithPosition: this.transformTokensWithPosition.bind(this),
|
|
2058
|
+
transformInline: this.transformInline.bind(this),
|
|
2059
|
+
parseFootnoteContent: this.parseFootnoteContent.bind(this)
|
|
2060
|
+
};
|
|
2061
|
+
}
|
|
2062
|
+
containerConfig;
|
|
2063
|
+
htmlTreeOptions;
|
|
2064
|
+
globalLinks = {};
|
|
2065
|
+
/** 用户传入的 marked 扩展 */
|
|
2066
|
+
userExtensions = [];
|
|
2067
|
+
/** 转换上下文(用于递归转换) */
|
|
2068
|
+
transformContext;
|
|
1542
2069
|
parse(text) {
|
|
1543
|
-
const
|
|
1544
|
-
const
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
2070
|
+
const normalizedText = text.replace(/[\u00A0\u200b\u202f]/g, " ");
|
|
2071
|
+
const optimisticRefExt = createOptimisticReferenceExtension();
|
|
2072
|
+
const explicitDefExt = createExplicitDefinitionExtension();
|
|
2073
|
+
const footnoteDefExt = createFootnoteDefinitionExtension();
|
|
2074
|
+
const userBlockExts = [];
|
|
2075
|
+
const userBlockStartExts = [];
|
|
2076
|
+
const userInlineExts = [];
|
|
2077
|
+
const userInlineStartExts = [];
|
|
2078
|
+
for (const ext of this.userExtensions) {
|
|
2079
|
+
if (ext.level === "block") {
|
|
2080
|
+
if (ext.tokenizer) userBlockExts.push(ext.tokenizer);
|
|
2081
|
+
if (ext.start) userBlockStartExts.push(ext.start);
|
|
2082
|
+
} else if (ext.level === "inline") {
|
|
2083
|
+
if (ext.tokenizer) userInlineExts.push(ext.tokenizer);
|
|
2084
|
+
if (ext.start) userInlineStartExts.push(ext.start);
|
|
2085
|
+
}
|
|
1548
2086
|
}
|
|
2087
|
+
const blockExts = [
|
|
2088
|
+
footnoteDefExt.tokenizer,
|
|
2089
|
+
explicitDefExt.tokenizer,
|
|
2090
|
+
...userBlockExts
|
|
2091
|
+
];
|
|
2092
|
+
const blockStartExts = [
|
|
2093
|
+
footnoteDefExt.start,
|
|
2094
|
+
explicitDefExt.start,
|
|
2095
|
+
...userBlockStartExts
|
|
2096
|
+
];
|
|
2097
|
+
const inlineExts = [optimisticRefExt.tokenizer, ...userInlineExts];
|
|
2098
|
+
const inlineStartExts = [optimisticRefExt.start, ...userInlineStartExts];
|
|
1549
2099
|
if (this.options.math) {
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
2100
|
+
const mathOptions = typeof this.options.math === "object" ? this.options.math : {};
|
|
2101
|
+
const blockMathExt = createBlockMathExtension(mathOptions);
|
|
2102
|
+
const inlineMathExt = createInlineMathExtension(mathOptions);
|
|
2103
|
+
blockExts.unshift(blockMathExt.tokenizer);
|
|
2104
|
+
blockStartExts.unshift(blockMathExt.start);
|
|
2105
|
+
inlineExts.unshift(inlineMathExt.tokenizer);
|
|
2106
|
+
inlineStartExts.unshift(inlineMathExt.start);
|
|
2107
|
+
}
|
|
2108
|
+
if (this.htmlTreeOptions) {
|
|
2109
|
+
const inlineHtmlExt = createInlineHtmlExtension();
|
|
2110
|
+
inlineExts.unshift(inlineHtmlExt.tokenizer);
|
|
2111
|
+
inlineStartExts.unshift(inlineHtmlExt.start);
|
|
2112
|
+
}
|
|
2113
|
+
const lexerOptions = {
|
|
2114
|
+
gfm: true,
|
|
2115
|
+
breaks: false,
|
|
2116
|
+
// 关闭软换行转 break,与 Micromark 保持一致
|
|
2117
|
+
...this.options,
|
|
2118
|
+
extensions: {
|
|
2119
|
+
inline: inlineExts,
|
|
2120
|
+
startInline: inlineStartExts,
|
|
2121
|
+
block: blockExts,
|
|
2122
|
+
startBlock: blockStartExts
|
|
2123
|
+
}
|
|
2124
|
+
};
|
|
2125
|
+
const lexerInstance = new Lexer(lexerOptions);
|
|
2126
|
+
if (lexerInstance.tokens && lexerInstance.tokens.links) {
|
|
2127
|
+
Object.assign(lexerInstance.tokens.links, this.globalLinks);
|
|
1562
2128
|
}
|
|
1563
|
-
|
|
1564
|
-
|
|
2129
|
+
let tokens = lexerInstance.lex(normalizedText);
|
|
2130
|
+
if (lexerInstance.tokens && lexerInstance.tokens.links) {
|
|
2131
|
+
Object.assign(this.globalLinks, lexerInstance.tokens.links);
|
|
1565
2132
|
}
|
|
1566
|
-
|
|
1567
|
-
let
|
|
1568
|
-
if (this.
|
|
1569
|
-
|
|
1570
|
-
} else {
|
|
1571
|
-
ast = this.convertHtmlToText(ast);
|
|
2133
|
+
tokens = this.preprocessTokens(tokens);
|
|
2134
|
+
let children = this.transformTokensWithPosition(tokens);
|
|
2135
|
+
if (this.htmlTreeOptions) {
|
|
2136
|
+
children = this.processHtmlNodes(children);
|
|
1572
2137
|
}
|
|
1573
|
-
return
|
|
2138
|
+
return {
|
|
2139
|
+
type: "root",
|
|
2140
|
+
children
|
|
2141
|
+
};
|
|
1574
2142
|
}
|
|
1575
2143
|
/**
|
|
1576
|
-
*
|
|
2144
|
+
* 预处理 tokens
|
|
1577
2145
|
*
|
|
1578
|
-
*
|
|
1579
|
-
* @returns 转换后的 AST
|
|
2146
|
+
* 处理容器指令和遗留的脚注定义(从 paragraph 中提取)
|
|
1580
2147
|
*/
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
2148
|
+
preprocessTokens(tokens) {
|
|
2149
|
+
const result = [];
|
|
2150
|
+
let i = 0;
|
|
2151
|
+
while (i < tokens.length) {
|
|
2152
|
+
const token = tokens[i];
|
|
2153
|
+
if (token.type === "paragraph") {
|
|
2154
|
+
const text = token.text;
|
|
2155
|
+
const footnoteMatch = text.match(/^\[\^([a-zA-Z0-9_-]+)\]:\s+([\s\S]*)$/);
|
|
2156
|
+
if (footnoteMatch) {
|
|
2157
|
+
const defToken = {
|
|
2158
|
+
type: "footnoteDefinition",
|
|
2159
|
+
identifier: footnoteMatch[1],
|
|
2160
|
+
text: footnoteMatch[2],
|
|
2161
|
+
tokens: new Lexer().inlineTokens(footnoteMatch[2]),
|
|
2162
|
+
raw: token.raw
|
|
2163
|
+
};
|
|
2164
|
+
result.push(defToken);
|
|
2165
|
+
i++;
|
|
2166
|
+
continue;
|
|
2167
|
+
}
|
|
2168
|
+
const containerStartMatch = text.match(/^:::(\s*)([a-zA-Z0-9_-]+)(.*?)(\n|$)/);
|
|
2169
|
+
if (containerStartMatch) {
|
|
2170
|
+
const name = containerStartMatch[2];
|
|
2171
|
+
const attrs = containerStartMatch[3].trim();
|
|
2172
|
+
let rawAccumulator = "";
|
|
2173
|
+
let j = i;
|
|
2174
|
+
let depth = 0;
|
|
2175
|
+
let foundEnd = false;
|
|
2176
|
+
let contentRaw = "";
|
|
2177
|
+
while (j < tokens.length) {
|
|
2178
|
+
const currentToken = tokens[j];
|
|
2179
|
+
rawAccumulator += currentToken.raw;
|
|
2180
|
+
const lines = rawAccumulator.split("\n");
|
|
2181
|
+
depth = 0;
|
|
2182
|
+
let startLineIndex = -1;
|
|
2183
|
+
let endLineIndex = -1;
|
|
2184
|
+
for (let k = 0; k < lines.length; k++) {
|
|
2185
|
+
const line = lines[k];
|
|
2186
|
+
if (line.match(/^:::(\s*)([a-zA-Z0-9_-]+)/)) {
|
|
2187
|
+
if (depth === 0 && startLineIndex === -1) startLineIndex = k;
|
|
2188
|
+
depth++;
|
|
2189
|
+
} else if (line.trim() === ":::") {
|
|
2190
|
+
depth--;
|
|
2191
|
+
if (depth === 0) {
|
|
2192
|
+
endLineIndex = k;
|
|
2193
|
+
foundEnd = true;
|
|
2194
|
+
break;
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
if (foundEnd) {
|
|
2199
|
+
const contentLines = lines.slice(startLineIndex + 1, endLineIndex);
|
|
2200
|
+
contentRaw = contentLines.join("\n");
|
|
2201
|
+
const remainingLines = lines.slice(endLineIndex + 1);
|
|
2202
|
+
const remainingText = remainingLines.join("\n");
|
|
2203
|
+
const containerToken = {
|
|
2204
|
+
type: "container",
|
|
2205
|
+
name,
|
|
2206
|
+
attrs,
|
|
2207
|
+
tokens: this.preprocessTokens(lexer(contentRaw)),
|
|
2208
|
+
raw: rawAccumulator
|
|
2209
|
+
};
|
|
2210
|
+
result.push(containerToken);
|
|
2211
|
+
if (remainingText.trim()) {
|
|
2212
|
+
const remainingTokens = this.preprocessTokens(lexer(remainingText));
|
|
2213
|
+
result.push(...remainingTokens);
|
|
2214
|
+
}
|
|
2215
|
+
i = j + 1;
|
|
2216
|
+
break;
|
|
2217
|
+
}
|
|
2218
|
+
j++;
|
|
2219
|
+
}
|
|
2220
|
+
if (foundEnd) continue;
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
result.push(token);
|
|
2224
|
+
i++;
|
|
2225
|
+
}
|
|
2226
|
+
return result;
|
|
1586
2227
|
}
|
|
1587
2228
|
/**
|
|
1588
|
-
*
|
|
2229
|
+
* 转换 tokens 为 MDAST 节点(带位置信息)
|
|
1589
2230
|
*/
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
2231
|
+
transformTokensWithPosition(tokens) {
|
|
2232
|
+
if (!tokens) return [];
|
|
2233
|
+
const results = [];
|
|
2234
|
+
let currentOffset = 0;
|
|
2235
|
+
for (const token of tokens) {
|
|
2236
|
+
const rawLength = token.raw?.length ?? 0;
|
|
2237
|
+
const node = transformBlockToken(token, this.transformContext);
|
|
2238
|
+
if (node) {
|
|
2239
|
+
node.position = {
|
|
2240
|
+
start: { line: 0, column: 0, offset: currentOffset },
|
|
2241
|
+
end: { line: 0, column: 0, offset: currentOffset + rawLength }
|
|
1601
2242
|
};
|
|
2243
|
+
results.push(node);
|
|
1602
2244
|
}
|
|
1603
|
-
|
|
1604
|
-
}
|
|
2245
|
+
currentOffset += rawLength;
|
|
2246
|
+
}
|
|
2247
|
+
return results;
|
|
1605
2248
|
}
|
|
1606
2249
|
/**
|
|
1607
|
-
*
|
|
2250
|
+
* 转换 tokens 为 MDAST 节点(不带位置信息)
|
|
1608
2251
|
*/
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
2252
|
+
transformTokens(tokens) {
|
|
2253
|
+
if (!tokens) return [];
|
|
2254
|
+
return tokens.map((t) => transformBlockToken(t, this.transformContext)).filter(Boolean);
|
|
2255
|
+
}
|
|
2256
|
+
/**
|
|
2257
|
+
* 转换行内 tokens
|
|
2258
|
+
*/
|
|
2259
|
+
transformInline(tokens) {
|
|
2260
|
+
if (!tokens) return [];
|
|
2261
|
+
const results = [];
|
|
2262
|
+
for (const token of tokens) {
|
|
2263
|
+
const result = transformInlineToken(token, this.transformContext);
|
|
2264
|
+
if (result) {
|
|
2265
|
+
if (Array.isArray(result)) {
|
|
2266
|
+
results.push(...result);
|
|
2267
|
+
} else {
|
|
2268
|
+
results.push(result);
|
|
2269
|
+
}
|
|
1621
2270
|
}
|
|
1622
|
-
|
|
1623
|
-
|
|
2271
|
+
}
|
|
2272
|
+
return results;
|
|
1624
2273
|
}
|
|
1625
2274
|
/**
|
|
1626
|
-
*
|
|
2275
|
+
* 解析脚注内容为 AST 节点
|
|
1627
2276
|
*/
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
};
|
|
1638
|
-
|
|
2277
|
+
parseFootnoteContent(content) {
|
|
2278
|
+
if (!content.trim()) {
|
|
2279
|
+
return [];
|
|
2280
|
+
}
|
|
2281
|
+
const normalizedContent = content.split("\n").map((line, index) => {
|
|
2282
|
+
if (index === 0) return line;
|
|
2283
|
+
if (line.startsWith(" ")) return line.slice(4);
|
|
2284
|
+
if (line.startsWith(" ")) return line.slice(1);
|
|
2285
|
+
return line;
|
|
2286
|
+
}).join("\n");
|
|
2287
|
+
const contentLexer = new Lexer({ gfm: true, breaks: true });
|
|
2288
|
+
const tokens = contentLexer.lex(normalizedContent);
|
|
2289
|
+
return this.transformTokens(tokens);
|
|
1639
2290
|
}
|
|
1640
2291
|
/**
|
|
1641
|
-
*
|
|
2292
|
+
* 处理 HTML 节点
|
|
2293
|
+
*
|
|
2294
|
+
* 使用 html-extension 的 transformHtmlNodes 来处理:
|
|
2295
|
+
* - 合并被空行分割的 HTML 节点
|
|
2296
|
+
* - 将 HTML 解析为 HtmlElementNode 树结构
|
|
1642
2297
|
*/
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
type: "
|
|
1646
|
-
|
|
1647
|
-
position: htmlNode.position
|
|
2298
|
+
processHtmlNodes(nodes) {
|
|
2299
|
+
const tempRoot = {
|
|
2300
|
+
type: "root",
|
|
2301
|
+
children: nodes
|
|
1648
2302
|
};
|
|
2303
|
+
const transformed = transformHtmlNodes(tempRoot, this.htmlTreeOptions);
|
|
2304
|
+
return transformed.children;
|
|
1649
2305
|
}
|
|
1650
2306
|
/**
|
|
1651
2307
|
* 将 AST 节点转换为 ParsedBlock
|
|
1652
|
-
*
|
|
1653
|
-
* @param nodes AST 节点列表
|
|
1654
|
-
* @param startOffset 起始偏移量
|
|
1655
|
-
* @param rawText 原始文本
|
|
1656
|
-
* @param status 块状态
|
|
1657
|
-
* @param generateBlockId 生成块 ID 的函数
|
|
1658
|
-
* @returns ParsedBlock 列表
|
|
1659
2308
|
*/
|
|
1660
2309
|
nodesToBlocks(nodes, startOffset, rawText, status, generateBlockId) {
|
|
1661
2310
|
const blocks = [];
|
|
1662
|
-
let currentOffset = startOffset;
|
|
1663
2311
|
for (const node of nodes) {
|
|
1664
|
-
const
|
|
1665
|
-
const
|
|
1666
|
-
const nodeText = rawText.substring(
|
|
2312
|
+
const relativeStart = node.position?.start?.offset ?? 0;
|
|
2313
|
+
const relativeEnd = node.position?.end?.offset ?? rawText.length;
|
|
2314
|
+
const nodeText = rawText.substring(relativeStart, relativeEnd);
|
|
2315
|
+
const absoluteStart = startOffset + relativeStart;
|
|
2316
|
+
const absoluteEnd = startOffset + relativeEnd;
|
|
1667
2317
|
blocks.push({
|
|
1668
2318
|
id: generateBlockId(),
|
|
1669
2319
|
status,
|
|
1670
2320
|
node,
|
|
1671
|
-
startOffset:
|
|
1672
|
-
endOffset:
|
|
2321
|
+
startOffset: absoluteStart,
|
|
2322
|
+
endOffset: absoluteEnd,
|
|
1673
2323
|
rawText: nodeText
|
|
1674
2324
|
});
|
|
1675
|
-
currentOffset = nodeEnd;
|
|
1676
2325
|
}
|
|
1677
2326
|
return blocks;
|
|
1678
2327
|
}
|
|
2328
|
+
/**
|
|
2329
|
+
* 更新配置选项
|
|
2330
|
+
* @param options 部分配置选项
|
|
2331
|
+
*/
|
|
2332
|
+
updateOptions(options) {
|
|
2333
|
+
Object.assign(this.options, options);
|
|
2334
|
+
if ("containers" in options) {
|
|
2335
|
+
this.containerConfig = typeof options.containers === "object" ? options.containers : options.containers === true ? {} : void 0;
|
|
2336
|
+
}
|
|
2337
|
+
if ("htmlTree" in options) {
|
|
2338
|
+
this.htmlTreeOptions = typeof options.htmlTree === "object" ? options.htmlTree : options.htmlTree === true ? {} : void 0;
|
|
2339
|
+
}
|
|
2340
|
+
if (options.plugins || options.markedExtensions) {
|
|
2341
|
+
this.userExtensions.length = 0;
|
|
2342
|
+
if (options.plugins) {
|
|
2343
|
+
this.userExtensions.push(...extractMarkedExtensions(options.plugins));
|
|
2344
|
+
}
|
|
2345
|
+
if (options.markedExtensions) {
|
|
2346
|
+
this.userExtensions.push(...options.markedExtensions);
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
1679
2350
|
};
|
|
1680
2351
|
|
|
1681
2352
|
// src/parser/IncremarkParser.ts
|
|
1682
2353
|
var IncremarkParser = class {
|
|
1683
|
-
buffer = "";
|
|
1684
2354
|
lines = [];
|
|
1685
2355
|
/** 行偏移量前缀和:lineOffsets[i] = 第i行起始位置的偏移量 */
|
|
1686
2356
|
lineOffsets = [0];
|
|
@@ -1705,7 +2375,8 @@ var IncremarkParser = class {
|
|
|
1705
2375
|
...options
|
|
1706
2376
|
};
|
|
1707
2377
|
this.context = createInitialContext();
|
|
1708
|
-
|
|
2378
|
+
const BuilderClass = options.astBuilder || MarkedAstBuilder;
|
|
2379
|
+
this.astBuilder = new BuilderClass(this.options);
|
|
1709
2380
|
this.boundaryDetector = new BoundaryDetector({ containers: this.astBuilder.containerConfig });
|
|
1710
2381
|
this.definitionManager = new DefinitionManager();
|
|
1711
2382
|
this.footnoteManager = new FootnoteManager();
|
|
@@ -1720,36 +2391,30 @@ var IncremarkParser = class {
|
|
|
1720
2391
|
this.definitionManager.extractFromBlocks(blocks);
|
|
1721
2392
|
this.footnoteManager.extractDefinitionsFromBlocks(blocks);
|
|
1722
2393
|
}
|
|
1723
|
-
/**
|
|
1724
|
-
* 收集 AST 中的脚注引用(按出现顺序)
|
|
1725
|
-
* 用于确定脚注的显示顺序
|
|
1726
|
-
*/
|
|
1727
|
-
collectFootnoteReferences(nodes) {
|
|
1728
|
-
this.footnoteManager.collectReferences(nodes);
|
|
1729
|
-
}
|
|
1730
2394
|
/**
|
|
1731
2395
|
* 增量更新 lines 和 lineOffsets
|
|
1732
|
-
*
|
|
2396
|
+
* 优化策略:只 split 新增的 chunk,不拼接旧字符串,避免长行性能劣化
|
|
1733
2397
|
*/
|
|
1734
|
-
updateLines() {
|
|
2398
|
+
updateLines(chunk) {
|
|
1735
2399
|
const prevLineCount = this.lines.length;
|
|
1736
2400
|
if (prevLineCount === 0) {
|
|
1737
|
-
this.lines =
|
|
2401
|
+
this.lines = chunk.split("\n");
|
|
1738
2402
|
this.lineOffsets = [0];
|
|
1739
|
-
for (let i = 0; i < this.lines.length; i++) {
|
|
2403
|
+
for (let i = 0; i < this.lines.length - 1; i++) {
|
|
1740
2404
|
this.lineOffsets.push(this.lineOffsets[i] + this.lines[i].length + 1);
|
|
1741
2405
|
}
|
|
1742
2406
|
return;
|
|
1743
2407
|
}
|
|
1744
|
-
const
|
|
1745
|
-
const
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
this.lines
|
|
1751
|
-
const
|
|
1752
|
-
this.lineOffsets.push(
|
|
2408
|
+
const chunkLines = chunk.split("\n");
|
|
2409
|
+
const lastLineIndex = prevLineCount - 1;
|
|
2410
|
+
this.lines[lastLineIndex] += chunkLines[0];
|
|
2411
|
+
for (let i = 1; i < chunkLines.length; i++) {
|
|
2412
|
+
const prevLineIndex = this.lines.length - 1;
|
|
2413
|
+
const prevLineStart = this.lineOffsets[prevLineIndex];
|
|
2414
|
+
const prevLineLength = this.lines[prevLineIndex].length;
|
|
2415
|
+
const newOneOffset = prevLineStart + prevLineLength + 1;
|
|
2416
|
+
this.lineOffsets.push(newOneOffset);
|
|
2417
|
+
this.lines.push(chunkLines[i]);
|
|
1753
2418
|
}
|
|
1754
2419
|
}
|
|
1755
2420
|
/**
|
|
@@ -1774,8 +2439,7 @@ var IncremarkParser = class {
|
|
|
1774
2439
|
* 追加新的 chunk 并返回增量更新
|
|
1775
2440
|
*/
|
|
1776
2441
|
append(chunk) {
|
|
1777
|
-
this.
|
|
1778
|
-
this.updateLines();
|
|
2442
|
+
this.updateLines(chunk);
|
|
1779
2443
|
const { line: stableBoundary, contextAtLine } = this.findStableBoundary();
|
|
1780
2444
|
const update = {
|
|
1781
2445
|
completed: [],
|
|
@@ -1794,6 +2458,8 @@ var IncremarkParser = class {
|
|
|
1794
2458
|
this.completedBlocks.push(...newBlocks);
|
|
1795
2459
|
update.completed = newBlocks;
|
|
1796
2460
|
this.updateDefinitionsFromCompletedBlocks(newBlocks);
|
|
2461
|
+
this.footnoteManager.collectReferencesFromCompletedBlocks(newBlocks);
|
|
2462
|
+
this.boundaryDetector.clearContextCache(this.pendingStartLine);
|
|
1797
2463
|
this.context = contextAtLine;
|
|
1798
2464
|
this.pendingStartLine = stableBoundary + 1;
|
|
1799
2465
|
}
|
|
@@ -1810,10 +2476,9 @@ var IncremarkParser = class {
|
|
|
1810
2476
|
type: "root",
|
|
1811
2477
|
children: [...this.completedBlocks.map((b) => b.node), ...update.pending.map((b) => b.node)]
|
|
1812
2478
|
};
|
|
1813
|
-
this.
|
|
2479
|
+
update.footnoteReferenceOrder = this.footnoteManager.collectReferencesFromPending(update.pending);
|
|
1814
2480
|
update.definitions = this.getDefinitionMap();
|
|
1815
2481
|
update.footnoteDefinitions = this.getFootnoteDefinitionMap();
|
|
1816
|
-
update.footnoteReferenceOrder = this.getFootnoteReferenceOrder();
|
|
1817
2482
|
this.emitChange(update.pending);
|
|
1818
2483
|
return update;
|
|
1819
2484
|
}
|
|
@@ -1825,7 +2490,7 @@ var IncremarkParser = class {
|
|
|
1825
2490
|
const state = {
|
|
1826
2491
|
completedBlocks: this.completedBlocks,
|
|
1827
2492
|
pendingBlocks,
|
|
1828
|
-
markdown: this.
|
|
2493
|
+
markdown: this.lines.join("\n"),
|
|
1829
2494
|
ast: {
|
|
1830
2495
|
type: "root",
|
|
1831
2496
|
children: [
|
|
@@ -1868,6 +2533,8 @@ var IncremarkParser = class {
|
|
|
1868
2533
|
this.completedBlocks.push(...finalBlocks);
|
|
1869
2534
|
update.completed = finalBlocks;
|
|
1870
2535
|
this.updateDefinitionsFromCompletedBlocks(finalBlocks);
|
|
2536
|
+
this.footnoteManager.collectReferencesFromCompletedBlocks(finalBlocks);
|
|
2537
|
+
this.boundaryDetector.clearContextCache(this.pendingStartLine);
|
|
1871
2538
|
}
|
|
1872
2539
|
}
|
|
1873
2540
|
this.lastPendingBlocks = [];
|
|
@@ -1876,7 +2543,6 @@ var IncremarkParser = class {
|
|
|
1876
2543
|
type: "root",
|
|
1877
2544
|
children: this.completedBlocks.map((b) => b.node)
|
|
1878
2545
|
};
|
|
1879
|
-
this.collectFootnoteReferences(update.ast.children);
|
|
1880
2546
|
update.definitions = this.getDefinitionMap();
|
|
1881
2547
|
update.footnoteDefinitions = this.getFootnoteDefinitionMap();
|
|
1882
2548
|
update.footnoteReferenceOrder = this.getFootnoteReferenceOrder();
|
|
@@ -1899,7 +2565,7 @@ var IncremarkParser = class {
|
|
|
1899
2565
|
...this.completedBlocks.map((b) => b.node),
|
|
1900
2566
|
...this.lastPendingBlocks.map((b) => b.node)
|
|
1901
2567
|
];
|
|
1902
|
-
this.
|
|
2568
|
+
this.footnoteManager.collectReferencesFromPending(this.lastPendingBlocks);
|
|
1903
2569
|
return {
|
|
1904
2570
|
type: "root",
|
|
1905
2571
|
children
|
|
@@ -1915,7 +2581,7 @@ var IncremarkParser = class {
|
|
|
1915
2581
|
* 获取当前缓冲区内容
|
|
1916
2582
|
*/
|
|
1917
2583
|
getBuffer() {
|
|
1918
|
-
return this.
|
|
2584
|
+
return this.lines.join("\n");
|
|
1919
2585
|
}
|
|
1920
2586
|
/**
|
|
1921
2587
|
* 获取 Definition 映射表(用于引用式图片和链接)
|
|
@@ -1949,7 +2615,6 @@ var IncremarkParser = class {
|
|
|
1949
2615
|
* 重置解析器状态
|
|
1950
2616
|
*/
|
|
1951
2617
|
reset() {
|
|
1952
|
-
this.buffer = "";
|
|
1953
2618
|
this.lines = [];
|
|
1954
2619
|
this.lineOffsets = [0];
|
|
1955
2620
|
this.completedBlocks = [];
|
|
@@ -1971,6 +2636,40 @@ var IncremarkParser = class {
|
|
|
1971
2636
|
this.append(content);
|
|
1972
2637
|
return this.finalize();
|
|
1973
2638
|
}
|
|
2639
|
+
/**
|
|
2640
|
+
* 更新解析器配置(动态更新,不需要重建 parser 实例)
|
|
2641
|
+
*
|
|
2642
|
+
* 注意:更新配置后会自动调用 reset() 重置状态
|
|
2643
|
+
*
|
|
2644
|
+
* @param options 部分配置选项
|
|
2645
|
+
*
|
|
2646
|
+
* @example
|
|
2647
|
+
* ```ts
|
|
2648
|
+
* // 动态启用 TeX 数学公式语法
|
|
2649
|
+
* parser.updateOptions({ math: { tex: true } })
|
|
2650
|
+
*
|
|
2651
|
+
* // 禁用 GFM
|
|
2652
|
+
* parser.updateOptions({ gfm: false })
|
|
2653
|
+
*
|
|
2654
|
+
* // 切换引擎
|
|
2655
|
+
* import { MicromarkAstBuilder } from '@incremark/core/engines/micromark'
|
|
2656
|
+
* parser.updateOptions({ astBuilder: MicromarkAstBuilder })
|
|
2657
|
+
* ```
|
|
2658
|
+
*/
|
|
2659
|
+
updateOptions(options) {
|
|
2660
|
+
this.reset();
|
|
2661
|
+
Object.assign(this.options, options);
|
|
2662
|
+
if (options.astBuilder) {
|
|
2663
|
+
const BuilderClass = options.astBuilder;
|
|
2664
|
+
if (!(this.astBuilder instanceof BuilderClass)) {
|
|
2665
|
+
this.astBuilder = new BuilderClass(this.options);
|
|
2666
|
+
} else {
|
|
2667
|
+
this.astBuilder.updateOptions(options);
|
|
2668
|
+
}
|
|
2669
|
+
} else {
|
|
2670
|
+
this.astBuilder.updateOptions(options);
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
1974
2673
|
};
|
|
1975
2674
|
function createIncremarkParser(options) {
|
|
1976
2675
|
return new IncremarkParser(options);
|
|
@@ -1993,10 +2692,9 @@ function countCharsInNode(n) {
|
|
|
1993
2692
|
}
|
|
1994
2693
|
return 1;
|
|
1995
2694
|
}
|
|
1996
|
-
function sliceAst(node, maxChars, accumulatedChunks
|
|
2695
|
+
function sliceAst(node, maxChars, accumulatedChunks) {
|
|
1997
2696
|
if (maxChars <= 0) return null;
|
|
1998
|
-
|
|
1999
|
-
let remaining = maxChars - skipChars;
|
|
2697
|
+
let remaining = maxChars;
|
|
2000
2698
|
let charIndex = 0;
|
|
2001
2699
|
const chunkRanges = [];
|
|
2002
2700
|
if (accumulatedChunks && accumulatedChunks.chunks.length > 0) {
|
|
@@ -2015,16 +2713,10 @@ function sliceAst(node, maxChars, accumulatedChunks, skipChars = 0) {
|
|
|
2015
2713
|
if (n.value && typeof n.value === "string") {
|
|
2016
2714
|
const nodeStart = charIndex;
|
|
2017
2715
|
const nodeEnd = charIndex + n.value.length;
|
|
2018
|
-
|
|
2019
|
-
charIndex = nodeEnd;
|
|
2020
|
-
return null;
|
|
2021
|
-
}
|
|
2022
|
-
const skipInNode = Math.max(0, skipChars - nodeStart);
|
|
2023
|
-
const take = Math.min(n.value.length - skipInNode, remaining);
|
|
2716
|
+
const take = Math.min(n.value.length, remaining);
|
|
2024
2717
|
remaining -= take;
|
|
2025
|
-
if (take === 0) return null;
|
|
2026
|
-
const slicedValue = n.value.slice(skipInNode, skipInNode + take);
|
|
2027
2718
|
charIndex = nodeEnd;
|
|
2719
|
+
const slicedValue = n.value.slice(0, take);
|
|
2028
2720
|
const result = {
|
|
2029
2721
|
...n,
|
|
2030
2722
|
value: slicedValue
|
|
@@ -2033,11 +2725,11 @@ function sliceAst(node, maxChars, accumulatedChunks, skipChars = 0) {
|
|
|
2033
2725
|
const nodeChunks = [];
|
|
2034
2726
|
let firstChunkLocalStart = take;
|
|
2035
2727
|
for (const range of chunkRanges) {
|
|
2036
|
-
const overlapStart = Math.max(range.start, nodeStart
|
|
2037
|
-
const overlapEnd = Math.min(range.end, nodeStart +
|
|
2728
|
+
const overlapStart = Math.max(range.start, nodeStart);
|
|
2729
|
+
const overlapEnd = Math.min(range.end, nodeStart + take);
|
|
2038
2730
|
if (overlapStart < overlapEnd) {
|
|
2039
|
-
const localStart = overlapStart -
|
|
2040
|
-
const localEnd = overlapEnd -
|
|
2731
|
+
const localStart = overlapStart - nodeStart;
|
|
2732
|
+
const localEnd = overlapEnd - nodeStart;
|
|
2041
2733
|
const chunkText = slicedValue.slice(localStart, localEnd);
|
|
2042
2734
|
if (chunkText.length > 0) {
|
|
2043
2735
|
if (nodeChunks.length === 0) {
|
|
@@ -2059,24 +2751,12 @@ function sliceAst(node, maxChars, accumulatedChunks, skipChars = 0) {
|
|
|
2059
2751
|
}
|
|
2060
2752
|
if (n.children && Array.isArray(n.children)) {
|
|
2061
2753
|
const newChildren = [];
|
|
2062
|
-
let childCharIndex = charIndex;
|
|
2063
2754
|
for (const child of n.children) {
|
|
2064
2755
|
if (remaining <= 0) break;
|
|
2065
|
-
const childChars = countCharsInNode(child);
|
|
2066
|
-
const childStart = childCharIndex;
|
|
2067
|
-
const childEnd = childCharIndex + childChars;
|
|
2068
|
-
if (childEnd <= skipChars) {
|
|
2069
|
-
childCharIndex = childEnd;
|
|
2070
|
-
continue;
|
|
2071
|
-
}
|
|
2072
|
-
const savedCharIndex = charIndex;
|
|
2073
|
-
charIndex = childStart;
|
|
2074
2756
|
const processed = process(child);
|
|
2075
|
-
charIndex = savedCharIndex;
|
|
2076
2757
|
if (processed) {
|
|
2077
2758
|
newChildren.push(processed);
|
|
2078
2759
|
}
|
|
2079
|
-
childCharIndex = childEnd;
|
|
2080
2760
|
}
|
|
2081
2761
|
if (newChildren.length === 0) {
|
|
2082
2762
|
return null;
|
|
@@ -2089,10 +2769,7 @@ function sliceAst(node, maxChars, accumulatedChunks, skipChars = 0) {
|
|
|
2089
2769
|
}
|
|
2090
2770
|
return process(node);
|
|
2091
2771
|
}
|
|
2092
|
-
function appendToAst(baseNode, sourceNode,
|
|
2093
|
-
if (endChars <= startChars) {
|
|
2094
|
-
return baseNode;
|
|
2095
|
-
}
|
|
2772
|
+
function appendToAst(baseNode, sourceNode, endChars, accumulatedChunks) {
|
|
2096
2773
|
const fullSlice = sliceAst(sourceNode, endChars, accumulatedChunks);
|
|
2097
2774
|
if (!fullSlice) {
|
|
2098
2775
|
return baseNode;
|
|
@@ -2617,7 +3294,6 @@ var BlockTransformer = class {
|
|
|
2617
3294
|
this.cachedDisplayNode = appendToAst(
|
|
2618
3295
|
this.cachedDisplayNode,
|
|
2619
3296
|
block.node,
|
|
2620
|
-
this.cachedProgress,
|
|
2621
3297
|
currentProgress,
|
|
2622
3298
|
this.getAccumulatedChunks()
|
|
2623
3299
|
);
|
|
@@ -2733,51 +3409,7 @@ function createPlugin(name, matcher, options = {}) {
|
|
|
2733
3409
|
...options
|
|
2734
3410
|
};
|
|
2735
3411
|
}
|
|
2736
|
-
/**
|
|
2737
|
-
* @file Micromark 扩展:支持增量解析的 Reference 语法
|
|
2738
|
-
*
|
|
2739
|
-
* @description
|
|
2740
|
-
* 在增量解析场景中,引用式图片/链接(如 `![Alt][id]`)可能在定义(`[id]: url`)之前出现。
|
|
2741
|
-
* 标准 micromark 会检查 parser.defined,如果 id 未定义就解析为文本。
|
|
2742
|
-
*
|
|
2743
|
-
* 本扩展通过覆盖 labelEnd 构造,移除 parser.defined 检查,
|
|
2744
|
-
* 使得 reference 语法总是被解析为 reference token,
|
|
2745
|
-
* 由渲染层根据实际的 definitionMap 决定如何渲染。
|
|
2746
|
-
*
|
|
2747
|
-
* @module micromark-reference-extension
|
|
2748
|
-
*
|
|
2749
|
-
* @features
|
|
2750
|
-
* - ✅ 支持所有 resource 语法(带 title 的图片/链接)
|
|
2751
|
-
* - ✅ 支持所有 reference 语法(full, collapsed, shortcut)
|
|
2752
|
-
* - ✅ 延迟验证:解析时不检查定义是否存在
|
|
2753
|
-
* - ✅ 使用官方 factory 函数,保证与 CommonMark 标准一致
|
|
2754
|
-
*
|
|
2755
|
-
* @dependencies
|
|
2756
|
-
* - micromark-factory-destination: 解析 URL(支持尖括号、括号平衡)
|
|
2757
|
-
* - micromark-factory-title: 解析 title(支持三种引号,支持多行)
|
|
2758
|
-
* - micromark-factory-label: 解析 label(支持转义、长度限制)
|
|
2759
|
-
* - micromark-factory-whitespace: 解析空白符(正确生成 lineEnding/linePrefix token)
|
|
2760
|
-
* - micromark-util-character: 字符判断工具
|
|
2761
|
-
* - micromark-util-symbol: 常量(codes, types, constants)
|
|
2762
|
-
* - micromark-util-types: TypeScript 类型定义
|
|
2763
|
-
*
|
|
2764
|
-
* @see {@link https://github.com/micromark/micromark} - micromark 官方文档
|
|
2765
|
-
* @see {@link https://spec.commonmark.org/0.30/#images} - CommonMark 图片规范
|
|
2766
|
-
* @see {@link https://spec.commonmark.org/0.30/#links} - CommonMark 链接规范
|
|
2767
|
-
*
|
|
2768
|
-
* @example
|
|
2769
|
-
* ```typescript
|
|
2770
|
-
* import { micromarkReferenceExtension } from './micromark-reference-extension'
|
|
2771
|
-
* import { fromMarkdown } from 'mdast-util-from-markdown'
|
|
2772
|
-
*
|
|
2773
|
-
* const extensions = [micromarkReferenceExtension()]
|
|
2774
|
-
* const ast = fromMarkdown(text, { extensions })
|
|
2775
|
-
* ```
|
|
2776
|
-
*
|
|
2777
|
-
* @author Incremark Team
|
|
2778
|
-
* @license MIT
|
|
2779
|
-
*/
|
|
2780
3412
|
|
|
2781
|
-
export { BlockTransformer, IncremarkParser, allPlugins, cloneNode, codeBlockPlugin, countChars, createBlockTransformer, createIncremarkParser, createPlugin, defaultPlugins, imagePlugin, mathPlugin, mermaidPlugin, sliceAst, thematicBreakPlugin };
|
|
3413
|
+
export { BlockTransformer, IncremarkParser, MarkedAstBuilder, allPlugins, cloneNode, codeBlockPlugin, countChars, createBlockTransformer, createIncremarkParser, createPlugin, defaultPlugins, imagePlugin, mathPlugin, mermaidPlugin, sliceAst, thematicBreakPlugin };
|
|
2782
3414
|
//# sourceMappingURL=index.js.map
|
|
2783
3415
|
//# sourceMappingURL=index.js.map
|