@wdprlib/parser 5.3.0 → 5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/index.cjs +2074 -1137
  2. package/dist/index.d.cts +2 -0
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +2074 -1137
  5. package/package.json +3 -2
  6. package/src/build-info.generated.ts +2 -2
  7. package/src/lexer/lexer.ts +34 -5
  8. package/src/lexer/quoted-string.ts +4 -4
  9. package/src/lexer/syntax-actions.ts +6 -1
  10. package/src/lexer/text-actions.ts +15 -1
  11. package/src/lexer/url-schemes.ts +78 -0
  12. package/src/parser/constants.ts +4 -0
  13. package/src/parser/parse/block.ts +1 -1
  14. package/src/parser/postprocess/divAdjacentParagraph.ts +23 -21
  15. package/src/parser/postprocess/spanStrip/merge.ts +8 -2
  16. package/src/parser/preprocess/typography.ts +25 -3
  17. package/src/parser/preprocess/utils/raw-regions.ts +59 -14
  18. package/src/parser/preprocess/whitespace/index.ts +8 -1
  19. package/src/parser/rules/block/bibliography/entry-content.ts +1 -1
  20. package/src/parser/rules/block/block-list/bare-content.ts +3 -1
  21. package/src/parser/rules/block/block-list/bare-paragraph.ts +7 -2
  22. package/src/parser/rules/block/block-list/item-content.ts +7 -3
  23. package/src/parser/rules/block/block-list/li-content.ts +8 -3
  24. package/src/parser/rules/block/block-list/li-item.ts +1 -1
  25. package/src/parser/rules/block/blockquote/build.ts +1 -1
  26. package/src/parser/rules/block/code/boundary.ts +76 -0
  27. package/src/parser/rules/block/code/content.ts +11 -40
  28. package/src/parser/rules/block/code/index.ts +8 -31
  29. package/src/parser/rules/block/code/open.ts +46 -0
  30. package/src/parser/rules/block/definition-list/item-key.ts +1 -1
  31. package/src/parser/rules/block/definition-list/item-value.ts +1 -1
  32. package/src/parser/rules/block/div/failed.ts +2 -0
  33. package/src/parser/rules/block/index.ts +3 -0
  34. package/src/parser/rules/block/list/line.ts +6 -3
  35. package/src/parser/rules/block/math/boundary.ts +104 -0
  36. package/src/parser/rules/block/math/index.ts +17 -57
  37. package/src/parser/rules/block/note/boundary.ts +88 -0
  38. package/src/parser/rules/block/note/index.ts +37 -0
  39. package/src/parser/rules/block/paragraph/index.ts +12 -4
  40. package/src/parser/rules/block/paragraph/normalize.ts +6 -1
  41. package/src/parser/rules/block/parsing/block-item.ts +1 -1
  42. package/src/parser/rules/block/parsing/content.ts +1 -1
  43. package/src/parser/rules/block/parsing/inline-content.ts +6 -3
  44. package/src/parser/rules/block/table/pipe/cell.ts +3 -1
  45. package/src/parser/rules/block/table-block/cell-content/index.ts +1 -1
  46. package/src/parser/rules/block/table-block/cell-content/segments.ts +5 -2
  47. package/src/parser/rules/block/table-block/cell-newline.ts +2 -1
  48. package/src/parser/rules/contracts/rule.ts +4 -2
  49. package/src/parser/rules/contracts/scope.ts +2 -0
  50. package/src/parser/rules/inline/anchor/child.ts +8 -2
  51. package/src/parser/rules/inline/anchor/content.ts +3 -1
  52. package/src/parser/rules/inline/anchor/index.ts +4 -1
  53. package/src/parser/rules/inline/anchor/newline.ts +2 -1
  54. package/src/parser/rules/inline/autolink.ts +153 -0
  55. package/src/parser/rules/inline/button/attributes.ts +17 -0
  56. package/src/parser/rules/inline/button/index.ts +17 -0
  57. package/src/parser/rules/inline/button/syntax.ts +56 -0
  58. package/src/parser/rules/inline/date/index.ts +17 -0
  59. package/src/parser/rules/inline/date/syntax.ts +46 -0
  60. package/src/parser/rules/inline/email/candidates.ts +134 -0
  61. package/src/parser/rules/inline/email/index.ts +36 -0
  62. package/src/parser/rules/inline/email/scan.ts +76 -0
  63. package/src/parser/rules/inline/expr/branch.ts +3 -1
  64. package/src/parser/rules/inline/footnote/boundary.ts +56 -0
  65. package/src/parser/rules/inline/footnote/content.ts +29 -41
  66. package/src/parser/rules/inline/footnote/elements.ts +9 -34
  67. package/src/parser/rules/inline/footnote/index.ts +4 -1
  68. package/src/parser/rules/inline/formatting/close.ts +12 -0
  69. package/src/parser/rules/inline/index.ts +14 -0
  70. package/src/parser/rules/inline/line-break/newline.ts +8 -1
  71. package/src/parser/rules/inline/link-bracket/direct-url.ts +11 -3
  72. package/src/parser/rules/inline/link-bracket/parsed.ts +9 -4
  73. package/src/parser/rules/inline/link-bracket/parts.ts +14 -36
  74. package/src/parser/rules/inline/link-bracket/special-target.ts +9 -0
  75. package/src/parser/rules/inline/link-single.ts +9 -7
  76. package/src/parser/rules/inline/link-triple/index.ts +1 -0
  77. package/src/parser/rules/inline/link-triple/label.ts +7 -1
  78. package/src/parser/rules/inline/parsing/automatic-line-break.ts +35 -0
  79. package/src/parser/rules/inline/parsing/block-boundary.ts +2 -0
  80. package/src/parser/rules/inline/parsing/block-start-predicates.ts +10 -0
  81. package/src/parser/rules/inline/parsing/inline-content.ts +36 -6
  82. package/src/parser/rules/inline/parsing/plain-text.ts +7 -2
  83. package/src/parser/rules/inline/parsing/preserved-line-break.ts +13 -0
  84. package/src/parser/rules/inline/parsing/raw-tag.ts +19 -0
  85. package/src/parser/rules/inline/raw/angle.ts +2 -1
  86. package/src/parser/rules/inline/raw/end.ts +21 -1
  87. package/src/parser/rules/inline/size/content.ts +1 -1
  88. package/src/parser/rules/inline/size/value.ts +11 -0
  89. package/src/parser/rules/inline/social/index.ts +17 -0
  90. package/src/parser/rules/inline/social/syntax.ts +40 -0
  91. package/src/parser/rules/inline/span/content.ts +3 -1
  92. package/src/parser/rules/inline/span/newline.ts +2 -1
  93. package/src/parser/rules/opaque-probe.ts +58 -0
  94. package/src/parser/rules/block/math/content.ts +0 -54
  95. package/src/parser/rules/block/math/name.ts +0 -35
  96. package/src/parser/rules/inline/footnote/child.ts +0 -22
  97. package/src/parser/rules/inline/footnote/newline.ts +0 -27
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // packages/parser/src/build-info.generated.ts
2
2
  var buildInfo = Object.freeze({
3
- version: "5.3.0",
4
- sha: "b71b84c705e05717f5ea12fd89684a91b669dbff",
3
+ version: "5.4.0",
4
+ sha: "5e221ddeccc585e64dd1a2eb49b14817c3c1be5d",
5
5
  dirty: false
6
6
  });
7
7
 
@@ -205,13 +205,13 @@ function isValidAnchorNameChar(code) {
205
205
  }
206
206
 
207
207
  // packages/parser/src/lexer/quoted-string.ts
