@fuzdev/mdz 0.3.0 → 0.5.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 (81) hide show
  1. package/LICENSE +10 -17
  2. package/README.md +6 -3
  3. package/dist/Mdz.svelte +8 -7
  4. package/dist/Mdz.svelte.d.ts.map +1 -1
  5. package/dist/MdzNodeView.svelte +74 -30
  6. package/dist/MdzNodeView.svelte.d.ts +1 -2
  7. package/dist/MdzNodeView.svelte.d.ts.map +1 -1
  8. package/dist/MdzPrecompiled.svelte +3 -3
  9. package/dist/MdzPrecompiled.svelte.d.ts.map +1 -1
  10. package/dist/MdzRoot.svelte +3 -3
  11. package/dist/MdzRoot.svelte.d.ts.map +1 -1
  12. package/dist/MdzStream.svelte +3 -3
  13. package/dist/MdzStream.svelte.d.ts.map +1 -1
  14. package/dist/MdzStreamNodeView.svelte +79 -31
  15. package/dist/MdzStreamNodeView.svelte.d.ts +1 -2
  16. package/dist/MdzStreamNodeView.svelte.d.ts.map +1 -1
  17. package/dist/context_helpers.js +1 -1
  18. package/dist/mdz.d.ts +12 -1
  19. package/dist/mdz.d.ts.map +1 -1
  20. package/dist/mdz.js +1 -1
  21. package/dist/mdz_contexts.d.ts +33 -6
  22. package/dist/mdz_contexts.d.ts.map +1 -1
  23. package/dist/mdz_debug_work.d.ts.map +1 -1
  24. package/dist/mdz_helpers.d.ts +31 -3
  25. package/dist/mdz_helpers.d.ts.map +1 -1
  26. package/dist/mdz_helpers.js +104 -12
  27. package/dist/mdz_lexer.d.ts +3 -1
  28. package/dist/mdz_lexer.d.ts.map +1 -1
  29. package/dist/mdz_lexer.js +115 -31
  30. package/dist/mdz_opcodes.d.ts +3 -1
  31. package/dist/mdz_opcodes.d.ts.map +1 -1
  32. package/dist/mdz_opcodes_to_nodes.d.ts.map +1 -1
  33. package/dist/mdz_opcodes_to_nodes.js +25 -17
  34. package/dist/mdz_stream_parser.d.ts.map +1 -1
  35. package/dist/mdz_stream_parser.js +8 -8
  36. package/dist/mdz_stream_parser_block.js +5 -5
  37. package/dist/mdz_stream_parser_inline.js +4 -4
  38. package/dist/mdz_stream_parser_link.d.ts.map +1 -1
  39. package/dist/mdz_stream_parser_link.js +83 -11
  40. package/dist/mdz_stream_parser_list.d.ts.map +1 -1
  41. package/dist/mdz_stream_parser_list.js +13 -7
  42. package/dist/mdz_stream_parser_state.d.ts.map +1 -1
  43. package/dist/mdz_stream_parser_state.js +7 -7
  44. package/dist/mdz_stream_parser_table.d.ts.map +1 -1
  45. package/dist/mdz_stream_parser_table.js +16 -9
  46. package/dist/mdz_stream_parser_text.js +2 -2
  47. package/dist/mdz_stream_parser_url.js +7 -7
  48. package/dist/mdz_stream_state.svelte.d.ts +3 -1
  49. package/dist/mdz_stream_state.svelte.d.ts.map +1 -1
  50. package/dist/mdz_stream_state.svelte.js +4 -0
  51. package/dist/mdz_to_svelte.d.ts +17 -4
  52. package/dist/mdz_to_svelte.d.ts.map +1 -1
  53. package/dist/mdz_to_svelte.js +22 -9
  54. package/dist/mdz_token_parser.d.ts.map +1 -1
  55. package/dist/mdz_token_parser.js +25 -11
  56. package/dist/svelte_preprocess_mdz.d.ts +6 -3
  57. package/dist/svelte_preprocess_mdz.d.ts.map +1 -1
  58. package/dist/svelte_preprocess_mdz.js +11 -11
  59. package/package.json +25 -40
  60. package/src/lib/context_helpers.ts +2 -2
  61. package/src/lib/mdz.ts +15 -3
  62. package/src/lib/mdz_contexts.ts +41 -11
  63. package/src/lib/mdz_debug_work.ts +1 -1
  64. package/src/lib/mdz_helpers.ts +164 -43
  65. package/src/lib/mdz_lexer.ts +173 -80
  66. package/src/lib/mdz_opcodes.ts +3 -1
  67. package/src/lib/mdz_opcodes_to_nodes.ts +40 -30
  68. package/src/lib/mdz_stream_parser.ts +17 -17
  69. package/src/lib/mdz_stream_parser_block.ts +14 -14
  70. package/src/lib/mdz_stream_parser_inline.ts +18 -18
  71. package/src/lib/mdz_stream_parser_link.ts +88 -18
  72. package/src/lib/mdz_stream_parser_list.ts +31 -25
  73. package/src/lib/mdz_stream_parser_state.ts +27 -27
  74. package/src/lib/mdz_stream_parser_table.ts +38 -31
  75. package/src/lib/mdz_stream_parser_text.ts +3 -3
  76. package/src/lib/mdz_stream_parser_url.ts +11 -11
  77. package/src/lib/mdz_stream_state.svelte.ts +11 -8
  78. package/src/lib/mdz_to_svelte.ts +71 -27
  79. package/src/lib/mdz_token_parser.ts +52 -38
  80. package/src/lib/svelte_preprocess_mdz.ts +66 -60
  81. package/src/lib/tsdoc_mdz.ts +1 -1
