@jianwen-lang/parser 0.1.1 → 0.1.2

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.
Files changed (34) hide show
  1. package/README.md +16 -1
  2. package/dist/core/block/rules/attribute-line.d.ts +13 -0
  3. package/dist/core/block/rules/attribute-line.js +227 -0
  4. package/dist/core/block/rules/code-block.d.ts +2 -0
  5. package/dist/core/block/rules/code-block.js +73 -0
  6. package/dist/core/block/rules/code-fence.d.ts +15 -0
  7. package/dist/core/block/rules/code-fence.js +37 -0
  8. package/dist/core/block/rules/content-title.d.ts +12 -0
  9. package/dist/core/block/rules/content-title.js +70 -0
  10. package/dist/core/block/rules/footnotes.d.ts +9 -0
  11. package/dist/core/block/rules/footnotes.js +105 -0
  12. package/dist/core/block/rules/html.d.ts +7 -0
  13. package/dist/core/block/rules/html.js +48 -0
  14. package/dist/core/block/rules/image.d.ts +9 -0
  15. package/dist/core/block/rules/image.js +78 -0
  16. package/dist/core/block/rules/list.d.ts +3 -0
  17. package/dist/core/block/rules/list.js +275 -0
  18. package/dist/core/block/rules/paragraph.d.ts +6 -0
  19. package/dist/core/block/rules/paragraph.js +55 -0
  20. package/dist/core/block/rules/quote.d.ts +13 -0
  21. package/dist/core/block/rules/quote.js +104 -0
  22. package/dist/core/block/rules/table.d.ts +2 -0
  23. package/dist/core/block/rules/table.js +199 -0
  24. package/dist/core/block/runtime.d.ts +25 -0
  25. package/dist/core/block/runtime.js +116 -0
  26. package/dist/core/block-parser.js +183 -1322
  27. package/dist/html/convert.d.ts +8 -0
  28. package/dist/html/convert.js +22 -0
  29. package/dist/html/render/blocks.d.ts +15 -0
  30. package/dist/html/render/blocks.js +50 -6
  31. package/dist/html/render/html.d.ts +16 -0
  32. package/dist/html/render/html.js +48 -14
  33. package/dist/html/render/utils.d.ts +1 -0
  34. package/package.json +5 -1
@@ -1,91 +1,51 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseBlocks = parseBlocks;
4
- const diagnostics_1 = require("./diagnostics");
5
- const location_1 = require("./location");
6
4
  const rules_1 = require("./block/rules");
5
+ const attribute_line_1 = require("./block/rules/attribute-line");
6
+ const code_block_1 = require("./block/rules/code-block");
7
+ const code_fence_1 = require("./block/rules/code-fence");
8
+ const content_title_1 = require("./block/rules/content-title");
9
+ const footnotes_1 = require("./block/rules/footnotes");
7
10
  const heading_1 = require("./block/rules/heading");
8
11
  const horizontal_rule_1 = require("./block/rules/horizontal-rule");
12
+ const html_1 = require("./block/rules/html");
13
+ const image_1 = require("./block/rules/image");
9
14
  const include_1 = require("./block/rules/include");
15
+ const list_1 = require("./block/rules/list");
16
+ const paragraph_1 = require("./block/rules/paragraph");
17
+ const quote_1 = require("./block/rules/quote");
18
+ const table_1 = require("./block/rules/table");
19
+ const runtime_1 = require("./block/runtime");
10
20
  const lexer_1 = require("../lexer/lexer");
21
+ const CONFIGURED_RULE_ORDER = [
22
+ 'coreRuleSet',
23
+ 'contentTitle',
24
+ 'quote',
25
+ 'table',
26
+ 'image',
27
+ 'html',
28
+ 'list',
29
+ ];
30
+ const PARAGRAPH_STOP_CHECKS = [
31
+ (_nextContent, nextTrimmed) => (0, footnotes_1.isFootnotesLine)(nextTrimmed),
32
+ (nextContent) => (0, include_1.matchInclude)(nextContent) !== undefined,
33
+ (nextContent) => (0, heading_1.matchHeading)(nextContent) !== undefined,
34
+ (nextContent) => (0, content_title_1.matchContentTitle)(nextContent) !== undefined,
35
+ (nextContent) => (0, quote_1.matchQuote)(nextContent) !== undefined,
36
+ (_nextContent, nextTrimmed) => (0, code_fence_1.matchCodeFenceStart)(nextTrimmed) !== undefined,
37
+ (_nextContent, nextTrimmed) => (0, horizontal_rule_1.matchHorizontalRule)(nextTrimmed) !== undefined,
38
+ (_nextContent, nextTrimmed) => (0, image_1.matchImageBlock)(nextTrimmed) !== undefined,
39
+ (_nextContent, nextTrimmed) => (0, html_1.matchHtmlBlock)(nextTrimmed) !== undefined,
40
+ (nextContent) => (0, list_1.isListItemStart)(nextContent),
41
+ ];
11
42
  function buildLineLocation(lineIndex, lineInfo) {
12
43
  return { line: lineIndex + 1, column: lineInfo.tabCount + 1 };
13
44
  }