208
- function scanQuotedString(state) {
208
+ function scanQuotedString(state, end = state.source.length) {
209
209
  let quoted = advance(state);
210
- while (!isAtEnd(state) && current(state) !== '"' && current(state) !== `
210
+ while (state.pos < end && current(state) !== '"' && current(state) !== `
211
211
  `) {
212
212
  quoted += advance(state);
213
213
  }
214
- if (current(state) === '"') {
214
+ if (state.pos < end && current(state) === '"') {
215
215
  quoted += advance(state);
216
216
  }
217
217
  return quoted;
@@ -296,6 +296,26 @@ function isCompactPlainTextBoundary(code) {
296
296
  }
297
297
  }
298
298
 
299
+ // packages/parser/src/lexer/url-schemes.ts
300
+ var URL_SCHEME_NAMES = new Set([
301
+ "http",
302
+ "https",
303
+ "ftp",
304
+ "gopher",
305
+ "news",
306
+ "mailto",
307
+ "mms"
308
+ ]);
309
+ var TRAILING_URL_SCHEME = /(?:^|[^A-Za-z0-9])(https?|ftp|gopher|news|mailto|mms)$/;
310
+ var URL_SCHEME_PREFIX = /^(?:(?:https?|ftp|gopher|news|mms):\/\/|mailto:)/;
311
+ var URL_PATTERN = /^(?:(?:https?|ftp|gopher|news|mms):\/\/|mailto:)(?:[^ \\/"']*\/)*[^ \t\n\\/"']*[A-Za-z%0-9/?=&~_]/;
312
+ function startsWithUrlScheme(src, pos) {
313
+ return URL_SCHEME_PREFIX.test(src.slice(pos, pos + 10));
314
+ }
315
+ function startsWithUrl(src, pos) {
316
+ return URL_PATTERN.test(src.slice(pos, pos + 2048));
317
+ }
318
+
299
319
  // packages/parser/src/lexer/syntax-actions.ts
300
320
  function scanSimpleSyntaxToken(src, pos, isLineStart) {
301
321
  switch (src[pos]) {
@@ -332,7 +352,7 @@ function scanSimpleSyntaxToken(src, pos, isLineStart) {
332
352
  }
333
353
  }
334
354
  function scanStarToken(src, pos, isLineStart) {
335
- if (src[pos + 1] === "*") {
355
+ if (src[pos + 1] === "*" && !startsWithUrl(src, pos + 2)) {
336
356
  return token("BOLD_MARKER", "**");
337
357
  }
338
358
  return isLineStart ? token("LIST_BULLET", "*") : token("STAR", "*");
@@ -518,7 +538,13 @@ function scanTextToken(src, pos) {
518
538
  return token3("TEXT", char);
519
539
  }
520
540
  function scanCompactTextToken(src, pos) {
521
- const end = findCompactPlainTextRunEnd(src, pos);
541
+ let end = findCompactPlainTextRunEnd(src, pos);
542
+ if (end > pos && src[end] === ":") {
543
+ const match = TRAILING_URL_SCHEME.exec(src.slice(pos, end));
544
+ if (match) {
545
+ end -= match[1].length;
546
+ }
547
+ }
522
548
  return end > pos ? runToken3(src, pos, end, "TEXT") : null;
523
549
  }
524
550
  function token3(type, value) {
@@ -564,6 +590,8 @@ class Lexer {
564
590
  splitBlockClosePositions = new Set;
565
591
  lastNonWhitespaceType = null;
566
592
  blockOpenerDepth = 0;
593
+ rawTagBounds = null;
594
+ rawClosesExhausted = false;
567
595
  constructor(source, options = {}) {
568
596
  this.options = {
569
597
  trackPositions: options.trackPositions ?? true,
@@ -584,8 +612,8 @@ class Lexer {
584
612
  current() {
585
613
  return current(this.state);
586
614
  }
587
- findInvalidAnchorNameEnd() {
588
- return findInvalidAnchorNameEnd(this.state.source, this.state.pos);
615
+ findInvalidAnchorNameEnd(source) {
616
+ return findInvalidAnchorNameEnd(source, this.state.pos);
589
617
  }
590
618
  advance(n = 1) {
591
619
  return advance(this.state, n);
@@ -597,6 +625,18 @@ class Lexer {
597
625
  this.state.tokens.push(createLexerToken(this.state, type, value, this.options.trackPositions));
598
626
  this.lastNonWhitespaceType = updateLastNonWhitespaceType(this.lastNonWhitespaceType, type);
599
627
  this.blockOpenerDepth = nextBlockOpenerDepth(this.blockOpenerDepth, type);
628
+ if (this.rawTagBounds === null && !this.rawClosesExhausted && (value.toLowerCase() === "button" || value.toLowerCase() === "social") && this.state.tokens.at(-2)?.type === "BLOCK_OPEN" && /\s/.test(this.current())) {
629
+ const close = this.state.source.indexOf("]]", this.state.pos);
630
+ if (close < 0) {
631
+ this.rawClosesExhausted = true;
632
+ } else {
633
+ this.rawTagBounds = {
634
+ source: this.state.source.slice(0, close),
635
+ close,
636
+ outerDepth: this.blockOpenerDepth - 1
637
+ };
638
+ }
639
+ }
600
640
  }
601
641
  emitTokenAction(action) {
602
642
  advanceByToken(this.state, action.type, action.length, action.value);
@@ -614,7 +654,14 @@ class Lexer {
614
654
  scanToken() {
615
655
  const char = this.current();
616
656
  const isLineStart = isSyntaxLineStart(this.state);
617
- const src = this.state.source;
657
+ const bounds = this.rawTagBounds;
658
+ const src = bounds?.source ?? this.state.source;
659
+ if (bounds && this.state.pos === bounds.close) {
660
+ this.emitTokenAction({ type: "BLOCK_CLOSE", value: "]]", length: 2 });
661
+ this.blockOpenerDepth = bounds.outerDepth;
662
+ this.rawTagBounds = null;
663
+ return;
664
+ }
618
665
  const spacingAction = scanSpacingToken(src, this.state.pos);
619
666
  if (spacingAction) {
620
667
  this.emitTokenAction(limitBlockquotePrefixSpace(spacingAction, this.state.tokens.at(-1)));
@@ -627,7 +674,7 @@ class Lexer {
627
674
  lineStart: isLineStart,
628
675
  physicalLineStart: this.state.lineStart,
629
676
  splitBlockClose: this.splitBlockClosePositions.has(this.state.pos),
630
- findInvalidAnchorNameEnd: () => this.findInvalidAnchorNameEnd()
677
+ findInvalidAnchorNameEnd: () => this.findInvalidAnchorNameEnd(src)
631
678
  });
632
679
  if (punctuation.handled) {
633
680
  if (punctuation.clearSplitBlockCloseAt !== undefined) {
@@ -642,7 +689,7 @@ class Lexer {
642
689
  if (char === '"') {
643
690
  const lastNonWs = this.lastNonWhitespaceTokenType();
644
691
  if (this.blockOpenerDepth > 0 && lastNonWs === "EQUALS") {
645
- this.addToken("QUOTED_STRING", scanQuotedString(this.state));
692
+ this.addToken("QUOTED_STRING", scanQuotedString(this.state, src.length));
646
693
  return;
647
694
  }
648
695
  this.advance();
@@ -830,7 +877,7 @@ function parseBlockItem(ctx) {
830
877
  }
831
878
  }
832
879
  const fallback = ctx.blockFallbackRule.parse(ctx);
833
- if (fallback.success && fallback.elements.length > 0) {
880
+ if (fallback.success) {
834
881
  return { elements: fallback.elements, consumed: fallback.consumed };
835
882
  }
836
883
  return { elements: [], consumed: 1 };
@@ -878,7 +925,8 @@ function parseBlocksUntil(ctx, closeCondition, options) {
878
925
  blockCtx.pos = pos;
879
926
  blockCtx.scope = blockScope;
880
927
  const result = parseBlockItem(blockCtx);
881
- elements.push(...result.elements);
928
+ for (const element of result.elements)
929
+ elements.push(element);
882
930
  consumed += result.consumed;
883
931
  pos += result.consumed;
884
932
  }
@@ -905,6 +953,42 @@ function getExcludedBlockRules(blockRules, excluded) {
905
953
  byExcluded.set(excluded, filtered);
906
954
  return filtered;
907
955
  }
956
+ // packages/parser/src/parser/rules/inline/parsing/automatic-line-break.ts
957
+ var origins = new WeakMap;
958
+ function createAutomaticLineBreak(token5) {
959
+ const element = { element: "line-break" };
960
+ origins.set(element, token5);
961
+ return element;
962
+ }
963
+ function precedingSingleNewline(ctx) {
964
+ let pos = ctx.pos - 1;
965
+ while (ctx.tokens[pos]?.type === "WHITESPACE")
966
+ pos--;
967
+ const newline = ctx.tokens[pos];
968
+ if (newline?.type !== "NEWLINE")
969
+ return;
970
+ pos--;
971
+ while (ctx.tokens[pos]?.type === "WHITESPACE")
972
+ pos--;
973
+ if (ctx.tokens[pos]?.type === "NEWLINE")
974
+ return;
975
+ return newline;
976
+ }
977
+ function stripAutomaticLineBreak(elements, token5) {
978
+ if (!token5)
979
+ return;
980
+ let index = elements.length - 1;
981
+ while (index >= 0) {
982
+ const element2 = elements[index];
983
+ if (element2?.element !== "text" || !/^[ \t]*$/.test(element2.data))
984
+ break;
985
+ index--;
986
+ }
987
+ const element = elements[index];
988
+ if (element && origins.get(element) === token5)
989
+ elements.length = index;
990
+ }
991
+
908
992
  // packages/parser/src/parser/rules/inline/parsing/rules.ts
909
993
  function canApplyInlineRule(rule, token5) {
910
994
  if (rule.startTokens.length === 0) {
@@ -927,18 +1011,479 @@ function getCandidateInlineRules(inlineRules, tokenType) {
927
1011
  byType.set(tokenType, candidates);
928
1012
  return candidates;
929
1013
  }
1014
+ // packages/parser/src/parser/rules/inline/parsing/raw-tag.ts
1015
+ var unclosedRanges = new WeakMap;
1016
+ function findRawTagClose(tokens, start, end) {
1017
+ const unclosed = unclosedRanges.get(tokens);
1018
+ if (unclosed && start >= unclosed.start && end <= unclosed.end)
1019
+ return null;
1020
+ for (let close = start;close < end; close++) {
1021
+ if (tokens[close]?.type === "BLOCK_CLOSE")
1022
+ return close;
1023
+ if (!tokens[close] || tokens[close]?.type === "EOF")
1024
+ break;
1025
+ }
1026
+ unclosedRanges.set(tokens, { start, end });
1027
+ return null;
1028
+ }
1029
+
1030
+ // packages/parser/src/parser/rules/inline/button/attributes.ts
1031
+ function parseButtonAttributes(source) {
1032
+ const sections = source.trim().split('="');
1033
+ const attrs = {};
1034
+ let key = sections[0].trim();
1035
+ for (const section of sections.slice(1)) {
1036
+ const quote = section.lastIndexOf('"');
1037
+ if (quote < 0)
1038
+ continue;
1039
+ if (key === "text" || key === "class" || key === "style") {
1040
+ attrs[key] = section.slice(0, quote).replace(/\\([\s\S]|$)/g, (_match, char) => char === "0" ? "\x00" : char);
1041
+ }
1042
+ key = section.slice(quote + 1).trim();
1043
+ }
1044
+ return attrs;
1045
+ }
1046
+
1047
+ // packages/parser/src/parser/rules/inline/button/syntax.ts
1048
+ function parseButtonSyntax(ctx, start, end) {
1049
+ const tokens = ctx.tokens;
1050
+ if (tokens[start]?.type !== "BLOCK_OPEN" || tokens[start + 1]?.value.toLowerCase() !== "button")
1051
+ return null;
1052
+ let pos = start + 2;
1053
+ const skipSpace = () => {
1054
+ const before = pos;
1055
+ while (pos < end && /^\s+$/.test(tokens[pos]?.value ?? ""))
1056
+ pos++;
1057
+ return pos > before;
1058
+ };
1059
+ if (!skipSpace())
1060
+ return null;
1061
+ let action = "";
1062
+ while (pos < end && tokens[pos]?.type !== "BLOCK_CLOSE" && !/^\s+$/.test(tokens[pos]?.value ?? "")) {
1063
+ const part = tokens[pos]?.value ?? "";
1064
+ if (!/^[a-z0-9_-]+$/i.test(part))
1065
+ return null;
1066
+ action += part;
1067
+ pos++;
1068
+ }
1069
+ if (!action)
1070
+ return null;
1071
+ action = action.replaceAll("_", "-");
1072
+ skipSpace();
1073
+ const close = findRawTagClose(tokens, pos, end);
1074
+ if (close === null)
1075
+ return null;
1076
+ const attrs = parseButtonAttributes(tokens.slice(pos, close).map((token5) => token5.value).join(""));
1077
+ const value = (key) => attrs[key] && attrs[key] !== "0" ? attrs[key] : null;
1078
+ const attributes = {};
1079
+ for (const name of ["class", "style"]) {
1080
+ const attr = value(name);
1081
+ if (attr !== null)
1082
+ attributes[name] = attr;
1083
+ }
1084
+ return { data: { action, text: value("text"), attributes }, end: close + 1 };
1085
+ }
1086
+
1087
+ // packages/parser/src/parser/rules/inline/social/syntax.ts
1088
+ var invalidBodies = new WeakMap;
1089
+ function parseSocialSyntax(ctx, start, end) {
1090
+ const tokens = ctx.tokens;
1091
+ if (tokens[start]?.type !== "BLOCK_OPEN" || tokens[start + 1]?.value.toLowerCase() !== "social")
1092
+ return null;
1093
+ const bodyStart = start + 2;
1094
+ if (tokens[bodyStart]?.type !== "BLOCK_CLOSE" && !/^\s+$/.test(tokens[bodyStart]?.value ?? ""))
1095
+ return null;
1096
+ const invalid = invalidBodies.get(tokens);
1097
+ if (invalid && bodyStart >= invalid.start && bodyStart <= invalid.bracket)
1098
+ return null;
1099
+ const close = findRawTagClose(tokens, bodyStart, end);
1100
+ if (close === null)
1101
+ return null;
1102
+ for (let pos = bodyStart;pos < close; pos++) {
1103
+ if (tokens[pos].value.includes("]")) {
1104
+ invalidBodies.set(tokens, { start: bodyStart, bracket: pos });
1105
+ return null;
1106
+ }
1107
+ }
1108
+ const body = tokens.slice(bodyStart, close).map((token5) => token5.value).join("");
1109
+ const sites = body.trim() ? body.split(",").map((site) => site.trim().toLowerCase()).filter(Boolean) : null;
1110
+ return { data: { sites }, end: close + 1 };
1111
+ }
1112
+
1113
+ // packages/parser/src/parser/rules/block/parsing/attributes/names.ts
1114
+ function consumeAttributeName(ctx, startPos, startConsumed, startName, options) {
1115
+ if (startName === "_" && isAttributeWordToken(ctx.tokens[startPos])) {
1116
+ startName += ctx.tokens[startPos]?.value ?? "";
1117
+ startPos++;
1118
+ startConsumed++;
1119
+ }
1120
+ return options.strikeHyphens ? consumeRawNameSuffix(ctx, startPos, startConsumed, startName, options.hyphenatedNames) : consumeSafeNameSuffix(ctx, startPos, startConsumed, startName);
1121
+ }
1122
+ function isAttributeNameToken(token5) {
1123
+ return isAttributeWordToken(token5) || token5?.type === "UNDERSCORE";
1124
+ }
1125
+ function consumeSafeNameSuffix(ctx, startPos, startConsumed, startName) {
1126
+ let name = startName;
1127
+ let pos = startPos;
1128
+ let consumed = startConsumed;
1129
+ while (isAttributeNameSeparator(ctx.tokens[pos]) && isAttributeWordToken(ctx.tokens[pos + 1])) {
1130
+ name += ctx.tokens[pos]?.value ?? "";
1131
+ pos++;
1132
+ consumed++;
1133
+ name += ctx.tokens[pos]?.value ?? "";
1134
+ pos++;
1135
+ consumed++;
1136
+ }
1137
+ return { name, pos, consumed };
1138
+ }
1139
+ function consumeRawNameSuffix(ctx, startPos, startConsumed, startName, hyphenatedNames) {
1140
+ let name = startName;
1141
+ let pos = startPos;
1142
+ let consumed = startConsumed;
1143
+ while (isAttributeNameSeparator(ctx.tokens[pos])) {
1144
+ while (isAttributeNameSeparator(ctx.tokens[pos])) {
1145
+ if (hyphenatedNames || ctx.tokens[pos]?.type === "UNDERSCORE") {
1146
+ name += ctx.tokens[pos]?.value ?? "-";
1147
+ }
1148
+ pos++;
1149
+ consumed++;
1150
+ }
1151
+ if (!isAttributeWordToken(ctx.tokens[pos])) {
1152
+ break;
1153
+ }
1154
+ if (hyphenatedNames || name.endsWith("_")) {
1155
+ name += ctx.tokens[pos]?.value ?? "";
1156
+ }
1157
+ pos++;
1158
+ consumed++;
1159
+ }
1160
+ return { name, pos, consumed };
1161
+ }
1162
+ function isAttributeWordToken(token5) {
1163
+ return token5?.type === "TEXT" || token5?.type === "IDENTIFIER";
1164
+ }
1165
+ function isAttributeNameSeparator(token5) {
1166
+ return token5?.type === "TEXT" && token5.value === "-" || token5?.type === "STRIKE_MARKER" || token5?.type === "UNDERSCORE";
1167
+ }
1168
+
1169
+ // packages/parser/src/parser/rules/block/parsing/attributes/values.ts
1170
+ function consumeAttributeValue(token5) {
1171
+ if (!token5) {
1172
+ return null;
1173
+ }
1174
+ if (token5.type === "QUOTED_STRING") {
1175
+ return { value: stripQuotes(token5.value) };
1176
+ }
1177
+ if (isAttributeNameToken(token5)) {
1178
+ return { value: token5.value };
1179
+ }
1180
+ return null;
1181
+ }
1182
+ function stripQuotes(value) {
1183
+ return value.startsWith('"') && value.endsWith('"') ? value.slice(1, -1) : value;
1184
+ }
1185
+
1186
+ // packages/parser/src/parser/rules/block/parsing/attributes/scanner.ts
1187
+ function scanAttributes(ctx, startPos, options) {
1188
+ const attrs = {};
1189
+ let pos = startPos;
1190
+ let consumed = 0;
1191
+ while (pos < ctx.tokens.length) {
1192
+ const token5 = ctx.tokens[pos];
1193
+ if (!token5 || isAttributeTerminator(token5)) {
1194
+ break;
1195
+ }
1196
+ if (token5.type === "WHITESPACE") {
1197
+ pos++;
1198
+ consumed++;
1199
+ continue;
1200
+ }
1201
+ if (!isAttributeNameToken(token5)) {
1202
+ pos++;
1203
+ consumed++;
1204
+ continue;
1205
+ }
1206
+ let name = token5.value;
1207
+ pos++;
1208
+ consumed++;
1209
+ const nameResult = consumeAttributeName(ctx, pos, consumed, name, options);
1210
+ pos = nameResult.pos;
1211
+ consumed = nameResult.consumed;
1212
+ name = nameResult.name.toLowerCase();
1213
+ if (ctx.tokens[pos]?.type !== "EQUALS") {
1214
+ attrs[name] = "true";
1215
+ continue;
1216
+ }
1217
+ pos++;
1218
+ consumed++;
1219
+ const valueResult = consumeAttributeValue(ctx.tokens[pos]);
1220
+ if (!valueResult) {
1221
+ continue;
1222
+ }
1223
+ attrs[name] = valueResult.value;
1224
+ pos++;
1225
+ consumed++;
1226
+ }
1227
+ return { attrs, consumed };
1228
+ }
1229
+ function isAttributeTerminator(token5) {
1230
+ return token5.type === "BLOCK_CLOSE" || token5.type === "NEWLINE" || token5.type === "EOF";
1231
+ }
1232
+
1233
+ // packages/parser/src/parser/rules/block/parsing/attributes/index.ts
1234
+ function parseAttributes(ctx, startPos) {
1235
+ const result = scanAttributes(ctx, startPos, { hyphenatedNames: true, strikeHyphens: false });
1236
+ return { attrs: filterUnsafeAttributes(result.attrs), consumed: result.consumed };
1237
+ }
1238
+ function parseAttributesRaw(ctx, startPos, hyphenatedNames = true) {
1239
+ return scanAttributes(ctx, startPos, { hyphenatedNames, strikeHyphens: true });
1240
+ }
1241
+
1242
+ // packages/parser/src/parser/rules/inline/date/syntax.ts
1243
+ function parseDateSyntax(ctx, start, end) {
1244
+ const tokens = ctx.tokens;
1245
+ if (tokens[start]?.type !== "BLOCK_OPEN" || tokens[start + 1]?.value !== "date")
1246
+ return null;
1247
+ let pos = start + 2;
1248
+ const skipSpace = () => {
1249
+ const before = pos;
1250
+ while (pos < end && /^(?:\s+)$/.test(tokens[pos]?.value ?? ""))
1251
+ pos++;
1252
+ return pos > before;
1253
+ };
1254
+ if (!skipSpace())
1255
+ return null;
1256
+ const value = tokens[pos]?.value ?? "";
1257
+ if (pos >= end || !/^\d+$/.test(value))
1258
+ return null;
1259
+ const timestamp = Number(value);
1260
+ if (!Number.isSafeInteger(timestamp) || timestamp > 8640000000000)
1261
+ return null;
1262
+ pos++;
1263
+ const separator = skipSpace();
1264
+ if (tokens[pos]?.type !== "BLOCK_CLOSE" && !separator)
1265
+ return null;
1266
+ let close = pos;
1267
+ for (;close < end; close++) {
1268
+ const token5 = tokens[close];
1269
+ if (!token5 || token5.type === "EOF" || token5.type === "BLOCK_OPEN" || /[\r\n]/.test(token5.value))
1270
+ return null;
1271
+ if (token5.type === "BLOCK_CLOSE")
1272
+ break;
1273
+ }
1274
+ if (close >= end)
1275
+ return null;
1276
+ const attributes = parseAttributesRaw(ctx, pos).attrs;
1277
+ const format = attributes.format || null;
1278
+ return {
1279
+ data: {
1280
+ value: { timestamp, timezone: "UTC" },
1281
+ format,
1282
+ hover: format?.split("|").slice(1).includes("agohover") ?? false
1283
+ },
1284
+ end: close + 1
1285
+ };
1286
+ }
1287
+
1288
+ // packages/parser/src/parser/rules/inline/email/scan.ts
1289
+ function isDomainChar(code) {
1290
+ return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 45;
1291
+ }
1292
+ function isLocalChar(code) {
1293
+ return isDomainChar(code) || code === 95;
1294
+ }
1295
+ function scanEmail(source, start, commentEnd) {
1296
+ const comments = [];
1297
+ let pos = start;
1298
+ const skipComments = () => {
1299
+ let end2 = commentEnd(pos);
1300
+ while (end2 > pos) {
1301
+ comments.push({ start: pos, end: end2 });
1302
+ pos = end2;
1303
+ end2 = commentEnd(pos);
1304
+ }
1305
+ };
1306
+ let localPart = false;
1307
+ while (pos < source.length) {
1308
+ skipComments();
1309
+ if (isLocalChar(source.charCodeAt(pos))) {
1310
+ localPart = true;
1311
+ pos++;
1312
+ } else if (source[pos] === "." && localPart) {
1313
+ localPart = false;
1314
+ pos++;
1315
+ } else
1316
+ break;
1317
+ }
1318
+ const localEnd = pos;
1319
+ if (!localPart || source[pos] !== "@")
1320
+ return { localEnd, comments };
1321
+ pos++;
1322
+ let domainPart = false;
1323
+ let dots = 0;
1324
+ let end = -1;
1325
+ while (pos < source.length) {
1326
+ skipComments();
1327
+ if (isDomainChar(source.charCodeAt(pos))) {
1328
+ domainPart = true;
1329
+ pos++;
1330
+ if (dots > 0)
1331
+ end = pos;
1332
+ } else if (source[pos] === "." && domainPart) {
1333
+ domainPart = false;
1334
+ dots++;
1335
+ pos++;
1336
+ } else
1337
+ break;
1338
+ }
1339
+ if (end === -1)
1340
+ return { localEnd, comments };
1341
+ const parts = [];
1342
+ let copied = start;
1343
+ for (const comment of comments) {
1344
+ if (comment.start >= end)
1345
+ break;
1346
+ parts.push(source.slice(copied, comment.start));
1347
+ copied = comment.end;
1348
+ }
1349
+ parts.push(source.slice(copied, end));
1350
+ return { end, address: parts.join(""), localEnd, comments };
1351
+ }
1352
+
1353
+ // packages/parser/src/parser/rules/inline/email/candidates.ts
1354
+ var caches = new WeakMap;
1355
+ var EMAIL_START_TOKENS = [
1356
+ "TEXT",
1357
+ "IDENTIFIER",
1358
+ "UNDERSCORE",
1359
+ "UNDERLINE_MARKER",
1360
+ "STRIKE_MARKER"
1361
+ ];
1362
+ function getCache(tokens) {
1363
+ let cache = caches.get(tokens);
1364
+ if (cache)
1365
+ return cache;
1366
+ const offsets = [0];
1367
+ const values = [];
1368
+ for (const token5 of tokens) {
1369
+ values.push(token5.value);
1370
+ offsets.push(offsets.at(-1) + token5.value.length);
1371
+ }
1372
+ cache = {
1373
+ source: values.join(""),
1374
+ offsets,
1375
+ candidates: new Map,
1376
+ commentEnds: new Map,
1377
+ unclosedComment: Infinity
1378
+ };
1379
+ caches.set(tokens, cache);
1380
+ return cache;
1381
+ }
1382
+ function commentEnd(cache, pos) {
1383
+ const opener = cache.source[pos] === `
1384
+ ` ? pos + 1 : pos;
1385
+ if (!cache.source.startsWith("[!--", opener))
1386
+ return pos;
1387
+ const cached = cache.commentEnds.get(pos);
1388
+ if (cached !== undefined)
1389
+ return cached;
1390
+ const close = opener >= cache.unclosedComment ? -1 : cache.source.indexOf("--]", opener + 4);
1391
+ if (close === -1)
1392
+ cache.unclosedComment = Math.min(cache.unclosedComment, opener);
1393
+ const end = close === -1 ? pos : close + 3;
1394
+ cache.commentEnds.set(pos, end);
1395
+ return end;
1396
+ }
1397
+ function getEmailCandidate(tokens, index) {
1398
+ const token5 = tokens[index];
1399
+ if (!token5 || !EMAIL_START_TOKENS.includes(token5.type))
1400
+ return null;
1401
+ const cache = getCache(tokens);
1402
+ if (cache.candidates.has(index))
1403
+ return cache.candidates.get(index);
1404
+ const value = tokens[index]?.value ?? "";
1405
+ let suffix = value.length;
1406
+ while (suffix > 0 && (isLocalChar(value.charCodeAt(suffix - 1)) || value[suffix - 1] === "."))
1407
+ suffix--;
1408
+ const doubled = value.lastIndexOf("..");
1409
+ if (doubled >= suffix)
1410
+ suffix = doubled + 2;
1411
+ while (value[suffix] === ".")
1412
+ suffix++;
1413
+ if (suffix >= value.length || !isLocalChar(value.charCodeAt(suffix))) {
1414
+ cache.candidates.set(index, null);
1415
+ return null;
1416
+ }
1417
+ const start = cache.offsets[index] + suffix;
1418
+ const scanned = scanEmail(cache.source, start, (pos) => commentEnd(cache, pos));
1419
+ if (scanned.end === undefined) {
1420
+ cache.candidates.set(index, null);
1421
+ let comment = 0;
1422
+ for (let i = index + 1;i < tokens.length && cache.offsets[i + 1] <= scanned.localEnd; i++) {
1423
+ const offset = cache.offsets[i];
1424
+ while (scanned.comments[comment] && scanned.comments[comment].end <= offset)
1425
+ comment++;
1426
+ const region = scanned.comments[comment];
1427
+ if (!region || offset < region.start)
1428
+ cache.candidates.set(i, null);
1429
+ }
1430
+ return null;
1431
+ }
1432
+ let endToken = index;
1433
+ while (cache.offsets[endToken + 1] < scanned.end)
1434
+ endToken++;
1435
+ const candidate = { start, end: scanned.end, endToken, address: scanned.address };
1436
+ cache.candidates.set(index, candidate);
1437
+ return candidate;
1438
+ }
1439
+ function getEmailGroup(tokens, index, limit) {
1440
+ const first = getEmailCandidate(tokens, index);
1441
+ if (!first || first.endToken >= limit)
1442
+ return null;
1443
+ const candidates = [first];
1444
+ let last = first;
1445
+ while (last.endToken > index) {
1446
+ const next = getEmailCandidate(tokens, last.endToken);
1447
+ if (!next || next.start < last.end || next.endToken >= limit)
1448
+ break;
1449
+ candidates.push(next);
1450
+ last = next;
1451
+ }
1452
+ const cache = getCache(tokens);
1453
+ return {
1454
+ candidates,
1455
+ source: cache.source,
1456
+ start: cache.offsets[index],
1457
+ end: cache.offsets[last.endToken + 1],
1458
+ endToken: last.endToken + 1
1459
+ };
1460
+ }
1461
+ function emailRegionEnd(tokens, index, limit) {
1462
+ return getEmailGroup(tokens, index, limit)?.endToken ?? index;
1463
+ }
1464
+
930
1465
  // packages/parser/src/parser/rules/inline/raw/end.ts
1466
+ var unclosedAngleRanges = new WeakMap;
931
1467
  function rawRegionEnd(tokens, start, end) {
932
1468
  const type = tokens[start]?.type;
933
1469
  const close = type === "RAW_OPEN" ? "RAW_OPEN" : type === "RAW_BLOCK_OPEN" ? "RAW_BLOCK_CLOSE" : null;
934
1470
  if (!close)
935
1471
  return start;
1472
+ const cached = type === "RAW_BLOCK_OPEN" ? unclosedAngleRanges.get(tokens) : undefined;
1473
+ if (cached && start >= cached.start && start < cached.end && (cached.lineEnd || end <= cached.end)) {
1474
+ return start;
1475
+ }
936
1476
  for (let pos = start + 1;pos < end; pos++) {
937
- if (tokens[pos]?.type === "NEWLINE" || tokens[pos]?.type === "EOF")
938
- break;
1477
+ if (tokens[pos]?.type === "NEWLINE" || tokens[pos]?.type === "EOF") {
1478
+ if (type === "RAW_BLOCK_OPEN")
1479
+ unclosedAngleRanges.set(tokens, { start, end: pos, lineEnd: true });
1480
+ return start;
1481
+ }
939
1482
  if (tokens[pos]?.type === close)
940
1483
  return pos + 1;
941
1484
  }
1485
+ if (type === "RAW_BLOCK_OPEN")
1486
+ unclosedAngleRanges.set(tokens, { start, end, lineEnd: false });
942
1487
  return start;
943
1488
  }
944
1489
  function protectedInlineRegionEnd(tokens, start, end) {
@@ -957,7 +1502,7 @@ var MIN_INLINE_TEXT_RUN_LENGTH = 32;
957
1502
  var MIN_INLINE_TEXT_RUN_DOCUMENT_TOKENS = 1e5;
958
1503
  function collectLongPlainTextRun(ctx, startPos, endType) {
959
1504
  const firstToken = ctx.tokens[startPos];
960
- if (firstToken?.type === "TEXT" && firstToken.value.length >= MIN_INLINE_TEXT_RUN_LENGTH && firstToken.value !== "(") {
1505
+ if (!getEmailCandidate(ctx.tokens, startPos) && firstToken?.type === "TEXT" && firstToken.value.length >= MIN_INLINE_TEXT_RUN_LENGTH && firstToken.value !== "(") {
961
1506
  return { value: firstToken.value, consumed: 1 };
962
1507
  }
963
1508
  let pos = startPos;
@@ -979,10 +1524,10 @@ function collectLongPlainTextRun(ctx, startPos, endType) {
979
1524
  }
980
1525
  function isPlainTextRunToken(ctx, pos) {
981
1526
  const token5 = ctx.tokens[pos];
982
- if (!token5)
1527
+ if (!token5 || getEmailCandidate(ctx.tokens, pos))
983
1528
  return false;
984
1529
  if (token5.type === "IDENTIFIER") {
985
- return true;
1530
+ return !(URL_SCHEME_NAMES.has(token5.value) && ctx.tokens[pos + 1]?.type === "COLON");
986
1531
  }
987
1532
  if (token5.type === "WHITESPACE") {
988
1533
  const next = ctx.tokens[pos + 1];
@@ -1131,6 +1676,10 @@ var BLOCK_START_TOKENS = [
1131
1676
  "HEADING_MARKER",
1132
1677
  "HR_MARKER",
1133
1678
  "TABLE_MARKER",
1679
+ "TABLE_HEADER",
1680
+ "TABLE_LEFT",
1681
+ "TABLE_CENTER",
1682
+ "TABLE_RIGHT",
1134
1683
  "COLON",
1135
1684
  "BLOCK_OPEN",
1136
1685
  "BLOCK_END_OPEN",
@@ -1208,14 +1757,727 @@ var INDENT_ACCEPTING_BLOCK_NAMES = new Set([
1208
1757
  "tabs"
1209
1758
  ]);
1210
1759
 
1211
- // packages/parser/src/parser/rules/inline/parsing/block-boundary.ts
1212
- function isExcludedBlockToken(ctx, tokenPos) {
1213
- const excluded = ctx.scope.excludedBlockNames;
1214
- if (!excluded?.size)
1215
- return false;
1216
- const token5 = ctx.tokens[tokenPos];
1217
- if (token5?.type !== "BLOCK_OPEN" && token5?.type !== "BLOCK_END_OPEN")
1218
- return false;
1760
+ // packages/parser/src/parser/rules/block/code/open.ts
1761
+ var attributeRanges = new WeakMap;
1762
+ function findAttributesEnd(tokens, start) {
1763
+ const cached = attributeRanges.get(tokens);
1764
+ if (cached && start >= cached.start && start <= cached.end)
1765
+ return cached.end;
1766
+ let end = start;
1767
+ while (end < tokens.length) {
1768
+ const type = tokens[end]?.type;
1769
+ if (type === "BLOCK_CLOSE" || type === "NEWLINE" || type === "EOF")
1770
+ break;
1771
+ end++;
1772
+ }
1773
+ attributeRanges.set(tokens, { start, end });
1774
+ return end;
1775
+ }
1776
+ function findCodeOpen(tokens, start) {
1777
+ const name = tokens[start + 1];
1778
+ if (tokens[start]?.type !== "BLOCK_OPEN" || name?.type !== "IDENTIFIER" && name?.type !== "TEXT" || name.value.toLowerCase() !== "code" || tokens[start + 2]?.type === "UNDERSCORE")
1779
+ return null;
1780
+ const attributesEnd = findAttributesEnd(tokens, start + 2);
1781
+ const repaired = tokens[attributesEnd]?.type !== "BLOCK_CLOSE";
1782
+ const last = tokens[attributesEnd - 1];
1783
+ if (repaired && (last?.type !== "QUOTED_STRING" || !last.value.includes("]]")))
1784
+ return null;
1785
+ let bodyStart = attributesEnd + (repaired ? 0 : 1);
1786
+ if (tokens[bodyStart]?.type === "NEWLINE")
1787
+ bodyStart++;
1788
+ return {
1789
+ attributesEnd,
1790
+ bodyStart,
1791
+ repaired,
1792
+ closingSwallowed: repaired && last.value.includes("[[/code]]")
1793
+ };
1794
+ }
1795
+
1796
+ // packages/parser/src/parser/rules/block/code/boundary.ts
1797
+ var caches2 = new WeakMap;
1798
+ function codeCloseEnd(tokens, start) {
1799
+ if (tokens[start]?.type !== "BLOCK_END_OPEN" || tokens[start + 1]?.value.toLowerCase() !== "code")
1800
+ return start;
1801
+ let end = start + 2;
1802
+ while (tokens[end]?.type === "WHITESPACE")
1803
+ end++;
1804
+ return tokens[end]?.type === "BLOCK_CLOSE" ? end + 1 : start;
1805
+ }
1806
+ function nestedCodeBodyStart(tokens, start) {
1807
+ if (tokens[start]?.type !== "BLOCK_OPEN" || tokens[start + 1]?.value.toLowerCase() !== "code")
1808
+ return start;
1809
+ for (let pos = start + 2;pos < tokens.length; pos++) {
1810
+ const type = tokens[pos]?.type;
1811
+ if (type === "NEWLINE" || type === "EOF" || type === "BLOCK_OPEN")
1812
+ return start;
1813
+ if (type === "BLOCK_CLOSE") {
1814
+ const open = findCodeOpen(tokens, start);
1815
+ return open && !open.repaired ? open.bodyStart : start;
1816
+ }
1817
+ }
1818
+ return start;
1819
+ }
1820
+ function findCodeBodyBounds(tokens, start) {
1821
+ let cache = caches2.get(tokens);
1822
+ if (!cache) {
1823
+ cache = new Map;
1824
+ caches2.set(tokens, cache);
1825
+ }
1826
+ const cached = cache.get(start);
1827
+ if (cached)
1828
+ return cached;
1829
+ const stack = [start];
1830
+ let pos = start;
1831
+ while (pos < tokens.length && tokens[pos]?.type !== "EOF") {
1832
+ const rawEnd = rawRegionEnd(tokens, pos, tokens.length);
1833
+ if (rawEnd > pos) {
1834
+ pos = rawEnd;
1835
+ continue;
1836
+ }
1837
+ const end = codeCloseEnd(tokens, pos);
1838
+ if (end > pos) {
1839
+ const result2 = { closeStart: pos, end, foundClose: true };
1840
+ cache.set(stack.pop(), result2);
1841
+ if (stack.length === 0)
1842
+ return result2;
1843
+ pos = end;
1844
+ continue;
1845
+ }
1846
+ const bodyStart = nestedCodeBodyStart(tokens, pos);
1847
+ if (bodyStart > pos) {
1848
+ const child = cache.get(bodyStart);
1849
+ if (child) {
1850
+ pos = child.end;
1851
+ if (!child.foundClose)
1852
+ break;
1853
+ } else {
1854
+ stack.push(bodyStart);
1855
+ pos = bodyStart;
1856
+ }
1857
+ continue;
1858
+ }
1859
+ pos++;
1860
+ }
1861
+ const result = { closeStart: pos, end: pos, foundClose: false };
1862
+ for (const bodyStart of stack)
1863
+ cache.set(bodyStart, result);
1864
+ return result;
1865
+ }
1866
+
1867
+ // packages/parser/src/parser/rules/block/math/boundary.ts
1868
+ var nameRanges = new WeakMap;
1869
+ var bodyCaches = new WeakMap;
1870
+ function findNameEnd(tokens, start) {
1871
+ const cached = nameRanges.get(tokens);
1872
+ if (cached && start >= cached.start && start <= cached.end)
1873
+ return cached.end;
1874
+ let end = start;
1875
+ while (end < tokens.length) {
1876
+ const type = tokens[end]?.type;
1877
+ if (type === "BLOCK_CLOSE" || type === "WHITESPACE" || type === "NEWLINE" || type === "EOF")
1878
+ break;
1879
+ end++;
1880
+ }
1881
+ nameRanges.set(tokens, { start, end });
1882
+ return end;
1883
+ }
1884
+ function findMathOpen(tokens, start) {
1885
+ const name = tokens[start + 1];
1886
+ if (tokens[start]?.type !== "BLOCK_OPEN" || name?.type !== "IDENTIFIER" && name?.type !== "TEXT" || name.value.toLowerCase() !== "math" || tokens[start + 2]?.type === "UNDERSCORE")
1887
+ return null;
1888
+ let pos = start + 2;
1889
+ while (tokens[pos]?.type === "WHITESPACE")
1890
+ pos++;
1891
+ const nameStart = pos;
1892
+ if (tokens[pos]?.type === "IDENTIFIER" || tokens[pos]?.type === "TEXT") {
1893
+ pos = findNameEnd(tokens, pos);
1894
+ }
1895
+ const nameEnd = pos;
1896
+ while (tokens[pos]?.type === "WHITESPACE")
1897
+ pos++;
1898
+ if (tokens[pos]?.type !== "BLOCK_CLOSE")
1899
+ return null;
1900
+ pos++;
1901
+ if (tokens[pos]?.type === "NEWLINE")
1902
+ pos++;
1903
+ return { nameStart, nameEnd, bodyStart: pos };
1904
+ }
1905
+ function mathCloseEnd(tokens, start) {
1906
+ if (tokens[start]?.type !== "BLOCK_END_OPEN" || tokens[start + 1]?.value.toLowerCase() !== "math" || tokens[start + 2]?.type !== "BLOCK_CLOSE")
1907
+ return start;
1908
+ const end = start + 3;
1909
+ return tokens[end]?.type === "NEWLINE" ? end + 1 : end;
1910
+ }
1911
+ function findMathBodyBounds(tokens, start) {
1912
+ let cache = bodyCaches.get(tokens);
1913
+ if (!cache) {
1914
+ cache = new Map;
1915
+ bodyCaches.set(tokens, cache);
1916
+ }
1917
+ const visited = [];
1918
+ let result = {
1919
+ closeStart: tokens.length,
1920
+ end: tokens.length,
1921
+ foundClose: false,
1922
+ hasContent: false
1923
+ };
1924
+ for (let pos = start;pos < tokens.length; pos++) {
1925
+ const cached = cache.get(pos);
1926
+ if (cached) {
1927
+ result = cached;
1928
+ break;
1929
+ }
1930
+ const end = mathCloseEnd(tokens, pos);
1931
+ if (end > pos) {
1932
+ result = { closeStart: pos, end, foundClose: true, hasContent: false };
1933
+ cache.set(pos, result);
1934
+ break;
1935
+ }
1936
+ visited.push(pos);
1937
+ }
1938
+ for (let i = visited.length - 1;i >= 0; i--) {
1939
+ const pos = visited[i];
1940
+ const token5 = tokens[pos];
1941
+ if (!result.hasContent && (token5.type === "BACKSLASH_BREAK" || token5.value.trim() !== "")) {
1942
+ result = { ...result, hasContent: true };
1943
+ }
1944
+ cache.set(pos, result);
1945
+ }
1946
+ return result;
1947
+ }
1948
+
1949
+ // packages/parser/src/parser/rules/opaque-probe.ts
1950
+ function createOpaqueProbe(ctx) {
1951
+ return {
1952
+ ...ctx,
1953
+ diagnostics: [],
1954
+ footnotes: [],
1955
+ tocEntries: [],
1956
+ codeBlocks: [],
1957
+ htmlBlocks: [],
1958
+ bibcites: [],
1959
+ scope: {
1960
+ ...ctx.scope,
1961
+ inlineEnd: undefined,
1962
+ tableFormatting: undefined,
1963
+ blockCloseCondition: () => true
1964
+ }
1965
+ };
1966
+ }
1967
+ function opaqueRuleEnd(probe, pos, rules, requireClosed = false) {
1968
+ const token5 = probe.tokens[pos];
1969
+ if (!token5)
1970
+ return pos;
1971
+ probe.pos = pos;
1972
+ for (const rule of rules) {
1973
+ if (!rule.startTokens.includes(token5.type))
1974
+ continue;
1975
+ if (rule.name === "code") {
1976
+ const open = findCodeOpen(probe.tokens, pos);
1977
+ if (!open)
1978
+ continue;
1979
+ if (open.closingSwallowed) {
1980
+ if (!requireClosed)
1981
+ return open.bodyStart;
1982
+ continue;
1983
+ }
1984
+ const bounds = findCodeBodyBounds(probe.tokens, open.bodyStart);
1985
+ if (!requireClosed || bounds.foundClose)
1986
+ return bounds.end;
1987
+ continue;
1988
+ }
1989
+ if (rule.name === "math") {
1990
+ const open = findMathOpen(probe.tokens, pos);
1991
+ if (!open)
1992
+ continue;
1993
+ const bounds = findMathBodyBounds(probe.tokens, open.bodyStart);
1994
+ if (bounds.hasContent && (!requireClosed || bounds.foundClose))
1995
+ return bounds.end;
1996
+ continue;
1997
+ }
1998
+ const result = rule.parse(probe);
1999
+ if (result.success)
2000
+ return pos + result.consumed;
2001
+ }
2002
+ return pos;
2003
+ }
2004
+
2005
+ // packages/parser/src/parser/rules/tokens.ts
2006
+ function currentToken(ctx) {
2007
+ return ctx.tokens[ctx.pos] ?? eofToken();
2008
+ }
2009
+ function eofToken() {
2010
+ return {
2011
+ type: "EOF",
2012
+ value: "",
2013
+ position: { start: { line: 0, column: 0, offset: 0 }, end: { line: 0, column: 0, offset: 0 } },
2014
+ lineStart: false
2015
+ };
2016
+ }
2017
+ function hasClosingMarkerBeforeNewline(ctx, markerType, markerValue) {
2018
+ let pos = ctx.pos;
2019
+ while (pos < (ctx.scope.inlineEnd ?? ctx.tokens.length)) {
2020
+ const token5 = ctx.tokens[pos];
2021
+ if (!token5 || token5.type === "NEWLINE" || token5.type === "EOF") {
2022
+ return false;
2023
+ }
2024
+ if (token5.type === markerType) {
2025
+ if (markerValue === undefined || token5.value === markerValue) {
2026
+ return true;
2027
+ }
2028
+ }
2029
+ pos++;
2030
+ }
2031
+ return false;
2032
+ }
2033
+ // packages/parser/src/parser/rules/block/code/attributes.ts
2034
+ function repairSwallowedCodeClose(ctx, pos, attrs) {
2035
+ const prevToken = ctx.tokens[pos - 1];
2036
+ if (prevToken?.type !== "QUOTED_STRING" || !prevToken.value.includes("]]")) {
2037
+ return null;
2038
+ }
2039
+ const rawValue = prevToken.value;
2040
+ const bracketIdx = rawValue.indexOf("]]");
2041
+ const truncatedValue = rawValue.startsWith('"') ? rawValue.slice(1, bracketIdx) : rawValue.slice(0, bracketIdx);
2042
+ for (const key of Object.keys(attrs)) {
2043
+ const stored = attrs[key];
2044
+ if (stored === rawValue || stored === rawValue.slice(1, -1) || stored === rawValue.slice(1)) {
2045
+ attrs[key] = truncatedValue;
2046
+ break;
2047
+ }
2048
+ }
2049
+ return {
2050
+ closingSwallowed: rawValue.includes("[[/code]]")
2051
+ };
2052
+ }
2053
+
2054
+ // packages/parser/src/parser/rules/block/paragraph/span-markers.ts
2055
+ function processCloseSpanMarkers(elements) {
2056
+ let result = null;
2057
+ for (let i = 0;i < elements.length; i++) {
2058
+ const elem = elements[i];
2059
+ if (!elem)
2060
+ continue;
2061
+ if (isCloseSpanMarker(elem)) {
2062
+ if (result === null) {
2063
+ result = elements.slice(0, i);
2064
+ }
2065
+ if (result.length > 0) {
2066
+ const spanContent = [...result];
2067
+ result.length = 0;
2068
+ result.push({
2069
+ element: "container",
2070
+ data: {
2071
+ type: "span",
2072
+ attributes: {},
2073
+ elements: spanContent
2074
+ }
2075
+ });
2076
+ }
2077
+ } else {
2078
+ result?.push(elem);
2079
+ }
2080
+ }
2081
+ return result ?? elements;
2082
+ }
2083
+ function isCloseSpanMarker(elem) {
2084
+ return elem.element === "container" && elem.data && typeof elem.data === "object" && "type" in elem.data && elem.data.type === "span" && "attributes" in elem.data && typeof elem.data.attributes === "object" && elem.data.attributes !== null && "_closeSpan" in elem.data.attributes;
2085
+ }
2086
+
2087
+ // packages/parser/src/parser/rules/inline/parsing/preserved-line-break.ts
2088
+ function createPreservedTrailingLineBreak() {
2089
+ const lineBreak = { element: "line-break" };
2090
+ lineBreak._preservedTrailingBreak = true;
2091
+ return lineBreak;
2092
+ }
2093
+ function createPreservedLeadingLineBreak() {
2094
+ const lineBreak = { element: "line-break", _preservedLeadingBreak: true };
2095
+ return lineBreak;
2096
+ }
2097
+ function isPreservedLeadingLineBreak(element) {
2098
+ return element?.element === "line-break" && element._preservedLeadingBreak === true;
2099
+ }
2100
+
2101
+ // packages/parser/src/parser/rules/block/paragraph/normalize.ts
2102
+ function normalizeParagraphElements(source) {
2103
+ let elements = processCloseSpanMarkers(source);
2104
+ removeTrailingLineBreaks(elements);
2105
+ removeTrailingWhitespaceText(elements);
2106
+ elements = removeLeadingLineBreaks(elements);
2107
+ return elements;
2108
+ }
2109
+ function removeTrailingLineBreaks(elements) {
2110
+ while (elements.length > 0 && elements[elements.length - 1]?.element === "line-break") {
2111
+ const lastEl = elements[elements.length - 1];
2112
+ if (lastEl._preservedTrailingBreak) {
2113
+ delete lastEl._preservedTrailingBreak;
2114
+ break;
2115
+ }
2116
+ elements.pop();
2117
+ }
2118
+ }
2119
+ function removeTrailingWhitespaceText(elements) {
2120
+ while (elements.length > 0) {
2121
+ const last = elements[elements.length - 1];
2122
+ if (last?.element === "text" && "data" in last && typeof last.data === "string" && last.data.trim() === "") {
2123
+ elements.pop();
2124
+ } else {
2125
+ break;
2126
+ }
2127
+ }
2128
+ }
2129
+ function removeLeadingLineBreaks(elements) {
2130
+ let first = 0;
2131
+ while (first < elements.length && elements[first]?.element === "line-break" && !isPreservedLeadingLineBreak(elements[first])) {
2132
+ first++;
2133
+ }
2134
+ return first > 0 ? elements.slice(first) : elements;
2135
+ }
2136
+
2137
+ // packages/parser/src/parser/rules/block/code/content.ts
2138
+ function collectCodeContent(ctx, startPos, closingSwallowed) {
2139
+ if (closingSwallowed)
2140
+ return { contents: "", consumed: 0, foundClose: true };
2141
+ const bounds = findCodeBodyBounds(ctx.tokens, startPos);
2142
+ return {
2143
+ contents: ctx.tokens.slice(startPos, bounds.closeStart).map((token5) => token5.value).join(""),
2144
+ consumed: bounds.end - startPos,
2145
+ foundClose: bounds.foundClose
2146
+ };
2147
+ }
2148
+
2149
+ // packages/parser/src/parser/rules/block/code/index.ts
2150
+ var codeBlockRule = {
2151
+ name: "code",
2152
+ startTokens: ["BLOCK_OPEN"],
2153
+ requiresLineStart: false,
2154
+ parse(ctx) {
2155
+ const openToken = currentToken(ctx);
2156
+ if (openToken.type !== "BLOCK_OPEN") {
2157
+ return { success: false };
2158
+ }
2159
+ const open = findCodeOpen(ctx.tokens, ctx.pos);
2160
+ if (!open)
2161
+ return { success: false };
2162
+ const attrResult = parseAttributesRaw(ctx, ctx.pos + 2);
2163
+ if (open.repaired)
2164
+ repairSwallowedCodeClose(ctx, open.attributesEnd, attrResult.attrs);
2165
+ let pos = open.bodyStart;
2166
+ let consumed = pos - ctx.pos;
2167
+ const contentResult = collectCodeContent(ctx, pos, open.closingSwallowed);
2168
+ let codeContent = contentResult.contents.replace(/\n$/, "");
2169
+ consumed += contentResult.consumed;
2170
+ pos += contentResult.consumed;
2171
+ if (!contentResult.foundClose) {
2172
+ ctx.diagnostics.push({
2173
+ severity: "warning",
2174
+ code: "unclosed-block",
2175
+ message: "Missing closing tag [[/code]] for [[code]]",
2176
+ position: openToken.position
2177
+ });
2178
+ }
2179
+ const codeBlockData = {
2180
+ contents: codeContent,
2181
+ language: attrResult.attrs.type ?? null,
2182
+ name: attrResult.attrs.name ?? null
2183
+ };
2184
+ ctx.codeBlocks.push(codeBlockData);
2185
+ const elements = [{ element: "code", data: codeBlockData }];
2186
+ if (ctx.tokens[pos]?.type === "NEWLINE" && !getParagraphNewlineBoundary(ctx, pos, false).shouldBreak && !ctx.scope.blockCloseCondition?.({ ...ctx, pos: pos + 1 })) {
2187
+ const after = parseInlineUntil({ ...ctx, pos: pos + 1 }, "PARAGRAPH_BREAK");
2188
+ elements.push({ element: "line-break" }, ...normalizeParagraphElements(after.elements));
2189
+ consumed += 1 + after.consumed;
2190
+ }
2191
+ return { success: true, elements, consumed };
2192
+ }
2193
+ };
2194
+
2195
+ // packages/parser/src/parser/rules/block/math/index.ts
2196
+ var mathBlockRule = {
2197
+ name: "math",
2198
+ startTokens: ["BLOCK_OPEN"],
2199
+ requiresLineStart: false,
2200
+ parse(ctx) {
2201
+ const openToken = currentToken(ctx);
2202
+ const open = findMathOpen(ctx.tokens, ctx.pos);
2203
+ if (!open)
2204
+ return { success: false };
2205
+ const bounds = findMathBodyBounds(ctx.tokens, open.bodyStart);
2206
+ if (!bounds.foundClose) {
2207
+ ctx.diagnostics.push({
2208
+ severity: "warning",
2209
+ code: "unclosed-block",
2210
+ message: "Missing closing tag [[/math]] for [[math]]",
2211
+ position: openToken.position
2212
+ });
2213
+ }
2214
+ if (!bounds.hasContent) {
2215
+ return { success: false };
2216
+ }
2217
+ const latexSource = ctx.tokens.slice(open.bodyStart, bounds.closeStart).map((token5) => token5.type === "BACKSLASH_BREAK" ? "\\\n" : token5.value).join("").trim();
2218
+ const name = ctx.tokens.slice(open.nameStart, open.nameEnd).map((token5) => token5.value).join("");
2219
+ return {
2220
+ success: true,
2221
+ elements: [
2222
+ {
2223
+ element: "math",
2224
+ data: {
2225
+ name: name || null,
2226
+ "latex-source": latexSource
2227
+ }
2228
+ }
2229
+ ],
2230
+ consumed: bounds.end - ctx.pos
2231
+ };
2232
+ }
2233
+ };
2234
+
2235
+ // packages/parser/src/parser/rules/inline/link-triple/fallback.ts
2236
+ function literalOpenLink(value) {
2237
+ return {
2238
+ success: true,
2239
+ elements: [{ element: "text", data: value }],
2240
+ consumed: 1
2241
+ };
2242
+ }
2243
+
2244
+ // packages/parser/src/parser/rules/inline/link-triple/label.ts
2245
+ function buildTripleLinkLabel(args) {
2246
+ if (args.isPage && args.foundPipe && !args.labelText.trim())
2247
+ return "page";
2248
+ return {
2249
+ text: getTripleLinkDisplayText({
2250
+ ...args,
2251
+ originalTarget: args.isPage && !args.foundPipe ? args.originalTarget.split("#")[0] : args.originalTarget
2252
+ })
2253
+ };
2254
+ }
2255
+ function getTripleLinkDisplayText(args) {
2256
+ const trimmedLabel = args.labelText.trim();
2257
+ if (args.foundPipe) {
2258
+ return trimmedLabel || args.finalTarget;
2259
+ }
2260
+ const colonIdx = args.originalTarget.indexOf(":");
2261
+ if (colonIdx !== -1 && !args.originalTarget.startsWith("http") && !args.originalTarget.startsWith("*")) {
2262
+ return args.originalTarget.slice(colonIdx + 1).trim();
2263
+ }
2264
+ return args.originalTarget;
2265
+ }
2266
+
2267
+ // packages/parser/src/parser/rules/inline/link-triple/syntax.ts
2268
+ function hasClosingLinkMarker(ctx, startPos) {
2269
+ let pos = startPos;
2270
+ while (pos < ctx.tokens.length) {
2271
+ const token5 = ctx.tokens[pos];
2272
+ if (!token5 || token5.type === "EOF") {
2273
+ return false;
2274
+ }
2275
+ if (token5.type === "LINK_CLOSE") {
2276
+ return true;
2277
+ }
2278
+ if (token5.type === "NEWLINE") {
2279
+ const next = ctx.tokens[pos + 1];
2280
+ if (next?.type === "NEWLINE" || next?.type === "LINK_CLOSE") {
2281
+ return false;
2282
+ }
2283
+ }
2284
+ pos++;
2285
+ }
2286
+ return false;
2287
+ }
2288
+ function collectTripleLinkParts(ctx, startPos) {
2289
+ let target = "";
2290
+ let labelText = "";
2291
+ let foundPipe = false;
2292
+ let consumed = 1;
2293
+ let pos = startPos;
2294
+ while (pos < ctx.tokens.length) {
2295
+ const token5 = ctx.tokens[pos];
2296
+ if (!token5 || token5.type === "LINK_CLOSE" || token5.type === "EOF") {
2297
+ break;
2298
+ }
2299
+ if (token5.type === "NEWLINE") {
2300
+ if (foundPipe) {
2301
+ labelText += " ";
2302
+ } else {
2303
+ target += " ";
2304
+ }
2305
+ consumed++;
2306
+ pos++;
2307
+ continue;
2308
+ }
2309
+ if (token5.type === "PIPE" && !foundPipe) {
2310
+ foundPipe = true;
2311
+ } else if (foundPipe) {
2312
+ labelText += token5.value;
2313
+ } else {
2314
+ target += token5.value;
2315
+ }
2316
+ consumed++;
2317
+ pos++;
2318
+ }
2319
+ if (ctx.tokens[pos]?.type === "LINK_CLOSE") {
2320
+ consumed++;
2321
+ }
2322
+ return { target, labelText, foundPipe, consumed };
2323
+ }
2324
+
2325
+ // packages/parser/src/parser/rules/inline/link-triple/interwiki.ts
2326
+ var INTERWIKI_PREFIXES = new Set(["wikipedia", "google", "dictionary", "wikidot"]);
2327
+ function isInterwikiTarget(target) {
2328
+ const colonIdx = target.indexOf(":");
2329
+ if (colonIdx <= 0 || target.includes("/")) {
2330
+ return false;
2331
+ }
2332
+ const prefix = target.slice(0, colonIdx).toLowerCase();
2333
+ return INTERWIKI_PREFIXES.has(prefix);
2334
+ }
2335
+
2336
+ // packages/parser/src/parser/rules/inline/link-triple/target.ts
2337
+ function normalizeTripleLinkTarget(trimmedTarget) {
2338
+ if (trimmedTarget.startsWith("*")) {
2339
+ return { target: trimmedTarget.slice(1), hasStar: true };
2340
+ }
2341
+ return { target: trimmedTarget, hasStar: false };
2342
+ }
2343
+ function isInvalidTripleLinkTarget(trimmedTarget, foundPipe) {
2344
+ return trimmedTarget === "" && foundPipe || /#{2,}/.test(trimmedTarget);
2345
+ }
2346
+ function determineLinkTypeAndLocation(target) {
2347
+ if (target.startsWith("#")) {
2348
+ return { linkType: "anchor", link: target };
2349
+ }
2350
+ if (target.startsWith("http://") || target.startsWith("https://")) {
2351
+ return { linkType: "direct", link: target };
2352
+ }
2353
+ if (isInterwikiTarget(target)) {
2354
+ return { linkType: "interwiki", link: target };
2355
+ }
2356
+ return { linkType: "page", link: { site: null, page: target } };
2357
+ }
2358
+
2359
+ // packages/parser/src/parser/rules/inline/link-triple/index.ts
2360
+ var linkTripleRule = {
2361
+ name: "linkTriple",
2362
+ startTokens: ["LINK_OPEN"],
2363
+ parse(ctx) {
2364
+ const startToken = currentToken(ctx);
2365
+ if (!hasClosingLinkMarker(ctx, ctx.pos + 1)) {
2366
+ return literalOpenLink(startToken.value);
2367
+ }
2368
+ const parts = collectTripleLinkParts(ctx, ctx.pos + 1);
2369
+ const trimmedTarget = parts.target.trim();
2370
+ if (isInvalidTripleLinkTarget(trimmedTarget, parts.foundPipe)) {
2371
+ return literalOpenLink(startToken.value);
2372
+ }
2373
+ const normalized = normalizeTripleLinkTarget(trimmedTarget);
2374
+ const { linkType, link } = determineLinkTypeAndLocation(normalized.target);
2375
+ const label = buildTripleLinkLabel({
2376
+ isPage: linkType === "page",
2377
+ foundPipe: parts.foundPipe,
2378
+ labelText: parts.labelText,
2379
+ finalTarget: normalized.target,
2380
+ originalTarget: trimmedTarget
2381
+ });
2382
+ return {
2383
+ success: true,
2384
+ elements: [
2385
+ {
2386
+ element: "link",
2387
+ data: {
2388
+ type: linkType,
2389
+ link,
2390
+ extra: null,
2391
+ label,
2392
+ target: normalized.hasStar && linkType === "direct" ? "new-tab" : null
2393
+ }
2394
+ }
2395
+ ],
2396
+ consumed: parts.consumed
2397
+ };
2398
+ }
2399
+ };
2400
+
2401
+ // packages/parser/src/parser/rules/block/note/boundary.ts
2402
+ var opaqueRules = [codeBlockRule, mathBlockRule, linkTripleRule];
2403
+ var boundsCache = new WeakMap;
2404
+ var closeCache = new WeakMap;
2405
+ function tagEnd(tokens, pos, name, close = false) {
2406
+ return tokens[pos]?.type === (close ? "BLOCK_END_OPEN" : "BLOCK_OPEN") && tokens[pos + 1]?.value.toLowerCase() === name && tokens[pos + 2]?.type === "BLOCK_CLOSE" ? pos + 3 : pos;
2407
+ }
2408
+ function findClose(probe, start, name) {
2409
+ let byName = closeCache.get(probe.tokens);
2410
+ if (!byName) {
2411
+ byName = new Map;
2412
+ closeCache.set(probe.tokens, byName);
2413
+ }
2414
+ let cache = byName.get(name);
2415
+ if (!cache) {
2416
+ cache = new Map;
2417
+ byName.set(name, cache);
2418
+ }
2419
+ if (cache.has(start))
2420
+ return cache.get(start);
2421
+ const visited = [];
2422
+ let result = -1;
2423
+ for (let pos = start;pos < probe.tokens.length; pos++) {
2424
+ if (cache.has(pos)) {
2425
+ result = cache.get(pos);
2426
+ break;
2427
+ }
2428
+ visited.push(pos);
2429
+ const protectedEnd = protectedInlineRegionEnd(probe.tokens, pos, probe.tokens.length);
2430
+ if (protectedEnd > pos) {
2431
+ pos = protectedEnd - 1;
2432
+ continue;
2433
+ }
2434
+ if (tagEnd(probe.tokens, pos, name, true) > pos) {
2435
+ result = pos;
2436
+ break;
2437
+ }
2438
+ const opaqueEnd = opaqueRuleEnd(probe, pos, opaqueRules, true);
2439
+ if (opaqueEnd > pos) {
2440
+ pos = opaqueEnd - 1;
2441
+ continue;
2442
+ }
2443
+ if (name === "note") {
2444
+ const footnoteStart = tagEnd(probe.tokens, pos, "footnote");
2445
+ if (footnoteStart > pos) {
2446
+ const end = findClose(probe, footnoteStart, "footnote");
2447
+ if (end >= 0)
2448
+ pos = end + 2;
2449
+ }
2450
+ }
2451
+ }
2452
+ for (const pos of visited)
2453
+ cache.set(pos, result);
2454
+ return result;
2455
+ }
2456
+ function findNoteBounds(ctx, start = ctx.pos) {
2457
+ const openEnd = tagEnd(ctx.tokens, start, "note");
2458
+ if (openEnd === start || ctx.tokens[openEnd]?.type !== "NEWLINE")
2459
+ return null;
2460
+ let cache = boundsCache.get(ctx.tokens);
2461
+ if (!cache) {
2462
+ cache = new Map;
2463
+ boundsCache.set(ctx.tokens, cache);
2464
+ }
2465
+ if (cache.has(start))
2466
+ return cache.get(start);
2467
+ const close = findClose(createOpaqueProbe(ctx), openEnd + 1, "note");
2468
+ const result = close < 0 ? null : { bodyStart: openEnd + 1, close, end: close + 3 };
2469
+ cache.set(start, result);
2470
+ return result;
2471
+ }
2472
+
2473
+ // packages/parser/src/parser/rules/inline/parsing/block-boundary.ts
2474
+ function isExcludedBlockToken(ctx, tokenPos) {
2475
+ const excluded = ctx.scope.excludedBlockNames;
2476
+ if (!excluded?.size)
2477
+ return false;
2478
+ const token5 = ctx.tokens[tokenPos];
2479
+ if (token5?.type !== "BLOCK_OPEN" && token5?.type !== "BLOCK_END_OPEN")
2480
+ return false;
1219
2481
  const nameResult = parseBlockName(ctx, tokenPos + 1);
1220
2482
  return nameResult !== null && excluded.has(nameResult.name);
1221
2483
  }
@@ -1230,6 +2492,8 @@ function isUnknownBlockToken(ctx, tokenPos) {
1230
2492
  }
1231
2493
  return true;
1232
2494
  }
2495
+ if (nameResult.name === "note")
2496
+ return findNoteBounds(ctx, tokenPos) === null;
1233
2497
  return !KNOWN_BLOCK_NAMES.has(nameResult.name);
1234
2498
  }
1235
2499
  function isIndentAcceptingBlock(ctx, tokenPos) {
@@ -1253,7 +2517,13 @@ function isParagraphBreakingBlockStart(ctx, newlinePos, lookAhead) {
1253
2517
  if (!nextMeaningfulToken.lineStart && !isIndentedBlockOpener) {
1254
2518
  return false;
1255
2519
  }
1256
- return !parseImageOpen({ ...ctx, pos: nextPos }) && !isOrphanCloseSpan(ctx, nextPos) && !isAnchorName(ctx, nextPos) && !isInvalidBlockOpen(ctx, nextPos) && !isInvalidHeading(ctx, nextPos) && !isExcludedBlockStart(ctx, nextPos) && !isUnknownBlockStart(ctx, nextPos);
2520
+ return !parseImageOpen({ ...ctx, pos: nextPos }) && !isOrphanCloseSpan(ctx, nextPos) && !isAnchorName(ctx, nextPos) && !isInvalidBlockOpen(ctx, nextPos) && !isInvalidHeading(ctx, nextPos) && !isInvalidListMarker(ctx, nextPos) && !isExcludedBlockStart(ctx, nextPos) && !isUnknownBlockStart(ctx, nextPos);
2521
+ }
2522
+ function isInvalidListMarker(ctx, markerPos) {
2523
+ const marker = ctx.tokens[markerPos];
2524
+ if (marker?.type !== "LIST_BULLET" && marker?.type !== "LIST_NUMBER")
2525
+ return false;
2526
+ return ctx.tokens[markerPos + 1]?.type !== "WHITESPACE";
1257
2527
  }
1258
2528
  function isOrphanCloseSpan(ctx, blockEndOpenPos) {
1259
2529
  const token5 = ctx.tokens[blockEndOpenPos];
@@ -1339,13 +2609,6 @@ function skipWhitespaceAfterNewline(ctx, newlinePos) {
1339
2609
  return lookAhead;
1340
2610
  }
1341
2611
 
1342
- // packages/parser/src/parser/rules/inline/parsing/preserved-line-break.ts
1343
- function createPreservedTrailingLineBreak() {
1344
- const lineBreak = { element: "line-break" };
1345
- lineBreak._preservedTrailingBreak = true;
1346
- return lineBreak;
1347
- }
1348
-
1349
2612
  // packages/parser/src/parser/rules/inline/parsing/simple-token.ts
1350
2613
  function parseSimpleInlineToken(token5, nextToken) {
1351
2614
  if (token5.type === "TEXT" && token5.value !== "(") {
@@ -1362,12 +2625,13 @@ function parseInlineUntil(ctx, endType) {
1362
2625
  const nodes = [];
1363
2626
  let consumed = 0;
1364
2627
  let pos = ctx.pos;
2628
+ let consumedEmptyRaw = false;
1365
2629
  const paragraphMode = endType === "PARAGRAPH_BREAK";
1366
2630
  const multiline = paragraphMode || FORMATTING_CLOSE_TOKENS.has(endType);
1367
2631
  let inlineEnd = ctx.scope.inlineEnd ?? ctx.tokens.length;
1368
2632
  if (!multiline) {
1369
2633
  for (let end = ctx.pos;end < inlineEnd; end++) {
1370
- const protectedEnd = protectedInlineRegionEnd(ctx.tokens, end, inlineEnd);
2634
+ const protectedEnd = Math.max(parseButtonSyntax(ctx, end, inlineEnd)?.end ?? end, parseSocialSyntax(ctx, end, inlineEnd)?.end ?? end, parseDateSyntax(ctx, end, inlineEnd)?.end ?? end, emailRegionEnd(ctx.tokens, end, inlineEnd), protectedInlineRegionEnd(ctx.tokens, end, inlineEnd));
1371
2635
  if (protectedEnd > end) {
1372
2636
  end = protectedEnd - 1;
1373
2637
  continue;
@@ -1416,10 +2680,11 @@ function parseInlineUntil(ctx, endType) {
1416
2680
  consumed++;
1417
2681
  continue;
1418
2682
  }
1419
- if (token5.type === endType) {
2683
+ const hasEmail = emailRegionEnd(ctx.tokens, pos, inlineEnd) > pos;
2684
+ if (token5.type === endType && !hasEmail) {
1420
2685
  break;
1421
2686
  }
1422
- if (canCollectLongPlainTextRuns) {
2687
+ if (canCollectLongPlainTextRuns && !hasEmail) {
1423
2688
  const plainTextRun = collectLongPlainTextRun(ctx, pos, endType);
1424
2689
  if (plainTextRun) {
1425
2690
  nodes.push({ element: "text", data: plainTextRun.value });
@@ -1429,7 +2694,7 @@ function parseInlineUntil(ctx, endType) {
1429
2694
  }
1430
2695
  }
1431
2696
  const simpleToken = parseSimpleInlineToken(token5, ctx.tokens[pos + 1]);
1432
- if (simpleToken) {
2697
+ if (simpleToken && !hasEmail) {
1433
2698
  nodes.push(simpleToken.element);
1434
2699
  consumed += simpleToken.consumed;
1435
2700
  pos += simpleToken.consumed;
@@ -1440,6 +2705,10 @@ function parseInlineUntil(ctx, endType) {
1440
2705
  for (const rule of getCandidateInlineRules(inlineRules, token5.type)) {
1441
2706
  const result = rule.parse(inlineCtx);
1442
2707
  if (result.success) {
2708
+ stripAutomaticLineBreak(nodes, result.stripLeadingLineBreak);
2709
+ if (rule.name === "raw" && result.elements.length === 0 && nodes.length === 0) {
2710
+ consumedEmptyRaw = true;
2711
+ }
1443
2712
  if (rule.name === "comment") {
1444
2713
  let after = pos + result.consumed;
1445
2714
  while (ctx.tokens[after]?.type === "WHITESPACE")
@@ -1456,165 +2725,39 @@ function parseInlineUntil(ctx, endType) {
1456
2725
  }
1457
2726
  if (nodes.at(-1)?.element === "line-break")
1458
2727
  nodes.pop();
1459
- }
1460
- }
1461
- nodes.push(...result.elements);
1462
- consumed += result.consumed;
1463
- pos += result.consumed;
1464
- matched = true;
1465
- break;
1466
- }
1467
- }
1468
- if (!matched) {
1469
- nodes.push({ element: "text", data: token5.value });
1470
- consumed++;
1471
- pos++;
1472
- }
1473
- }
1474
- return { elements: nodes, consumed };
1475
- }
1476
- var FORMATTING_CLOSE_TOKENS = new Set([
1477
- "BOLD_MARKER",
1478
- "ITALIC_MARKER",
1479
- "UNDERLINE_MARKER",
1480
- "STRIKE_MARKER",
1481
- "SUPER_MARKER",
1482
- "SUB_MARKER",
1483
- "MONO_CLOSE",
1484
- "COLOR_MARKER"
1485
- ]);
1486
- // packages/parser/src/parser/rules/block/parsing/attributes/names.ts
1487
- function consumeAttributeName(ctx, startPos, startConsumed, startName, options) {
1488
- if (startName === "_" && isAttributeWordToken(ctx.tokens[startPos])) {
1489
- startName += ctx.tokens[startPos]?.value ?? "";
1490
- startPos++;
1491
- startConsumed++;
1492
- }
1493
- return options.strikeHyphens ? consumeRawNameSuffix(ctx, startPos, startConsumed, startName, options.hyphenatedNames) : consumeSafeNameSuffix(ctx, startPos, startConsumed, startName);
1494
- }
1495
- function isAttributeNameToken(token5) {
1496
- return isAttributeWordToken(token5) || token5?.type === "UNDERSCORE";
1497
- }
1498
- function consumeSafeNameSuffix(ctx, startPos, startConsumed, startName) {
1499
- let name = startName;
1500
- let pos = startPos;
1501
- let consumed = startConsumed;
1502
- while (isAttributeNameSeparator(ctx.tokens[pos]) && isAttributeWordToken(ctx.tokens[pos + 1])) {
1503
- name += ctx.tokens[pos]?.value ?? "";
1504
- pos++;
1505
- consumed++;
1506
- name += ctx.tokens[pos]?.value ?? "";
1507
- pos++;
1508
- consumed++;
1509
- }
1510
- return { name, pos, consumed };
1511
- }
1512
- function consumeRawNameSuffix(ctx, startPos, startConsumed, startName, hyphenatedNames) {
1513
- let name = startName;
1514
- let pos = startPos;
1515
- let consumed = startConsumed;
1516
- while (isAttributeNameSeparator(ctx.tokens[pos])) {
1517
- while (isAttributeNameSeparator(ctx.tokens[pos])) {
1518
- if (hyphenatedNames || ctx.tokens[pos]?.type === "UNDERSCORE") {
1519
- name += ctx.tokens[pos]?.value ?? "-";
1520
- }
1521
- pos++;
1522
- consumed++;
1523
- }
1524
- if (!isAttributeWordToken(ctx.tokens[pos])) {
1525
- break;
1526
- }
1527
- if (hyphenatedNames || name.endsWith("_")) {
1528
- name += ctx.tokens[pos]?.value ?? "";
1529
- }
1530
- pos++;
1531
- consumed++;
1532
- }
1533
- return { name, pos, consumed };
1534
- }
1535
- function isAttributeWordToken(token5) {
1536
- return token5?.type === "TEXT" || token5?.type === "IDENTIFIER";
1537
- }
1538
- function isAttributeNameSeparator(token5) {
1539
- return token5?.type === "TEXT" && token5.value === "-" || token5?.type === "STRIKE_MARKER" || token5?.type === "UNDERSCORE";
1540
- }
1541
-
1542
- // packages/parser/src/parser/rules/block/parsing/attributes/values.ts
1543
- function consumeAttributeValue(token5) {
1544
- if (!token5) {
1545
- return null;
1546
- }
1547
- if (token5.type === "QUOTED_STRING") {
1548
- return { value: stripQuotes(token5.value) };
1549
- }
1550
- if (isAttributeNameToken(token5)) {
1551
- return { value: token5.value };
1552
- }
1553
- return null;
1554
- }
1555
- function stripQuotes(value) {
1556
- return value.startsWith('"') && value.endsWith('"') ? value.slice(1, -1) : value;
1557
- }
1558
-
1559
- // packages/parser/src/parser/rules/block/parsing/attributes/scanner.ts
1560
- function scanAttributes(ctx, startPos, options) {
1561
- const attrs = {};
1562
- let pos = startPos;
1563
- let consumed = 0;
1564
- while (pos < ctx.tokens.length) {
1565
- const token5 = ctx.tokens[pos];
1566
- if (!token5 || isAttributeTerminator(token5)) {
1567
- break;
2728
+ }
2729
+ }
2730
+ for (const element of result.elements) {
2731
+ nodes.push(paragraphMode && consumedEmptyRaw && nodes.length === 0 && token5.type === "NEWLINE" && element.element === "line-break" ? createPreservedLeadingLineBreak() : element);
2732
+ }
2733
+ consumed += result.consumed;
2734
+ pos += result.consumed;
2735
+ matched = true;
2736
+ break;
2737
+ }
1568
2738
  }
1569
- if (token5.type === "WHITESPACE") {
1570
- pos++;
2739
+ if (!matched) {
2740
+ nodes.push({ element: "text", data: token5.value });
1571
2741
  consumed++;
1572
- continue;
1573
- }
1574
- if (!isAttributeNameToken(token5)) {
1575
2742
  pos++;
1576
- consumed++;
1577
- continue;
1578
- }
1579
- let name = token5.value;
1580
- pos++;
1581
- consumed++;
1582
- const nameResult = consumeAttributeName(ctx, pos, consumed, name, options);
1583
- pos = nameResult.pos;
1584
- consumed = nameResult.consumed;
1585
- name = nameResult.name.toLowerCase();
1586
- if (ctx.tokens[pos]?.type !== "EQUALS") {
1587
- attrs[name] = "true";
1588
- continue;
1589
- }
1590
- pos++;
1591
- consumed++;
1592
- const valueResult = consumeAttributeValue(ctx.tokens[pos]);
1593
- if (!valueResult) {
1594
- continue;
1595
2743
  }
1596
- attrs[name] = valueResult.value;
1597
- pos++;
1598
- consumed++;
1599
2744
  }
1600
- return { attrs, consumed };
1601
- }
1602
- function isAttributeTerminator(token5) {
1603
- return token5.type === "BLOCK_CLOSE" || token5.type === "NEWLINE" || token5.type === "EOF";
1604
- }
1605
-
1606
- // packages/parser/src/parser/rules/block/parsing/attributes/index.ts
1607
- function parseAttributes(ctx, startPos) {
1608
- const result = scanAttributes(ctx, startPos, { hyphenatedNames: true, strikeHyphens: false });
1609
- return { attrs: filterUnsafeAttributes(result.attrs), consumed: result.consumed };
1610
- }
1611
- function parseAttributesRaw(ctx, startPos, hyphenatedNames = true) {
1612
- return scanAttributes(ctx, startPos, { hyphenatedNames, strikeHyphens: true });
2745
+ return { elements: nodes, consumed };
1613
2746
  }
2747
+ var FORMATTING_CLOSE_TOKENS = new Set([
2748
+ "BOLD_MARKER",
2749
+ "ITALIC_MARKER",
2750
+ "UNDERLINE_MARKER",
2751
+ "STRIKE_MARKER",
2752
+ "SUPER_MARKER",
2753
+ "SUB_MARKER",
2754
+ "MONO_CLOSE",
2755
+ "COLOR_MARKER"
2756
+ ]);
1614
2757
  // packages/parser/src/parser/parse/block.ts
1615
- function parseNextBlock(ctx, skipWhitespace2, isAtEnd2) {
2758
+ function parseNextBlock(ctx, skipWhitespace2, isAtEnd3) {
1616
2759
  skipWhitespace2();
1617
- if (isAtEnd2()) {
2760
+ if (isAtEnd3()) {
1618
2761
  return [];
1619
2762
  }
1620
2763
  const token5 = ctx.tokens[ctx.pos];
@@ -1633,7 +2776,7 @@ function parseNextBlock(ctx, skipWhitespace2, isAtEnd2) {
1633
2776
  }
1634
2777
  }
1635
2778
  const result = ctx.blockFallbackRule.parse(ctx);
1636
- if (result.success && result.elements.length > 0) {
2779
+ if (result.success) {
1637
2780
  ctx.pos += result.consumed;
1638
2781
  return result.elements;
1639
2782
  }
@@ -1643,35 +2786,6 @@ function parseNextBlock(ctx, skipWhitespace2, isAtEnd2) {
1643
2786
 
1644
2787
  // packages/parser/src/parser/parse/context.ts
1645
2788
  import { DEFAULT_SETTINGS } from "@wdprlib/ast";
1646
-
1647
- // packages/parser/src/parser/rules/tokens.ts
1648
- function currentToken(ctx) {
1649
- return ctx.tokens[ctx.pos] ?? eofToken();
1650
- }
1651
- function eofToken() {
1652
- return {
1653
- type: "EOF",
1654
- value: "",
1655
- position: { start: { line: 0, column: 0, offset: 0 }, end: { line: 0, column: 0, offset: 0 } },
1656
- lineStart: false
1657
- };
1658
- }
1659
- function hasClosingMarkerBeforeNewline(ctx, markerType, markerValue) {
1660
- let pos = ctx.pos;
1661
- while (pos < (ctx.scope.inlineEnd ?? ctx.tokens.length)) {
1662
- const token5 = ctx.tokens[pos];
1663
- if (!token5 || token5.type === "NEWLINE" || token5.type === "EOF") {
1664
- return false;
1665
- }
1666
- if (token5.type === markerType) {
1667
- if (markerValue === undefined || token5.value === markerValue) {
1668
- return true;
1669
- }
1670
- }
1671
- pos++;
1672
- }
1673
- return false;
1674
- }
1675
2789
  // packages/parser/src/parser/rules/block/heading/open.ts
1676
2790
  function parseHeadingOpen(ctx) {
1677
2791
  const marker = ctx.tokens[ctx.pos];
@@ -1917,10 +3031,11 @@ function parseNativeListLine(ctx, startPos) {
1917
3031
  const ltype = markerToken.type === "LIST_BULLET" ? "bullet" : "numbered";
1918
3032
  pos++;
1919
3033
  consumed++;
1920
- if (ctx.tokens[pos]?.type === "WHITESPACE") {
1921
- pos++;
1922
- consumed++;
3034
+ if (ctx.tokens[pos]?.type !== "WHITESPACE") {
3035
+ return { kind: "stop" };
1923
3036
  }
3037
+ pos++;
3038
+ consumed++;
1924
3039
  const inlineCtx = { ...ctx, pos };
1925
3040
  const inlineResult = parseInlineUntil(inlineCtx, "NEWLINE");
1926
3041
  consumed += inlineResult.consumed;
@@ -2030,9 +3145,9 @@ function appendBareParagraphElements(state, elements) {
2030
3145
  function appendBareParagraphText(state, text) {
2031
3146
  state.current.push({ element: "text", data: text });
2032
3147
  }
2033
- function appendBareParagraphLineBreakIfNeeded(state) {
3148
+ function appendBareParagraphLineBreakIfNeeded(state, token5) {
2034
3149
  if (state.current.length > 0) {
2035
- state.current.push({ element: "line-break" });
3150
+ state.current.push(createAutomaticLineBreak(token5));
2036
3151
  }
2037
3152
  }
2038
3153
  function flushBareParagraph(state) {
@@ -2137,7 +3252,7 @@ function parseBareListContent(ctx, startPos, listType) {
2137
3252
  if (consecutiveNewlines >= 2) {
2138
3253
  flushBareParagraph(paragraphState);
2139
3254
  } else {
2140
- appendBareParagraphLineBreakIfNeeded(paragraphState);
3255
+ appendBareParagraphLineBreakIfNeeded(paragraphState, token5);
2141
3256
  }
2142
3257
  continue;
2143
3258
  }
@@ -2149,6 +3264,7 @@ function parseBareListContent(ctx, startPos, listType) {
2149
3264
  for (const rule of getCandidateInlineRules(ctx.inlineRules, token5.type)) {
2150
3265
  const result = rule.parse(inlineCtx);
2151
3266
  if (result.success) {
3267
+ stripAutomaticLineBreak(paragraphState.current, result.stripLeadingLineBreak);
2152
3268
  appendBareParagraphElements(paragraphState, result.elements);
2153
3269
  consumed += result.consumed;
2154
3270
  pos += result.consumed;
@@ -2187,7 +3303,7 @@ function parseListItemBlockContent(ctx, pos, token5) {
2187
3303
  for (const rule of getCandidateBlockRules(filteredBlockRules, token5)) {
2188
3304
  const result = rule.parse(blockCtx);
2189
3305
  if (result.success) {
2190
- return { matched: true, elements: result.elements, consumed: result.consumed };
3306
+ return { matched: true, ...result };
2191
3307
  }
2192
3308
  }
2193
3309
  return { matched: false, elements: [], consumed: 0 };
@@ -2206,7 +3322,7 @@ function parseListItemInlineContent(ctx, pos, tokenType) {
2206
3322
  for (const rule of getCandidateInlineRules(ctx.inlineRules, tokenType)) {
2207
3323
  const result = rule.parse(inlineCtx);
2208
3324
  if (result.success) {
2209
- return { matched: true, elements: result.elements, consumed: result.consumed };
3325
+ return { matched: true, ...result };
2210
3326
  }
2211
3327
  }
2212
3328
  return { matched: false, elements: [], consumed: 0 };
@@ -2248,7 +3364,9 @@ function collectPostLiTrailingContent(ctx, startPos, listType) {
2248
3364
  }
2249
3365
  const inlineResult = parseListItemInlineContent(ctx, pos, token5.type);
2250
3366
  if (inlineResult.matched) {
2251
- elements.push(...inlineResult.elements);
3367
+ stripAutomaticLineBreak(elements, inlineResult.stripLeadingLineBreak);
3368
+ for (const element of inlineResult.elements)
3369
+ elements.push(element);
2252
3370
  consumed += inlineResult.consumed;
2253
3371
  pos += inlineResult.consumed;
2254
3372
  continue;
@@ -2293,7 +3411,7 @@ function collectLiItemContent(ctx, startPos, listType, parseNestedList) {
2293
3411
  if (token5.type === "NEWLINE") {
2294
3412
  const newlineResult = consumeLiItemNewlines(ctx, pos, elements.length > 0);
2295
3413
  if (newlineResult.addLineBreak) {
2296
- elements.push({ element: "line-break" });
3414
+ elements.push(createAutomaticLineBreak(token5));
2297
3415
  }
2298
3416
  pos += newlineResult.consumed;
2299
3417
  consumed += newlineResult.consumed;
@@ -2301,14 +3419,17 @@ function collectLiItemContent(ctx, startPos, listType, parseNestedList) {
2301
3419
  }
2302
3420
  const blockResult = parseListItemBlockContent(ctx, pos, token5);
2303
3421
  if (blockResult.matched) {
2304
- elements.push(...blockResult.elements);
3422
+ for (const element of blockResult.elements)
3423
+ elements.push(element);
2305
3424
  consumed += blockResult.consumed;
2306
3425
  pos += blockResult.consumed;
2307
3426
  continue;
2308
3427
  }
2309
3428
  const inlineResult = parseListItemInlineContent(ctx, pos, token5.type);
2310
3429
  if (inlineResult.matched) {
2311
- elements.push(...inlineResult.elements);
3430
+ stripAutomaticLineBreak(elements, inlineResult.stripLeadingLineBreak);
3431
+ for (const element of inlineResult.elements)
3432
+ elements.push(element);
2312
3433
  consumed += inlineResult.consumed;
2313
3434
  pos += inlineResult.consumed;
2314
3435
  continue;
@@ -2379,7 +3500,8 @@ function parseLiItem(ctx, startPos, listType, parseNestedList) {
2379
3500
  consumed += closeConsumed;
2380
3501
  pos += closeConsumed;
2381
3502
  const trailingResult = collectPostLiTrailingContent(ctx, pos, listType);
2382
- contentElements.push(...trailingResult.elements);
3503
+ for (const element of trailingResult.elements)
3504
+ contentElements.push(element);
2383
3505
  consumed += trailingResult.consumed;
2384
3506
  }
2385
3507
  return {
@@ -2639,7 +3761,7 @@ function sliceLineTokens(ctx, lines) {
2639
3761
  for (const { start, end } of lines) {
2640
3762
  for (let pos = start;pos < end; pos++) {
2641
3763
  const token5 = ctx.tokens[pos];
2642
- if (token5) {
3764
+ if (token5 && token5.type !== "BACKSLASH_BREAK") {
2643
3765
  tokens.push(token5);
2644
3766
  }
2645
3767
  }
@@ -2809,7 +3931,8 @@ function parseDefinitionItemKey(ctx, startPos) {
2809
3931
  const inlineCtx = { ...ctx, pos };
2810
3932
  const result = parseInlineUntil(inlineCtx, "COLON");
2811
3933
  if (result.elements.length > 0) {
2812
- key.push(...result.elements);
3934
+ for (const element of result.elements)
3935
+ key.push(element);
2813
3936
  for (let i = 0;i < result.consumed; i++) {
2814
3937
  const t = ctx.tokens[pos + i];
2815
3938
  if (t)
@@ -2865,7 +3988,8 @@ function parseDefinitionItemValue(ctx, startPos) {
2865
3988
  const inlineCtx = { ...ctx, pos };
2866
3989
  const result = parseInlineUntil(inlineCtx, "NEWLINE");
2867
3990
  if (result.elements.length > 0) {
2868
- value.push(...result.elements);
3991
+ for (const element of result.elements)
3992
+ value.push(element);
2869
3993
  pos += result.consumed;
2870
3994
  consumed += result.consumed;
2871
3995
  } else {
@@ -2947,78 +4071,93 @@ var definitionListRule = {
2947
4071
  }
2948
4072
  };
2949
4073
 
2950
- // packages/parser/src/parser/rules/block/paragraph/content.ts
2951
- function parseInlineContent(ctx) {
2952
- return parseInlineUntil(ctx, "PARAGRAPH_BREAK");
4074
+ // packages/parser/src/parser/postprocess/divAdjacentParagraph.ts
4075
+ var unparsedDivStarts = new WeakSet;
4076
+ function markUnparsedDivStart(elements) {
4077
+ const first = elements[0];
4078
+ const text = first?.element === "container" && first.data.type === "paragraph" ? first.data.elements[0] : first;
4079
+ if (text?.element === "text")
4080
+ unparsedDivStarts.add(text);
2953
4081
  }
2954
-
2955
- // packages/parser/src/parser/rules/block/paragraph/span-markers.ts
2956
- function processCloseSpanMarkers(elements) {
2957
- let result = null;
4082
+ function isUnparsedDivParagraph(el) {
4083
+ if (!el || el.element !== "container")
4084
+ return false;
4085
+ return el.data.type === "paragraph" && el.data.elements.some((child) => unparsedDivStarts.has(child));
4086
+ }
4087
+ function isDivContainer(el) {
4088
+ if (!el || el.element !== "container")
4089
+ return false;
4090
+ return el.data.type === "div";
4091
+ }
4092
+ function suppressAtLevel(elements) {
4093
+ if (elements.length <= 1)
4094
+ return elements;
4095
+ const unwrap = Array.from({ length: elements.length }, () => false);
2958
4096
  for (let i = 0;i < elements.length; i++) {
2959
- const elem = elements[i];
2960
- if (!elem)
4097
+ if (!isUnparsedDivParagraph(elements[i]))
2961
4098
  continue;
2962
- if (isCloseSpanMarker(elem)) {
2963
- if (result === null) {
2964
- result = elements.slice(0, i);
2965
- }
2966
- if (result.length > 0) {
2967
- const spanContent = [...result];
2968
- result.length = 0;
2969
- result.push({
2970
- element: "container",
2971
- data: {
2972
- type: "span",
2973
- attributes: {},
2974
- elements: spanContent
2975
- }
2976
- });
4099
+ const prevIsDiv = i > 0 && isDivContainer(elements[i - 1]);
4100
+ const nextIsDiv = i < elements.length - 1 && isDivContainer(elements[i + 1]);
4101
+ if (prevIsDiv || nextIsDiv) {
4102
+ unwrap[i] = true;
4103
+ }
4104
+ }
4105
+ const result = [];
4106
+ for (let i = 0;i < elements.length; i++) {
4107
+ const el = elements[i];
4108
+ if (!el)
4109
+ continue;
4110
+ if (unwrap[i] && el.element === "container") {
4111
+ const inner = el.data.elements;
4112
+ if (i > 0 && isDivContainer(elements[i - 1])) {
4113
+ result.push({ element: "line-break" });
2977
4114
  }
4115
+ result.push(...inner);
2978
4116
  } else {
2979
- result?.push(elem);
4117
+ result.push(el);
2980
4118
  }
2981
4119
  }
2982
- return result ?? elements;
4120
+ return result;
2983
4121
  }
2984
- function isCloseSpanMarker(elem) {
2985
- return elem.element === "container" && elem.data && typeof elem.data === "object" && "type" in elem.data && elem.data.type === "span" && "attributes" in elem.data && typeof elem.data.attributes === "object" && elem.data.attributes !== null && "_closeSpan" in elem.data.attributes;
4122
+ function suppressDivAdjacentParagraphs(elements) {
4123
+ return suppressAtLevel(elements);
2986
4124
  }
2987
4125
 
2988
- // packages/parser/src/parser/rules/block/paragraph/normalize.ts
2989
- function normalizeParagraphElements(source) {
2990
- let elements = processCloseSpanMarkers(source);
2991
- removeTrailingLineBreaks2(elements);
2992
- removeTrailingWhitespaceText(elements);
2993
- elements = removeLeadingLineBreaks(elements);
2994
- return elements;
4126
+ // packages/parser/src/parser/rules/block/div/close.ts
4127
+ function isDivClose(ctx) {
4128
+ const token5 = ctx.tokens[ctx.pos];
4129
+ if (token5?.type !== "BLOCK_END_OPEN")
4130
+ return false;
4131
+ const closeNameResult = parseBlockName(ctx, ctx.pos + 1);
4132
+ return closeNameResult?.name === "div";
2995
4133
  }
2996
- function removeTrailingLineBreaks2(elements) {
2997
- while (elements.length > 0 && elements[elements.length - 1]?.element === "line-break") {
2998
- const lastEl = elements[elements.length - 1];
2999
- if (lastEl._preservedTrailingBreak) {
3000
- delete lastEl._preservedTrailingBreak;
3001
- break;
3002
- }
3003
- elements.pop();
4134
+ function consumeDivClose(ctx, startPos) {
4135
+ let pos = startPos;
4136
+ let consumed = 0;
4137
+ if (ctx.tokens[pos]?.type !== "BLOCK_END_OPEN") {
4138
+ return { pos, consumed };
3004
4139
  }
3005
- }
3006
- function removeTrailingWhitespaceText(elements) {
3007
- while (elements.length > 0) {
3008
- const last = elements[elements.length - 1];
3009
- if (last?.element === "text" && "data" in last && typeof last.data === "string" && last.data.trim() === "") {
3010
- elements.pop();
3011
- } else {
3012
- break;
3013
- }
4140
+ pos++;
4141
+ consumed++;
4142
+ const closeNameResult = parseBlockName(ctx, pos);
4143
+ if (closeNameResult) {
4144
+ pos += closeNameResult.consumed;
4145
+ consumed += closeNameResult.consumed;
3014
4146
  }
3015
- }
3016
- function removeLeadingLineBreaks(elements) {
3017
- let first = 0;
3018
- while (first < elements.length && elements[first]?.element === "line-break") {
3019
- first++;
4147
+ if (ctx.tokens[pos]?.type === "BLOCK_CLOSE") {
4148
+ pos++;
4149
+ consumed++;
3020
4150
  }
3021
- return first > 0 ? elements.slice(first) : elements;
4151
+ if (ctx.tokens[pos]?.type === "NEWLINE") {
4152
+ pos++;
4153
+ consumed++;
4154
+ }
4155
+ return { pos, consumed };
4156
+ }
4157
+
4158
+ // packages/parser/src/parser/rules/block/paragraph/content.ts
4159
+ function parseInlineContent(ctx) {
4160
+ return parseInlineUntil(ctx, "PARAGRAPH_BREAK");
3022
4161
  }
3023
4162
 
3024
4163
  // packages/parser/src/parser/rules/block/paragraph/index.ts
@@ -3028,25 +4167,30 @@ var paragraphRule = {
3028
4167
  requiresLineStart: false,
3029
4168
  parse(ctx) {
3030
4169
  const result = parseInlineContent(ctx);
3031
- if (result.elements.length === 0) {
4170
+ if (result.consumed === 0) {
3032
4171
  return { success: false };
3033
4172
  }
3034
4173
  const elements = normalizeParagraphElements(result.elements);
3035
4174
  if (elements.length === 0) {
3036
- return { success: false };
4175
+ return { success: true, elements: [], consumed: result.consumed };
3037
4176
  }
3038
4177
  const nextPos = ctx.pos + result.consumed;
3039
4178
  const nextToken = ctx.tokens[nextPos];
3040
4179
  if (nextToken?.type === "COLON" && nextToken.lineStart) {
4180
+ if (isPreservedLeadingLineBreak(elements[0]))
4181
+ elements[0] = { element: "line-break" };
3041
4182
  return {
3042
4183
  success: true,
3043
4184
  elements: [...elements, { element: "line-break" }],
3044
4185
  consumed: result.consumed
3045
4186
  };
3046
4187
  }
4188
+ const wrapped = wrapParagraphElements(elements);
4189
+ if (isDivClose(ctx))
4190
+ markUnparsedDivStart(wrapped);
3047
4191
  return {
3048
4192
  success: true,
3049
- elements: wrapParagraphElements(elements),
4193
+ elements: wrapped,
3050
4194
  consumed: result.consumed
3051
4195
  };
3052
4196
  }
@@ -3057,7 +4201,7 @@ function wrapParagraphElements(elements) {
3057
4201
  let bare = false;
3058
4202
  const flush = (trimBreaks = false) => {
3059
4203
  const content = trimBreaks ? normalizeParagraphElements(group) : group;
3060
- while (content[0]?.element === "line-break")
4204
+ while (content[0]?.element === "line-break" && !isPreservedLeadingLineBreak(content[0]))
3061
4205
  content.shift();
3062
4206
  while (content.length) {
3063
4207
  const last = content.at(-1);
@@ -3069,6 +4213,8 @@ function wrapParagraphElements(elements) {
3069
4213
  content.shift();
3070
4214
  if (content[0]?.element === "text")
3071
4215
  content[0] = { element: "text", data: content[0].data.trimStart() };
4216
+ if (isPreservedLeadingLineBreak(content[0]))
4217
+ content[0] = { element: "line-break" };
3072
4218
  if (content.length)
3073
4219
  output.push(...bare || content.some((el) => el.element === "image") ? content : [
3074
4220
  {
@@ -3090,37 +4236,36 @@ function wrapParagraphElements(elements) {
3090
4236
  return output;
3091
4237
  }
3092
4238
 
3093
- // packages/parser/src/parser/rules/block/div/close.ts
3094
- function isDivClose(ctx) {
3095
- const token5 = ctx.tokens[ctx.pos];
3096
- if (token5?.type !== "BLOCK_END_OPEN")
3097
- return false;
3098
- const closeNameResult = parseBlockName(ctx, ctx.pos + 1);
3099
- return closeNameResult?.name === "div";
3100
- }
3101
- function consumeDivClose(ctx, startPos) {
3102
- let pos = startPos;
3103
- let consumed = 0;
3104
- if (ctx.tokens[pos]?.type !== "BLOCK_END_OPEN") {
3105
- return { pos, consumed };
3106
- }
3107
- pos++;
3108
- consumed++;
3109
- const closeNameResult = parseBlockName(ctx, pos);
3110
- if (closeNameResult) {
3111
- pos += closeNameResult.consumed;
3112
- consumed += closeNameResult.consumed;
3113
- }
3114
- if (ctx.tokens[pos]?.type === "BLOCK_CLOSE") {
3115
- pos++;
3116
- consumed++;
3117
- }
3118
- if (ctx.tokens[pos]?.type === "NEWLINE") {
3119
- pos++;
3120
- consumed++;
4239
+ // packages/parser/src/parser/rules/block/note/index.ts
4240
+ var excludedBlockNames = new Set(["note"]);
4241
+ var noteRule = {
4242
+ name: "note",
4243
+ startTokens: ["BLOCK_OPEN"],
4244
+ requiresLineStart: false,
4245
+ parse(ctx) {
4246
+ const bounds = findNoteBounds(ctx);
4247
+ if (!bounds)
4248
+ return { success: false };
4249
+ const body = parseBlocksUntil({
4250
+ ...ctx,
4251
+ tokens: ctx.tokens.slice(bounds.bodyStart, bounds.close),
4252
+ pos: 0,
4253
+ scope: {
4254
+ ...ctx.scope,
4255
+ inlineEnd: undefined,
4256
+ tableFormatting: undefined,
4257
+ blockCloseCondition: undefined
4258
+ }
4259
+ }, () => false, { excludedBlockNames });
4260
+ return {
4261
+ success: true,
4262
+ consumed: bounds.end - ctx.pos,
4263
+ elements: [
4264
+ { element: "container", data: { type: "note", attributes: {}, elements: body.elements } }
4265
+ ]
4266
+ };
3121
4267
  }
3122
- return { pos, consumed };
3123
- }
4268
+ };
3124
4269
 
3125
4270
  // packages/parser/src/parser/rules/block/div/nesting.ts
3126
4271
  var divCloseCountCache = new WeakMap;
@@ -3289,6 +4434,7 @@ function consumeFailedDiv(ctx) {
3289
4434
  pos++;
3290
4435
  consumed++;
3291
4436
  }
4437
+ markUnparsedDivStart(elements);
3292
4438
  return {
3293
4439
  success: true,
3294
4440
  elements: [
@@ -3400,126 +4546,6 @@ var divRule = {
3400
4546
  }
3401
4547
  };
3402
4548
 
3403
- // packages/parser/src/parser/rules/block/code/attributes.ts
3404
- function repairSwallowedCodeClose(ctx, pos, attrs) {
3405
- const prevToken = ctx.tokens[pos - 1];
3406
- if (prevToken?.type !== "QUOTED_STRING" || !prevToken.value.includes("]]")) {
3407
- return null;
3408
- }
3409
- const rawValue = prevToken.value;
3410
- const bracketIdx = rawValue.indexOf("]]");
3411
- const truncatedValue = rawValue.startsWith('"') ? rawValue.slice(1, bracketIdx) : rawValue.slice(0, bracketIdx);
3412
- for (const key of Object.keys(attrs)) {
3413
- const stored = attrs[key];
3414
- if (stored === rawValue || stored === rawValue.slice(1, -1) || stored === rawValue.slice(1)) {
3415
- attrs[key] = truncatedValue;
3416
- break;
3417
- }
3418
- }
3419
- return {
3420
- closingSwallowed: rawValue.includes("[[/code]]")
3421
- };
3422
- }
3423
-
3424
- // packages/parser/src/parser/rules/block/code/content.ts
3425
- function collectCodeContent(ctx, startPos, closingSwallowed) {
3426
- const contentParts = [];
3427
- let pos = startPos;
3428
- let consumed = 0;
3429
- let foundClose = closingSwallowed;
3430
- while (!closingSwallowed && pos < ctx.tokens.length) {
3431
- const token5 = ctx.tokens[pos];
3432
- if (!token5 || token5.type === "EOF") {
3433
- break;
3434
- }
3435
- if (token5.type === "BLOCK_END_OPEN") {
3436
- const closeNameResult = parseBlockName(ctx, pos + 1);
3437
- if (closeNameResult?.name === "code") {
3438
- foundClose = true;
3439
- const closeConsumed = consumeCodeClose(ctx, pos, closeNameResult.consumed);
3440
- consumed += closeConsumed;
3441
- break;
3442
- }
3443
- }
3444
- contentParts.push(token5.value);
3445
- pos++;
3446
- consumed++;
3447
- }
3448
- return { contents: contentParts.join(""), consumed, foundClose };
3449
- }
3450
- function consumeCodeClose(ctx, startPos, closeNameConsumed) {
3451
- let pos = startPos + 1 + closeNameConsumed;
3452
- let consumed = 1 + closeNameConsumed;
3453
- if (ctx.tokens[pos]?.type === "BLOCK_CLOSE") {
3454
- pos++;
3455
- consumed++;
3456
- }
3457
- return consumed;
3458
- }
3459
-
3460
- // packages/parser/src/parser/rules/block/code/index.ts
3461
- var codeBlockRule = {
3462
- name: "code",
3463
- startTokens: ["BLOCK_OPEN"],
3464
- requiresLineStart: false,
3465
- parse(ctx) {
3466
- const openToken = currentToken(ctx);
3467
- if (openToken.type !== "BLOCK_OPEN") {
3468
- return { success: false };
3469
- }
3470
- let pos = ctx.pos + 1;
3471
- let consumed = 1;
3472
- const nameResult = parseBlockName(ctx, pos);
3473
- if (!nameResult || nameResult.name !== "code") {
3474
- return { success: false };
3475
- }
3476
- pos += nameResult.consumed;
3477
- consumed += nameResult.consumed;
3478
- const attrResult = parseAttributesRaw(ctx, pos);
3479
- pos += attrResult.consumed;
3480
- consumed += attrResult.consumed;
3481
- let closingSwallowed = false;
3482
- if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
3483
- const repaired = repairSwallowedCodeClose(ctx, pos, attrResult.attrs);
3484
- if (!repaired)
3485
- return { success: false };
3486
- closingSwallowed = repaired.closingSwallowed;
3487
- } else {
3488
- pos++;
3489
- consumed++;
3490
- }
3491
- if (ctx.tokens[pos]?.type === "NEWLINE") {
3492
- pos++;
3493
- consumed++;
3494
- }
3495
- const contentResult = collectCodeContent(ctx, pos, closingSwallowed);
3496
- let codeContent = contentResult.contents.replace(/\n$/, "");
3497
- consumed += contentResult.consumed;
3498
- pos += contentResult.consumed;
3499
- if (!contentResult.foundClose) {
3500
- ctx.diagnostics.push({
3501
- severity: "warning",
3502
- code: "unclosed-block",
3503
- message: "Missing closing tag [[/code]] for [[code]]",
3504
- position: openToken.position
3505
- });
3506
- }
3507
- const codeBlockData = {
3508
- contents: codeContent,
3509
- language: attrResult.attrs.type ?? null,
3510
- name: attrResult.attrs.name ?? null
3511
- };
3512
- ctx.codeBlocks.push(codeBlockData);
3513
- const elements = [{ element: "code", data: codeBlockData }];
3514
- if (ctx.tokens[pos]?.type === "NEWLINE" && !getParagraphNewlineBoundary(ctx, pos, false).shouldBreak && !ctx.scope.blockCloseCondition?.({ ...ctx, pos: pos + 1 })) {
3515
- const after = parseInlineUntil({ ...ctx, pos: pos + 1 }, "PARAGRAPH_BREAK");
3516
- elements.push({ element: "line-break" }, ...normalizeParagraphElements(after.elements));
3517
- consumed += 1 + after.consumed;
3518
- }
3519
- return { success: true, elements, consumed };
3520
- }
3521
- };
3522
-
3523
4549
  // packages/parser/src/parser/rules/block/collapsible/attributes.ts
3524
4550
  function parseMultilineAttributes(ctx, startPos) {
3525
4551
  const attrs = {};
@@ -3948,7 +4974,9 @@ function parseTableCell(ctx, startPos, cellStart) {
3948
4974
  for (const rule of getCandidateInlineRules(inlineRules, token5.type)) {
3949
4975
  const result = rule.parse(inlineCtx);
3950
4976
  if (result.success) {
3951
- children.push(...result.elements);
4977
+ stripAutomaticLineBreak(children, result.stripLeadingLineBreak);
4978
+ for (const element of result.elements)
4979
+ children.push(element);
3952
4980
  consumed += result.consumed;
3953
4981
  pos += result.consumed;
3954
4982
  matched = true;
@@ -4215,7 +5243,8 @@ function createCellContentAccumulator() {
4215
5243
  addInline(element) {
4216
5244
  currentSegment.push(element);
4217
5245
  },
4218
- addInlineElements(nextElements) {
5246
+ addInlineElements(nextElements, stripLeadingLineBreak) {
5247
+ stripAutomaticLineBreak(currentSegment, stripLeadingLineBreak);
4219
5248
  currentSegment.push(...nextElements);
4220
5249
  },
4221
5250
  addBlockElements(blockElements) {
@@ -4315,7 +5344,7 @@ function consumeCellContentNewline(ctx, startPos, content) {
4315
5344
  return { consumed };
4316
5345
  }
4317
5346
  if (!content.isEmpty()) {
4318
- content.addInline({ element: "line-break" });
5347
+ content.addInline(createAutomaticLineBreak(ctx.tokens[startPos]));
4319
5348
  }
4320
5349
  return { consumed };
4321
5350
  }
@@ -4366,7 +5395,7 @@ function parseCellContent(ctx, closeCondition) {
4366
5395
  for (const rule of getCandidateInlineRules(ctx.inlineRules, token5.type)) {
4367
5396
  const result = rule.parse(inlineCtx);
4368
5397
  if (result.success) {
4369
- content.addInlineElements(result.elements);
5398
+ content.addInlineElements(result.elements, result.stripLeadingLineBreak);
4370
5399
  consumed += result.consumed;
4371
5400
  pos += result.consumed;
4372
5401
  matched = true;
@@ -6786,7 +7815,7 @@ function resolveIfTags(data, pageTags) {
6786
7815
  // packages/parser/src/parser/preprocess/utils/raw-regions.ts
6787
7816
  var BASE_PLACEHOLDER_OPEN = "";
6788
7817
  var BASE_PLACEHOLDER_CLOSE = "";
6789
- var RAW_BLOCK_OPEN_PATTERN = /\[\[\s*(code|html)\b[^\]]*\]\]/iy;
7818
+ var RAW_BLOCK_OPEN_PATTERN = /\[\[html\b[^\]]*\]\]/iy;
6790
7819
  function makeUniqueSentinels(source) {
6791
7820
  let openRun = 0;
6792
7821
  let closeRun = 0;
@@ -6805,10 +7834,21 @@ function makeUniqueSentinels(source) {
6805
7834
  }
6806
7835
  function maskRawRegions(source, sentinels) {
6807
7836
  const placeholders = [];
7837
+ let tokens;
7838
+ const getTokens = () => tokens ??= tokenize(source);
6808
7839
  let masked = "";
6809
7840
  let i = 0;
6810
7841
  while (i < source.length) {
6811
- const rawBlock = tryMaskRawBlock(source, i, placeholders, sentinels);
7842
+ if (source.startsWith("[!--", i)) {
7843
+ const close = source.indexOf("--]", i + 4);
7844
+ if (close !== -1) {
7845
+ const end = close + 3;
7846
+ masked += source.slice(i, end);
7847
+ i = end;
7848
+ continue;
7849
+ }
7850
+ }
7851
+ const rawBlock = tryMaskRawBlock(source, i, placeholders, sentinels, getTokens);
6812
7852
  if (rawBlock) {
6813
7853
  masked += rawBlock.placeholder;
6814
7854
  i = rawBlock.end;
@@ -6829,16 +7869,25 @@ function restorePlaceholders(source, placeholders, sentinels) {
6829
7869
  const pattern = new RegExp(`${escapeRegex(sentinels.open)}(\\d+)${escapeRegex(sentinels.close)}`, "g");
6830
7870
  return source.replace(pattern, (_, idx) => placeholders[Number(idx)] ?? "");
6831
7871
  }
6832
- function tryMaskRawBlock(source, pos, placeholders, sentinels) {
7872
+ function tryMaskRawBlock(source, pos, placeholders, sentinels, getTokens) {
6833
7873
  if (source[pos] !== "[" || source[pos + 1] !== "[")
6834
7874
  return null;
7875
+ if (source.slice(pos, pos + 6).toLowerCase() === "[[code") {
7876
+ const tokens = getTokens();
7877
+ const start = tokenAtOffset(tokens, pos);
7878
+ const open = findCodeOpen(tokens, start);
7879
+ if (!open)
7880
+ return null;
7881
+ const bounds = open.closingSwallowed ? null : findCodeBodyBounds(tokens, open.bodyStart);
7882
+ const end = open.closingSwallowed ? tokens[open.attributesEnd - 1].position.end.offset : bounds.foundClose ? tokens[bounds.end - 1].position.end.offset : source.length;
7883
+ return { placeholder: pushPlaceholder(placeholders, source.slice(pos, end), sentinels), end };
7884
+ }
6835
7885
  RAW_BLOCK_OPEN_PATTERN.lastIndex = pos;
6836
7886
  const openMatch = RAW_BLOCK_OPEN_PATTERN.exec(source);
6837
7887
  if (!openMatch)
6838
7888
  return null;
6839
- const name = openMatch[1].toLowerCase();
6840
7889
  const openLen = openMatch[0].length;
6841
- const closePattern = new RegExp(`\\[\\[\\/\\s*${name}\\s*\\]\\]`, "ig");
7890
+ const closePattern = /\[\[\/\s*html\s*\]\]/gi;
6842
7891
  closePattern.lastIndex = pos + openLen;
6843
7892
  const closeMatch = closePattern.exec(source);
6844
7893
  if (closeMatch) {
@@ -6848,12 +7897,19 @@ function tryMaskRawBlock(source, pos, placeholders, sentinels) {
6848
7897
  end
6849
7898
  };
6850
7899
  }
6851
- if (name !== "code")
6852
- return null;
6853
- return {
6854
- placeholder: pushPlaceholder(placeholders, source.slice(pos), sentinels),
6855
- end: source.length
6856
- };
7900
+ return null;
7901
+ }
7902
+ function tokenAtOffset(tokens, offset) {
7903
+ let low = 0;
7904
+ let high = tokens.length;
7905
+ while (low < high) {
7906
+ const middle = Math.floor((low + high) / 2);
7907
+ if (tokens[middle].position.start.offset < offset)
7908
+ low = middle + 1;
7909
+ else
7910
+ high = middle;
7911
+ }
7912
+ return tokens[low]?.position.start.offset === offset ? low : tokens.length;
6857
7913
  }
6858
7914
  function tryMaskRawInline(source, pos, placeholders, sentinels) {
6859
7915
  if (source[pos] === "@" && source[pos + 1] === "<") {
@@ -6865,17 +7921,22 @@ function tryMaskRawInline(source, pos, placeholders, sentinels) {
6865
7921
  return null;
6866
7922
  }
6867
7923
  function tryMaskSingleLineRaw(source, pos, openerLength, close, placeholders, sentinels) {
6868
- const closePos = source.indexOf(close, pos + openerLength);
6869
- const newline = source.indexOf(`
6870
- `, pos + openerLength);
6871
- if (closePos === -1 || newline !== -1 && newline < closePos)
7924
+ const end = singleLineRawEnd(source, pos, openerLength, close);
7925
+ if (end === pos)
6872
7926
  return null;
6873
- const end = closePos + close.length;
6874
7927
  return {
6875
7928
  placeholder: pushPlaceholder(placeholders, source.slice(pos, end), sentinels),
6876
7929
  end
6877
7930
  };
6878
7931
  }
7932
+ function singleLineRawEnd(source, pos, openerLength, close) {
7933
+ const closePos = source.indexOf(close, pos + openerLength);
7934
+ const newline = source.indexOf(`
7935
+ `, pos + openerLength);
7936
+ if (closePos === -1 || newline !== -1 && newline < closePos)
7937
+ return pos;
7938
+ return closePos + close.length;
7939
+ }
6879
7940
  function pushPlaceholder(placeholders, text, sentinels) {
6880
7941
  const idx = placeholders.length;
6881
7942
  placeholders.push(text);
@@ -9045,167 +10106,32 @@ function parseVariables(tokens) {
9045
10106
  for (const token5 of tokens) {
9046
10107
  if (token5 === "|") {
9047
10108
  assignVariable(vars, current2);
9048
- current2 = "";
9049
- } else {
9050
- current2 += token5;
9051
- }
9052
- }
9053
- assignVariable(vars, current2);
9054
- return vars;
9055
- }
9056
- function assignVariable(vars, source) {
9057
- if (!source.trim()) {
9058
- return;
9059
- }
9060
- const eqIndex = source.indexOf("=");
9061
- if (eqIndex === -1) {
9062
- return;
9063
- }
9064
- const key = source.slice(0, eqIndex).trim();
9065
- if (!key) {
9066
- return;
9067
- }
9068
- vars[key] = source.slice(eqIndex + 1).trim();
9069
- }
9070
-
9071
- // packages/parser/src/parser/rules/block/include/index.ts
9072
- var includeRule = {
9073
- name: "include",
9074
- startTokens: ["BLOCK_OPEN"],
9075
- requiresLineStart: false,
9076
- parse(ctx) {
9077
- const openToken = currentToken(ctx);
9078
- if (openToken.type !== "BLOCK_OPEN") {
9079
- return { success: false };
9080
- }
9081
- let pos = ctx.pos + 1;
9082
- let consumed = 1;
9083
- const nameResult = parseBlockName(ctx, pos);
9084
- if (!nameResult || nameResult.name.toLowerCase() !== "include") {
9085
- return { success: false };
9086
- }
9087
- if (!ctx.settings.enablePageSyntax) {
9088
- return { success: false };
9089
- }
9090
- pos += nameResult.consumed;
9091
- consumed += nameResult.consumed;
9092
- while (ctx.tokens[pos]?.type === "WHITESPACE") {
9093
- pos++;
9094
- consumed++;
9095
- }
9096
- const args = collectIncludeArguments(ctx, pos);
9097
- pos += args.consumed;
9098
- consumed += args.consumed;
9099
- if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
9100
- return { success: false };
9101
- }
9102
- pos++;
9103
- consumed++;
9104
- const directiveEnd = pos;
9105
- if (ctx.tokens[pos]?.type === "NEWLINE") {
9106
- pos++;
9107
- consumed++;
9108
- }
9109
- if (!args.target) {
9110
- return { success: false };
9111
- }
9112
- const location = parsePageRef(args.target);
9113
- if (ctx.deferInclude?.(location)) {
9114
- const source = ctx.tokens.slice(ctx.pos, directiveEnd).map((token5) => token5.value).join("");
9115
- const elements = [];
9116
- const lines = source.split(`
9117
- `);
9118
- for (let index = 0;index < lines.length; index++) {
9119
- if (index > 0)
9120
- elements.push(lineBreak());
9121
- if (lines[index] !== "")
9122
- elements.push(text(lines[index]));
9123
- }
9124
- return { success: true, elements: [paragraph(elements)], consumed };
9125
- }
9126
- return {
9127
- success: true,
9128
- elements: [
9129
- {
9130
- element: "include",
9131
- data: {
9132
- "paragraph-safe": false,
9133
- variables: parseVariables(args.argumentTokens),
9134
- location,
9135
- elements: []
9136
- }
9137
- }
9138
- ],
9139
- consumed
9140
- };
9141
- }
9142
- };
9143
-
9144
- // packages/parser/src/parser/rules/block/math/content.ts
9145
- function collectMathContent(ctx, startPos) {
9146
- let latexSource = "";
9147
- let pos = startPos;
9148
- let consumed = 0;
9149
- let foundClose = false;
9150
- while (pos < ctx.tokens.length) {
9151
- const token5 = ctx.tokens[pos];
9152
- if (!token5)
9153
- break;
9154
- if (token5.type === "BLOCK_END_OPEN") {
9155
- const closeNameResult = parseBlockName(ctx, pos + 1);
9156
- if (closeNameResult?.name === "math") {
9157
- foundClose = true;
9158
- break;
9159
- }
9160
- }
9161
- latexSource += token5.type === "BACKSLASH_BREAK" ? "\\\n" : token5.value;
9162
- pos++;
9163
- consumed++;
9164
- }
9165
- return { latexSource, consumed, foundClose };
9166
- }
9167
- function consumeMathClose(ctx, startPos) {
9168
- let pos = startPos + 1;
9169
- let consumed = 1;
9170
- const closeNameResult = parseBlockName(ctx, pos);
9171
- if (closeNameResult) {
9172
- pos += closeNameResult.consumed;
9173
- consumed += closeNameResult.consumed;
9174
- }
9175
- if (ctx.tokens[pos]?.type === "BLOCK_CLOSE") {
9176
- pos++;
9177
- consumed++;
9178
- }
9179
- if (ctx.tokens[pos]?.type === "NEWLINE") {
9180
- consumed++;
10109
+ current2 = "";
10110
+ } else {
10111
+ current2 += token5;
10112
+ }
9181
10113
  }
9182
- return consumed;
10114
+ assignVariable(vars, current2);
10115
+ return vars;
9183
10116
  }
9184
-
9185
- // packages/parser/src/parser/rules/block/math/name.ts
9186
- function parseMathName(ctx, startPos) {
9187
- let pos = startPos;
9188
- let consumed = 0;
9189
- let name = "";
9190
- const first = ctx.tokens[pos];
9191
- if (first?.type !== "IDENTIFIER" && first?.type !== "TEXT") {
9192
- return { name: null, consumed: 0 };
10117
+ function assignVariable(vars, source) {
10118
+ if (!source.trim()) {
10119
+ return;
9193
10120
  }
9194
- while (pos < ctx.tokens.length) {
9195
- const token5 = ctx.tokens[pos];
9196
- if (!token5 || token5.type === "BLOCK_CLOSE" || token5.type === "WHITESPACE" || token5.type === "NEWLINE") {
9197
- break;
9198
- }
9199
- name += token5.value;
9200
- pos++;
9201
- consumed++;
10121
+ const eqIndex = source.indexOf("=");
10122
+ if (eqIndex === -1) {
10123
+ return;
10124
+ }
10125
+ const key = source.slice(0, eqIndex).trim();
10126
+ if (!key) {
10127
+ return;
9202
10128
  }
9203
- return { name: name === "" ? null : name, consumed };
10129
+ vars[key] = source.slice(eqIndex + 1).trim();
9204
10130
  }
9205
10131
 
9206
- // packages/parser/src/parser/rules/block/math/index.ts
9207
- var mathBlockRule = {
9208
- name: "math",
10132
+ // packages/parser/src/parser/rules/block/include/index.ts
10133
+ var includeRule = {
10134
+ name: "include",
9209
10135
  startTokens: ["BLOCK_OPEN"],
9210
10136
  requiresLineStart: false,
9211
10137
  parse(ctx) {
@@ -9216,7 +10142,10 @@ var mathBlockRule = {
9216
10142
  let pos = ctx.pos + 1;
9217
10143
  let consumed = 1;
9218
10144
  const nameResult = parseBlockName(ctx, pos);
9219
- if (!nameResult || nameResult.name !== "math") {
10145
+ if (!nameResult || nameResult.name.toLowerCase() !== "include") {
10146
+ return { success: false };
10147
+ }
10148
+ if (!ctx.settings.enablePageSyntax) {
9220
10149
  return { success: false };
9221
10150
  }
9222
10151
  pos += nameResult.consumed;
@@ -9225,49 +10154,46 @@ var mathBlockRule = {
9225
10154
  pos++;
9226
10155
  consumed++;
9227
10156
  }
9228
- const mathName = parseMathName(ctx, pos);
9229
- pos += mathName.consumed;
9230
- consumed += mathName.consumed;
9231
- while (ctx.tokens[pos]?.type === "WHITESPACE") {
9232
- pos++;
9233
- consumed++;
9234
- }
10157
+ const args = collectIncludeArguments(ctx, pos);
10158
+ pos += args.consumed;
10159
+ consumed += args.consumed;
9235
10160
  if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
9236
10161
  return { success: false };
9237
10162
  }
9238
10163
  pos++;
9239
10164
  consumed++;
10165
+ const directiveEnd = pos;
9240
10166
  if (ctx.tokens[pos]?.type === "NEWLINE") {
9241
10167
  pos++;
9242
10168
  consumed++;
9243
10169
  }
9244
- const contentResult = collectMathContent(ctx, pos);
9245
- const latexSource = contentResult.latexSource.trim();
9246
- consumed += contentResult.consumed;
9247
- pos += contentResult.consumed;
9248
- if (!contentResult.foundClose) {
9249
- ctx.diagnostics.push({
9250
- severity: "warning",
9251
- code: "unclosed-block",
9252
- message: "Missing closing tag [[/math]] for [[math]]",
9253
- position: openToken.position
9254
- });
9255
- } else {
9256
- const closeConsumed = consumeMathClose(ctx, pos);
9257
- pos += closeConsumed;
9258
- consumed += closeConsumed;
9259
- }
9260
- if (!latexSource) {
10170
+ if (!args.target) {
9261
10171
  return { success: false };
9262
10172
  }
10173
+ const location = parsePageRef(args.target);
10174
+ if (ctx.deferInclude?.(location)) {
10175
+ const source = ctx.tokens.slice(ctx.pos, directiveEnd).map((token5) => token5.value).join("");
10176
+ const elements = [];
10177
+ const lines = source.split(`
10178
+ `);
10179
+ for (let index = 0;index < lines.length; index++) {
10180
+ if (index > 0)
10181
+ elements.push(lineBreak());
10182
+ if (lines[index] !== "")
10183
+ elements.push(text(lines[index]));
10184
+ }
10185
+ return { success: true, elements: [paragraph(elements)], consumed };
10186
+ }
9263
10187
  return {
9264
10188
  success: true,
9265
10189
  elements: [
9266
10190
  {
9267
- element: "math",
10191
+ element: "include",
9268
10192
  data: {
9269
- name: mathName.name,
9270
- "latex-source": latexSource
10193
+ "paragraph-safe": false,
10194
+ variables: parseVariables(args.argumentTokens),
10195
+ location,
10196
+ elements: []
9271
10197
  }
9272
10198
  }
9273
10199
  ],
@@ -10137,7 +11063,8 @@ function parseBibliographyContent(ctx, startPos) {
10137
11063
  const inlineCtx = { ...ctx, pos };
10138
11064
  const result = parseInlineUntil(inlineCtx, "NEWLINE");
10139
11065
  if (result.elements.length > 0) {
10140
- content.push(...result.elements);
11066
+ for (const element of result.elements)
11067
+ content.push(element);
10141
11068
  pos += result.consumed;
10142
11069
  consumed += result.consumed;
10143
11070
  } else {
@@ -10566,6 +11493,7 @@ var blockRules = [
10566
11493
  iftagsRule,
10567
11494
  bibliographyRule,
10568
11495
  galleryRule,
11496
+ noteRule,
10569
11497
  divRule
10570
11498
  ];
10571
11499
  // packages/parser/src/parser/rules/inline/formatting/close.ts
@@ -10578,6 +11506,11 @@ function findFormattingClose(ctx, start, marker) {
10578
11506
  return null;
10579
11507
  if (!table && token5.type === "NEWLINE" && getParagraphNewlineBoundary(ctx, pos, true).shouldBreak)
10580
11508
  return null;
11509
+ const inlineRegionEnd = Math.max(emailRegionEnd(ctx.tokens, pos, end), parseButtonSyntax(ctx, pos, end)?.end ?? pos, parseSocialSyntax(ctx, pos, end)?.end ?? pos);
11510
+ if (inlineRegionEnd > pos) {
11511
+ pos = inlineRegionEnd - 1;
11512
+ continue;
11513
+ }
10581
11514
  if (token5.type === marker && !table?.suppressedClosers.has(pos))
10582
11515
  return pos;
10583
11516
  const protectedEnd = protectedInlineRegionEnd(ctx.tokens, pos, end);
@@ -10623,253 +11556,93 @@ function parseDelimitedContainer(ctx, closeToken, type, options = {}) {
10623
11556
  consumed
10624
11557
  };
10625
11558
  }
10626
- return {
10627
- success: true,
10628
- elements: [createInlineContainer(type, result.elements)],
10629
- consumed
10630
- };
10631
- }
10632
-
10633
- // packages/parser/src/parser/rules/inline/bold.ts
10634
- var boldRule = {
10635
- name: "bold",
10636
- startTokens: ["BOLD_MARKER"],
10637
- parse(ctx) {
10638
- return parseDelimitedContainer(ctx, "BOLD_MARKER", "bold", { discardEmpty: true });
10639
- }
10640
- };
10641
-
10642
- // packages/parser/src/parser/rules/inline/italic.ts
10643
- var italicRule = {
10644
- name: "italic",
10645
- startTokens: ["ITALIC_MARKER"],
10646
- parse(ctx) {
10647
- return parseDelimitedContainer(ctx, "ITALIC_MARKER", "italics");
10648
- }
10649
- };
10650
-
10651
- // packages/parser/src/parser/rules/inline/underline/index.ts
10652
- var underlineRule = {
10653
- name: "underline",
10654
- startTokens: ["UNDERLINE_MARKER"],
10655
- parse(ctx) {
10656
- return parseDelimitedContainer(ctx, "UNDERLINE_MARKER", "underline", { discardEmpty: true });
10657
- }
10658
- };
10659
-
10660
- // packages/parser/src/parser/rules/inline/strikethrough/parse.ts
10661
- function parseStrikethroughContent(ctx) {
10662
- return parseDelimitedContainer(ctx, "STRIKE_MARKER", "strikethrough");
10663
- }
10664
-
10665
- // packages/parser/src/parser/rules/inline/strikethrough/syntax.ts
10666
- function hasValidStrikethroughClose(ctx) {
10667
- const close = findFormattingClose(ctx, ctx.pos + 1, "STRIKE_MARKER");
10668
- return close !== null && close > ctx.pos + 1 && ctx.tokens[close - 1]?.type !== "WHITESPACE";
10669
- }
10670
-
10671
- // packages/parser/src/parser/rules/inline/strikethrough/index.ts
10672
- var strikethroughRule = {
10673
- name: "strikethrough",
10674
- startTokens: ["STRIKE_MARKER"],
10675
- parse(ctx) {
10676
- if (!hasValidStrikethroughClose(ctx)) {
10677
- return {
10678
- success: true,
10679
- elements: [{ element: "text", data: "—" }],
10680
- consumed: 1
10681
- };
10682
- }
10683
- return parseStrikethroughContent(ctx);
10684
- }
10685
- };
10686
-
10687
- // packages/parser/src/parser/rules/inline/superscript.ts
10688
- var superscriptRule = {
10689
- name: "superscript",
10690
- startTokens: ["SUPER_MARKER"],
10691
- parse(ctx) {
10692
- return parseDelimitedContainer(ctx, "SUPER_MARKER", "superscript", {
10693
- discardEmpty: true
10694
- });
10695
- }
10696
- };
10697
-
10698
- // packages/parser/src/parser/rules/inline/subscript.ts
10699
- var subscriptRule = {
10700
- name: "subscript",
10701
- startTokens: ["SUB_MARKER"],
10702
- parse(ctx) {
10703
- return parseDelimitedContainer(ctx, "SUB_MARKER", "subscript", { discardEmpty: true });
10704
- }
10705
- };
10706
-
10707
- // packages/parser/src/parser/rules/inline/monospace.ts
10708
- var monospaceRule = {
10709
- name: "monospace",
10710
- startTokens: ["MONO_MARKER"],
10711
- parse(ctx) {
10712
- return parseDelimitedContainer(ctx, "MONO_CLOSE", "monospace");
10713
- }
10714
- };
10715
-
10716
- // packages/parser/src/parser/rules/inline/link-triple/fallback.ts
10717
- function literalOpenLink(value) {
10718
- return {
10719
- success: true,
10720
- elements: [{ element: "text", data: value }],
10721
- consumed: 1
10722
- };
10723
- }
10724
-
10725
- // packages/parser/src/parser/rules/inline/link-triple/label.ts
10726
- function buildTripleLinkLabel(args) {
10727
- return {
10728
- text: getTripleLinkDisplayText(args)
10729
- };
10730
- }
10731
- function getTripleLinkDisplayText(args) {
10732
- const trimmedLabel = args.labelText.trim();
10733
- if (args.foundPipe) {
10734
- return trimmedLabel || args.finalTarget;
10735
- }
10736
- const colonIdx = args.originalTarget.indexOf(":");
10737
- if (colonIdx !== -1 && !args.originalTarget.startsWith("http") && !args.originalTarget.startsWith("*")) {
10738
- return args.originalTarget.slice(colonIdx + 1).trim();
10739
- }
10740
- return args.originalTarget;
10741
- }
10742
-
10743
- // packages/parser/src/parser/rules/inline/link-triple/syntax.ts
10744
- function hasClosingLinkMarker(ctx, startPos) {
10745
- let pos = startPos;
10746
- while (pos < ctx.tokens.length) {
10747
- const token5 = ctx.tokens[pos];
10748
- if (!token5 || token5.type === "EOF") {
10749
- return false;
10750
- }
10751
- if (token5.type === "LINK_CLOSE") {
10752
- return true;
10753
- }
10754
- if (token5.type === "NEWLINE") {
10755
- const next = ctx.tokens[pos + 1];
10756
- if (next?.type === "NEWLINE" || next?.type === "LINK_CLOSE") {
10757
- return false;
10758
- }
10759
- }
10760
- pos++;
10761
- }
10762
- return false;
10763
- }
10764
- function collectTripleLinkParts(ctx, startPos) {
10765
- let target = "";
10766
- let labelText = "";
10767
- let foundPipe = false;
10768
- let consumed = 1;
10769
- let pos = startPos;
10770
- while (pos < ctx.tokens.length) {
10771
- const token5 = ctx.tokens[pos];
10772
- if (!token5 || token5.type === "LINK_CLOSE" || token5.type === "EOF") {
10773
- break;
10774
- }
10775
- if (token5.type === "NEWLINE") {
10776
- if (foundPipe) {
10777
- labelText += " ";
10778
- } else {
10779
- target += " ";
10780
- }
10781
- consumed++;
10782
- pos++;
10783
- continue;
10784
- }
10785
- if (token5.type === "PIPE" && !foundPipe) {
10786
- foundPipe = true;
10787
- } else if (foundPipe) {
10788
- labelText += token5.value;
10789
- } else {
10790
- target += token5.value;
10791
- }
10792
- consumed++;
10793
- pos++;
10794
- }
10795
- if (ctx.tokens[pos]?.type === "LINK_CLOSE") {
10796
- consumed++;
10797
- }
10798
- return { target, labelText, foundPipe, consumed };
11559
+ return {
11560
+ success: true,
11561
+ elements: [createInlineContainer(type, result.elements)],
11562
+ consumed
11563
+ };
10799
11564
  }
10800
11565
 
10801
- // packages/parser/src/parser/rules/inline/link-triple/interwiki.ts
10802
- var INTERWIKI_PREFIXES = new Set(["wikipedia", "google", "dictionary", "wikidot"]);
10803
- function isInterwikiTarget(target) {
10804
- const colonIdx = target.indexOf(":");
10805
- if (colonIdx <= 0 || target.includes("/")) {
10806
- return false;
11566
+ // packages/parser/src/parser/rules/inline/bold.ts
11567
+ var boldRule = {
11568
+ name: "bold",
11569
+ startTokens: ["BOLD_MARKER"],
11570
+ parse(ctx) {
11571
+ return parseDelimitedContainer(ctx, "BOLD_MARKER", "bold", { discardEmpty: true });
10807
11572
  }
10808
- const prefix = target.slice(0, colonIdx).toLowerCase();
10809
- return INTERWIKI_PREFIXES.has(prefix);
10810
- }
11573
+ };
10811
11574
 
10812
- // packages/parser/src/parser/rules/inline/link-triple/target.ts
10813
- function normalizeTripleLinkTarget(trimmedTarget) {
10814
- if (trimmedTarget.startsWith("*")) {
10815
- return { target: trimmedTarget.slice(1), hasStar: true };
11575
+ // packages/parser/src/parser/rules/inline/italic.ts
11576
+ var italicRule = {
11577
+ name: "italic",
11578
+ startTokens: ["ITALIC_MARKER"],
11579
+ parse(ctx) {
11580
+ return parseDelimitedContainer(ctx, "ITALIC_MARKER", "italics");
10816
11581
  }
10817
- return { target: trimmedTarget, hasStar: false };
11582
+ };
11583
+
11584
+ // packages/parser/src/parser/rules/inline/underline/index.ts
11585
+ var underlineRule = {
11586
+ name: "underline",
11587
+ startTokens: ["UNDERLINE_MARKER"],
11588
+ parse(ctx) {
11589
+ return parseDelimitedContainer(ctx, "UNDERLINE_MARKER", "underline", { discardEmpty: true });
11590
+ }
11591
+ };
11592
+
11593
+ // packages/parser/src/parser/rules/inline/strikethrough/parse.ts
11594
+ function parseStrikethroughContent(ctx) {
11595
+ return parseDelimitedContainer(ctx, "STRIKE_MARKER", "strikethrough");
10818
11596
  }
10819
- function isInvalidTripleLinkTarget(trimmedTarget, foundPipe) {
10820
- return trimmedTarget === "" && foundPipe || /#{2,}/.test(trimmedTarget);
11597
+
11598
+ // packages/parser/src/parser/rules/inline/strikethrough/syntax.ts
11599
+ function hasValidStrikethroughClose(ctx) {
11600
+ const close = findFormattingClose(ctx, ctx.pos + 1, "STRIKE_MARKER");
11601
+ return close !== null && close > ctx.pos + 1 && ctx.tokens[close - 1]?.type !== "WHITESPACE";
10821
11602
  }
10822
- function determineLinkTypeAndLocation(target) {
10823
- if (target.startsWith("#")) {
10824
- return { linkType: "anchor", link: target };
11603
+
11604
+ // packages/parser/src/parser/rules/inline/strikethrough/index.ts
11605
+ var strikethroughRule = {
11606
+ name: "strikethrough",
11607
+ startTokens: ["STRIKE_MARKER"],
11608
+ parse(ctx) {
11609
+ if (!hasValidStrikethroughClose(ctx)) {
11610
+ return {
11611
+ success: true,
11612
+ elements: [{ element: "text", data: "—" }],
11613
+ consumed: 1
11614
+ };
11615
+ }
11616
+ return parseStrikethroughContent(ctx);
10825
11617
  }
10826
- if (target.startsWith("http://") || target.startsWith("https://")) {
10827
- return { linkType: "direct", link: target };
11618
+ };
11619
+
11620
+ // packages/parser/src/parser/rules/inline/superscript.ts
11621
+ var superscriptRule = {
11622
+ name: "superscript",
11623
+ startTokens: ["SUPER_MARKER"],
11624
+ parse(ctx) {
11625
+ return parseDelimitedContainer(ctx, "SUPER_MARKER", "superscript", {
11626
+ discardEmpty: true
11627
+ });
10828
11628
  }
10829
- if (isInterwikiTarget(target)) {
10830
- return { linkType: "interwiki", link: target };
11629
+ };
11630
+
11631
+ // packages/parser/src/parser/rules/inline/subscript.ts
11632
+ var subscriptRule = {
11633
+ name: "subscript",
11634
+ startTokens: ["SUB_MARKER"],
11635
+ parse(ctx) {
11636
+ return parseDelimitedContainer(ctx, "SUB_MARKER", "subscript", { discardEmpty: true });
10831
11637
  }
10832
- return { linkType: "page", link: { site: null, page: target } };
10833
- }
11638
+ };
10834
11639
 
10835
- // packages/parser/src/parser/rules/inline/link-triple/index.ts
10836
- var linkTripleRule = {
10837
- name: "linkTriple",
10838
- startTokens: ["LINK_OPEN"],
11640
+ // packages/parser/src/parser/rules/inline/monospace.ts
11641
+ var monospaceRule = {
11642
+ name: "monospace",
11643
+ startTokens: ["MONO_MARKER"],
10839
11644
  parse(ctx) {
10840
- const startToken = currentToken(ctx);
10841
- if (!hasClosingLinkMarker(ctx, ctx.pos + 1)) {
10842
- return literalOpenLink(startToken.value);
10843
- }
10844
- const parts = collectTripleLinkParts(ctx, ctx.pos + 1);
10845
- const trimmedTarget = parts.target.trim();
10846
- if (isInvalidTripleLinkTarget(trimmedTarget, parts.foundPipe)) {
10847
- return literalOpenLink(startToken.value);
10848
- }
10849
- const normalized = normalizeTripleLinkTarget(trimmedTarget);
10850
- const { linkType, link } = determineLinkTypeAndLocation(normalized.target);
10851
- const label = buildTripleLinkLabel({
10852
- foundPipe: parts.foundPipe,
10853
- labelText: parts.labelText,
10854
- finalTarget: normalized.target,
10855
- originalTarget: trimmedTarget
10856
- });
10857
- return {
10858
- success: true,
10859
- elements: [
10860
- {
10861
- element: "link",
10862
- data: {
10863
- type: linkType,
10864
- link,
10865
- extra: null,
10866
- label,
10867
- target: normalized.hasStar && linkType === "direct" ? "new-tab" : null
10868
- }
10869
- }
10870
- ],
10871
- consumed: parts.consumed
10872
- };
11645
+ return parseDelimitedContainer(ctx, "MONO_CLOSE", "monospace");
10873
11646
  }
10874
11647
  };
10875
11648
 
@@ -10880,7 +11653,7 @@ function normalizeAnchor(anchor) {
10880
11653
 
10881
11654
  // packages/parser/src/parser/rules/inline/link-bracket/direct-url.ts
10882
11655
  function isDirectBracketUrl(url) {
10883
- return url !== "" && (url.startsWith("/") || url.startsWith("http://") || url.startsWith("https://"));
11656
+ return url !== "" && (url.startsWith("/") || startsWithUrlScheme(url, 0));
10884
11657
  }
10885
11658
 
10886
11659
  // packages/parser/src/parser/rules/inline/link-bracket/parts.ts
@@ -10889,35 +11662,24 @@ function collectBracketLinkParts(ctx, startPos) {
10889
11662
  return null;
10890
11663
  }
10891
11664
  let pos = startPos;
10892
- let consumed = 0;
10893
- let first = "";
10894
- while (pos < ctx.tokens.length) {
10895
- const token5 = ctx.tokens[pos];
10896
- if (!token5 || token5.type === "WHITESPACE" || token5.type === "BRACKET_CLOSE" || token5.type === "NEWLINE" || token5.type === "EOF") {
10897
- break;
10898
- }
10899
- first += token5.value;
10900
- pos++;
10901
- consumed++;
10902
- }
10903
- while (ctx.tokens[pos]?.type === "WHITESPACE") {
10904
- pos++;
10905
- consumed++;
10906
- }
10907
- let label = "";
10908
- while (pos < ctx.tokens.length) {
11665
+ const end = Math.min(ctx.scope.inlineEnd ?? ctx.tokens.length, ctx.tokens.length);
11666
+ const values = [];
11667
+ while (pos < end) {
10909
11668
  const token5 = ctx.tokens[pos];
10910
- if (!token5 || token5.type === "BRACKET_CLOSE" || token5.type === "NEWLINE" || token5.type === "EOF") {
11669
+ if (!token5 || token5.type === "BRACKET_CLOSE" || token5.type === "NEWLINE" || token5.type === "EOF")
10911
11670
  break;
10912
- }
10913
- label += token5.value;
11671
+ values.push(token5.value);
10914
11672
  pos++;
10915
- consumed++;
10916
11673
  }
10917
- if (ctx.tokens[pos]?.type !== "BRACKET_CLOSE") {
11674
+ if (pos >= end || ctx.tokens[pos]?.type !== "BRACKET_CLOSE")
10918
11675
  return null;
10919
- }
10920
- return { first, label, consumed: consumed + 1 };
11676
+ const content = values.join("");
11677
+ const separator = content.search(/[ \t]/);
11678
+ return {
11679
+ first: separator === -1 ? content : content.slice(0, separator),
11680
+ label: separator === -1 ? "" : content.slice(separator).trimStart(),
11681
+ consumed: pos - startPos + 1
11682
+ };
10921
11683
  }
10922
11684
 
10923
11685
  // packages/parser/src/parser/rules/inline/link-bracket/prefix.ts
@@ -10928,6 +11690,14 @@ function parseBracketLinkPrefix(ctx, startPos) {
10928
11690
  return { target: null, bodyStart: startPos, consumed: 0 };
10929
11691
  }
10930
11692
 
11693
+ // packages/parser/src/parser/rules/inline/link-bracket/special-target.ts
11694
+ function isBracketEmail(target) {
11695
+ return /^[_a-z0-9-]+(?:\.[_a-z0-9-]+)*@[a-z0-9-]+(?:\.[a-z0-9-]+)+$/i.test(target);
11696
+ }
11697
+ function wikipediaPage(target) {
11698
+ return /^wikipedia:((?!:)[A-Za-z0-9_/=&~#.:;+-]+)$/.exec(target)?.[1] ?? null;
11699
+ }
11700
+
10931
11701
  // packages/parser/src/parser/rules/inline/link-bracket/parsed.ts
10932
11702
  function parseSingleBracketLink(ctx) {
10933
11703
  const prefix = parseBracketLinkPrefix(ctx, ctx.pos + 1);
@@ -10936,17 +11706,20 @@ function parseSingleBracketLink(ctx) {
10936
11706
  return null;
10937
11707
  }
10938
11708
  const link = parts.first.trim();
10939
- if (!isDirectBracketUrl(link)) {
11709
+ const email = isBracketEmail(link);
11710
+ const wikiPage = wikipediaPage(link);
11711
+ if (!email && wikiPage === null && !isDirectBracketUrl(link)) {
10940
11712
  return null;
10941
11713
  }
10942
- const labelText = parts.label.trim();
11714
+ const labelText = parts.label.trim() || wikiPage;
10943
11715
  if (!labelText) {
10944
11716
  return null;
10945
11717
  }
10946
11718
  return {
10947
- link,
11719
+ link: email ? `mailto:${link}` : link,
11720
+ interwiki: wikiPage !== null,
10948
11721
  labelText,
10949
- target: prefix.target,
11722
+ target: wikiPage !== null ? "new-tab" : prefix.target,
10950
11723
  consumed: 1 + prefix.consumed + parts.consumed
10951
11724
  };
10952
11725
  }
@@ -10999,7 +11772,7 @@ var linkSingleRule = {
10999
11772
  {
11000
11773
  element: "link",
11001
11774
  data: {
11002
- type: "direct",
11775
+ type: parsed.interwiki ? "interwiki" : "direct",
11003
11776
  link: parsed.link,
11004
11777
  extra: null,
11005
11778
  label: linkLabel,
@@ -11068,6 +11841,134 @@ var linkStarRule = {
11068
11841
  }
11069
11842
  };
11070
11843
 
11844
+ // packages/parser/src/parser/rules/inline/autolink.ts
11845
+ var URL_BOUNDARY_TOKENS = new Set([
11846
+ "WHITESPACE",
11847
+ "NEWLINE",
11848
+ "EOF",
11849
+ "BLOCK_OPEN",
11850
+ "BLOCK_END_OPEN",
11851
+ "BLOCK_CLOSE",
11852
+ "LINK_OPEN",
11853
+ "LINK_CLOSE",
11854
+ "COMMENT_OPEN",
11855
+ "BACKSLASH_BREAK"
11856
+ ]);
11857
+ var autolinkRule = {
11858
+ name: "autolink",
11859
+ startTokens: ["IDENTIFIER", "STAR", "LIST_BULLET"],
11860
+ parse(ctx) {
11861
+ let pos = ctx.pos;
11862
+ let target = null;
11863
+ const first = ctx.tokens[pos];
11864
+ if (!first) {
11865
+ return { success: false };
11866
+ }
11867
+ if (first.type === "STAR" || first.type === "LIST_BULLET") {
11868
+ if (first.value !== "*") {
11869
+ return { success: false };
11870
+ }
11871
+ target = "new-tab";
11872
+ pos++;
11873
+ }
11874
+ const scheme = ctx.tokens[pos];
11875
+ if (scheme?.type !== "IDENTIFIER" || !URL_SCHEME_NAMES.has(scheme.value)) {
11876
+ return { success: false };
11877
+ }
11878
+ if (ctx.tokens[pos + 1]?.type !== "COLON") {
11879
+ return { success: false };
11880
+ }
11881
+ const prev = ctx.tokens[ctx.pos - 1];
11882
+ if (prev && !first.lineStart) {
11883
+ const lastChar = prev.value[prev.value.length - 1] ?? "";
11884
+ if (/[A-Za-z]/.test(lastChar)) {
11885
+ return { success: false };
11886
+ }
11887
+ }
11888
+ const values = [];
11889
+ let end = pos;
11890
+ const inlineEnd = ctx.scope.inlineEnd ?? ctx.tokens.length;
11891
+ while (end < inlineEnd) {
11892
+ const token5 = ctx.tokens[end];
11893
+ if (!token5 || URL_BOUNDARY_TOKENS.has(token5.type)) {
11894
+ break;
11895
+ }
11896
+ if (token5.type === "RAW_OPEN" || token5.type === "RAW_BLOCK_OPEN") {
11897
+ if (rawRegionEnd(ctx.tokens, end, inlineEnd) > end)
11898
+ break;
11899
+ }
11900
+ values.push(token5.value);
11901
+ end++;
11902
+ if (/[ \t\n\\"']/.test(token5.value))
11903
+ break;
11904
+ }
11905
+ const candidate = values.join("");
11906
+ const match = URL_PATTERN.exec(candidate);
11907
+ if (!match) {
11908
+ return { success: false };
11909
+ }
11910
+ const url = match[0];
11911
+ let length = 0;
11912
+ let count = 0;
11913
+ while (length < url.length) {
11914
+ length += values[count]?.length ?? 0;
11915
+ count++;
11916
+ }
11917
+ const rest = candidate.slice(url.length, length);
11918
+ const elements = [
11919
+ {
11920
+ element: "link",
11921
+ data: {
11922
+ type: "direct",
11923
+ link: url,
11924
+ extra: null,
11925
+ label: { text: url },
11926
+ target
11927
+ }
11928
+ }
11929
+ ];
11930
+ if (rest !== "") {
11931
+ elements.push({ element: "text", data: rest });
11932
+ }
11933
+ return {
11934
+ success: true,
11935
+ elements,
11936
+ consumed: pos - ctx.pos + count
11937
+ };
11938
+ }
11939
+ };
11940
+
11941
+ // packages/parser/src/parser/rules/inline/email/index.ts
11942
+ var emailRule = {
11943
+ name: "email",
11944
+ startTokens: EMAIL_START_TOKENS,
11945
+ parse(ctx) {
11946
+ const group = getEmailGroup(ctx.tokens, ctx.pos, ctx.scope.inlineEnd ?? ctx.tokens.length);
11947
+ if (!group)
11948
+ return { success: false };
11949
+ const elements = [];
11950
+ let copied = group.start;
11951
+ for (const candidate of group.candidates) {
11952
+ if (copied < candidate.start)
11953
+ elements.push({ element: "text", data: group.source.slice(copied, candidate.start) });
11954
+ elements.push(ctx.scope.suppressEmailLinks ? { element: "text", data: candidate.address } : {
11955
+ element: "link",
11956
+ data: {
11957
+ type: "direct",
11958
+ link: `mailto:${candidate.address}`,
11959
+ label: { text: candidate.address },
11960
+ target: null,
11961
+ extra: null
11962
+ }
11963
+ });
11964
+ copied = candidate.end;
11965
+ }
11966
+ if (copied < group.end)
11967
+ elements.push({ element: "text", data: group.source.slice(copied, group.end) });
11968
+ return { success: true, elements, consumed: group.endToken - ctx.pos };
11969
+ }
11970
+ };
11971
+
11071
11972
  // packages/parser/src/parser/rules/inline/color/syntax.ts
11072
11973
  function parseColorContent(ctx) {
11073
11974
  const close = findFormattingClose(ctx, ctx.pos + 1, "COLOR_MARKER");
@@ -11154,7 +12055,7 @@ var newlineLineBreakRule = {
11154
12055
  }
11155
12056
  return {
11156
12057
  success: true,
11157
- elements: [{ element: "line-break" }],
12058
+ elements: [createAutomaticLineBreak(currentTok)],
11158
12059
  consumed: 1
11159
12060
  };
11160
12061
  }
@@ -11177,6 +12078,9 @@ function isValidBlockStartAfterNewline(ctx, tokenPos) {
11177
12078
  if (!isBlockStartToken(token5?.type) || !token5?.lineStart) {
11178
12079
  return false;
11179
12080
  }
12081
+ if (token5.type === "LIST_BULLET" || token5.type === "LIST_NUMBER") {
12082
+ return ctx.tokens[tokenPos + 1]?.type === "WHITESPACE";
12083
+ }
11180
12084
  if (token5.type !== "HEADING_MARKER") {
11181
12085
  return true;
11182
12086
  }
@@ -11434,6 +12338,9 @@ var htmlInlineRule = {
11434
12338
  }
11435
12339
  };
11436
12340
 
12341
+ // packages/parser/src/parser/rules/inline/raw/angle.ts
12342
+ import { decodeHTML } from "entities";
12343
+
11437
12344
  // packages/parser/src/parser/rules/inline/raw/result.ts
11438
12345
  function rawElement(value, consumed) {
11439
12346
  return {
@@ -11481,7 +12388,7 @@ function parseAngleRaw(ctx) {
11481
12388
  if (ctx.tokens[pos]?.type === "RAW_BLOCK_CLOSE") {
11482
12389
  consumed++;
11483
12390
  }
11484
- return rawElement(value, consumed);
12391
+ return rawElement(decodeHTML(value), consumed);
11485
12392
  }
11486
12393
 
11487
12394
  // packages/parser/src/parser/rules/inline/raw/double-at.ts
@@ -11704,7 +12611,7 @@ function consumeSpanNewline(ctx, pos, paragraphStrip, afterBlankLine, children,
11704
12611
  return { consumed: consumed2, afterBlankLine: paragraphStrip };
11705
12612
  }
11706
12613
  const targetChildren = paragraphStrip && afterBlankLine ? escapedChildren : children;
11707
- targetChildren.push({ element: "line-break" });
12614
+ targetChildren.push(createAutomaticLineBreak(ctx.tokens[pos]));
11708
12615
  let consumed = 1;
11709
12616
  let nextPos = pos + 1;
11710
12617
  while (ctx.tokens[nextPos]?.type === "WHITESPACE" && ctx.tokens[nextPos]?.lineStart) {
@@ -11792,7 +12699,9 @@ function parseOneSpanChild(ctx, pos, targetChildren) {
11792
12699
  for (const rule of getCandidateInlineRules(inlineRules, token5.type)) {
11793
12700
  const result = rule.parse(inlineCtx);
11794
12701
  if (result.success) {
11795
- targetChildren.push(...result.elements);
12702
+ stripAutomaticLineBreak(targetChildren, result.stripLeadingLineBreak);
12703
+ for (const element of result.elements)
12704
+ targetChildren.push(element);
11796
12705
  return { consumed: result.consumed };
11797
12706
  }
11798
12707
  }
@@ -11860,6 +12769,48 @@ var closeSpanRule = {
11860
12769
  }
11861
12770
  };
11862
12771
 
12772
+ // packages/parser/src/parser/rules/inline/button/index.ts
12773
+ var buttonRule = {
12774
+ name: "button",
12775
+ startTokens: ["BLOCK_OPEN"],
12776
+ parse(ctx) {
12777
+ const result = parseButtonSyntax(ctx, ctx.pos, ctx.scope.inlineEnd ?? ctx.tokens.length);
12778
+ return result ? {
12779
+ success: true,
12780
+ consumed: result.end - ctx.pos,
12781
+ elements: [{ element: "button", data: result.data }]
12782
+ } : { success: false };
12783
+ }
12784
+ };
12785
+
12786
+ // packages/parser/src/parser/rules/inline/social/index.ts
12787
+ var socialRule = {
12788
+ name: "social",
12789
+ startTokens: ["BLOCK_OPEN"],
12790
+ parse(ctx) {
12791
+ const result = parseSocialSyntax(ctx, ctx.pos, ctx.scope.inlineEnd ?? ctx.tokens.length);
12792
+ return result ? {
12793
+ success: true,
12794
+ consumed: result.end - ctx.pos,
12795
+ elements: [{ element: "social", data: result.data }]
12796
+ } : { success: false };
12797
+ }
12798
+ };
12799
+
12800
+ // packages/parser/src/parser/rules/inline/date/index.ts
12801
+ var dateRule = {
12802
+ name: "date",
12803
+ startTokens: ["BLOCK_OPEN"],
12804
+ parse(ctx) {
12805
+ const parsed = parseDateSyntax(ctx, ctx.pos, ctx.scope.inlineEnd ?? ctx.tokens.length);
12806
+ return parsed ? {
12807
+ success: true,
12808
+ consumed: parsed.end - ctx.pos,
12809
+ elements: [{ element: "date", data: parsed.data }]
12810
+ } : { success: false };
12811
+ }
12812
+ };
12813
+
11863
12814
  // packages/parser/src/parser/rules/inline/size/content.ts
11864
12815
  function parseSizeContent(ctx, startPos) {
11865
12816
  const children = [];
@@ -11881,7 +12832,8 @@ function parseSizeContent(ctx, startPos) {
11881
12832
  const inlineCtx = { ...ctx, pos };
11882
12833
  const inlineResult = parseInlineUntil(inlineCtx, "BLOCK_END_OPEN");
11883
12834
  if (inlineResult.elements.length > 0) {
11884
- children.push(...inlineResult.elements);
12835
+ for (const element of inlineResult.elements)
12836
+ children.push(element);
11885
12837
  pos += inlineResult.consumed;
11886
12838
  consumed += inlineResult.consumed;
11887
12839
  } else {
@@ -11910,6 +12862,16 @@ function tryConsumeSizeClose(ctx, pos) {
11910
12862
 
11911
12863
  // packages/parser/src/parser/rules/inline/size/value.ts
11912
12864
  var VALID_SIZE_UNITS = ["px", "em", "rem", "ex", "%", "cm", "mm", "in", "pc"];
12865
+ var VALID_SIZE_KEYWORDS = [
12866
+ "smaller",
12867
+ "larger",
12868
+ "xx-small",
12869
+ "x-small",
12870
+ "small",
12871
+ "large",
12872
+ "x-large",
12873
+ "xx-large"
12874
+ ];
11913
12875
  function parseSizeValue(ctx, startPos) {
11914
12876
  let pos = startPos;
11915
12877
  let consumed = 0;
@@ -11934,6 +12896,8 @@ function parseSizeValue(ctx, startPos) {
11934
12896
  return isValidSizeValue(size) ? { size, consumed } : null;
11935
12897
  }
11936
12898
  function isValidSizeValue(size) {
12899
+ if (VALID_SIZE_KEYWORDS.includes(size))
12900
+ return true;
11937
12901
  const unitPattern = VALID_SIZE_UNITS.join("|");
11938
12902
  return new RegExp(`^(\\d+(?:\\.\\d+)?)(${unitPattern})$`, "i").test(size);
11939
12903
  }
@@ -12005,19 +12969,6 @@ var sizeRule = {
12005
12969
  }
12006
12970
  };
12007
12971
 
12008
- // packages/parser/src/parser/rules/inline/footnote/child.ts
12009
- function parseFootnoteChild(ctx, pos) {
12010
- const token5 = ctx.tokens[pos];
12011
- if (!token5) {
12012
- return { elements: [], consumed: 0 };
12013
- }
12014
- const inlineResult = parseInlineUntil({ ...ctx, pos }, "BLOCK_END_OPEN");
12015
- if (inlineResult.elements.length > 0) {
12016
- return { elements: inlineResult.elements, consumed: inlineResult.consumed };
12017
- }
12018
- return { elements: [{ element: "text", data: token5.value }], consumed: 1 };
12019
- }
12020
-
12021
12972
  // packages/parser/src/parser/rules/inline/footnote/close.ts
12022
12973
  function tryConsumeFootnoteClose(ctx, pos) {
12023
12974
  if (ctx.tokens[pos]?.type !== "BLOCK_END_OPEN") {
@@ -12039,96 +12990,73 @@ function tryConsumeFootnoteClose(ctx, pos) {
12039
12990
  return { consumed };
12040
12991
  }
12041
12992
 
12042
- // packages/parser/src/parser/rules/inline/footnote/newline.ts
12043
- function consumeFootnoteNewline(ctx, pos) {
12044
- let nextPos = pos + 1;
12045
- let consumed = 1;
12046
- while (ctx.tokens[nextPos]?.type === "WHITESPACE") {
12047
- nextPos++;
12048
- consumed++;
12049
- }
12050
- if (ctx.tokens[nextPos]?.type !== "NEWLINE") {
12051
- return { consumed, paragraphBreak: false };
12993
+ // packages/parser/src/parser/rules/inline/math-inline/syntax.ts
12994
+ function parseInlineMathSource(ctx, startPos) {
12995
+ let pos = startPos;
12996
+ let consumed = 0;
12997
+ if (ctx.tokens[pos]?.type !== "TEXT" || ctx.tokens[pos]?.value !== "$") {
12998
+ return null;
12052
12999
  }
12053
- while (ctx.tokens[nextPos]?.type === "NEWLINE") {
12054
- nextPos++;
13000
+ pos++;
13001
+ consumed++;
13002
+ while (ctx.tokens[pos]?.type === "WHITESPACE") {
13003
+ pos++;
12055
13004
  consumed++;
12056
13005
  }
12057
- return { consumed, paragraphBreak: true };
12058
- }
12059
-
12060
- // packages/parser/src/parser/rules/inline/footnote/content.ts
12061
- function parseFootnoteContent(ctx, startPos) {
12062
- const paragraphs = [[]];
12063
- let currentParagraph = 0;
12064
- let pos = startPos;
12065
- let consumed = 0;
13006
+ let latexSource = "";
12066
13007
  while (pos < ctx.tokens.length) {
12067
13008
  const token5 = ctx.tokens[pos];
12068
- if (!token5 || token5.type === "EOF") {
12069
- break;
12070
- }
12071
- const closeResult = tryConsumeFootnoteClose(ctx, pos);
12072
- if (closeResult) {
12073
- return {
12074
- paragraphs,
12075
- consumed: consumed + closeResult.consumed,
12076
- foundClose: true
12077
- };
13009
+ if (!token5 || token5.type === "NEWLINE") {
13010
+ return null;
12078
13011
  }
12079
- if (token5.type === "NEWLINE") {
12080
- const newlineResult = consumeFootnoteNewline(ctx, pos);
12081
- pos += newlineResult.consumed;
12082
- consumed += newlineResult.consumed;
12083
- if (newlineResult.paragraphBreak) {
12084
- currentParagraph++;
12085
- paragraphs[currentParagraph] = [];
12086
- } else {
12087
- paragraphs[currentParagraph].push({ element: "line-break" });
12088
- }
12089
- continue;
13012
+ if (token5.type === "TEXT" && token5.value === "$" && ctx.tokens[pos + 1]?.type === "BLOCK_CLOSE") {
13013
+ break;
12090
13014
  }
12091
- const child = parseFootnoteChild(ctx, pos);
12092
- paragraphs[currentParagraph].push(...child.elements);
12093
- pos += child.consumed;
12094
- consumed += child.consumed;
13015
+ latexSource += token5.value;
13016
+ pos++;
13017
+ consumed++;
13018
+ }
13019
+ if (ctx.tokens[pos]?.type !== "TEXT" || ctx.tokens[pos]?.value !== "$") {
13020
+ return null;
13021
+ }
13022
+ pos++;
13023
+ consumed++;
13024
+ if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
13025
+ return null;
12095
13026
  }
12096
- return { paragraphs, consumed, foundClose: false };
13027
+ return {
13028
+ latexSource: latexSource.trim(),
13029
+ consumed: consumed + 1
13030
+ };
12097
13031
  }
12098
13032
 
12099
- // packages/parser/src/parser/rules/inline/footnote/elements.ts
12100
- function buildFootnoteChildren(paragraphs) {
12101
- const children = [];
12102
- for (let i = 0;i < paragraphs.length; i++) {
12103
- const para = trimLineBreaks(paragraphs[i] ?? []);
12104
- if (para.length === 0) {
12105
- continue;
13033
+ // packages/parser/src/parser/rules/inline/math-inline/index.ts
13034
+ var mathInlineRule = {
13035
+ name: "math-inline",
13036
+ startTokens: ["BLOCK_OPEN"],
13037
+ parse(ctx) {
13038
+ const openToken = currentToken(ctx);
13039
+ if (openToken.type !== "BLOCK_OPEN") {
13040
+ return { success: false };
12106
13041
  }
12107
- if (i === 0) {
12108
- children.push(...para);
12109
- } else {
12110
- children.push({
12111
- element: "container",
12112
- data: {
12113
- type: "paragraph",
12114
- attributes: {},
12115
- elements: para
12116
- }
12117
- });
13042
+ const parsed = parseInlineMathSource(ctx, ctx.pos + 1);
13043
+ if (!parsed) {
13044
+ return { success: false };
12118
13045
  }
13046
+ return {
13047
+ success: true,
13048
+ elements: [
13049
+ {
13050
+ element: "math-inline",
13051
+ data: {
13052
+ "latex-source": parsed.latexSource
13053
+ }
13054
+ }
13055
+ ],
13056
+ consumed: 1 + parsed.consumed
13057
+ };
12119
13058
  }
12120
- return children;
12121
- }
12122
- function trimLineBreaks(elements) {
12123
- const result = [...elements];
12124
- while (result.length > 0 && result[0]?.element === "line-break") {
12125
- result.shift();
12126
- }
12127
- while (result.length > 0 && result[result.length - 1]?.element === "line-break") {
12128
- result.pop();
12129
- }
12130
- return result;
12131
- }
13059
+ };
12132
13060
 
12133
13061
  // packages/parser/src/parser/rules/inline/footnote/open.ts
12134
13062
  function parseFootnoteOpen(ctx) {
@@ -12155,6 +13083,91 @@ function parseFootnoteOpen(ctx) {
12155
13083
  return { bodyStart: pos, consumed };
12156
13084
  }
12157
13085
 
13086
+ // packages/parser/src/parser/rules/inline/footnote/boundary.ts
13087
+ var opaqueRules2 = [
13088
+ codeBlockRule,
13089
+ htmlBlockRule,
13090
+ mathBlockRule,
13091
+ mathInlineRule,
13092
+ linkTripleRule,
13093
+ linkSingleRule,
13094
+ linkStarRule,
13095
+ linkAnchorRule
13096
+ ];
13097
+ function findFootnoteEnd(ctx, start) {
13098
+ const probe = createOpaqueProbe(ctx);
13099
+ let depth = 0;
13100
+ for (let pos = start;pos < ctx.tokens.length; pos++) {
13101
+ if (ctx.tokens[pos]?.type === "EOF")
13102
+ return pos;
13103
+ const protectedEnd = protectedInlineRegionEnd(ctx.tokens, pos, ctx.tokens.length);
13104
+ if (protectedEnd > pos) {
13105
+ pos = protectedEnd - 1;
13106
+ continue;
13107
+ }
13108
+ probe.pos = pos;
13109
+ const close = tryConsumeFootnoteClose(probe, pos);
13110
+ if (close) {
13111
+ if (depth === 0)
13112
+ return pos;
13113
+ depth--;
13114
+ pos += close.consumed - 1;
13115
+ continue;
13116
+ }
13117
+ const open = parseFootnoteOpen(probe);
13118
+ if (open) {
13119
+ depth++;
13120
+ pos += open.consumed - 1;
13121
+ continue;
13122
+ }
13123
+ const opaqueEnd = opaqueRuleEnd(probe, pos, opaqueRules2);
13124
+ if (opaqueEnd > pos)
13125
+ pos = opaqueEnd - 1;
13126
+ }
13127
+ return ctx.tokens.length;
13128
+ }
13129
+
13130
+ // packages/parser/src/parser/rules/inline/footnote/content.ts
13131
+ function parseFootnoteContent(ctx, startPos) {
13132
+ const end = findFootnoteEnd(ctx, startPos);
13133
+ let leadingNewlines = 0;
13134
+ for (let pos = startPos;pos < end; pos++) {
13135
+ const type = ctx.tokens[pos]?.type;
13136
+ if (type === "NEWLINE")
13137
+ leadingNewlines++;
13138
+ else if (type !== "WHITESPACE")
13139
+ break;
13140
+ }
13141
+ const bodyCtx = {
13142
+ ...ctx,
13143
+ tokens: ctx.tokens.slice(startPos, end),
13144
+ pos: 0,
13145
+ scope: {
13146
+ ...ctx.scope,
13147
+ inlineEnd: undefined,
13148
+ tableFormatting: undefined,
13149
+ blockCloseCondition: undefined
13150
+ }
13151
+ };
13152
+ const result = parseBlocksUntil(bodyCtx, () => false);
13153
+ const close = tryConsumeFootnoteClose(ctx, end);
13154
+ return {
13155
+ elements: result.elements,
13156
+ consumed: end - startPos + (close?.consumed ?? 0),
13157
+ foundClose: close !== null,
13158
+ leadingParagraphBreak: leadingNewlines >= 2
13159
+ };
13160
+ }
13161
+
13162
+ // packages/parser/src/parser/rules/inline/footnote/elements.ts
13163
+ function buildFootnoteChildren(elements, leadingParagraphBreak) {
13164
+ const first = elements[0];
13165
+ if (!leadingParagraphBreak && first?.element === "container" && first.data.type === "paragraph") {
13166
+ return [...first.data.elements, ...elements.slice(1)];
13167
+ }
13168
+ return elements;
13169
+ }
13170
+
12158
13171
  // packages/parser/src/parser/rules/inline/footnote/index.ts
12159
13172
  var footnoteRule = {
12160
13173
  name: "footnote",
@@ -12170,7 +13183,7 @@ var footnoteRule = {
12170
13183
  }
12171
13184
  const contentResult = parseFootnoteContent(ctx, openResult.bodyStart);
12172
13185
  const consumed = openResult.consumed + contentResult.consumed;
12173
- const children = buildFootnoteChildren(contentResult.paragraphs);
13186
+ const children = buildFootnoteChildren(contentResult.elements, contentResult.leadingParagraphBreak);
12174
13187
  if (!contentResult.foundClose) {
12175
13188
  ctx.diagnostics.push({
12176
13189
  severity: "warning",
@@ -12530,11 +13543,15 @@ function parseAnchorChild(ctx, pos) {
12530
13543
  if (!token5) {
12531
13544
  return { elements: [], consumed: 0 };
12532
13545
  }
12533
- const inlineCtx = { ...ctx, pos };
13546
+ const inlineCtx = {
13547
+ ...ctx,
13548
+ pos,
13549
+ scope: { ...ctx.scope, suppressEmailLinks: true }
13550
+ };
12534
13551
  for (const rule of getCandidateInlineRules(inlineRules, token5.type)) {
12535
13552
  const result = rule.parse(inlineCtx);
12536
13553
  if (result.success) {
12537
- return { elements: result.elements, consumed: result.consumed };
13554
+ return result;
12538
13555
  }
12539
13556
  }
12540
13557
  return { elements: [{ element: "text", data: token5.value }], consumed: 1 };
@@ -12594,7 +13611,7 @@ function consumeAnchorNewline(ctx, pos, paragraphStrip, children) {
12594
13611
  let consumed = 1;
12595
13612
  let nextPos = pos + 1;
12596
13613
  if (!paragraphStrip) {
12597
- children.push({ element: "line-break" });
13614
+ children.push(createAutomaticLineBreak(ctx.tokens[pos]));
12598
13615
  while (ctx.tokens[nextPos]?.type === "WHITESPACE" && ctx.tokens[nextPos]?.lineStart) {
12599
13616
  nextPos++;
12600
13617
  consumed++;
@@ -12647,7 +13664,9 @@ function parseAnchorContent(ctx, startPos, paragraphStrip) {
12647
13664
  continue;
12648
13665
  }
12649
13666
  const child = parseAnchorChild(ctx, pos);
12650
- children.push(...child.elements);
13667
+ stripAutomaticLineBreak(children, child.stripLeadingLineBreak);
13668
+ for (const element of child.elements)
13669
+ children.push(element);
12651
13670
  pos += child.consumed;
12652
13671
  consumed += child.consumed;
12653
13672
  }
@@ -12721,75 +13740,8 @@ var anchorRule = {
12721
13740
  }
12722
13741
  }
12723
13742
  ],
12724
- consumed
12725
- };
12726
- }
12727
- };
12728
-
12729
- // packages/parser/src/parser/rules/inline/math-inline/syntax.ts
12730
- function parseInlineMathSource(ctx, startPos) {
12731
- let pos = startPos;
12732
- let consumed = 0;
12733
- if (ctx.tokens[pos]?.type !== "TEXT" || ctx.tokens[pos]?.value !== "$") {
12734
- return null;
12735
- }
12736
- pos++;
12737
- consumed++;
12738
- while (ctx.tokens[pos]?.type === "WHITESPACE") {
12739
- pos++;
12740
- consumed++;
12741
- }
12742
- let latexSource = "";
12743
- while (pos < ctx.tokens.length) {
12744
- const token5 = ctx.tokens[pos];
12745
- if (!token5 || token5.type === "NEWLINE") {
12746
- return null;
12747
- }
12748
- if (token5.type === "TEXT" && token5.value === "$" && ctx.tokens[pos + 1]?.type === "BLOCK_CLOSE") {
12749
- break;
12750
- }
12751
- latexSource += token5.value;
12752
- pos++;
12753
- consumed++;
12754
- }
12755
- if (ctx.tokens[pos]?.type !== "TEXT" || ctx.tokens[pos]?.value !== "$") {
12756
- return null;
12757
- }
12758
- pos++;
12759
- consumed++;
12760
- if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
12761
- return null;
12762
- }
12763
- return {
12764
- latexSource: latexSource.trim(),
12765
- consumed: consumed + 1
12766
- };
12767
- }
12768
-
12769
- // packages/parser/src/parser/rules/inline/math-inline/index.ts
12770
- var mathInlineRule = {
12771
- name: "math-inline",
12772
- startTokens: ["BLOCK_OPEN"],
12773
- parse(ctx) {
12774
- const openToken = currentToken(ctx);
12775
- if (openToken.type !== "BLOCK_OPEN") {
12776
- return { success: false };
12777
- }
12778
- const parsed = parseInlineMathSource(ctx, ctx.pos + 1);
12779
- if (!parsed) {
12780
- return { success: false };
12781
- }
12782
- return {
12783
- success: true,
12784
- elements: [
12785
- {
12786
- element: "math-inline",
12787
- data: {
12788
- "latex-source": parsed.latexSource
12789
- }
12790
- }
12791
- ],
12792
- consumed: 1 + parsed.consumed
13743
+ consumed,
13744
+ stripLeadingLineBreak: openResult.paragraphStrip ? precedingSingleNewline(ctx) : undefined
12793
13745
  };
12794
13746
  }
12795
13747
  };
@@ -12900,7 +13852,9 @@ function parseInlineBranch(ctx, startPos) {
12900
13852
  }
12901
13853
  const result = rule.parse(inlineCtx);
12902
13854
  if (result.success) {
12903
- elements.push(...result.elements);
13855
+ stripAutomaticLineBreak(elements, result.stripLeadingLineBreak);
13856
+ for (const element of result.elements)
13857
+ elements.push(element);
12904
13858
  consumed += result.consumed;
12905
13859
  pos += result.consumed;
12906
13860
  matched = true;
@@ -13223,6 +14177,7 @@ var textRule = {
13223
14177
 
13224
14178
  // packages/parser/src/parser/rules/inline/index.ts
13225
14179
  var inlineRules = [
14180
+ emailRule,
13226
14181
  boldRule,
13227
14182
  italicRule,
13228
14183
  underlineRule,
@@ -13234,6 +14189,7 @@ var inlineRules = [
13234
14189
  linkSingleRule,
13235
14190
  linkAnchorRule,
13236
14191
  linkStarRule,
14192
+ autolinkRule,
13237
14193
  colorRule,
13238
14194
  backslashLineBreakRule,
13239
14195
  underscoreLineBreakRule,
@@ -13243,6 +14199,9 @@ var inlineRules = [
13243
14199
  rawRule,
13244
14200
  imageRule,
13245
14201
  embedBlockRule,
14202
+ buttonRule,
14203
+ socialRule,
14204
+ dateRule,
13246
14205
  sizeRule,
13247
14206
  footnoteRule,
13248
14207
  spanRule,
@@ -13498,6 +14457,8 @@ function mergeSpanStripParagraphs(children) {
13498
14457
  i++;
13499
14458
  continue;
13500
14459
  }
14460
+ const firstMeaningful = paraData.elements.find((child) => child.element !== "line-break" && !isWhitespaceText(child));
14461
+ const keepParagraph = firstMeaningful && !isSpanStripMarker(firstMeaningful);
13501
14462
  const mergedChildren = [...paraData.elements];
13502
14463
  i++;
13503
14464
  while (i < expandedChildren.length) {
@@ -13510,7 +14471,8 @@ function mergeSpanStripParagraphs(children) {
13510
14471
  break;
13511
14472
  }
13512
14473
  const hasSpanStrip = hasParagraphStripSpan(nextPara);
13513
- mergedChildren.push(...nextParaData.elements);
14474
+ for (const element of nextParaData.elements)
14475
+ mergedChildren.push(element);
13514
14476
  i++;
13515
14477
  if (!hasSpanStrip) {
13516
14478
  const peekNext = expandedChildren[i];
@@ -13521,7 +14483,7 @@ function mergeSpanStripParagraphs(children) {
13521
14483
  }
13522
14484
  const escapedSpans = extractEscapedSpans(mergedChildren);
13523
14485
  removeLineBreaksAroundSpanStrip(mergedChildren);
13524
- if (escapedSpans.length > 0) {
14486
+ if (escapedSpans.length > 0 || keepParagraph) {
13525
14487
  if (mergedChildren.length > 0) {
13526
14488
  result.push(paragraphElement(mergedChildren));
13527
14489
  }
@@ -13742,50 +14704,6 @@ function cleanInternalFlags(elements) {
13742
14704
  }
13743
14705
  return removeEmptySpansAndAdjacentWhitespace(cleaned ?? elements);
13744
14706
  }
13745
- // packages/parser/src/parser/postprocess/divAdjacentParagraph.ts
13746
- function isParagraphContainer2(el) {
13747
- if (!el || el.element !== "container")
13748
- return false;
13749
- return el.data.type === "paragraph";
13750
- }
13751
- function isDivContainer(el) {
13752
- if (!el || el.element !== "container")
13753
- return false;
13754
- return el.data.type === "div";
13755
- }
13756
- function suppressAtLevel(elements) {
13757
- if (elements.length <= 1)
13758
- return elements;
13759
- const unwrap = Array.from({ length: elements.length }, () => false);
13760
- for (let i = 0;i < elements.length; i++) {
13761
- if (!isParagraphContainer2(elements[i]))
13762
- continue;
13763
- const prevIsDiv = i > 0 && isDivContainer(elements[i - 1]);
13764
- const nextIsDiv = i < elements.length - 1 && isDivContainer(elements[i + 1]);
13765
- if (prevIsDiv || nextIsDiv) {
13766
- unwrap[i] = true;
13767
- }
13768
- }
13769
- const result = [];
13770
- for (let i = 0;i < elements.length; i++) {
13771
- const el = elements[i];
13772
- if (!el)
13773
- continue;
13774
- if (unwrap[i] && el.element === "container") {
13775
- const inner = el.data.elements;
13776
- if (i > 0 && isDivContainer(elements[i - 1])) {
13777
- result.push({ element: "line-break" });
13778
- }
13779
- result.push(...inner);
13780
- } else {
13781
- result.push(el);
13782
- }
13783
- }
13784
- return result;
13785
- }
13786
- function suppressDivAdjacentParagraphs(elements) {
13787
- return suppressAtLevel(elements);
13788
- }
13789
14707
  // packages/parser/src/parser/parse/footnotes.ts
13790
14708
  function containsFootnoteBlock(elements) {
13791
14709
  let found = false;
@@ -14063,7 +14981,9 @@ function substitute(text2) {
14063
14981
  result = result.replace(WHITESPACE_ONLY_LINE, "");
14064
14982
  }
14065
14983
  if (result.indexOf("\\\n") !== -1) {
14066
- result = result.replace(CONCAT_LINES, String.fromCharCode(57344));
14984
+ const sentinels = makeUniqueSentinels(result);
14985
+ const { masked, placeholders } = maskRawRegions(result, sentinels);
14986
+ result = restorePlaceholders(masked.replace(CONCAT_LINES, String.fromCharCode(57344)), placeholders, sentinels);
14067
14987
  }
14068
14988
  if (result.indexOf("\t") !== -1) {
14069
14989
  result = result.replace(TABS, " ");
@@ -14130,14 +15050,26 @@ function replaceDelimitedTypography(text2, opener, closer, leftQuote, rightQuote
14130
15050
  let searchFrom = 0;
14131
15051
  let result = "";
14132
15052
  let lastCopied = 0;
15053
+ let closeIndex = -1;
15054
+ let newlineIndex = -1;
14133
15055
  while (searchFrom < text2.length) {
14134
15056
  const openIndex = text2.indexOf(opener, searchFrom);
14135
15057
  if (openIndex === -1)
14136
15058
  break;
14137
15059
  const contentStart = openIndex + opener.length;
14138
- const closeIndex = text2.indexOf(closer, contentStart);
15060
+ if (closeIndex < contentStart)
15061
+ closeIndex = text2.indexOf(closer, contentStart);
14139
15062
  if (closeIndex === -1)
14140
15063
  break;
15064
+ if (newlineIndex < contentStart) {
15065
+ const nextNewline = text2.indexOf(`
15066
+ `, contentStart);
15067
+ newlineIndex = nextNewline === -1 ? text2.length : nextNewline;
15068
+ }
15069
+ if (newlineIndex < closeIndex) {
15070
+ searchFrom = newlineIndex + 1;
15071
+ continue;
15072
+ }
14141
15073
  result += text2.slice(lastCopied, openIndex);
14142
15074
  result += leftQuote;
14143
15075
  result += text2.slice(contentStart, closeIndex);
@@ -14148,7 +15080,12 @@ function replaceDelimitedTypography(text2, opener, closer, leftQuote, rightQuote
14148
15080
  return lastCopied === 0 ? text2 : result + text2.slice(lastCopied);
14149
15081
  }
14150
15082
  function substitute2(text2) {
14151
- let result = text2;
15083
+ if (!text2.includes("`") && !text2.includes(",,") && !text2.includes("...") && !text2.includes(". . .")) {
15084
+ return text2;
15085
+ }
15086
+ const sentinels = makeUniqueSentinels(text2);
15087
+ const { masked, placeholders } = maskRawRegions(text2, sentinels);
15088
+ let result = masked;
14152
15089
  if (result.includes("``") && result.includes("''")) {
14153
15090
  result = replaceDelimitedTypography(result, "``", "''", LEFT_DOUBLE_QUOTE, RIGHT_DOUBLE_QUOTE);
14154
15091
  }
@@ -14164,7 +15101,7 @@ function substitute2(text2) {
14164
15101
  if (result.includes(". . .")) {
14165
15102
  result = replaceExactEllipsisPattern(result, ". . .");
14166
15103
  }
14167
- return result;
15104
+ return restorePlaceholders(result, placeholders, sentinels);
14168
15105
  }
14169
15106
 
14170
15107
  // packages/parser/src/parser/preprocess/index.ts