@@ -5,9 +5,13 @@
5
5
  * @module
6
6
  */
7
7
 
8
- import type {MdzNodeTypeContainer} from './mdz_opcodes.ts';
8
+ import type { MdzNodeTypeContainer } from './mdz_opcodes.ts';
9
+ import type { MdzAttribute } from './mdz.ts';
9
10
  import {
10
11
  A_UPPER,
12
+ APOSTROPHE,
13
+ DOUBLE_QUOTE,
14
+ EQUALS,
11
15
  LEFT_BRACKET,
12
16
  LEFT_PAREN,
13
17
  MAX_INLINE_NESTING_DEPTH,
@@ -21,7 +25,7 @@ import {
21
25
  is_tag_name_char,
22
26
  is_valid_path_char,
23
27
  mdz_is_safe_reference,
24
- mdz_is_url,
28
+ mdz_is_url
25
29
  } from './mdz_helpers.ts';
26
30
  import {
27
31
  type MdzStreamParserState,
@@ -36,10 +40,10 @@ import {
36
40
  offset,
37
41
  pop_stack_entry,
38
42
  push_stack_entry,
39
- revert_above,
43
+ revert_above
40
44
  } from './mdz_stream_parser_state.ts';
41
- import {consume_delimiter_as_text} from './mdz_stream_parser_text.ts';
42
- import {mdz_debug_work} from './mdz_debug_work.ts';
45
+ import { consume_delimiter_as_text } from './mdz_stream_parser_text.ts';
46
+ import { mdz_debug_work } from './mdz_debug_work.ts';
43
47
 
44
48
  // -- Links --
45
49
 
@@ -57,7 +61,7 @@ export const try_link_open = (state: MdzStreamParserState): boolean => {
57
61
 
58
62
  const id = alloc_id(state);
59
63
  const link_start = offset(state);
60
- emit(state, {type: 'open', id, node_type: 'Link', start: link_start});
64
+ emit(state, { type: 'open', id, node_type: 'Link', start: link_start });
61
65
  push_stack_entry(state, id, 'Link', link_start, '[');
62
66
  state.active_text_id = null;
63
67
  state.pos++;
@@ -76,12 +80,12 @@ export const try_link_open = (state: MdzStreamParserState): boolean => {
76
80
  export const abort_link_to_text = (
77
81
  state: MdzStreamParserState,
78
82
  link_stack_idx: number,
79
- bracket_pos: number,
83
+ bracket_pos: number
80
84
  ): void => {
81
85
  flush_text(state);
82
86
  revert_above(state, link_stack_idx);
83
87
  const entry = pop_stack_entry(state);
84
- emit(state, {type: 'revert', id: entry.id, replacement_text: '[', start: entry.start});
88
+ emit(state, { type: 'revert', id: entry.id, replacement_text: '[', start: entry.start });
85
89
  state.active_text_id = null;
86
90
  accumulate_text(state, ']', offset(state, bracket_pos));
87
91
  state.pos = bracket_pos + 1;
@@ -164,7 +168,7 @@ export const try_complete_link = (state: MdzStreamParserState, link_stack_idx: n
164
168
  id: entry.id,
165
169
  end: offset(state, i + 1),
166
170
  reference,
167
- link_type,
171
+ link_type
168
172
  });
169
173
  state.active_text_id = null;
170
174
  state.pos = i + 1;
@@ -211,11 +215,77 @@ export const try_tag_open = (state: MdzStreamParserState): TryResult => {
211
215
 
212
216
  const name = state.buffer.slice(name_start, i);
213
217
 
214
- // skip whitespace
215
- while (i < state.buffer.length && state.buffer.charCodeAt(i) === SPACE) {
216
- i++;
218
+ // Attribute loop: consume space-separated attributes until `/>` or `>`.
219
+ // This widens the held-tag-open rescan surface (the TODO above) with the
220
+ // attribute/value text — still line-bounded, since a newline anywhere aborts
221
+ // the whole tag (`not_match`). Every buffer-end point holds (`need_more`) so
222
+ // the same tree is produced regardless of where feeds split. The quoted-value
223
+ // terminator search reuses `buffer_index_of` (memoized, append-aware) so a
224
+ // long streamed value doesn't rescan quadratically across held retries,
225
+ // matching `try_complete_link`.
226
+ const attributes: Array<MdzAttribute> = [];
227
+ const seen = new Set<string>();
228
+ for (;;) {
229
+ // skip a run of literal spaces only (tabs/newlines are not SPACE)
230
+ let had_space = false;
231
+ while (i < state.buffer.length && state.buffer.charCodeAt(i) === SPACE) {
232
+ i++;
233
+ had_space = true;
234
+ }
235
+ if (i >= state.buffer.length) return 'need_more';
236
+
237
+ const c = state.buffer.charCodeAt(i);
238
+ // `/>` or `>` ends the open tag — break to the existing self-close / commit
239
+ if (c === SLASH || c === RIGHT_ANGLE) break;
240
+ // otherwise an attribute name must follow, space-separated from what
241
+ // precedes it (`<a x="1"y="2">` — `y` abuts the closing quote — reverts)
242
+ if (!is_letter(c)) return 'not_match';
243
+ if (!had_space) return 'not_match';
244
+
245
+ // collect the attribute name (same grammar as tag names)
246
+ const attr_start = i;
247
+ i++; // first char already passed is_letter
248
+ while (i < state.buffer.length && is_tag_name_char(state.buffer.charCodeAt(i))) {
249
+ i++;
250
+ }
251
+ if (i >= state.buffer.length) return 'need_more'; // mid-name — could still grow
252
+ const attr_name = state.buffer.slice(attr_start, i);
253
+ // duplicate names are ambiguous (Svelte compile-errors) → literal tag
254
+ if (seen.has(attr_name)) return 'not_match';
255
+ seen.add(attr_name);
256
+
257
+ let value: string | true = true; // bare name is boolean `true`
258
+ if (state.buffer.charCodeAt(i) === EQUALS) {
259
+ i++; // consume =
260
+ if (i >= state.buffer.length) return 'need_more'; // nothing after `=` yet
261
+ // the value must be immediately quoted — no space, no `{`, no unquoted
262
+ const quote_code = state.buffer.charCodeAt(i);
263
+ if (quote_code !== DOUBLE_QUOTE && quote_code !== APOSTROPHE) return 'not_match';
264
+ i++; // consume opening quote
265
+ const value_start = i;
266
+ // find the matching quote and the next newline via the memoized,
267
+ // append-aware search; a `>` inside the value is content, consumed
268
+ // atomically here so a `</Name>`-shaped substring never reaches
269
+ // `try_close_tag`
270
+ const quote_str = quote_code === DOUBLE_QUOTE ? '"' : "'";
271
+ const q = buffer_index_of(state, quote_str, value_start);
272
+ const n = buffer_index_of(state, '\n', value_start);
273
+ if (q !== -1 && (n === -1 || q < n)) {
274
+ value = state.buffer.slice(value_start, q);
275
+ i = q + 1; // past the closing quote
276
+ } else if (n !== -1 && (q === -1 || n < q)) {
277
+ return 'not_match'; // newline inside the value → literal tag
278
+ } else {
279
+ return 'need_more'; // neither found yet — unterminated so far, hold
280
+ }
281
+ }
282
+ attributes.push({
283
+ name: attr_name,
284
+ value,
285
+ start: offset(state, attr_start),
286
+ end: offset(state, i)
287
+ });
217
288
  }
218
- if (i >= state.buffer.length) return 'need_more';
219
289
 
220
290
  // check for self-closing />
221
291
  if (state.buffer.charCodeAt(i) === SLASH && i + 1 >= state.buffer.length) {
@@ -230,8 +300,8 @@ export const try_tag_open = (state: MdzStreamParserState): TryResult => {
230
300
  ensure_paragraph(state);
231
301
  const id = alloc_id(state);
232
302
  const tag_start = offset(state, start);
233
- emit(state, {type: 'open', id, node_type, start: tag_start, name});
234
- emit(state, {type: 'close', id, end: offset(state, i + 2)});
303
+ emit(state, { type: 'open', id, node_type, start: tag_start, name, attributes });
304
+ emit(state, { type: 'close', id, end: offset(state, i + 2) });
235
305
  state.active_text_id = null;
236
306
  state.pos = i + 2;
237
307
  state.column += i + 2 - start;
@@ -253,8 +323,8 @@ export const try_tag_open = (state: MdzStreamParserState): TryResult => {
253
323
  ensure_paragraph(state);
254
324
  const id = alloc_id(state);
255
325
  const tag_start = offset(state, start);
256
- const delimiter = state.buffer.slice(start, i); // e.g., "<Alert>"
257
- emit(state, {type: 'open', id, node_type, start: tag_start, name});
326
+ const delimiter = state.buffer.slice(start, i); // e.g., "<Alert>" or "<Alert x="1">"
327
+ emit(state, { type: 'open', id, node_type, start: tag_start, name, attributes });
258
328
  push_stack_entry(state, id, node_type, tag_start, delimiter, name);
259
329
  state.active_text_id = null;
260
330
  state.pos = i;
@@ -307,7 +377,7 @@ export const try_close_tag = (state: MdzStreamParserState): TryResult => {
307
377
  flush_text(state);
308
378
  revert_above(state, found_idx);
309
379
  const entry = pop_stack_entry(state);
310
- emit(state, {type: 'close', id: entry.id, end: offset(state, i)});
380
+ emit(state, { type: 'close', id: entry.id, end: offset(state, i) });
311
381
  state.active_text_id = null;
312
382
  state.column += i - state.pos;
313
383
  state.pos = i;
@@ -27,9 +27,9 @@ import {
27
27
  ZERO,
28
28
  is_line_whitespace,
29
29
  mdz_match_blockquote_prefix,
30
- mdz_blockquote_line_has_content,
30
+ mdz_blockquote_line_has_content
31
31
  } from './mdz_helpers.ts';
32
- import {match_table_in_item_head, open_table_in_item} from './mdz_stream_parser_table.ts';
32
+ import { match_table_in_item_head, open_table_in_item } from './mdz_stream_parser_table.ts';
33
33
  import {
34
34
  type MdzStreamParserState,
35
35
  type TryResult,
@@ -44,7 +44,7 @@ import {
44
44
  extend_accumulated_end,
45
45
  flush_text,
46
46
  offset,
47
- push_stack_entry,
47
+ push_stack_entry
48
48
  } from './mdz_stream_parser_state.ts';
49
49
 
50
50
  /**
@@ -76,7 +76,7 @@ export interface MdzStreamListMarker {
76
76
  const rest_is_blank = (
77
77
  state: MdzStreamParserState,
78
78
  from: number,
79
- forced: boolean,
79
+ forced: boolean
80
80
  ): boolean | 'pending' => {
81
81
  let i = from;
82
82
  while (i < state.buffer.length && is_line_whitespace(state.buffer.charCodeAt(i))) {
@@ -98,9 +98,9 @@ const rest_is_blank = (
98
98
  export const match_list_marker = (
99
99
  state: MdzStreamParserState,
100
100
  i: number,
101
- forced: boolean,
101
+ forced: boolean
102
102
  ): MdzStreamListMarker | 'pending' | null => {
103
- const {buffer} = state;
103
+ const { buffer } = state;
104
104
  const c = buffer.charCodeAt(i);
105
105
  if (c === HYPHEN) {
106
106
  const after = i + 1;
@@ -113,7 +113,7 @@ export const match_list_marker = (
113
113
  start: i,
114
114
  marker_end: after,
115
115
  content_start: after,
116
- empty: true,
116
+ empty: true
117
117
  };
118
118
  }
119
119
  // in bounds: `blank === false` means the scan found a char at or past `after`
@@ -121,7 +121,7 @@ export const match_list_marker = (
121
121
  const content_start = after + 1;
122
122
  const empty = rest_is_blank(state, content_start, forced);
123
123
  if (empty === 'pending') return 'pending';
124
- return {ordered: false, number: 0, start: i, marker_end: after, content_start, empty};
124
+ return { ordered: false, number: 0, start: i, marker_end: after, content_start, empty };
125
125
  }
126
126
  if (c >= ZERO && c <= NINE) {
127
127
  let j = i + 1;
@@ -142,7 +142,7 @@ export const match_list_marker = (
142
142
  start: i,
143
143
  marker_end: after,
144
144
  content_start: after,
145
- empty: true,
145
+ empty: true
146
146
  };
147
147
  }
148
148
  // in bounds: `blank === false` means the scan found a char at or past `after`
@@ -150,7 +150,7 @@ export const match_list_marker = (
150
150
  const content_start = after + 1;
151
151
  const empty = rest_is_blank(state, content_start, forced);
152
152
  if (empty === 'pending') return 'pending';
153
- return {ordered: true, number, start: i, marker_end: after, content_start, empty};
153
+ return { ordered: true, number, start: i, marker_end: after, content_start, empty };
154
154
  }
155
155
  return null;
156
156
  };
@@ -179,9 +179,9 @@ export interface MdzStreamFenceOpener {
179
179
  export const match_fence_opener = (
180
180
  state: MdzStreamParserState,
181
181
  i: number,
182
- forced: boolean,
182
+ forced: boolean
183
183
  ): MdzStreamFenceOpener | 'pending' | null => {
184
- const {buffer} = state;
184
+ const { buffer } = state;
185
185
  let j = i;
186
186
  let count = 0;
187
187
  while (j < buffer.length && buffer.charCodeAt(j) === BACKTICK) {
@@ -202,7 +202,7 @@ export const match_fence_opener = (
202
202
  }
203
203
  if (j >= buffer.length) return forced ? null : 'pending'; // the opener needs its newline
204
204
  if (buffer.charCodeAt(j) !== NEWLINE) return null;
205
- return {backtick_count: count, lang, content_start: j + 1};
205
+ return { backtick_count: count, lang, content_start: j + 1 };
206
206
  };
207
207
 
208
208
  /** Emit a `ListItem` open for `marker` and push its frame. */
@@ -210,9 +210,9 @@ const open_list_item = (state: MdzStreamParserState, marker: MdzStreamListMarker
210
210
  const id = alloc_id(state);
211
211
  const start = offset(state, marker.start);
212
212
  if (marker.ordered) {
213
- emit(state, {type: 'open', id, node_type: 'ListItem', start, number: marker.number});
213
+ emit(state, { type: 'open', id, node_type: 'ListItem', start, number: marker.number });
214
214
  } else {
215
- emit(state, {type: 'open', id, node_type: 'ListItem', start});
215
+ emit(state, { type: 'open', id, node_type: 'ListItem', start });
216
216
  }
217
217
  push_stack_entry(state, id, 'ListItem', start);
218
218
  if (marker.empty) {
@@ -226,7 +226,7 @@ const open_list_item = (state: MdzStreamParserState, marker: MdzStreamListMarker
226
226
  const open_list_level = (
227
227
  state: MdzStreamParserState,
228
228
  indent: number,
229
- marker: MdzStreamListMarker,
229
+ marker: MdzStreamListMarker
230
230
  ): void => {
231
231
  const id = alloc_id(state);
232
232
  const start = offset(state, marker.start);
@@ -237,14 +237,14 @@ const open_list_level = (
237
237
  node_type: 'List',
238
238
  start,
239
239
  ordered: true,
240
- start_number: marker.number,
240
+ start_number: marker.number
241
241
  });
242
242
  } else {
243
- emit(state, {type: 'open', id, node_type: 'List', start, ordered: false});
243
+ emit(state, { type: 'open', id, node_type: 'List', start, ordered: false });
244
244
  }
245
245
  push_stack_entry(state, id, 'List', start);
246
246
  open_list_item(state, marker);
247
- state.list_levels.push({indent, ordered: marker.ordered});
247
+ state.list_levels.push({ indent, ordered: marker.ordered });
248
248
  };
249
249
 
250
250
  /**
@@ -256,7 +256,7 @@ const open_list_level = (
256
256
  const begin_item_run = (
257
257
  state: MdzStreamParserState,
258
258
  marker: MdzStreamListMarker,
259
- line_start: number,
259
+ line_start: number
260
260
  ): void => {
261
261
  let i = marker.content_start;
262
262
  while (i < state.buffer.length && is_line_whitespace(state.buffer.charCodeAt(i))) {
@@ -289,7 +289,7 @@ const list_continuation_target = (state: MdzStreamParserState, indent: number):
289
289
  const continue_run = (
290
290
  state: MdzStreamParserState,
291
291
  nl_pos: number,
292
- content_pos: number,
292
+ content_pos: number
293
293
  ): boolean => {
294
294
  accumulate_text(state, '\n', offset(state, nl_pos));
295
295
  extend_accumulated_end(state, offset(state, content_pos));
@@ -352,9 +352,9 @@ export const try_list_start = (state: MdzStreamParserState, forced: boolean): Tr
352
352
  */
353
353
  export const process_list_line = (
354
354
  state: MdzStreamParserState,
355
- forced: boolean,
355
+ forced: boolean
356
356
  ): boolean | 'blockquote' => {
357
- const {buffer} = state;
357
+ const { buffer } = state;
358
358
  const nl_pos = state.pos;
359
359
  const line_start = nl_pos + 1;
360
360
  let j = line_start;
@@ -462,13 +462,19 @@ export const process_list_line = (
462
462
  }
463
463
  const id = alloc_id(state);
464
464
  const cb_start = offset(state, j);
465
- emit(state, {type: 'open', id, node_type: 'Codeblock', start: cb_start, lang: fence.lang});
465
+ emit(state, {
466
+ type: 'open',
467
+ id,
468
+ node_type: 'Codeblock',
469
+ start: cb_start,
470
+ lang: fence.lang
471
+ });
466
472
  state.codeblock = {
467
473
  id,
468
474
  backtick_count: fence.backtick_count,
469
475
  text_id: null,
470
476
  fence_indent: indent,
471
- start: cb_start,
477
+ start: cb_start
472
478
  };
473
479
  state.active_text_id = null;
474
480
  state.pos = fence.content_start;
@@ -8,10 +8,10 @@
8
8
  * @module
9
9
  */
10
10
 
11
- import type {MdzNodeTypeContainer, MdzNodeId, MdzOpcode} from './mdz_opcodes.ts';
12
- import type {MdzTableCellParser} from './mdz.ts';
13
- import {NEWLINE, has_non_whitespace, mdz_heading_id_from_text} from './mdz_helpers.ts';
14
- import {mdz_debug_work} from './mdz_debug_work.ts';
11
+ import type { MdzNodeTypeContainer, MdzNodeId, MdzOpcode } from './mdz_opcodes.ts';
12
+ import type { MdzTableCellParser } from './mdz.ts';
13
+ import { NEWLINE, has_non_whitespace, mdz_heading_id_from_text } from './mdz_helpers.ts';
14
+ import { mdz_debug_work } from './mdz_debug_work.ts';
15
15
 
16
16
  /**
17
17
  * Tri-state result for `try_*` parser handlers.
@@ -114,7 +114,7 @@ export interface MdzBlockquoteState {
114
114
  * offsets — one per quote line; the line's content and terminating
115
115
  * newline are contiguous in both coordinate spaces.
116
116
  */
117
- segments: Array<{inner_start: number; source_start: number}>;
117
+ segments: Array<{ inner_start: number; source_start: number }>;
118
118
  /** Total bytes fed to the inner document so far. */
119
119
  inner_len: number;
120
120
  /**
@@ -218,7 +218,7 @@ export interface MdzStreamParserState {
218
218
  * a dedent to that indent ends the table, returning control to the list).
219
219
  * `cell_parser` is the table's shared cell parser, rebound per row.
220
220
  */
221
- table: {id: MdzNodeId; item_indent: number | null; cell_parser: MdzTableCellParser} | null;
221
+ table: { id: MdzNodeId; item_indent: number | null; cell_parser: MdzTableCellParser } | null;
222
222
  /**
223
223
  * Whether the innermost open ListItem's first inline run is current.
224
224
  * Content then flows directly into the ListItem frame — the sync AST
@@ -343,7 +343,7 @@ export const create_state = (): MdzStreamParserState => ({
343
343
  last_empty_item_end: 0,
344
344
  base_offset: 0,
345
345
  search_memo: null,
346
- open_counts: {Bold: 0, Italic: 0, Strikethrough: 0, Link: 0},
346
+ open_counts: { Bold: 0, Italic: 0, Strikethrough: 0, Link: 0 },
347
347
  open_link_tag_depth: 0,
348
348
  open_tag_counts: null,
349
349
  in_heading: false,
@@ -357,7 +357,7 @@ export const create_state = (): MdzStreamParserState => ({
357
357
  paragraph_stack_idx: -1,
358
358
  last_text_id: null,
359
359
  last_text_ended_with_newline: false,
360
- last_text_was_singleton_newline: false,
360
+ last_text_was_singleton_newline: false
361
361
  });
362
362
 
363
363
  /** @nodocs */
@@ -377,7 +377,7 @@ export const push_stack_entry = (
377
377
  node_type: MdzNodeTypeContainer,
378
378
  start: number,
379
379
  delimiter: string = '',
380
- tag_name?: string,
380
+ tag_name?: string
381
381
  ): void => {
382
382
  state.stack.push({
383
383
  id,
@@ -386,7 +386,7 @@ export const push_stack_entry = (
386
386
  tag_name,
387
387
  has_children: false,
388
388
  has_non_whitespace_content: false,
389
- start,
389
+ start
390
390
  });
391
391
  if (node_type === 'Paragraph') {
392
392
  state.paragraph_stack_idx = state.stack.length - 1;
@@ -412,7 +412,7 @@ export const push_stack_entry = (
412
412
  const bump_open_count = (
413
413
  state: MdzStreamParserState,
414
414
  node_type: MdzNodeTypeContainer,
415
- delta: number,
415
+ delta: number
416
416
  ): void => {
417
417
  const counts = state.open_counts;
418
418
  if (node_type === 'Bold') counts.Bold += delta;
@@ -502,7 +502,7 @@ const memo_index_of = (
502
502
  state: MdzStreamParserState,
503
503
  memo: BufferSearchMemo,
504
504
  needle: string,
505
- from: number,
505
+ from: number
506
506
  ): number => {
507
507
  const global_from = state.base_offset + from;
508
508
  const global_end = state.base_offset + state.buffer.length;
@@ -548,13 +548,13 @@ const memo_index_of = (
548
548
  export const buffer_index_of = (
549
549
  state: MdzStreamParserState,
550
550
  needle: string,
551
- from: number,
551
+ from: number
552
552
  ): number => {
553
553
  const map = (state.search_memo ??= new Map());
554
554
  let memo = map.get(needle);
555
555
  if (memo === undefined) {
556
556
  // "scanned [0, 0), found nothing" — memo_index_of handles the full scan
557
- memo = {from: 0, result: -1, searched_to: 0};
557
+ memo = { from: 0, result: -1, searched_to: 0 };
558
558
  map.set(needle, memo);
559
559
  }
560
560
  return memo_index_of(state, memo, needle, from);
@@ -642,7 +642,7 @@ export const emit = (state: MdzStreamParserState, op: MdzOpcode): void => {
642
642
  export const accumulate_text = (
643
643
  state: MdzStreamParserState,
644
644
  text: string,
645
- start_offset: number,
645
+ start_offset: number
646
646
  ): void => {
647
647
  if (state.accumulated_text.length === 0) {
648
648
  state.accumulated_text_start = start_offset;
@@ -678,7 +678,7 @@ export const flush_text = (state: MdzStreamParserState): void => {
678
678
  type: 'append_text',
679
679
  id: state.active_text_id,
680
680
  content: state.accumulated_text,
681
- end: state.accumulated_text_end,
681
+ end: state.accumulated_text_end
682
682
  });
683
683
  } else {
684
684
  ensure_paragraph(state);
@@ -690,7 +690,7 @@ export const flush_text = (state: MdzStreamParserState): void => {
690
690
  content: state.accumulated_text,
691
691
  text_type: 'Text',
692
692
  start,
693
- end: state.accumulated_text_end,
693
+ end: state.accumulated_text_end
694
694
  });
695
695
  state.active_text_id = id;
696
696
  }
@@ -709,7 +709,7 @@ export const ensure_paragraph = (state: MdzStreamParserState): void => {
709
709
  if (state.in_heading || state.in_paragraph || state.in_list_item_run) return;
710
710
  const id = alloc_id(state);
711
711
  const start = offset(state);
712
- emit(state, {type: 'open', id, node_type: 'Paragraph', start});
712
+ emit(state, { type: 'open', id, node_type: 'Paragraph', start });
713
713
  push_stack_entry(state, id, 'Paragraph', start);
714
714
  state.in_paragraph = true;
715
715
  };
@@ -769,7 +769,7 @@ export const revert_above = (state: MdzStreamParserState, target_idx: number): v
769
769
  type: 'revert',
770
770
  id: entry.id,
771
771
  replacement_text: entry.delimiter,
772
- start: entry.start,
772
+ start: entry.start
773
773
  });
774
774
  }
775
775
  state.active_text_id = null;
@@ -820,7 +820,7 @@ export const revert_failed_close = (state: MdzStreamParserState, stack_idx: numb
820
820
  type: 'revert',
821
821
  id: entry.id,
822
822
  replacement_text: entry.delimiter,
823
- start: entry.start,
823
+ start: entry.start
824
824
  });
825
825
  // structural opcode seals the prior text run (mirrors `emit`)
826
826
  seal_text_run(state);
@@ -852,7 +852,7 @@ export const trim_trailing_newline = (state: MdzStreamParserState): void => {
852
852
  }
853
853
  if (state.last_text_id === null || !state.last_text_ended_with_newline) return;
854
854
  // emit trim opcode; consumer adjusts content and removes empty nodes
855
- emit(state, {type: 'trim_text', id: state.last_text_id, count: 1});
855
+ emit(state, { type: 'trim_text', id: state.last_text_id, count: 1 });
856
856
  // if this trim empties a singleton text node, clear active_text_id so
857
857
  // subsequent content doesn't merge into a deleted node via append_text
858
858
  if (state.last_text_was_singleton_newline) {
@@ -879,8 +879,8 @@ export const close_paragraph = (state: MdzStreamParserState): void => {
879
879
  emit(
880
880
  state,
881
881
  discard
882
- ? {type: 'close', id: entry.id, end: offset(state), discard: true}
883
- : {type: 'close', id: entry.id, end: offset(state)},
882
+ ? { type: 'close', id: entry.id, end: offset(state), discard: true }
883
+ : { type: 'close', id: entry.id, end: offset(state) }
884
884
  );
885
885
  state.active_text_id = null;
886
886
  state.in_paragraph = false;
@@ -913,7 +913,7 @@ export const close_heading = (state: MdzStreamParserState): void => {
913
913
  const heading_id = mdz_heading_id_from_text(heading_text);
914
914
  // the heading is now the top of the stack
915
915
  const entry = pop_stack_entry(state);
916
- emit(state, {type: 'close', id: entry.id, end: offset(state), heading_id});
916
+ emit(state, { type: 'close', id: entry.id, end: offset(state), heading_id });
917
917
  state.active_text_id = null;
918
918
  };
919
919
 
@@ -933,7 +933,7 @@ export const close_codeblock_at_eof = (state: MdzStreamParserState): void => {
933
933
  if (state.codeblock.fence_indent === 0) {
934
934
  trim_trailing_newline(state);
935
935
  }
936
- emit(state, {type: 'close', id: state.codeblock.id, end: offset(state)});
936
+ emit(state, { type: 'close', id: state.codeblock.id, end: offset(state) });
937
937
  state.codeblock = null;
938
938
  };
939
939
 
@@ -987,7 +987,7 @@ export const close_list_item = (state: MdzStreamParserState): void => {
987
987
  emit(state, {
988
988
  type: 'close',
989
989
  id: entry.id,
990
- end: entry.has_children ? offset(state) : state.last_empty_item_end,
990
+ end: entry.has_children ? offset(state) : state.last_empty_item_end
991
991
  });
992
992
  state.active_text_id = null;
993
993
  };
@@ -1000,7 +1000,7 @@ export const close_list_item = (state: MdzStreamParserState): void => {
1000
1000
  export const close_list_level = (state: MdzStreamParserState): void => {
1001
1001
  close_list_item(state);
1002
1002
  const entry = pop_stack_entry(state);
1003
- emit(state, {type: 'close', id: entry.id, end: offset(state)});
1003
+ emit(state, { type: 'close', id: entry.id, end: offset(state) });
1004
1004
  state.list_levels.pop();
1005
1005
  };
1006
1006