@incremark/core 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/detector/index.d.ts +118 -1
- package/dist/detector/index.js +216 -82
- package/dist/detector/index.js.map +1 -1
- package/dist/index-CfgnWMWh.d.ts +225 -0
- package/dist/index.d.ts +31 -40
- package/dist/index.js +943 -684
- package/dist/index.js.map +1 -1
- package/dist/utils/index.d.ts +17 -1
- package/dist/utils/index.js +21 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +2 -1
- package/dist/index-BfVDhalw.d.ts +0 -410
package/dist/detector/index.d.ts
CHANGED
|
@@ -1,4 +1,121 @@
|
|
|
1
|
-
|
|
1
|
+
import { c as BlockContext, C as ContainerConfig, d as ContainerMatch } from '../index-CfgnWMWh.js';
|
|
2
2
|
import 'mdast';
|
|
3
3
|
import 'micromark-util-types';
|
|
4
4
|
import 'mdast-util-from-markdown';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 块类型检测与边界判断
|
|
8
|
+
*
|
|
9
|
+
* Markdown 块级元素的识别规则
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 检测行是否是代码块 fence 开始
|
|
14
|
+
*/
|
|
15
|
+
declare function detectFenceStart(line: string): {
|
|
16
|
+
char: string;
|
|
17
|
+
length: number;
|
|
18
|
+
} | null;
|
|
19
|
+
/**
|
|
20
|
+
* 检测行是否是代码块 fence 结束
|
|
21
|
+
*/
|
|
22
|
+
declare function detectFenceEnd(line: string, context: BlockContext): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* 检测是否是空行或仅包含空白字符
|
|
25
|
+
*/
|
|
26
|
+
declare function isEmptyLine(line: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* 检测是否是 Setext 标题下划线(=== 或 ---)
|
|
29
|
+
* @param line 当前行
|
|
30
|
+
* @param prevLine 前一行
|
|
31
|
+
* @returns 是否是 Setext 标题下划线
|
|
32
|
+
*/
|
|
33
|
+
declare function isSetextHeadingUnderline(line: string, prevLine?: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* 检测是否是标题行
|
|
36
|
+
*/
|
|
37
|
+
declare function isHeading(line: string): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* 检测是否是 thematic break(水平线)
|
|
40
|
+
*/
|
|
41
|
+
declare function isThematicBreak(line: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* 检测是否是列表项开始
|
|
44
|
+
*
|
|
45
|
+
* CommonMark 规范:列表项可以是以下形式:
|
|
46
|
+
* - `- text`(无缩进)
|
|
47
|
+
* - `1. text`(有序列表)
|
|
48
|
+
* - ` - text`(缩进4个空格,作为上一个列表项的延续)
|
|
49
|
+
*
|
|
50
|
+
* 注意:` - text` 这种形式,虽然 `-` 后面没有空格,
|
|
51
|
+
* 但因为前面有4个空格的缩进,所以是列表项的有效形式。
|
|
52
|
+
*/
|
|
53
|
+
declare function isListItemStart(line: string): {
|
|
54
|
+
ordered: boolean;
|
|
55
|
+
indent: number;
|
|
56
|
+
} | null;
|
|
57
|
+
/**
|
|
58
|
+
* 检测是否是引用块开始
|
|
59
|
+
*/
|
|
60
|
+
declare function isBlockquoteStart(line: string): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* 检测是否是 HTML 块
|
|
63
|
+
*/
|
|
64
|
+
declare function isHtmlBlock(line: string): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* 检测表格分隔行
|
|
67
|
+
*/
|
|
68
|
+
declare function isTableDelimiter(line: string): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* 检测是否是脚注定义的起始行
|
|
71
|
+
* 格式: [^id]: content
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* isFootnoteDefinitionStart('[^1]: 脚注内容') // true
|
|
75
|
+
* isFootnoteDefinitionStart('[^note]: 内容') // true
|
|
76
|
+
* isFootnoteDefinitionStart(' 缩进内容') // false
|
|
77
|
+
*/
|
|
78
|
+
declare function isFootnoteDefinitionStart(line: string): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* 检测是否是脚注定义的延续行(缩进行)
|
|
81
|
+
* 至少4个空格或1个tab
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* isFootnoteContinuation(' 第二行') // true
|
|
85
|
+
* isFootnoteContinuation('\t第二行') // true
|
|
86
|
+
* isFootnoteContinuation(' 两个空格') // false
|
|
87
|
+
*/
|
|
88
|
+
declare function isFootnoteContinuation(line: string): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* 检测容器开始或结束
|
|
91
|
+
*
|
|
92
|
+
* 支持格式:
|
|
93
|
+
* - ::: name 开始
|
|
94
|
+
* - ::: name attr 开始(带属性)
|
|
95
|
+
* - ::: 结束
|
|
96
|
+
* - :::::: name 开始(更长的标记,用于嵌套)
|
|
97
|
+
*/
|
|
98
|
+
declare function detectContainer(line: string, config?: ContainerConfig): ContainerMatch | null;
|
|
99
|
+
/**
|
|
100
|
+
* 检测容器结束
|
|
101
|
+
*/
|
|
102
|
+
declare function detectContainerEnd(line: string, context: BlockContext, config?: ContainerConfig): boolean;
|
|
103
|
+
/**
|
|
104
|
+
* 判断两行之间是否构成块边界
|
|
105
|
+
*/
|
|
106
|
+
declare function isBlockBoundary(prevLine: string, currentLine: string, context: BlockContext): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* 创建初始上下文
|
|
109
|
+
*/
|
|
110
|
+
declare function createInitialContext(): BlockContext;
|
|
111
|
+
/**
|
|
112
|
+
* 更新上下文(处理一行后)
|
|
113
|
+
*
|
|
114
|
+
* @param line 当前行
|
|
115
|
+
* @param context 当前上下文
|
|
116
|
+
* @param containerConfig 容器配置
|
|
117
|
+
* @returns 更新后的上下文
|
|
118
|
+
*/
|
|
119
|
+
declare function updateContext(line: string, context: BlockContext, containerConfig?: ContainerConfig | boolean): BlockContext;
|
|
120
|
+
|
|
121
|
+
export { createInitialContext, detectContainer, detectContainerEnd, detectFenceEnd, detectFenceStart, isBlockBoundary, isBlockquoteStart, isEmptyLine, isFootnoteContinuation, isFootnoteDefinitionStart, isHeading, isHtmlBlock, isListItemStart, isSetextHeadingUnderline, isTableDelimiter, isThematicBreak, updateContext };
|
package/dist/detector/index.js
CHANGED
|
@@ -3,14 +3,12 @@ var RE_FENCE_START = /^(\s*)((`{3,})|(~{3,}))/;
|
|
|
3
3
|
var RE_EMPTY_LINE = /^\s*$/;
|
|
4
4
|
var RE_HEADING = /^#{1,6}\s/;
|
|
5
5
|
var RE_THEMATIC_BREAK = /^(\*{3,}|-{3,}|_{3,})\s*$/;
|
|
6
|
-
var RE_UNORDERED_LIST = /^(\s*)([-*+])\s/;
|
|
7
|
-
var RE_ORDERED_LIST = /^(\s*)(\d{1,9})[.)]\s/;
|
|
8
6
|
var RE_BLOCKQUOTE = /^\s{0,3}>/;
|
|
9
7
|
var RE_HTML_BLOCK_1 = /^\s{0,3}<(script|pre|style|textarea|!--|!DOCTYPE|\?|!\[CDATA\[)/i;
|
|
10
8
|
var RE_HTML_BLOCK_2 = /^\s{0,3}<\/?[a-zA-Z][a-zA-Z0-9-]*(\s|>|$)/;
|
|
11
9
|
var RE_TABLE_DELIMITER = /^\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)*\|?$/;
|
|
12
10
|
var RE_ESCAPE_SPECIAL = /[.*+?^${}()|[\]\\]/g;
|
|
13
|
-
var RE_FOOTNOTE_DEFINITION = /^\[\^[^\]]
|
|
11
|
+
var RE_FOOTNOTE_DEFINITION = /^\[\^([^\]]+)\]:\s/;
|
|
14
12
|
var RE_FOOTNOTE_CONTINUATION = /^(?: |\t)/;
|
|
15
13
|
var fenceEndPatternCache = /* @__PURE__ */ new Map();
|
|
16
14
|
var containerPatternCache = /* @__PURE__ */ new Map();
|
|
@@ -38,6 +36,40 @@ function detectFenceEnd(line, context) {
|
|
|
38
36
|
function isEmptyLine(line) {
|
|
39
37
|
return RE_EMPTY_LINE.test(line);
|
|
40
38
|
}
|
|
39
|
+
function isSetextHeadingUnderline(line, prevLine) {
|
|
40
|
+
const trimmed = line.trim();
|
|
41
|
+
if (!/^={3,}$|^-{3,}$/.test(trimmed)) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
if (!prevLine) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
const trimmedPrev = prevLine.trim();
|
|
48
|
+
if (trimmedPrev === "") {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
if (/^#{1,6}\s/.test(trimmedPrev) || /^(\*{3,}|-{3,}|_{3,})\s*$/.test(trimmedPrev)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
if (/^(\s*)([-*+])\s/.test(trimmedPrev) || /^(\s*)(\d{1,9})[.)]\s/.test(trimmedPrev)) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
if (/^\s{0,3}>/.test(trimmedPrev)) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
if (/^(\s*)(`{3,}|~{3,})/.test(trimmedPrev)) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const underlineIndent = line.match(/^(\s*)/)?.[1].length ?? 0;
|
|
64
|
+
if (underlineIndent > 3) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
const contentIndent = prevLine.match(/^(\s*)/)?.[1].length ?? 0;
|
|
68
|
+
if (contentIndent > 3) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
41
73
|
function isHeading(line) {
|
|
42
74
|
return RE_HEADING.test(line);
|
|
43
75
|
}
|
|
@@ -45,13 +77,23 @@ function isThematicBreak(line) {
|
|
|
45
77
|
return RE_THEMATIC_BREAK.test(line.trim());
|
|
46
78
|
}
|
|
47
79
|
function isListItemStart(line) {
|
|
48
|
-
const
|
|
49
|
-
if (
|
|
50
|
-
return
|
|
80
|
+
const hasListMarker = /^(\s*)([-*+]|\d{1,9}[.)])/.test(line);
|
|
81
|
+
if (!hasListMarker) {
|
|
82
|
+
return null;
|
|
51
83
|
}
|
|
52
|
-
const
|
|
53
|
-
if (
|
|
54
|
-
|
|
84
|
+
const match = line.match(/^(\s*)([-*+]|\d{1,9}[.)])(.*)/);
|
|
85
|
+
if (match) {
|
|
86
|
+
const indent = match[1].length;
|
|
87
|
+
const marker = match[2];
|
|
88
|
+
const rest = match[3];
|
|
89
|
+
if (rest.trim()) {
|
|
90
|
+
const isOrdered = /^\d{1,9}[.)]/.test(marker);
|
|
91
|
+
return { ordered: isOrdered, indent };
|
|
92
|
+
}
|
|
93
|
+
if (/^\s+$/.test(rest)) {
|
|
94
|
+
const isOrdered = /^\d{1,9}[.)]/.test(marker);
|
|
95
|
+
return { ordered: isOrdered, indent };
|
|
96
|
+
}
|
|
55
97
|
}
|
|
56
98
|
return null;
|
|
57
99
|
}
|
|
@@ -124,44 +166,36 @@ function isBlockBoundary(prevLine, currentLine, context) {
|
|
|
124
166
|
}
|
|
125
167
|
return false;
|
|
126
168
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (isEmptyLine(line)) {
|
|
139
|
-
return true;
|
|
140
|
-
}
|
|
141
|
-
const contentIndent = line.match(/^(\s*)/)?.[1].length ?? 0;
|
|
142
|
-
return contentIndent > listIndent;
|
|
143
|
-
}
|
|
144
|
-
function updateContext(line, context, containerConfig) {
|
|
145
|
-
const newContext = { ...context };
|
|
146
|
-
const containerCfg = containerConfig === true ? {} : containerConfig === false ? void 0 : containerConfig;
|
|
147
|
-
if (context.inFencedCode) {
|
|
148
|
-
if (detectFenceEnd(line, context)) {
|
|
149
|
-
newContext.inFencedCode = false;
|
|
150
|
-
newContext.fenceChar = void 0;
|
|
151
|
-
newContext.fenceLength = void 0;
|
|
169
|
+
var CodeContextUpdater = class {
|
|
170
|
+
update(line, context) {
|
|
171
|
+
const newContext = { ...context };
|
|
172
|
+
if (context.inFencedCode) {
|
|
173
|
+
if (detectFenceEnd(line, context)) {
|
|
174
|
+
newContext.inFencedCode = false;
|
|
175
|
+
newContext.fenceChar = void 0;
|
|
176
|
+
newContext.fenceLength = void 0;
|
|
177
|
+
return newContext;
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
152
180
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return
|
|
181
|
+
const fence = detectFenceStart(line);
|
|
182
|
+
if (fence) {
|
|
183
|
+
newContext.inFencedCode = true;
|
|
184
|
+
newContext.fenceChar = fence.char;
|
|
185
|
+
newContext.fenceLength = fence.length;
|
|
186
|
+
return newContext;
|
|
187
|
+
}
|
|
188
|
+
return null;
|
|
161
189
|
}
|
|
162
|
-
|
|
190
|
+
};
|
|
191
|
+
var ContainerContextUpdater = class {
|
|
192
|
+
update(line, context, config) {
|
|
193
|
+
if (config === void 0) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
const newContext = { ...context };
|
|
163
197
|
if (context.inContainer) {
|
|
164
|
-
if (detectContainerEnd(line, context,
|
|
198
|
+
if (detectContainerEnd(line, context, config)) {
|
|
165
199
|
newContext.containerDepth = context.containerDepth - 1;
|
|
166
200
|
if (newContext.containerDepth === 0) {
|
|
167
201
|
newContext.inContainer = false;
|
|
@@ -170,14 +204,14 @@ function updateContext(line, context, containerConfig) {
|
|
|
170
204
|
}
|
|
171
205
|
return newContext;
|
|
172
206
|
}
|
|
173
|
-
const nested = detectContainer(line,
|
|
207
|
+
const nested = detectContainer(line, config);
|
|
174
208
|
if (nested && !nested.isEnd) {
|
|
175
209
|
newContext.containerDepth = context.containerDepth + 1;
|
|
176
210
|
return newContext;
|
|
177
211
|
}
|
|
178
212
|
return newContext;
|
|
179
213
|
} else {
|
|
180
|
-
const container = detectContainer(line,
|
|
214
|
+
const container = detectContainer(line, config);
|
|
181
215
|
if (container && !container.isEnd) {
|
|
182
216
|
newContext.inContainer = true;
|
|
183
217
|
newContext.containerMarkerLength = container.markerLength;
|
|
@@ -186,58 +220,158 @@ function updateContext(line, context, containerConfig) {
|
|
|
186
220
|
return newContext;
|
|
187
221
|
}
|
|
188
222
|
}
|
|
223
|
+
return null;
|
|
189
224
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
225
|
+
};
|
|
226
|
+
var FootnoteContextUpdater = class {
|
|
227
|
+
update(line, context) {
|
|
228
|
+
const newContext = { ...context };
|
|
229
|
+
if (!context.inFootnote && isFootnoteDefinitionStart(line)) {
|
|
230
|
+
const identifier = line.match(RE_FOOTNOTE_DEFINITION)?.[1];
|
|
231
|
+
newContext.inFootnote = true;
|
|
232
|
+
newContext.footnoteIdentifier = identifier;
|
|
233
|
+
return newContext;
|
|
234
|
+
}
|
|
235
|
+
if (context.inFootnote) {
|
|
236
|
+
if (isFootnoteDefinitionStart(line)) {
|
|
237
|
+
const identifier = line.match(RE_FOOTNOTE_DEFINITION)?.[1];
|
|
238
|
+
newContext.footnoteIdentifier = identifier;
|
|
239
|
+
return newContext;
|
|
240
|
+
}
|
|
241
|
+
if (isEmptyLine(line)) {
|
|
242
|
+
return { ...context };
|
|
243
|
+
}
|
|
244
|
+
const listItem = isListItemStart(line);
|
|
193
245
|
if (listItem) {
|
|
194
|
-
if (listItem.
|
|
246
|
+
if (listItem.indent === 0) {
|
|
247
|
+
newContext.inFootnote = false;
|
|
248
|
+
newContext.footnoteIdentifier = void 0;
|
|
249
|
+
} else {
|
|
250
|
+
return { ...context };
|
|
251
|
+
}
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
if (isHeading(line) || detectFenceStart(line) || isBlockquoteStart(line)) {
|
|
255
|
+
newContext.inFootnote = false;
|
|
256
|
+
newContext.footnoteIdentifier = void 0;
|
|
257
|
+
return newContext;
|
|
258
|
+
}
|
|
259
|
+
if (isFootnoteContinuation(line)) {
|
|
260
|
+
return { ...context };
|
|
261
|
+
}
|
|
262
|
+
newContext.inFootnote = false;
|
|
263
|
+
newContext.footnoteIdentifier = void 0;
|
|
264
|
+
return newContext;
|
|
265
|
+
}
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
var ListContextUpdater = class {
|
|
270
|
+
/**
|
|
271
|
+
* 检测是否是列表项的延续内容(缩进内容或空行)
|
|
272
|
+
*/
|
|
273
|
+
isListContinuation(line, listIndent) {
|
|
274
|
+
if (isEmptyLine(line)) {
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
const contentIndent = line.match(/^(\s*)/)?.[1].length ?? 0;
|
|
278
|
+
return contentIndent > listIndent;
|
|
279
|
+
}
|
|
280
|
+
update(line, context) {
|
|
281
|
+
const newContext = { ...context };
|
|
282
|
+
const listItem = isListItemStart(line);
|
|
283
|
+
if (context.inList) {
|
|
284
|
+
if (context.listMayEnd) {
|
|
285
|
+
if (listItem) {
|
|
286
|
+
if (listItem.ordered === context.listOrdered && listItem.indent === context.listIndent) {
|
|
287
|
+
newContext.listMayEnd = false;
|
|
288
|
+
return newContext;
|
|
289
|
+
}
|
|
290
|
+
newContext.listOrdered = listItem.ordered;
|
|
291
|
+
newContext.listIndent = listItem.indent;
|
|
292
|
+
newContext.listMayEnd = false;
|
|
293
|
+
return newContext;
|
|
294
|
+
} else if (this.isListContinuation(line, context.listIndent ?? 0)) {
|
|
295
|
+
newContext.listMayEnd = isEmptyLine(line);
|
|
296
|
+
return newContext;
|
|
297
|
+
} else {
|
|
298
|
+
newContext.inList = false;
|
|
299
|
+
newContext.listOrdered = void 0;
|
|
300
|
+
newContext.listIndent = void 0;
|
|
195
301
|
newContext.listMayEnd = false;
|
|
196
302
|
return newContext;
|
|
197
303
|
}
|
|
198
|
-
newContext.inList = true;
|
|
199
|
-
newContext.listOrdered = listItem.ordered;
|
|
200
|
-
newContext.listIndent = listItem.indent;
|
|
201
|
-
newContext.listMayEnd = false;
|
|
202
|
-
return newContext;
|
|
203
|
-
} else if (isListContinuation(line, context.listIndent ?? 0)) {
|
|
204
|
-
newContext.listMayEnd = isEmptyLine(line);
|
|
205
|
-
return newContext;
|
|
206
304
|
} else {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
305
|
+
if (listItem) {
|
|
306
|
+
return null;
|
|
307
|
+
} else if (isEmptyLine(line)) {
|
|
308
|
+
newContext.listMayEnd = true;
|
|
309
|
+
return newContext;
|
|
310
|
+
} else if (this.isListContinuation(line, context.listIndent ?? 0)) {
|
|
311
|
+
return null;
|
|
312
|
+
} else {
|
|
313
|
+
newContext.inList = false;
|
|
314
|
+
newContext.listOrdered = void 0;
|
|
315
|
+
newContext.listIndent = void 0;
|
|
316
|
+
newContext.listMayEnd = false;
|
|
317
|
+
return newContext;
|
|
318
|
+
}
|
|
212
319
|
}
|
|
213
320
|
} else {
|
|
214
321
|
if (listItem) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
newContext.
|
|
218
|
-
return newContext;
|
|
219
|
-
} else if (isListContinuation(line, context.listIndent ?? 0)) {
|
|
220
|
-
return newContext;
|
|
221
|
-
} else {
|
|
222
|
-
newContext.inList = false;
|
|
223
|
-
newContext.listOrdered = void 0;
|
|
224
|
-
newContext.listIndent = void 0;
|
|
322
|
+
newContext.inList = true;
|
|
323
|
+
newContext.listOrdered = listItem.ordered;
|
|
324
|
+
newContext.listIndent = listItem.indent;
|
|
225
325
|
newContext.listMayEnd = false;
|
|
226
326
|
return newContext;
|
|
227
327
|
}
|
|
228
328
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
function createInitialContext() {
|
|
333
|
+
return {
|
|
334
|
+
inFencedCode: false,
|
|
335
|
+
listDepth: 0,
|
|
336
|
+
blockquoteDepth: 0,
|
|
337
|
+
inContainer: false,
|
|
338
|
+
containerDepth: 0,
|
|
339
|
+
inList: false,
|
|
340
|
+
inFootnote: false,
|
|
341
|
+
footnoteIdentifier: void 0
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
var ContextManager = class {
|
|
345
|
+
updaters = [
|
|
346
|
+
new CodeContextUpdater(),
|
|
347
|
+
new ContainerContextUpdater(),
|
|
348
|
+
new FootnoteContextUpdater(),
|
|
349
|
+
new ListContextUpdater()
|
|
350
|
+
];
|
|
351
|
+
/**
|
|
352
|
+
* 更新上下文(处理一行后)
|
|
353
|
+
*
|
|
354
|
+
* @param line 当前行
|
|
355
|
+
* @param context 当前上下文
|
|
356
|
+
* @param containerConfig 容器配置
|
|
357
|
+
* @returns 更新后的上下文
|
|
358
|
+
*/
|
|
359
|
+
update(line, context, containerConfig) {
|
|
360
|
+
const config = containerConfig === true ? {} : containerConfig === false ? void 0 : containerConfig;
|
|
361
|
+
for (const updater of this.updaters) {
|
|
362
|
+
const result = updater.update(line, context, config);
|
|
363
|
+
if (result !== null) {
|
|
364
|
+
return result;
|
|
365
|
+
}
|
|
236
366
|
}
|
|
367
|
+
return { ...context };
|
|
237
368
|
}
|
|
238
|
-
|
|
369
|
+
};
|
|
370
|
+
var contextManager = new ContextManager();
|
|
371
|
+
function updateContext(line, context, containerConfig) {
|
|
372
|
+
return contextManager.update(line, context, containerConfig);
|
|
239
373
|
}
|
|
240
374
|
|
|
241
|
-
export { createInitialContext, detectContainer, detectContainerEnd, detectFenceEnd, detectFenceStart, isBlockBoundary, isBlockquoteStart, isEmptyLine, isFootnoteContinuation, isFootnoteDefinitionStart, isHeading, isHtmlBlock, isListItemStart, isTableDelimiter, isThematicBreak, updateContext };
|
|
375
|
+
export { createInitialContext, detectContainer, detectContainerEnd, detectFenceEnd, detectFenceStart, isBlockBoundary, isBlockquoteStart, isEmptyLine, isFootnoteContinuation, isFootnoteDefinitionStart, isHeading, isHtmlBlock, isListItemStart, isSetextHeadingUnderline, isTableDelimiter, isThematicBreak, updateContext };
|
|
242
376
|
//# sourceMappingURL=index.js.map
|
|
243
377
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/detector/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * 块类型检测与边界判断\n *\n * Markdown 块级元素的识别规则\n */\n\nimport type { BlockContext, ContainerConfig, ContainerMatch } from '../types'\n\n// ============ 预编译正则表达式(性能优化) ============\n\nconst RE_FENCE_START = /^(\\s*)((`{3,})|(~{3,}))/\nconst RE_EMPTY_LINE = /^\\s*$/\nconst RE_HEADING = /^#{1,6}\\s/\nconst RE_THEMATIC_BREAK = /^(\\*{3,}|-{3,}|_{3,})\\s*$/\nconst RE_UNORDERED_LIST = /^(\\s*)([-*+])\\s/\nconst RE_ORDERED_LIST = /^(\\s*)(\\d{1,9})[.)]\\s/\nconst RE_BLOCKQUOTE = /^\\s{0,3}>/\nconst RE_HTML_BLOCK_1 = /^\\s{0,3}<(script|pre|style|textarea|!--|!DOCTYPE|\\?|!\\[CDATA\\[)/i\nconst RE_HTML_BLOCK_2 = /^\\s{0,3}<\\/?[a-zA-Z][a-zA-Z0-9-]*(\\s|>|$)/\nconst RE_TABLE_DELIMITER = /^\\|?\\s*:?-{3,}:?\\s*(\\|\\s*:?-{3,}:?\\s*)*\\|?$/\nconst RE_ESCAPE_SPECIAL = /[.*+?^${}()|[\\]\\\\]/g\nconst RE_FOOTNOTE_DEFINITION = /^\\[\\^[^\\]]+\\]:\\s/\nconst RE_FOOTNOTE_CONTINUATION = /^(?: |\\t)/\n\n/** fence 结束模式缓存 */\nconst fenceEndPatternCache = new Map<string, RegExp>()\n\n/** 容器模式缓存 */\nconst containerPatternCache = new Map<string, RegExp>()\n\n// ============ 代码块检测 ============\n\n/**\n * 检测行是否是代码块 fence 开始\n */\nexport function detectFenceStart(line: string): { char: string; length: number } | null {\n const match = line.match(RE_FENCE_START)\n if (match) {\n const fence = match[2]\n const char = fence[0]\n return { char, length: fence.length }\n }\n return null\n}\n\n/**\n * 检测行是否是代码块 fence 结束\n */\nexport function detectFenceEnd(line: string, context: BlockContext): boolean {\n if (!context.inFencedCode || !context.fenceChar || !context.fenceLength) {\n return false\n }\n\n // 使用缓存的正则表达式\n const cacheKey = `${context.fenceChar}-${context.fenceLength}`\n let pattern = fenceEndPatternCache.get(cacheKey)\n if (!pattern) {\n pattern = new RegExp(`^\\\\s{0,3}${context.fenceChar}{${context.fenceLength},}\\\\s*$`)\n fenceEndPatternCache.set(cacheKey, pattern)\n }\n return pattern.test(line)\n}\n\n// ============ 行类型检测 ============\n\n/**\n * 检测是否是空行或仅包含空白字符\n */\nexport function isEmptyLine(line: string): boolean {\n return RE_EMPTY_LINE.test(line)\n}\n\n/**\n * 检测是否是标题行\n */\nexport function isHeading(line: string): boolean {\n return RE_HEADING.test(line)\n}\n\n/**\n * 检测是否是 thematic break(水平线)\n */\nexport function isThematicBreak(line: string): boolean {\n return RE_THEMATIC_BREAK.test(line.trim())\n}\n\n/**\n * 检测是否是列表项开始\n */\nexport function isListItemStart(line: string): { ordered: boolean; indent: number } | null {\n // 无序列表: - * +\n const unordered = line.match(RE_UNORDERED_LIST)\n if (unordered) {\n return { ordered: false, indent: unordered[1].length }\n }\n\n // 有序列表: 1. 2) 等\n const ordered = line.match(RE_ORDERED_LIST)\n if (ordered) {\n return { ordered: true, indent: ordered[1].length }\n }\n\n return null\n}\n\n/**\n * 检测是否是引用块开始\n */\nexport function isBlockquoteStart(line: string): boolean {\n return RE_BLOCKQUOTE.test(line)\n}\n\n/**\n * 检测是否是 HTML 块\n */\nexport function isHtmlBlock(line: string): boolean {\n return RE_HTML_BLOCK_1.test(line) || RE_HTML_BLOCK_2.test(line)\n}\n\n/**\n * 检测表格分隔行\n */\nexport function isTableDelimiter(line: string): boolean {\n return RE_TABLE_DELIMITER.test(line.trim())\n}\n\n// ============ 脚注检测 ============\n\n/**\n * 检测是否是脚注定义的起始行\n * 格式: [^id]: content\n * \n * @example\n * isFootnoteDefinitionStart('[^1]: 脚注内容') // true\n * isFootnoteDefinitionStart('[^note]: 内容') // true\n * isFootnoteDefinitionStart(' 缩进内容') // false\n */\nexport function isFootnoteDefinitionStart(line: string): boolean {\n return RE_FOOTNOTE_DEFINITION.test(line)\n}\n\n/**\n * 检测是否是脚注定义的延续行(缩进行)\n * 至少4个空格或1个tab\n * \n * @example\n * isFootnoteContinuation(' 第二行') // true\n * isFootnoteContinuation('\\t第二行') // true\n * isFootnoteContinuation(' 两个空格') // false\n */\nexport function isFootnoteContinuation(line: string): boolean {\n return RE_FOOTNOTE_CONTINUATION.test(line)\n}\n\n// ============ 容器检测 ============\n\n/**\n * 检测容器开始或结束\n *\n * 支持格式:\n * - ::: name 开始\n * - ::: name attr 开始(带属性)\n * - ::: 结束\n * - :::::: name 开始(更长的标记,用于嵌套)\n */\nexport function detectContainer(line: string, config?: ContainerConfig): ContainerMatch | null {\n const marker = config?.marker || ':'\n const minLength = config?.minMarkerLength || 3\n\n // 使用缓存的正则表达式\n const cacheKey = `${marker}-${minLength}`\n let pattern = containerPatternCache.get(cacheKey)\n if (!pattern) {\n const escapedMarker = marker.replace(RE_ESCAPE_SPECIAL, '\\\\$&')\n // 支持两种格式:\n // 1. ::: name attr (有空格分隔)\n // 2. :::name{...} (directive 语法,无空格)\n pattern = new RegExp(\n `^(\\\\s*)(${escapedMarker}{${minLength},})(?:\\\\s*(\\\\w[\\\\w-]*))?(?:\\\\{[^}]*\\\\})?(?:\\\\s+(.*))?\\\\s*$`\n )\n containerPatternCache.set(cacheKey, pattern)\n }\n\n const match = line.match(pattern)\n if (!match) {\n return null\n }\n\n const markerLength = match[2].length\n const name = match[3] || ''\n const isEnd = !name && !match[4]\n\n if (!isEnd && config?.allowedNames && config.allowedNames.length > 0) {\n if (!config.allowedNames.includes(name)) {\n return null\n }\n }\n\n return { name, markerLength, isEnd }\n}\n\n/**\n * 检测容器结束\n */\nexport function detectContainerEnd(\n line: string,\n context: BlockContext,\n config?: ContainerConfig\n): boolean {\n if (!context.inContainer || !context.containerMarkerLength) {\n return false\n }\n\n const result = detectContainer(line, config)\n if (!result) {\n return false\n }\n\n return result.isEnd && result.markerLength >= context.containerMarkerLength\n}\n\n// ============ 边界检测 ============\n\n/**\n * 判断两行之间是否构成块边界\n */\nexport function isBlockBoundary(\n prevLine: string,\n currentLine: string,\n context: BlockContext\n): boolean {\n if (context.inFencedCode) {\n return detectFenceEnd(currentLine, context)\n }\n\n if (isEmptyLine(prevLine) && !isEmptyLine(currentLine)) {\n return true\n }\n\n if (isHeading(currentLine) && !isEmptyLine(prevLine)) {\n return true\n }\n\n if (isThematicBreak(currentLine)) {\n return true\n }\n\n if (detectFenceStart(currentLine)) {\n return true\n }\n\n return false\n}\n\n// ============ 上下文管理 ============\n\n/**\n * 创建初始上下文\n */\nexport function createInitialContext(): BlockContext {\n return {\n inFencedCode: false,\n listDepth: 0,\n blockquoteDepth: 0,\n inContainer: false,\n containerDepth: 0,\n inList: false\n }\n}\n\n/**\n * 检测是否是列表项的延续内容(缩进内容或空行)\n * @param line 当前行\n * @param listIndent 列表的基础缩进\n */\nfunction isListContinuation(line: string, listIndent: number): boolean {\n // 空行可能是列表内部的段落分隔\n if (isEmptyLine(line)) {\n return true\n }\n\n // 检查是否有足够的缩进(列表内容至少需要缩进到列表标记之后)\n // 通常列表标记后至少需要 2 个字符的缩进(如 \"1. \" 或 \"- \")\n const contentIndent = line.match(/^(\\s*)/)?.[1].length ?? 0\n return contentIndent > listIndent\n}\n\n/**\n * 更新上下文(处理一行后)\n */\nexport function updateContext(\n line: string,\n context: BlockContext,\n containerConfig?: ContainerConfig | boolean\n): BlockContext {\n const newContext = { ...context }\n\n const containerCfg =\n containerConfig === true ? {} : containerConfig === false ? undefined : containerConfig\n\n // 代码块优先级最高\n if (context.inFencedCode) {\n if (detectFenceEnd(line, context)) {\n newContext.inFencedCode = false\n newContext.fenceChar = undefined\n newContext.fenceLength = undefined\n }\n return newContext\n }\n\n const fence = detectFenceStart(line)\n if (fence) {\n newContext.inFencedCode = true\n newContext.fenceChar = fence.char\n newContext.fenceLength = fence.length\n return newContext\n }\n\n // 容器处理\n if (containerCfg !== undefined) {\n if (context.inContainer) {\n // 检查是否是容器结束\n if (detectContainerEnd(line, context, containerCfg)) {\n newContext.containerDepth = context.containerDepth - 1\n if (newContext.containerDepth === 0) {\n newContext.inContainer = false\n newContext.containerMarkerLength = undefined\n newContext.containerName = undefined\n }\n return newContext\n }\n\n // 检查是否是嵌套容器开始\n const nested = detectContainer(line, containerCfg)\n if (nested && !nested.isEnd) {\n newContext.containerDepth = context.containerDepth + 1\n return newContext\n }\n\n // ⚠️ 关键:在容器内,无论是什么内容(空行、列表、段落等),都保持 inContainer = true\n // 只有容器结束标记才能改变容器状态\n // 这里不需要做任何操作,因为 newContext 已经复制了 context,inContainer 已经是 true\n return newContext\n } else {\n // 不在容器内,检查是否是容器开始\n const container = detectContainer(line, containerCfg)\n if (container && !container.isEnd) {\n newContext.inContainer = true\n newContext.containerMarkerLength = container.markerLength\n newContext.containerName = container.name\n newContext.containerDepth = 1\n return newContext\n }\n }\n }\n\n // 列表处理\n const listItem = isListItemStart(line)\n\n if (context.inList) {\n // 已经在列表中\n if (context.listMayEnd) {\n // 上一行是空行,需要确认列表是否结束\n if (listItem) {\n // 遇到新的列表项\n // 检查是否是同类型列表的延续\n if (listItem.ordered === context.listOrdered && listItem.indent === context.listIndent) {\n // 同类型同级别列表项,列表继续\n newContext.listMayEnd = false\n return newContext\n }\n // 不同类型或不同级别,列表结束,新列表开始\n newContext.inList = true\n newContext.listOrdered = listItem.ordered\n newContext.listIndent = listItem.indent\n newContext.listMayEnd = false\n return newContext\n } else if (isListContinuation(line, context.listIndent ?? 0)) {\n // 缩进内容或空行,列表继续\n newContext.listMayEnd = isEmptyLine(line)\n return newContext\n } else {\n // 非列表内容,列表结束\n newContext.inList = false\n newContext.listOrdered = undefined\n newContext.listIndent = undefined\n newContext.listMayEnd = false\n return newContext\n }\n } else {\n // 上一行不是空行\n if (listItem) {\n // 新列表项(可能是同级或嵌套)\n return newContext\n } else if (isEmptyLine(line)) {\n // 遇到空行,列表可能结束\n newContext.listMayEnd = true\n return newContext\n } else if (isListContinuation(line, context.listIndent ?? 0)) {\n // 缩进内容,列表继续\n return newContext\n } else {\n // 非缩进非列表内容,列表结束\n newContext.inList = false\n newContext.listOrdered = undefined\n newContext.listIndent = undefined\n newContext.listMayEnd = false\n return newContext\n }\n }\n } else {\n // 不在列表中\n if (listItem) {\n // 列表开始\n newContext.inList = true\n newContext.listOrdered = listItem.ordered\n newContext.listIndent = listItem.indent\n newContext.listMayEnd = false\n return newContext\n }\n }\n\n return newContext\n}\n\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/detector/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * 块类型检测与边界判断\n *\n * Markdown 块级元素的识别规则\n */\n\nimport type { BlockContext, ContainerConfig, ContainerMatch } from '../types'\n\n// ============ 预编译正则表达式(性能优化) ============\n\nconst RE_FENCE_START = /^(\\s*)((`{3,})|(~{3,}))/\nconst RE_EMPTY_LINE = /^\\s*$/\nconst RE_HEADING = /^#{1,6}\\s/\nconst RE_THEMATIC_BREAK = /^(\\*{3,}|-{3,}|_{3,})\\s*$/\nconst RE_UNORDERED_LIST = /^(\\s*)([-*+])\\s/\nconst RE_ORDERED_LIST = /^(\\s*)(\\d{1,9})[.)]\\s/\nconst RE_BLOCKQUOTE = /^\\s{0,3}>/\nconst RE_HTML_BLOCK_1 = /^\\s{0,3}<(script|pre|style|textarea|!--|!DOCTYPE|\\?|!\\[CDATA\\[)/i\nconst RE_HTML_BLOCK_2 = /^\\s{0,3}<\\/?[a-zA-Z][a-zA-Z0-9-]*(\\s|>|$)/\nconst RE_TABLE_DELIMITER = /^\\|?\\s*:?-{3,}:?\\s*(\\|\\s*:?-{3,}:?\\s*)*\\|?$/\nconst RE_ESCAPE_SPECIAL = /[.*+?^${}()|[\\]\\\\]/g\nconst RE_FOOTNOTE_DEFINITION = /^\\[\\^([^\\]]+)\\]:\\s/\nconst RE_FOOTNOTE_CONTINUATION = /^(?: |\\t)/\n\n/** fence 结束模式缓存 */\nconst fenceEndPatternCache = new Map<string, RegExp>()\n\n/** 容器模式缓存 */\nconst containerPatternCache = new Map<string, RegExp>()\n\n// ============ 代码块检测 ============\n\n/**\n * 检测行是否是代码块 fence 开始\n */\nexport function detectFenceStart(line: string): { char: string; length: number } | null {\n const match = line.match(RE_FENCE_START)\n if (match) {\n const fence = match[2]\n const char = fence[0]\n return { char, length: fence.length }\n }\n return null\n}\n\n/**\n * 检测行是否是代码块 fence 结束\n */\nexport function detectFenceEnd(line: string, context: BlockContext): boolean {\n if (!context.inFencedCode || !context.fenceChar || !context.fenceLength) {\n return false\n }\n\n // 使用缓存的正则表达式\n const cacheKey = `${context.fenceChar}-${context.fenceLength}`\n let pattern = fenceEndPatternCache.get(cacheKey)\n if (!pattern) {\n pattern = new RegExp(`^\\\\s{0,3}${context.fenceChar}{${context.fenceLength},}\\\\s*$`)\n fenceEndPatternCache.set(cacheKey, pattern)\n }\n return pattern.test(line)\n}\n\n// ============ 行类型检测 ============\n\n/**\n * 检测是否是空行或仅包含空白字符\n */\nexport function isEmptyLine(line: string): boolean {\n return RE_EMPTY_LINE.test(line)\n}\n\n/**\n * 检测是否是 Setext 标题下划线(=== 或 ---)\n * @param line 当前行\n * @param prevLine 前一行\n * @returns 是否是 Setext 标题下划线\n */\nexport function isSetextHeadingUnderline(line: string, prevLine?: string): boolean {\n const trimmed = line.trim()\n\n // 检查是否是 === 或 --- 形式的下划线\n if (!/^={3,}$|^-{3,}$/.test(trimmed)) {\n return false\n }\n\n // 如果没有前一行,不能判断\n if (!prevLine) {\n return false\n }\n\n const trimmedPrev = prevLine.trim()\n\n // 前一行是空行,不是 Setext 标题\n if (trimmedPrev === '') {\n return false\n }\n\n // 前一行是 ATX 标题或 thematic break,不是 Setext 标题\n if (/^#{1,6}\\s/.test(trimmedPrev) || /^(\\*{3,}|-{3,}|_{3,})\\s*$/.test(trimmedPrev)) {\n return false\n }\n\n // 前一行是列表项,不是 Setext 标题\n if (/^(\\s*)([-*+])\\s/.test(trimmedPrev) || /^(\\s*)(\\d{1,9})[.)]\\s/.test(trimmedPrev)) {\n return false\n }\n\n // 前一行是引用块,不是 Setext 标题\n if (/^\\s{0,3}>/.test(trimmedPrev)) {\n return false\n }\n\n // 前一行是代码块 fence,不是 Setext 标题\n if (/^(\\s*)(`{3,}|~{3,})/.test(trimmedPrev)) {\n return false\n }\n\n // 检查下划线行的缩进(不超过3个空格)\n const underlineIndent = line.match(/^(\\s*)/)?.[1].length ?? 0\n if (underlineIndent > 3) {\n return false\n }\n\n // 检查前一行缩进(不超过3个空格)\n const contentIndent = prevLine.match(/^(\\s*)/)?.[1].length ?? 0\n if (contentIndent > 3) {\n return false\n }\n\n return true\n}\n\n/**\n * 检测是否是标题行\n */\nexport function isHeading(line: string): boolean {\n return RE_HEADING.test(line)\n}\n\n/**\n * 检测是否是 thematic break(水平线)\n */\nexport function isThematicBreak(line: string): boolean {\n return RE_THEMATIC_BREAK.test(line.trim())\n}\n\n/**\n * 检测是否是列表项开始\n *\n * CommonMark 规范:列表项可以是以下形式:\n * - `- text`(无缩进)\n * - `1. text`(有序列表)\n * - ` - text`(缩进4个空格,作为上一个列表项的延续)\n *\n * 注意:` - text` 这种形式,虽然 `-` 后面没有空格,\n * 但因为前面有4个空格的缩进,所以是列表项的有效形式。\n */\nexport function isListItemStart(line: string): { ordered: boolean; indent: number } | null {\n // 先检查是否以列表标记开头(-、*、+、数字)\n const hasListMarker = /^(\\s*)([-*+]|\\d{1,9}[.)])/.test(line)\n \n if (!hasListMarker) {\n return null\n }\n \n // 如果有列表标记,检查是否是列表项的延续(缩进4+个空格)\n const match = line.match(/^(\\s*)([-*+]|\\d{1,9}[.)])(.*)/)\n if (match) {\n const indent = match[1].length\n const marker = match[2]\n const rest = match[3]\n \n // 如果标记后有内容,检查是否是有效的列表项\n if (rest.trim()) {\n const isOrdered = /^\\d{1,9}[.)]/.test(marker)\n return { ordered: isOrdered, indent }\n }\n \n // 标记后只有空格,可能是缩进的列表项\n // 如 \" - text\" 或 \" 1. text\"\n if (/^\\s+$/.test(rest)) {\n const isOrdered = /^\\d{1,9}[.)]/.test(marker)\n return { ordered: isOrdered, indent }\n }\n }\n \n return null\n}\n\n/**\n * 检测是否是引用块开始\n */\nexport function isBlockquoteStart(line: string): boolean {\n return RE_BLOCKQUOTE.test(line)\n}\n\n/**\n * 检测是否是 HTML 块\n */\nexport function isHtmlBlock(line: string): boolean {\n return RE_HTML_BLOCK_1.test(line) || RE_HTML_BLOCK_2.test(line)\n}\n\n/**\n * 检测表格分隔行\n */\nexport function isTableDelimiter(line: string): boolean {\n return RE_TABLE_DELIMITER.test(line.trim())\n}\n\n// ============ 脚注检测 ============\n\n/**\n * 检测是否是脚注定义的起始行\n * 格式: [^id]: content\n * \n * @example\n * isFootnoteDefinitionStart('[^1]: 脚注内容') // true\n * isFootnoteDefinitionStart('[^note]: 内容') // true\n * isFootnoteDefinitionStart(' 缩进内容') // false\n */\nexport function isFootnoteDefinitionStart(line: string): boolean {\n return RE_FOOTNOTE_DEFINITION.test(line)\n}\n\n/**\n * 检测是否是脚注定义的延续行(缩进行)\n * 至少4个空格或1个tab\n * \n * @example\n * isFootnoteContinuation(' 第二行') // true\n * isFootnoteContinuation('\\t第二行') // true\n * isFootnoteContinuation(' 两个空格') // false\n */\nexport function isFootnoteContinuation(line: string): boolean {\n return RE_FOOTNOTE_CONTINUATION.test(line)\n}\n\n// ============ 容器检测 ============\n\n/**\n * 检测容器开始或结束\n *\n * 支持格式:\n * - ::: name 开始\n * - ::: name attr 开始(带属性)\n * - ::: 结束\n * - :::::: name 开始(更长的标记,用于嵌套)\n */\nexport function detectContainer(line: string, config?: ContainerConfig): ContainerMatch | null {\n const marker = config?.marker || ':'\n const minLength = config?.minMarkerLength || 3\n\n // 使用缓存的正则表达式\n const cacheKey = `${marker}-${minLength}`\n let pattern = containerPatternCache.get(cacheKey)\n if (!pattern) {\n const escapedMarker = marker.replace(RE_ESCAPE_SPECIAL, '\\\\$&')\n // 支持两种格式:\n // 1. ::: name attr (有空格分隔)\n // 2. :::name{...} (directive 语法,无空格)\n pattern = new RegExp(\n `^(\\\\s*)(${escapedMarker}{${minLength},})(?:\\\\s*(\\\\w[\\\\w-]*))?(?:\\\\{[^}]*\\\\})?(?:\\\\s+(.*))?\\\\s*$`\n )\n containerPatternCache.set(cacheKey, pattern)\n }\n\n const match = line.match(pattern)\n if (!match) {\n return null\n }\n\n const markerLength = match[2].length\n const name = match[3] || ''\n const isEnd = !name && !match[4]\n\n if (!isEnd && config?.allowedNames && config.allowedNames.length > 0) {\n if (!config.allowedNames.includes(name)) {\n return null\n }\n }\n\n return { name, markerLength, isEnd }\n}\n\n/**\n * 检测容器结束\n */\nexport function detectContainerEnd(\n line: string,\n context: BlockContext,\n config?: ContainerConfig\n): boolean {\n if (!context.inContainer || !context.containerMarkerLength) {\n return false\n }\n\n const result = detectContainer(line, config)\n if (!result) {\n return false\n }\n\n return result.isEnd && result.markerLength >= context.containerMarkerLength\n}\n\n// ============ 边界检测 ============\n\n/**\n * 判断两行之间是否构成块边界\n */\nexport function isBlockBoundary(\n prevLine: string,\n currentLine: string,\n context: BlockContext\n): boolean {\n if (context.inFencedCode) {\n return detectFenceEnd(currentLine, context)\n }\n\n if (isEmptyLine(prevLine) && !isEmptyLine(currentLine)) {\n return true\n }\n\n if (isHeading(currentLine) && !isEmptyLine(prevLine)) {\n return true\n }\n\n if (isThematicBreak(currentLine)) {\n return true\n }\n\n if (detectFenceStart(currentLine)) {\n return true\n }\n\n return false\n}\n\n// ============ 上下文管理 ============\n\n/**\n * 上下文更新器接口\n */\ninterface ContextUpdater {\n /**\n * 尝试更新上下文\n * @param line 当前行\n * @param context 当前上下文\n * @returns 更新后的上下文,如果不处理返回 null\n */\n update(line: string, context: BlockContext, config?: ContainerConfig): BlockContext | null\n}\n\n/**\n * 代码块上下文更新器\n * 优先级:1(最高)\n */\nclass CodeContextUpdater implements ContextUpdater {\n update(line: string, context: BlockContext): BlockContext | null {\n const newContext = { ...context }\n\n if (context.inFencedCode) {\n if (detectFenceEnd(line, context)) {\n newContext.inFencedCode = false\n newContext.fenceChar = undefined\n newContext.fenceLength = undefined\n return newContext\n }\n return null // 在代码块内,不处理其他逻辑\n }\n\n const fence = detectFenceStart(line)\n if (fence) {\n newContext.inFencedCode = true\n newContext.fenceChar = fence.char\n newContext.fenceLength = fence.length\n return newContext\n }\n\n return null\n }\n}\n\n/**\n * 容器上下文更新器\n * 优先级:2\n */\nclass ContainerContextUpdater implements ContextUpdater {\n update(line: string, context: BlockContext, config?: ContainerConfig): BlockContext | null {\n if (config === undefined) {\n return null\n }\n\n const newContext = { ...context }\n\n if (context.inContainer) {\n // 检查是否是容器结束\n if (detectContainerEnd(line, context, config)) {\n newContext.containerDepth = context.containerDepth - 1\n if (newContext.containerDepth === 0) {\n newContext.inContainer = false\n newContext.containerMarkerLength = undefined\n newContext.containerName = undefined\n }\n return newContext\n }\n\n // 检查是否是嵌套容器开始\n const nested = detectContainer(line, config)\n if (nested && !nested.isEnd) {\n newContext.containerDepth = context.containerDepth + 1\n return newContext\n }\n\n // ⚠️ 关键:在容器内,无论是什么内容(空行、列表、段落等),都保持 inContainer = true\n // 只有容器结束标记才能改变容器状态\n // 这里不需要做任何操作,因为 newContext 已经复制了 context,inContainer 已经是 true\n return newContext\n } else {\n // 不在容器内,检查是否是容器开始\n const container = detectContainer(line, config)\n if (container && !container.isEnd) {\n newContext.inContainer = true\n newContext.containerMarkerLength = container.markerLength\n newContext.containerName = container.name\n newContext.containerDepth = 1\n return newContext\n }\n }\n\n return null\n }\n}\n\n/**\n * 脚注上下文更新器\n * 优先级:3\n */\nclass FootnoteContextUpdater implements ContextUpdater {\n update(line: string, context: BlockContext): BlockContext | null {\n const newContext = { ...context }\n\n // 脚注定义开始(不在脚注中)\n if (!context.inFootnote && isFootnoteDefinitionStart(line)) {\n const identifier = line.match(RE_FOOTNOTE_DEFINITION)?.[1]\n newContext.inFootnote = true\n newContext.footnoteIdentifier = identifier\n return newContext\n }\n\n // 在脚注中\n if (context.inFootnote) {\n // 遇到新脚注定义:前一个脚注结束,新脚注开始\n if (isFootnoteDefinitionStart(line)) {\n const identifier = line.match(RE_FOOTNOTE_DEFINITION)?.[1]\n newContext.footnoteIdentifier = identifier\n return newContext\n }\n\n // 空行:保持脚注状态(支持脚注内部的多段落)\n // 返回当前上下文,阻止责任链继续\n if (isEmptyLine(line)) {\n return { ...context }\n }\n\n // 列表项处理\n const listItem = isListItemStart(line)\n if (listItem) {\n // 无缩进列表项:脚注结束\n // 缩进列表项:脚注的延续内容(包含嵌套列表)\n if (listItem.indent === 0) {\n newContext.inFootnote = false\n newContext.footnoteIdentifier = undefined\n } else {\n // 缩进列表项:脚注的延续内容,返回当前上下文阻止责任链\n return { ...context }\n }\n return null // 让列表处理器处理无缩进情况\n }\n\n // 其他块结束脚注\n if (isHeading(line) || detectFenceStart(line) || isBlockquoteStart(line)) {\n newContext.inFootnote = false\n newContext.footnoteIdentifier = undefined\n return newContext\n }\n\n // 脚注延续:以4+空格开头\n if (isFootnoteContinuation(line)) {\n return { ...context }\n }\n\n // 其他内容(普通文本、表格等),脚注结束\n newContext.inFootnote = false\n newContext.footnoteIdentifier = undefined\n return newContext\n }\n\n return null\n }\n}\n\n/**\n * 列表上下文更新器\n * 优先级:4\n */\nclass ListContextUpdater implements ContextUpdater {\n /**\n * 检测是否是列表项的延续内容(缩进内容或空行)\n */\n private isListContinuation(line: string, listIndent: number): boolean {\n // 空行可能是列表内部的段落分隔\n if (isEmptyLine(line)) {\n return true\n }\n\n // 检查是否有足够的缩进\n const contentIndent = line.match(/^(\\s*)/)?.[1].length ?? 0\n return contentIndent > listIndent\n }\n\n update(line: string, context: BlockContext): BlockContext | null {\n const newContext = { ...context }\n const listItem = isListItemStart(line)\n\n if (context.inList) {\n // 已经在列表中\n if (context.listMayEnd) {\n // 上一行是空行,需要确认列表是否结束\n if (listItem) {\n // 遇到新的列表项\n // 检查是否是同类型列表的延续\n if (listItem.ordered === context.listOrdered && listItem.indent === context.listIndent) {\n // 同类型同级别列表项,列表继续\n newContext.listMayEnd = false\n return newContext\n }\n // 不同类型或不同级别,列表结束,新列表开始\n newContext.listOrdered = listItem.ordered\n newContext.listIndent = listItem.indent\n newContext.listMayEnd = false\n return newContext\n } else if (this.isListContinuation(line, context.listIndent ?? 0)) {\n // 缩进内容或空行,列表继续\n newContext.listMayEnd = isEmptyLine(line)\n return newContext\n } else {\n // 非列表内容,列表结束\n newContext.inList = false\n newContext.listOrdered = undefined\n newContext.listIndent = undefined\n newContext.listMayEnd = false\n return newContext\n }\n } else {\n // 上一行不是空行\n if (listItem) {\n // 新列表项(可能是同级或嵌套)\n return null\n } else if (isEmptyLine(line)) {\n // 遇到空行,列表可能结束\n newContext.listMayEnd = true\n return newContext\n } else if (this.isListContinuation(line, context.listIndent ?? 0)) {\n // 缩进内容,列表继续\n return null\n } else {\n // 非缩进非列表内容,列表结束\n newContext.inList = false\n newContext.listOrdered = undefined\n newContext.listIndent = undefined\n newContext.listMayEnd = false\n return newContext\n }\n }\n } else {\n // 不在列表中\n if (listItem) {\n // 列表开始\n newContext.inList = true\n newContext.listOrdered = listItem.ordered\n newContext.listIndent = listItem.indent\n newContext.listMayEnd = false\n return newContext\n }\n }\n\n return null\n }\n}\n\n/**\n * 创建初始上下文\n */\nexport function createInitialContext(): BlockContext {\n return {\n inFencedCode: false,\n listDepth: 0,\n blockquoteDepth: 0,\n inContainer: false,\n containerDepth: 0,\n inList: false,\n inFootnote: false,\n footnoteIdentifier: undefined\n }\n}\n\n/**\n * 上下文管理器\n * 使用责任链模式更新上下文\n */\nclass ContextManager {\n private readonly updaters: ContextUpdater[] = [\n new CodeContextUpdater(),\n new ContainerContextUpdater(),\n new FootnoteContextUpdater(),\n new ListContextUpdater()\n ]\n\n /**\n * 更新上下文(处理一行后)\n *\n * @param line 当前行\n * @param context 当前上下文\n * @param containerConfig 容器配置\n * @returns 更新后的上下文\n */\n update(line: string, context: BlockContext, containerConfig?: ContainerConfig | boolean): BlockContext {\n // 规范化容器配置\n const config = containerConfig === true ? {} : containerConfig === false ? undefined : containerConfig\n\n // 依次调用上下文更新器\n for (const updater of this.updaters) {\n const result = updater.update(line, context, config)\n if (result !== null) {\n return result\n }\n }\n\n // 没有任何更新器处理,返回原上下文\n return { ...context }\n }\n}\n\n// 上下文管理器单例\nconst contextManager = new ContextManager()\n\n/**\n * 更新上下文(处理一行后)\n *\n * @param line 当前行\n * @param context 当前上下文\n * @param containerConfig 容器配置\n * @returns 更新后的上下文\n */\nexport function updateContext(\n line: string,\n context: BlockContext,\n containerConfig?: ContainerConfig | boolean\n): BlockContext {\n return contextManager.update(line, context, containerConfig)\n}\n\n"]}
|