14
- const MULTI_ARROW_WARNING_CODE = 'layout-multi-arrow';
15
- const UNKNOWN_ATTRIBUTE_WARNING_CODE = 'unknown-attribute-token';
16
- function buildBlockAttrs(pending, tabCount) {
17
- const baseAttrs = pending.attrs ? { ...pending.attrs } : undefined;
18
- const positionFromTabs = mapTabsToPosition(tabCount);
19
- let blockAttrs = baseAttrs;
20
- if (positionFromTabs) {
21
- if (!blockAttrs) {
22
- blockAttrs = {};
23
- }
24
- if (!blockAttrs.position) {
25
- blockAttrs.position = positionFromTabs;
26
- }
27
- }
28
- if (pending.foldNext) {
29
- if (!blockAttrs) {
30
- blockAttrs = {};
31
- }
32
- blockAttrs.fold = true;
33
- }
34
- return blockAttrs;
35
- }
36
- function resetPending(pending) {
37
- pending.attrs = undefined;
38
- pending.foldNext = false;
39
- pending.tagName = undefined;
40
- pending.isComment = false;
41
- pending.isDisabled = false;
42
- pending.isSheet = false;
43
- pending.isHtml = false;
44
- }
45
- function wrapTaggedIfNeeded(block, pending, blockAttrs, lineLocation) {
46
- if (!pending.tagName) {
47
- return block;
48
- }
49
- const tagged = {
50
- type: 'taggedBlock',
51
- name: pending.tagName,
52
- child: block,
53
- blockAttrs,
54
- };
55
- (0, location_1.setNodeLocation)(tagged, lineLocation);
56
- return tagged;
57
- }
58
- function wrapCommentIfNeeded(block, pending, lineLocation) {
59
- if (!pending.isComment) {
60
- return block;
61
- }
62
- const comment = {
63
- type: 'commentBlock',
64
- children: [block],
65
- blockAttrs: undefined,
66
- };
67
- (0, location_1.setNodeLocation)(comment, lineLocation);
68
- return comment;
69
- }
70
- function pushBlockAndResetPending(blocks, block, lineInfo, pending, lastBlockPosition) {
71
- blocks.push(block);
72
- const nextPosition = resolveNextPosition(getBlockAttributes(block), lineInfo.tabCount, lastBlockPosition);
73
- resetPending(pending);
74
- return nextPosition;
75
- }
76
45
  function parseBlocks(source, errors) {
77
46
  const blocks = [];
78
47
  const lines = source.split(/\r?\n/);
79
- const pending = {
80
- attrs: undefined,
81
- foldNext: false,
82
- tagName: undefined,
83
- isComment: false,
84
- isDisabled: false,
85
- isSheet: false,
86
- isHtml: false,
87
- };
88
- let lastBlockPosition = 'L';
48
+ const runtime = (0, runtime_1.createBlockParseRuntime)();
89
49
  let i = 0;
90
50
  while (i < lines.length) {
91
51
  const raw = lines[i];
@@ -100,1286 +60,187 @@ function parseBlocks(source, errors) {
100
60
  i += 1;
101
61
  continue;
102
62
  }
103
- if (isFootnotesLine(trimmedContent)) {
104
- const rawLines = [lineInfo.raw];
105
- const regionLines = [];
106
- let jFoot = i + 1;
107
- while (jFoot < lines.length) {
108
- const nextRaw = lines[jFoot];
109
- if (nextRaw === undefined) {
110
- jFoot += 1;
111
- continue;
112
- }
113
- const nextInfo = getLineInfo(nextRaw);
114
- const nextTrimmed = nextInfo.content.trim();
115
- if (nextTrimmed.length === 0) {
116
- break;
117
- }
118
- rawLines.push(nextInfo.raw);
119
- regionLines.push(nextInfo.content);
120
- jFoot += 1;
121
- }
122
- const blockAttrs = buildBlockAttrs(pending, lineInfo.tabCount);
123
- let block;
124
- if (pending.isDisabled) {
125
- const disabled = {
126
- type: 'disabledBlock',
127
- raw: rawLines.join('\n'),
128
- blockAttrs,
129
- };
130
- (0, location_1.setNodeLocation)(disabled, lineLocation);
131
- block = disabled;
132
- }
133
- else {
134
- const children = parseFootnoteDefs(regionLines, errors);
135
- const footnotesBlock = {
136
- type: 'footnotes',
137
- children,
138
- blockAttrs,
139
- };
140
- (0, location_1.setNodeLocation)(footnotesBlock, lineLocation);
141
- block = wrapTaggedIfNeeded(footnotesBlock, pending, blockAttrs, lineLocation);
142
- }
143
- block = wrapCommentIfNeeded(block, pending, lineLocation);
144
- lastBlockPosition = pushBlockAndResetPending(blocks, block, lineInfo, pending, lastBlockPosition);
145
- i = jFoot;
146
- continue;
147
- }
148
- // Check for code blocks - both plain ``` and attributed [html]``` forms
149
- const codeFenceStart = matchCodeFenceStart(trimmedContent);
150
- const attrCodeFenceStart = codeFenceStart ? null : matchAttributedCodeFence(trimmedContent);
151
- const effectiveCodeFence = codeFenceStart || attrCodeFenceStart;
152
- if (effectiveCodeFence) {
153
- const rawLines = [lineInfo.raw];
154
- const codeLines = [];
155
- // Record the indentation level of the opening ``` to strip from content lines
156
- const fenceIndent = lineInfo.tabCount;
157
- let jCode = i + 1;
158
- let closed = false;
159
- while (jCode < lines.length) {
160
- const nextRaw = lines[jCode];
161
- if (nextRaw === undefined) {
162
- jCode += 1;
163
- continue;
164
- }
165
- const nextInfo = getLineInfo(nextRaw);
166
- const nextTrimmed = nextInfo.content.trim();
167
- if (isCodeFenceEnd(nextTrimmed)) {
168
- rawLines.push(nextInfo.raw);
169
- closed = true;
170
- jCode += 1;
171
- break;
172
- }
173
- rawLines.push(nextInfo.raw);
174
- // Strip leading tabs matching the fence indentation level
175
- let codeLine = nextRaw;
176
- for (let t = 0; t < fenceIndent && codeLine.startsWith('\t'); t++) {
177
- codeLine = codeLine.slice(1);
178
- }
179
- codeLines.push(codeLine);
180
- jCode += 1;
181
- }
182
- if (!closed) {
183
- (0, diagnostics_1.reportParseError)(errors, {
184
- message: 'Code block is not closed with ```',
185
- line: i + 1,
186
- });
187
- }
188
- const blockAttrs = buildBlockAttrs(pending, lineInfo.tabCount);
189
- // Determine if this is an HTML block from either pending state or inline attribute
190
- const isHtmlBlock = pending.isHtml || (attrCodeFenceStart?.isHtml ?? false);
191
- let block;
192
- if (pending.isDisabled) {
193
- const disabled = {
194
- type: 'disabledBlock',
195
- raw: rawLines.join('\n'),
196
- blockAttrs,
197
- };
198
- (0, location_1.setNodeLocation)(disabled, lineLocation);
199
- block = disabled;
200
- }
201
- else {
202
- const codeBlock = {
203
- type: 'code',
204
- language: effectiveCodeFence.language,
205
- value: codeLines.join('\n'),
206
- htmlLike: isHtmlBlock ? true : undefined,
207
- blockAttrs,
208
- };
209
- (0, location_1.setNodeLocation)(codeBlock, lineLocation);
210
- block = wrapTaggedIfNeeded(codeBlock, pending, blockAttrs, lineLocation);
211
- }
212
- block = wrapCommentIfNeeded(block, pending, lineLocation);
213
- lastBlockPosition = pushBlockAndResetPending(blocks, block, lineInfo, pending, lastBlockPosition);
214
- i = jCode;
215
- continue;
216
- }
217
- if (isAttributeOnlyLine(trimmedContent)) {
218
- const result = parseAttributeLine(trimmedContent, i + 1, errors, pending.attrs, lastBlockPosition, lineInfo.tabCount);
219
- if (result.recognizedAny) {
220
- if (result.attrs) {
221
- pending.attrs = mergeBlockAttributes(pending.attrs, result.attrs);
222
- }
223
- if (result.foldNext) {
224
- pending.foldNext = true;
225
- }
226
- if (result.tagName) {
227
- pending.tagName = result.tagName;
228
- }
229
- if (result.isComment) {
230
- pending.isComment = true;
231
- }
232
- if (result.isDisabled) {
233
- pending.isDisabled = true;
234
- }
235
- if (result.isSheet) {
236
- pending.isSheet = true;
237
- }
238
- if (result.isHtml) {
239
- pending.isHtml = true;
240
- }
241
- i += 1;
242
- continue;
243
- }
244
- }
245
63
  const commitBlock = (block, blockAttrs, options) => {
246
- const allowTag = options?.allowTag !== false;
247
- const taggedOrOriginal = allowTag
248
- ? wrapTaggedIfNeeded(block, pending, blockAttrs, lineLocation)
249
- : block;
250
- const finalBlock = wrapCommentIfNeeded(taggedOrOriginal, pending, lineLocation);
251
- lastBlockPosition = pushBlockAndResetPending(blocks, finalBlock, lineInfo, pending, lastBlockPosition);
252
- options?.postResetPending?.(pending);
64
+ (0, runtime_1.commitParsedBlock)({
65
+ runtime,
66
+ blocks,
67
+ block,
68
+ blockAttrs,
69
+ lineInfo,
70
+ lineLocation,
71
+ options,
72
+ });
253
73
  };
254
- const nextIndexFromRule = (0, rules_1.tryParseBlockRules)({
74
+ const nextIndexFromFootnotes = (0, footnotes_1.tryParseFootnotesBlock)({
255
75
  lines,
256
76
  index: i,
257
77
  lineInfo,
258
78
  trimmedContent,
259
79
  lineLocation,
260
- pending,
80
+ pending: runtime.pending,
261
81
  errors,
262
- buildBlockAttrs: (tabCount) => buildBlockAttrs(pending, tabCount),
82
+ parseBlocks,
83
+ buildBlockAttrs: (tabCount) => (0, runtime_1.buildBlockAttrs)(runtime, tabCount),
263
84
  commitBlock,
264
85
  });
265
- if (nextIndexFromRule !== null) {
266
- i = nextIndexFromRule;
86
+ if (nextIndexFromFootnotes !== null) {
87
+ i = nextIndexFromFootnotes;
267
88
  continue;
268
89
  }
269
- const contentTitleMatch = matchContentTitle(lineInfo.content);
270
- if (contentTitleMatch) {
271
- const text = contentTitleMatch.text;
272
- let consumedByImageTitle = false;
273
- if (!pending.attrs &&
274
- !pending.foldNext &&
275
- !pending.tagName &&
276
- !pending.isComment &&
277
- !pending.isDisabled &&
278
- !pending.isSheet &&
279
- !pending.isHtml) {
280
- const lastBlock = blocks[blocks.length - 1];
281
- if (lastBlock && lastBlock.type === 'image') {
282
- lastBlock.title = text;
283
- consumedByImageTitle = true;
284
- }
285
- else if (lastBlock &&
286
- lastBlock.type === 'taggedBlock' &&
287
- lastBlock.child.type === 'image') {
288
- lastBlock.child.title = text;
289
- consumedByImageTitle = true;
290
- }
291
- }
292
- if (consumedByImageTitle) {
293
- resetPending(pending);
90
+ const nextIndexFromCodeBlock = (0, code_block_1.tryParseCodeBlock)({
91
+ lines,
92
+ index: i,
93
+ lineInfo,
94
+ trimmedContent,
95
+ lineLocation,
96
+ pending: runtime.pending,
97
+ errors,
98
+ buildBlockAttrs: (tabCount) => (0, runtime_1.buildBlockAttrs)(runtime, tabCount),
99
+ commitBlock,
100
+ });
101
+ if (nextIndexFromCodeBlock !== null) {
102
+ i = nextIndexFromCodeBlock;
103
+ continue;
104
+ }
105
+ if ((0, attribute_line_1.isAttributeOnlyLine)(trimmedContent)) {
106
+ const consumed = (0, attribute_line_1.tryConsumeAttributeOnlyLine)({
107
+ text: trimmedContent,
108
+ lineNumber: i + 1,
109
+ errors,
110
+ pending: runtime.pending,
111
+ fallbackPosition: runtime.lastBlockPosition,
112
+ tabCount: lineInfo.tabCount,
113
+ });
114
+ if (consumed) {
294
115
  i += 1;
295
116
  continue;
296
117
  }
297
- const blockAttrs = buildBlockAttrs(pending, lineInfo.tabCount);
298
- let block;
299
- if (pending.isDisabled) {
300
- const disabled = {
301
- type: 'disabledBlock',
302
- raw: lineInfo.raw,
303
- blockAttrs,
304
- };
305
- (0, location_1.setNodeLocation)(disabled, lineLocation);
306
- block = disabled;
307
- }
308
- else {
309
- const contentTitle = {
310
- type: 'contentTitle',
311
- children: [{ type: 'text', value: text }],
312
- };
313
- (0, location_1.setNodeLocation)(contentTitle, lineLocation);
314
- block = wrapTaggedIfNeeded(contentTitle, pending, blockAttrs, lineLocation);
315
- }
316
- block = wrapCommentIfNeeded(block, pending, lineLocation);
317
- lastBlockPosition = pushBlockAndResetPending(blocks, block, lineInfo, pending, lastBlockPosition);
318
- i += 1;
319
- continue;
320
- }
321
- const quoteMatch = matchQuote(lineInfo.content);
322
- if (quoteMatch) {
323
- const { level } = quoteMatch;
324
- const innerLines = [];
325
- const rawLines = [];
326
- const first = matchQuote(lineInfo.content);
327
- if (first) {
328
- const normalized = normalizeQuoteLine(first, level);
329
- if (normalized !== undefined) {
330
- innerLines.push(normalized);
331
- }
332
- }
333
- rawLines.push(lineInfo.raw);
334
- let jQuote = i + 1;
335
- while (jQuote < lines.length) {
336
- const nextRaw = lines[jQuote];
337
- if (nextRaw === undefined) {
338
- jQuote += 1;
339
- continue;
340
- }
341
- const nextInfo = getLineInfo(nextRaw);
342
- const nextTrimmed = nextInfo.content.trim();
343
- if (nextTrimmed.length === 0) {
344
- break;
345
- }
346
- const m = matchQuote(nextInfo.content);
347
- if (!m) {
348
- break;
349
- }
350
- const normalized = normalizeQuoteLine(m, level);
351
- if (normalized === undefined) {
352
- break;
353
- }
354
- innerLines.push(normalized);
355
- rawLines.push(nextInfo.raw);
356
- jQuote += 1;
357
- }
358
- const innerSource = innerLines.join('\n');
359
- const blockAttrs = buildBlockAttrs(pending, lineInfo.tabCount);
360
- let block;
361
- if (pending.isDisabled) {
362
- const disabled = {
363
- type: 'disabledBlock',
364
- raw: rawLines.join('\n'),
365
- blockAttrs,
366
- };
367
- (0, location_1.setNodeLocation)(disabled, lineLocation);
368
- block = disabled;
369
- }
370
- else {
371
- const children = parseBlocks(innerSource, errors);
372
- adjustNestedQuoteLevels(children, level);
373
- const quote = {
374
- type: 'quote',
375
- level,
376
- children,
377
- blockAttrs,
378
- };
379
- (0, location_1.setNodeLocation)(quote, lineLocation);
380
- block = wrapTaggedIfNeeded(quote, pending, blockAttrs, lineLocation);
381
- }
382
- block = wrapCommentIfNeeded(block, pending, lineLocation);
383
- lastBlockPosition = pushBlockAndResetPending(blocks, block, lineInfo, pending, lastBlockPosition);
384
- i = jQuote;
385
- continue;
386
118
  }
387
- if (pending.isSheet && isTableRow(lineInfo.content)) {
388
- const rawLines = [lineInfo.raw];
389
- const rowContents = [
390
- { text: lineInfo.content, raw: lineInfo.raw, line: i + 1 },
391
- ];
392
- let jTable = i + 1;
393
- while (jTable < lines.length) {
394
- const nextRaw = lines[jTable];
395
- if (nextRaw === undefined) {
396
- jTable += 1;
397
- continue;
398
- }
399
- const nextInfo = getLineInfo(nextRaw);
400
- const nextTrimmed = nextInfo.content.trim();
401
- if (nextTrimmed.length === 0) {
119
+ let handledByConfiguredRule = false;
120
+ for (const ruleName of CONFIGURED_RULE_ORDER) {
121
+ let nextIndex = null;
122
+ switch (ruleName) {
123
+ case 'coreRuleSet':
124
+ nextIndex = (0, rules_1.tryParseBlockRules)({
125
+ lines,
126
+ index: i,
127
+ lineInfo,
128
+ trimmedContent,
129
+ lineLocation,
130
+ pending: runtime.pending,
131
+ errors,
132
+ buildBlockAttrs: (tabCount) => (0, runtime_1.buildBlockAttrs)(runtime, tabCount),
133
+ commitBlock,
134
+ });
402
135
  break;
403
- }
404
- if (isAttributeOnlyLine(nextTrimmed)) {
136
+ case 'contentTitle':
137
+ nextIndex = (0, content_title_1.tryParseContentTitleBlock)({
138
+ lines,
139
+ index: i,
140
+ lineInfo,
141
+ trimmedContent,
142
+ lineLocation,
143
+ pending: runtime.pending,
144
+ errors,
145
+ blocks,
146
+ resetPending: () => (0, runtime_1.resetPending)(runtime.pending),
147
+ buildBlockAttrs: (tabCount) => (0, runtime_1.buildBlockAttrs)(runtime, tabCount),
148
+ commitBlock,
149
+ });
405
150
  break;
406
- }
407
- if (!isTableRow(nextInfo.content)) {
151
+ case 'quote':
152
+ nextIndex = (0, quote_1.tryParseQuoteBlock)({
153
+ lines,
154
+ index: i,
155
+ lineInfo,
156
+ trimmedContent,
157
+ lineLocation,
158
+ pending: runtime.pending,
159
+ errors,
160
+ parseBlocks,
161
+ buildBlockAttrs: (tabCount) => (0, runtime_1.buildBlockAttrs)(runtime, tabCount),
162
+ commitBlock,
163
+ });
408
164
  break;
409
- }
410
- rawLines.push(nextInfo.raw);
411
- rowContents.push({ text: nextInfo.content, raw: nextInfo.raw, line: jTable + 1 });
412
- jTable += 1;
413
- }
414
- const { rows, align } = parseTableRows(rowContents, errors);
415
- const blockAttrs = buildBlockAttrs(pending, lineInfo.tabCount);
416
- let tableAlign = align;
417
- if (!tableAlign && blockAttrs?.align && blockAttrs.align !== 'left') {
418
- const columnCount = getMaxTableColumnCount(rows);
419
- if (columnCount > 0) {
420
- tableAlign = Array.from({ length: columnCount }, () => blockAttrs.align);
421
- }
422
- }
423
- let block;
424
- if (pending.isDisabled) {
425
- const disabled = {
426
- type: 'disabledBlock',
427
- raw: rawLines.join('\n'),
428
- blockAttrs,
429
- };
430
- (0, location_1.setNodeLocation)(disabled, lineLocation);
431
- block = disabled;
432
- }
433
- else {
434
- const tableBlock = {
435
- type: 'table',
436
- rows,
437
- align: tableAlign,
438
- blockAttrs,
439
- };
440
- (0, location_1.setNodeLocation)(tableBlock, lineLocation);
441
- block = wrapTaggedIfNeeded(tableBlock, pending, blockAttrs, lineLocation);
442
- }
443
- block = wrapCommentIfNeeded(block, pending, lineLocation);
444
- lastBlockPosition = pushBlockAndResetPending(blocks, block, lineInfo, pending, lastBlockPosition);
445
- i = jTable;
446
- continue;
447
- }
448
- const imageMatch = matchImageBlock(trimmedContent);
449
- if (imageMatch) {
450
- const blockAttrs = buildBlockAttrs(pending, lineInfo.tabCount);
451
- let block;
452
- if (pending.isDisabled) {
453
- const disabled = {
454
- type: 'disabledBlock',
455
- raw: lineInfo.raw,
456
- blockAttrs,
457
- };
458
- (0, location_1.setNodeLocation)(disabled, lineLocation);
459
- block = disabled;
460
- }
461
- else {
462
- const image = {
463
- type: 'image',
464
- url: imageMatch.url,
465
- shape: imageMatch.shape,
466
- roundedRadius: imageMatch.roundedRadius,
467
- blockAttrs,
468
- };
469
- (0, location_1.setNodeLocation)(image, lineLocation);
470
- block = wrapTaggedIfNeeded(image, pending, blockAttrs, lineLocation);
471
- }
472
- block = wrapCommentIfNeeded(block, pending, lineLocation);
473
- lastBlockPosition = pushBlockAndResetPending(blocks, block, lineInfo, pending, lastBlockPosition);
474
- i += 1;
475
- continue;
476
- }
477
- const htmlMatch = matchHtmlBlock(trimmedContent);
478
- if (htmlMatch) {
479
- const blockAttrs = buildBlockAttrs(pending, lineInfo.tabCount);
480
- let block;
481
- if (pending.isDisabled) {
482
- const disabled = {
483
- type: 'disabledBlock',
484
- raw: lineInfo.raw,
485
- blockAttrs,
486
- };
487
- (0, location_1.setNodeLocation)(disabled, lineLocation);
488
- block = disabled;
489
- }
490
- else {
491
- const htmlBlock = {
492
- type: 'html',
493
- source: htmlMatch.source,
494
- blockAttrs,
495
- };
496
- (0, location_1.setNodeLocation)(htmlBlock, lineLocation);
497
- block = wrapTaggedIfNeeded(htmlBlock, pending, blockAttrs, lineLocation);
498
- }
499
- block = wrapCommentIfNeeded(block, pending, lineLocation);
500
- lastBlockPosition = pushBlockAndResetPending(blocks, block, lineInfo, pending, lastBlockPosition);
501
- i += 1;
502
- continue;
503
- }
504
- const listFirstMatch = matchListItem(lineInfo.content, i + 1, lineInfo.tabCount + 1);
505
- if (listFirstMatch) {
506
- const listMatches = [listFirstMatch];
507
- const rawLines = [lineInfo.raw];
508
- let jList = i + 1;
509
- while (jList < lines.length) {
510
- const nextRaw = lines[jList];
511
- if (nextRaw === undefined) {
512
- jList += 1;
513
- continue;
514
- }
515
- const nextInfo = getLineInfo(nextRaw);
516
- const nextTrimmed = nextInfo.content.trim();
517
- if (nextTrimmed.length === 0) {
165
+ case 'table':
166
+ nextIndex = (0, table_1.tryParseTableBlock)({
167
+ lines,
168
+ index: i,
169
+ lineInfo,
170
+ trimmedContent,
171
+ lineLocation,
172
+ pending: runtime.pending,
173
+ errors,
174
+ buildBlockAttrs: (tabCount) => (0, runtime_1.buildBlockAttrs)(runtime, tabCount),
175
+ commitBlock,
176
+ });
518
177
  break;
519
- }
520
- if (isAttributeOnlyLine(nextTrimmed)) {
178
+ case 'image':
179
+ nextIndex = (0, image_1.tryParseImageBlock)({
180
+ lines,
181
+ index: i,
182
+ lineInfo,
183
+ trimmedContent,
184
+ lineLocation,
185
+ pending: runtime.pending,
186
+ errors,
187
+ buildBlockAttrs: (tabCount) => (0, runtime_1.buildBlockAttrs)(runtime, tabCount),
188
+ commitBlock,
189
+ });
521
190
  break;
522
- }
523
- const nextMatch = matchListItem(nextInfo.content, jList + 1, nextInfo.tabCount + 1);
524
- if (!nextMatch) {
525
- // Check if this is a code block that should be attached to the previous list item
526
- // Support both plain ``` and attributed [html]``` forms
527
- const codeFence = matchCodeFenceStart(nextTrimmed);
528
- const attrCodeFence = codeFence ? null : matchAttributedCodeFence(nextTrimmed);
529
- const effectiveFence = codeFence || attrCodeFence;
530
- if (effectiveFence && listMatches.length > 0) {
531
- // Parse the code block
532
- const codeLines = [];
533
- const fenceIndent = nextInfo.tabCount;
534
- let kCode = jList + 1;
535
- let codeClosed = false;
536
- while (kCode < lines.length) {
537
- const codeRaw = lines[kCode];
538
- if (codeRaw === undefined) {
539
- kCode += 1;
540
- continue;
541
- }
542
- const codeInfo = getLineInfo(codeRaw);
543
- const codeTrimmed = codeInfo.content.trim();
544
- if (isCodeFenceEnd(codeTrimmed)) {
545
- codeClosed = true;
546
- kCode += 1;
547
- break;
548
- }
549
- // Strip leading tabs matching the fence indentation level
550
- let codeLine = codeRaw;
551
- for (let t = 0; t < fenceIndent && codeLine.startsWith('\t'); t++) {
552
- codeLine = codeLine.slice(1);
553
- }
554
- codeLines.push(codeLine);
555
- kCode += 1;
556
- }
557
- if (codeClosed) {
558
- const isHtmlBlock = attrCodeFence?.isHtml ?? false;
559
- const codeBlock = {
560
- type: 'code',
561
- language: effectiveFence.language,
562
- value: codeLines.join('\n'),
563
- htmlLike: isHtmlBlock ? true : undefined,
564
- };
565
- (0, location_1.setNodeLocation)(codeBlock, buildLineLocation(jList, nextInfo));
566
- // Attach to the last list item
567
- const lastItem = listMatches[listMatches.length - 1];
568
- if (lastItem) {
569
- if (!lastItem.childBlocks) {
570
- lastItem.childBlocks = [];
571
- }
572
- lastItem.childBlocks.push(codeBlock);
573
- }
574
- jList = kCode;
575
- continue;
576
- }
577
- }
191
+ case 'html':
192
+ nextIndex = (0, html_1.tryParseHtmlBlock)({
193
+ lines,
194
+ index: i,
195
+ lineInfo,
196
+ trimmedContent,
197
+ lineLocation,
198
+ pending: runtime.pending,
199
+ errors,
200
+ buildBlockAttrs: (tabCount) => (0, runtime_1.buildBlockAttrs)(runtime, tabCount),
201
+ commitBlock,
202
+ });
578
203
  break;
579
- }
580
- if (nextMatch.kind !== listFirstMatch.kind) {
204
+ case 'list':
205
+ nextIndex = (0, list_1.tryParseListBlock)({
206
+ lines,
207
+ index: i,
208
+ lineInfo,
209
+ trimmedContent,
210
+ lineLocation,
211
+ pending: runtime.pending,
212
+ errors,
213
+ buildBlockAttrs: (tabCount) => (0, runtime_1.buildBlockAttrs)(runtime, tabCount),
214
+ commitBlock,
215
+ });
581
216
  break;
582
- }
583
- if (nextMatch.indent < listFirstMatch.indent) {
217
+ default:
584
218
  break;
585
- }
586
- listMatches.push(nextMatch);
587
- rawLines.push(nextInfo.raw);
588
- jList += 1;
589
- }
590
- const blockAttrs = buildBlockAttrs(pending, lineInfo.tabCount);
591
- let block;
592
- if (pending.isDisabled) {
593
- const disabled = {
594
- type: 'disabledBlock',
595
- raw: rawLines.join('\n'),
596
- blockAttrs,
597
- };
598
- (0, location_1.setNodeLocation)(disabled, lineLocation);
599
- block = disabled;
600
219
  }
601
- else {
602
- const { items } = buildListItems(listMatches, 0, listFirstMatch.indent);
603
- let listKind = listFirstMatch.kind;
604
- const firstItem = items[0];
605
- if (firstItem) {
606
- listKind = firstItem.kind;
607
- }
608
- const listBlock = {
609
- type: 'list',
610
- kind: listKind,
611
- orderedStyle: listKind === 'ordered' ? 'decimal' : undefined,
612
- children: items,
613
- blockAttrs,
614
- };
615
- (0, location_1.setNodeLocation)(listBlock, lineLocation);
616
- block = wrapTaggedIfNeeded(listBlock, pending, blockAttrs, lineLocation);
617
- }
618
- block = wrapCommentIfNeeded(block, pending, lineLocation);
619
- lastBlockPosition = pushBlockAndResetPending(blocks, block, lineInfo, pending, lastBlockPosition);
620
- i = jList;
621
- continue;
622
- }
623
- const blockRawLines = [lineInfo.raw];
624
- const blockTextLines = [lineInfo.content];
625
- let j = i + 1;
626
- while (j < lines.length) {
627
- const nextRaw = lines[j];
628
- if (nextRaw === undefined) {
629
- j += 1;
630
- continue;
631
- }
632
- const nextInfo = getLineInfo(nextRaw);
633
- const nextTrimmed = nextInfo.content.trim();
634
- if (nextTrimmed.length === 0) {
635
- break;
636
- }
637
- if (isAttributeOnlyLine(nextTrimmed)) {
638
- break;
639
- }
640
- if (isFootnotesLine(nextTrimmed)) {
641
- break;
642
- }
643
- if ((0, include_1.matchInclude)(nextInfo.content)) {
644
- break;
645
- }
646
- if ((0, heading_1.matchHeading)(nextInfo.content)) {
647
- break;
648
- }
649
- if (matchContentTitle(nextInfo.content)) {
220
+ if (nextIndex !== null) {
221
+ i = nextIndex;
222
+ handledByConfiguredRule = true;
650
223
  break;
651
224
  }
652
- if (matchQuote(nextInfo.content)) {
653
- break;
654
- }
655
- if (matchCodeFenceStart(nextTrimmed)) {
656
- break;
657
- }
658
- if ((0, horizontal_rule_1.matchHorizontalRule)(nextTrimmed)) {
659
- break;
660
- }
661
- if (matchImageBlock(nextTrimmed)) {
662
- break;
663
- }
664
- if (matchHtmlBlock(nextTrimmed)) {
665
- break;
666
- }
667
- if (matchListItem(nextInfo.content)) {
668
- break;
669
- }
670
- blockRawLines.push(nextInfo.raw);
671
- blockTextLines.push(nextInfo.content);
672
- j += 1;
673
225
  }
674
- const blockText = blockTextLines.join('\n');
675
- const blockAttrs = buildBlockAttrs(pending, lineInfo.tabCount);
676
- let block;
677
- if (pending.isDisabled) {
678
- const disabled = {
679
- type: 'disabledBlock',
680
- raw: blockRawLines.join('\n'),
681
- blockAttrs,
682
- };
683
- (0, location_1.setNodeLocation)(disabled, lineLocation);
684
- block = disabled;
685
- }
686
- else {
687
- const paragraph = {
688
- type: 'paragraph',
689
- children: [{ type: 'text', value: blockText }],
690
- blockAttrs,
691
- };
692
- (0, location_1.setNodeLocation)(paragraph, lineLocation);
693
- block = paragraph;
694
- if (pending.tagName) {
695
- block = wrapTaggedIfNeeded(block, pending, blockAttrs, lineLocation);
696
- }
226
+ if (handledByConfiguredRule) {
227
+ continue;
697
228
  }
698
- block = wrapCommentIfNeeded(block, pending, lineLocation);
699
- lastBlockPosition = pushBlockAndResetPending(blocks, block, lineInfo, pending, lastBlockPosition);
700
- i = j;
229
+ i = (0, paragraph_1.tryParseParagraphBlock)({
230
+ lines,
231
+ index: i,
232
+ lineInfo,
233
+ trimmedContent,
234
+ lineLocation,
235
+ pending: runtime.pending,
236
+ errors,
237
+ buildBlockAttrs: (tabCount) => (0, runtime_1.buildBlockAttrs)(runtime, tabCount),
238
+ commitBlock,
239
+ shouldStopParagraph: (nextContent, nextTrimmed) => PARAGRAPH_STOP_CHECKS.some((rule) => rule(nextContent, nextTrimmed)),
240
+ });
701
241
  }
702
242
  return blocks;
703
243
  }
704
- function getBlockAttributes(block) {
705
- if ('blockAttrs' in block) {
706
- const candidate = block.blockAttrs;
707
- return candidate;
708
- }
709
- return undefined;
710
- }
711
- function resolveNextPosition(attrs, tabCount, fallback) {
712
- if (attrs?.position) {
713
- return attrs.position;
714
- }
715
- const tabPosition = mapTabsToPosition(tabCount);
716
- if (tabPosition) {
717
- return tabPosition;
718
- }
719
- return fallback;
720
- }
721
244
  function getLineInfo(raw) {
722
245
  return (0, lexer_1.getLineInfo)(raw);
723
246
  }
724
- function mapTabsToPosition(tabCount) {
725
- if (tabCount === 0) {
726
- return 'L';
727
- }
728
- if (tabCount === 1) {
729
- return 'C';
730
- }
731
- if (tabCount >= 2) {
732
- return 'R';
733
- }
734
- return undefined;
735
- }
736
- function isAttributeOnlyLine(text) {
737
- if (text.length === 0) {
738
- return false;
739
- }
740
- const trimmed = text.trim();
741
- if ((0, include_1.matchInclude)(trimmed)) {
742
- return false;
743
- }
744
- let i = 0;
745
- while (i < text.length) {
746
- const ch = text[i];
747
- if (ch === '[') {
748
- const end = text.indexOf(']', i + 1);
749
- if (end === -1) {
750
- return false;
751
- }
752
- i = end + 1;
753
- continue;
754
- }
755
- if (ch === ' ' || ch === '\t') {
756
- i += 1;
757
- continue;
758
- }
759
- return false;
760
- }
761
- return true;
762
- }
763
- function isFootnotesLine(trimmed) {
764
- return trimmed === '[footnotes]';
765
- }
766
- function parseFootnoteDefs(lines, errors) {
767
- const defs = [];
768
- let i = 0;
769
- while (i < lines.length) {
770
- const raw = lines[i];
771
- if (raw === undefined) {
772
- i += 1;
773
- continue;
774
- }
775
- const trimmed = raw.trim();
776
- const match = trimmed.match(/^\[fn=([^\]]+)\]\s*$/);
777
- if (!match) {
778
- i += 1;
779
- continue;
780
- }
781
- const idGroup = match[1];
782
- if (!idGroup) {
783
- i += 1;
784
- continue;
785
- }
786
- const id = idGroup.trim();
787
- i += 1;
788
- const contentLines = [];
789
- while (i < lines.length) {
790
- const innerRaw = lines[i];
791
- if (innerRaw === undefined) {
792
- i += 1;
793
- continue;
794
- }
795
- const innerTrimmed = innerRaw.trim();
796
- if (innerTrimmed.length === 0) {
797
- break;
798
- }
799
- if (/^\[fn=[^\]]+\]\s*$/.test(innerTrimmed)) {
800
- break;
801
- }
802
- contentLines.push(innerRaw);
803
- i += 1;
804
- }
805
- const contentSource = contentLines.join('\n');
806
- const children = contentSource.length > 0 ? parseBlocks(contentSource, errors) : [];
807
- const def = {
808
- type: 'footnoteDef',
809
- id,
810
- children,
811
- };
812
- defs.push(def);
813
- }
814
- return defs;
815
- }
816
- function isTableRow(content) {
817
- const trimmed = content.trim();
818
- if (!trimmed.startsWith('|')) {
819
- return false;
820
- }
821
- return trimmed.indexOf('|', 1) !== -1;
822
- }
823
- function parseTableRows(lines, errors) {
824
- if (lines.length === 0) {
825
- return { rows: [], align: undefined };
826
- }
827
- for (const line of lines) {
828
- const trimmed = line.text.trim();
829
- if (isTableRow(trimmed) && !trimmed.endsWith('|')) {
830
- const contentStart = line.raw.length - line.text.length;
831
- const trimmedContent = line.text.replace(/\s+$/, '');
832
- const column = contentStart + trimmedContent.length + 1;
833
- (0, diagnostics_1.reportParseError)(errors, {
834
- message: 'Table row is missing closing "|" border',
835
- line: line.line,
836
- column,
837
- });
838
- }
839
- }
840
- const remaining = [...lines];
841
- let align;
842
- // Check if the first line is an alignment row (JianWen native syntax: alignment row comes first)
843
- const firstLine = remaining[0];
844
- if (firstLine !== undefined) {
845
- const firstTrimmed = firstLine.text.trim();
846
- if (isAlignmentRow(firstTrimmed)) {
847
- align = parseAlignmentLine(firstTrimmed);
848
- remaining.shift();
849
- }
850
- }
851
- // Check if the second line is an alignment row (Markdown compatible syntax: alignment row after header row)
852
- if (align === undefined && remaining.length >= 2) {
853
- const secondLine = remaining[1];
854
- if (secondLine !== undefined) {
855
- const secondTrimmed = secondLine.text.trim();
856
- if (isAlignmentRow(secondTrimmed)) {
857
- align = parseAlignmentLine(secondTrimmed);
858
- remaining.splice(1, 1); // Remove the second line (alignment row)
859
- }
860
- }
861
- }
862
- const rows = [];
863
- for (const line of remaining) {
864
- const trimmed = line.text.trim();
865
- if (!isTableRow(trimmed)) {
866
- continue;
867
- }
868
- const rawCells = trimmed.split('|').slice(1, -1);
869
- const cells = [];
870
- for (let ci = 0; ci < rawCells.length; ci += 1) {
871
- const rawCell = rawCells[ci];
872
- if (rawCell === undefined) {
873
- continue;
874
- }
875
- let cellText = rawCell.trim();
876
- let cellAlign;
877
- if (cellText.startsWith('[r]')) {
878
- cellAlign = 'right';
879
- cellText = cellText.slice(3).trim();
880
- }
881
- else if (cellText.startsWith('[c]')) {
882
- cellAlign = 'center';
883
- cellText = cellText.slice(3).trim();
884
- }
885
- else if (align && ci < align.length) {
886
- cellAlign = align[ci];
887
- }
888
- const cell = {
889
- type: 'tableCell',
890
- children: cellText.length > 0 ? [{ type: 'text', value: cellText }] : [],
891
- align: cellAlign,
892
- };
893
- (0, location_1.setNodeLocation)(cell, { line: line.line, column: 1 });
894
- cells.push(cell);
895
- }
896
- const row = {
897
- type: 'tableRow',
898
- cells,
899
- };
900
- rows.push(row);
901
- }
902
- return { rows, align };
903
- }
904
- function isAlignmentRow(trimmed) {
905
- if (!isTableRow(trimmed)) {
906
- return false;
907
- }
908
- const segments = trimmed.split('|').slice(1, -1);
909
- if (segments.length === 0) {
910
- return false;
911
- }
912
- for (const seg of segments) {
913
- const t = seg.trim();
914
- if (!/^:?-+:?$/.test(t)) {
915
- return false;
916
- }
917
- }
918
- return true;
919
- }
920
- function parseAlignmentLine(trimmed) {
921
- const segments = trimmed.split('|').slice(1, -1);
922
- const result = [];
923
- for (const seg of segments) {
924
- const t = seg.trim();
925
- const left = t.startsWith(':');
926
- const right = t.endsWith(':');
927
- if (left && right) {
928
- result.push('center');
929
- }
930
- else if (right) {
931
- result.push('right');
932
- }
933
- else {
934
- result.push('left');
935
- }
936
- }
937
- return result;
938
- }
939
- function getMaxTableColumnCount(rows) {
940
- let max = 0;
941
- for (const row of rows) {
942
- if (row.cells.length > max) {
943
- max = row.cells.length;
944
- }
945
- }
946
- return max;
947
- }
948
- function matchImageBlock(trimmed) {
949
- const m = trimmed.match(/^\[([^\]]+)\]\(([^)]+)\)\s*$/);
950
- if (!m) {
951
- return undefined;
952
- }
953
- const inside = m[1];
954
- const urlGroup = m[2];
955
- if (inside === undefined || urlGroup === undefined) {
956
- return undefined;
957
- }
958
- const url = urlGroup.trim();
959
- if (url.length === 0) {
960
- return undefined;
961
- }
962
- const parts = inside
963
- .split(',')
964
- .map((part) => part.trim())
965
- .filter((part) => part.length > 0);
966
- if (!parts.includes('img')) {
967
- return undefined;
968
- }
969
- let shape;
970
- let roundedRadius;
971
- for (const part of parts) {
972
- if (part === 'rounded') {
973
- shape = 'rounded';
974
- continue;
975
- }
976
- if (part === 'square') {
977
- shape = 'square';
978
- continue;
979
- }
980
- const roundedMatch = part.match(/^rounded=([0-9.]+)$/);
981
- if (roundedMatch && roundedMatch[1]) {
982
- const num = parseFloat(roundedMatch[1]);
983
- if (!isNaN(num) && num > 0) {
984
- shape = 'rounded';
985
- roundedRadius = num;
986
- }
987
- }
988
- }
989
- return { url, shape, roundedRadius };
990
- }
991
- function matchHtmlBlock(trimmed) {
992
- const m = trimmed.match(/^\[html\]\(([^)]+)\)\s*$/);
993
- if (!m) {
994
- return undefined;
995
- }
996
- const urlGroup = m[1];
997
- if (urlGroup === undefined) {
998
- return undefined;
999
- }
1000
- const source = urlGroup.trim();
1001
- if (source.length === 0) {
1002
- return undefined;
1003
- }
1004
- return { source };
1005
- }
1006
- function matchContentTitle(content) {
1007
- const m = content.match(/^>\s+(.+)$/);
1008
- if (!m) {
1009
- return undefined;
1010
- }
1011
- const group = m[1];
1012
- if (group === undefined) {
1013
- return undefined;
1014
- }
1015
- const text = group.trimEnd();
1016
- return { text };
1017
- }
1018
- function matchQuote(content) {
1019
- const m = content.match(/^(@+)\s+(.+)$/);
1020
- if (!m) {
1021
- return undefined;
1022
- }
1023
- const atGroup = m[1];
1024
- const textGroup = m[2];
1025
- if (atGroup === undefined || textGroup === undefined) {
1026
- return undefined;
1027
- }
1028
- const level = atGroup.length;
1029
- const text = textGroup.trimEnd();
1030
- return { level, text };
1031
- }
1032
- function normalizeQuoteLine(match, baseLevel) {
1033
- if (match.level < baseLevel) {
1034
- return undefined;
1035
- }
1036
- const relativeLevel = match.level - baseLevel;
1037
- if (relativeLevel === 0) {
1038
- return match.text;
1039
- }
1040
- const nestedMarkers = '@'.repeat(relativeLevel);
1041
- return `${nestedMarkers} ${match.text}`;
1042
- }
1043
- function adjustNestedQuoteLevels(nodes, parentLevel) {
1044
- for (const node of nodes) {
1045
- if (node.type !== 'quote') {
1046
- continue;
1047
- }
1048
- node.level += parentLevel;
1049
- adjustNestedQuoteLevels(node.children, node.level);
1050
- }
1051
- }
1052
- function matchCodeFenceStart(trimmed) {
1053
- if (!trimmed.startsWith('```')) {
1054
- return undefined;
1055
- }
1056
- const m = trimmed.match(/^```([^\s`]*)\s*$/);
1057
- if (!m) {
1058
- return undefined;
1059
- }
1060
- const group = m[1];
1061
- if (group === undefined || group.length === 0) {
1062
- return { language: undefined };
1063
- }
1064
- return { language: group };
1065
- }
1066
- function isCodeFenceEnd(trimmed) {
1067
- return /^```\s*$/.test(trimmed);
1068
- }
1069
- /**
1070
- * Match code fence with optional leading attributes like [html]```
1071
- * Returns the language and whether it's an HTML block
1072
- */
1073
- function matchAttributedCodeFence(trimmed) {
1074
- // Match pattern: optional [attr]... followed by ```language?
1075
- const match = trimmed.match(/^(\[.+\])*\s*```([^\s`]*)\s*$/);
1076
- if (!match) {
1077
- return undefined;
1078
- }
1079
- const attrPart = match[1] || '';
1080
- const language = match[2] || undefined;
1081
- const isHtml = /\[html\]/i.test(attrPart);
1082
- return { language, isHtml };
1083
- }
1084
- function matchListItem(content, line, column) {
1085
- const taskMatch = content.match(/^(-+)(\[(.|..)?\])(?:\s+(.*))?$/);
1086
- if (taskMatch) {
1087
- const dashes = taskMatch[1];
1088
- const markerGroup = taskMatch[2];
1089
- const markerInner = taskMatch[3];
1090
- const textGroup = taskMatch[4] || '';
1091
- if (!dashes || !markerGroup) {
1092
- return undefined;
1093
- }
1094
- const indent = dashes.length;
1095
- const taskStatus = mapTaskStatus(markerInner);
1096
- const text = textGroup.trimEnd();
1097
- const textColumn = getListTextColumn(column, taskMatch[0], textGroup);
1098
- return { kind: 'task', indent, taskStatus, text, line, column: textColumn };
1099
- }
1100
- const orderedMatch = content.match(/^(\d+(?:\.\d+)*)\.?\s*(\[(.|..)?\])?(?:\s+(.*))?$/);
1101
- if (orderedMatch) {
1102
- const ordinalGroup = orderedMatch[1];
1103
- const markerGroup = orderedMatch[2];
1104
- const markerInner = orderedMatch[3];
1105
- const textGroup = orderedMatch[4] || '';
1106
- if (!ordinalGroup) {
1107
- return undefined;
1108
- }
1109
- const ordinal = ordinalGroup;
1110
- const indent = ordinal.split('.').length;
1111
- const taskStatus = markerGroup ? mapTaskStatus(markerInner) : undefined;
1112
- const text = textGroup.trimEnd();
1113
- const textColumn = getListTextColumn(column, orderedMatch[0], textGroup);
1114
- return { kind: 'ordered', indent, ordinal, taskStatus, text, line, column: textColumn };
1115
- }
1116
- const foldableMatch = content.match(/^(\++)(\[(.|..)?\])?(?:\s+(.*))?$/);
1117
- if (foldableMatch) {
1118
- const pluses = foldableMatch[1];
1119
- const markerGroup = foldableMatch[2];
1120
- const markerInner = foldableMatch[3];
1121
- const textGroup = foldableMatch[4] || '';
1122
- if (!pluses) {
1123
- return undefined;
1124
- }
1125
- const indent = pluses.length;
1126
- const taskStatus = markerGroup ? mapTaskStatus(markerInner) : undefined;
1127
- const text = textGroup.trimEnd();
1128
- const textColumn = getListTextColumn(column, foldableMatch[0], textGroup);
1129
- return {
1130
- kind: 'foldable',
1131
- indent,
1132
- ordinal: ordinalFromIndent(indent),
1133
- taskStatus,
1134
- text,
1135
- line,
1136
- column: textColumn,
1137
- };
1138
- }
1139
- const bulletMatch = content.match(/^(-+)(?:\s+(.*))?$/);
1140
- if (bulletMatch) {
1141
- const dashes = bulletMatch[1];
1142
- const textGroup = bulletMatch[2] || '';
1143
- if (!dashes) {
1144
- return undefined;
1145
- }
1146
- const indent = dashes.length;
1147
- const text = textGroup.trimEnd();
1148
- const textColumn = getListTextColumn(column, bulletMatch[0], textGroup);
1149
- return { kind: 'bullet', indent, text, line, column: textColumn };
1150
- }
1151
- return undefined;
1152
- }
1153
- function getListTextColumn(baseColumn, matchText, rawText) {
1154
- if (baseColumn === undefined) {
1155
- return undefined;
1156
- }
1157
- const prefixLength = matchText.length - rawText.length;
1158
- return baseColumn + Math.max(0, prefixLength);
1159
- }
1160
- function mapTaskStatus(markerInner) {
1161
- if (!markerInner || markerInner === '') {
1162
- return 'unknown';
1163
- }
1164
- if (markerInner === 'o') {
1165
- return 'in_progress';
1166
- }
1167
- if (markerInner === 'x') {
1168
- return 'not_done';
1169
- }
1170
- if (markerInner === 'v') {
1171
- return 'done';
1172
- }
1173
- return 'unknown';
1174
- }
1175
- function ordinalFromIndent(indent) {
1176
- if (indent <= 0) {
1177
- return '1';
1178
- }
1179
- return Array(indent).fill('1').join('.');
1180
- }
1181
- function buildListItems(matches, startIndex, baseIndent) {
1182
- const items = [];
1183
- let index = startIndex;
1184
- while (index < matches.length) {
1185
- const current = matches[index];
1186
- if (!current) {
1187
- break;
1188
- }
1189
- if (current.indent < baseIndent) {
1190
- break;
1191
- }
1192
- if (current.indent > baseIndent) {
1193
- break;
1194
- }
1195
- const paragraph = {
1196
- type: 'paragraph',
1197
- children: [{ type: 'text', value: current.text }],
1198
- };
1199
- if (current.line !== undefined && current.column !== undefined) {
1200
- (0, location_1.setNodeLocation)(paragraph, {
1201
- line: current.line,
1202
- column: current.column,
1203
- });
1204
- }
1205
- const childrenBlocks = [paragraph];
1206
- // Add any child blocks (code blocks, etc.) that follow this list item
1207
- if (current.childBlocks && current.childBlocks.length > 0) {
1208
- childrenBlocks.push(...current.childBlocks);
1209
- }
1210
- index += 1;
1211
- const next = matches[index];
1212
- if (next && next.indent > current.indent) {
1213
- const childResult = buildListItems(matches, index, next.indent);
1214
- childrenBlocks.push({
1215
- type: 'list',
1216
- kind: next.kind,
1217
- orderedStyle: next.kind === 'ordered' ? 'decimal' : undefined,
1218
- children: childResult.items,
1219
- });
1220
- index = childResult.nextIndex;
1221
- }
1222
- const item = {
1223
- type: 'listItem',
1224
- kind: current.kind,
1225
- ordinal: current.ordinal,
1226
- taskStatus: current.taskStatus,
1227
- indent: current.indent,
1228
- children: childrenBlocks,
1229
- };
1230
- items.push(item);
1231
- }
1232
- return { items, nextIndex: index };
1233
- }
1234
- function parseAttributeLine(text, lineNumber, errors, baseAttrs, fallbackPosition, tabCount) {
1235
- const result = {
1236
- attrs: undefined,
1237
- foldNext: false,
1238
- tagName: undefined,
1239
- isComment: false,
1240
- isDisabled: false,
1241
- isSheet: false,
1242
- isHtml: false,
1243
- recognizedAny: false,
1244
- };
1245
- let attrs = baseAttrs ? { ...baseAttrs } : undefined;
1246
- const unknownTokens = [];
1247
- let arrowCount = 0;
1248
- let i = 0;
1249
- while (i < text.length) {
1250
- const start = text.indexOf('[', i);
1251
- if (start === -1) {
1252
- break;
1253
- }
1254
- const end = text.indexOf(']', start + 1);
1255
- if (end === -1) {
1256
- break;
1257
- }
1258
- const inside = text.slice(start + 1, end).trim();
1259
- if (inside.length === 0) {
1260
- i = end + 1;
1261
- continue;
1262
- }
1263
- const parts = inside.split(',');
1264
- for (const rawPart of parts) {
1265
- const part = rawPart.trim();
1266
- if (part.length === 0) {
1267
- continue;
1268
- }
1269
- if (part === 'c') {
1270
- attrs = ensureBlockAttributes(attrs);
1271
- attrs.align = 'center';
1272
- result.recognizedAny = true;
1273
- continue;
1274
- }
1275
- if (part === 'r') {
1276
- attrs = ensureBlockAttributes(attrs);
1277
- attrs.align = 'right';
1278
- result.recognizedAny = true;
1279
- continue;
1280
- }
1281
- if (part === '->' || part === '<-' || part === '<->') {
1282
- arrowCount += 1;
1283
- result.recognizedAny = true;
1284
- if (part === '->' && arrowCount > 2) {
1285
- (0, diagnostics_1.reportParseWarning)(errors, {
1286
- message: 'More than two [->] attributes in a row; extra [->] will be treated as plain text.',
1287
- line: lineNumber,
1288
- code: MULTI_ARROW_WARNING_CODE,
1289
- });
1290
- continue;
1291
- }
1292
- attrs = ensureBlockAttributes(attrs);
1293
- if (part === '->' || part === '<->') {
1294
- const basePosition = attrs.position ??
1295
- fallbackPosition ??
1296
- (tabCount !== undefined ? mapTabsToPosition(tabCount) : undefined);
1297
- attrs.position = shiftPositionRight(basePosition);
1298
- attrs.sameLine = true;
1299
- }
1300
- if (part === '<-' || part === '<->') {
1301
- attrs.truncateRight = true;
1302
- }
1303
- continue;
1304
- }
1305
- if (part === 'fold') {
1306
- result.foldNext = true;
1307
- result.recognizedAny = true;
1308
- continue;
1309
- }
1310
- if (part === 'sheet') {
1311
- result.isSheet = true;
1312
- result.recognizedAny = true;
1313
- continue;
1314
- }
1315
- if (part === 'html') {
1316
- result.isHtml = true;
1317
- result.recognizedAny = true;
1318
- continue;
1319
- }
1320
- if (part === 'comment') {
1321
- result.isComment = true;
1322
- result.recognizedAny = true;
1323
- continue;
1324
- }
1325
- if (part === 'disable' || part === 'd') {
1326
- result.isDisabled = true;
1327
- result.recognizedAny = true;
1328
- continue;
1329
- }
1330
- if (part.startsWith('tag=')) {
1331
- result.tagName = part.slice('tag='.length);
1332
- result.recognizedAny = true;
1333
- continue;
1334
- }
1335
- if (part.startsWith('t=')) {
1336
- result.tagName = part.slice(2);
1337
- result.recognizedAny = true;
1338
- continue;
1339
- }
1340
- if (part.startsWith('f=')) {
1341
- result.tagName = part.slice(2);
1342
- result.recognizedAny = true;
1343
- continue;
1344
- }
1345
- unknownTokens.push(part);
1346
- continue;
1347
- }
1348
- i = end + 1;
1349
- }
1350
- if (!result.recognizedAny && unknownTokens.length > 0) {
1351
- for (const token of unknownTokens) {
1352
- (0, diagnostics_1.reportParseWarning)(errors, {
1353
- message: `Unknown block attribute token "${token}"`,
1354
- line: lineNumber,
1355
- code: UNKNOWN_ATTRIBUTE_WARNING_CODE,
1356
- });
1357
- }
1358
- }
1359
- result.attrs = attrs;
1360
- return result;
1361
- }
1362
- function ensureBlockAttributes(attrs) {
1363
- if (attrs) {
1364
- return attrs;
1365
- }
1366
- return {};
1367
- }
1368
- function mergeBlockAttributes(base, extra) {
1369
- if (!base) {
1370
- return { ...extra };
1371
- }
1372
- return { ...base, ...extra };
1373
- }
1374
- function shiftPositionRight(position) {
1375
- if (position === 'L') {
1376
- return 'C';
1377
- }
1378
- if (position === 'C') {
1379
- return 'R';
1380
- }
1381
- if (position === 'R') {
1382
- return 'R';
1383
- }
1384
- return 'C';
1385
- }