@fuzdev/mdz 0.2.0 → 0.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.
- package/README.md +4 -0
- package/dist/Mdz.svelte +18 -9
- package/dist/Mdz.svelte.d.ts +9 -2
- package/dist/Mdz.svelte.d.ts.map +1 -1
- package/dist/MdzNodeView.svelte +129 -115
- package/dist/MdzNodeView.svelte.d.ts +4 -1
- package/dist/MdzNodeView.svelte.d.ts.map +1 -1
- package/dist/MdzPrecompiled.svelte +3 -3
- package/dist/MdzPrecompiled.svelte.d.ts.map +1 -1
- package/dist/MdzRoot.svelte +3 -3
- package/dist/MdzRoot.svelte.d.ts.map +1 -1
- package/dist/MdzStream.svelte +6 -6
- package/dist/MdzStream.svelte.d.ts.map +1 -1
- package/dist/MdzStreamNodeView.svelte +129 -118
- package/dist/MdzStreamNodeView.svelte.d.ts +4 -1
- package/dist/MdzStreamNodeView.svelte.d.ts.map +1 -1
- package/dist/context_helpers.js +1 -1
- package/dist/mdz.d.ts +6 -0
- package/dist/mdz.d.ts.map +1 -1
- package/dist/mdz.js +8 -0
- package/dist/mdz_contexts.d.ts.map +1 -1
- package/dist/mdz_debug_work.d.ts +38 -0
- package/dist/mdz_debug_work.d.ts.map +1 -0
- package/dist/mdz_debug_work.js +37 -0
- package/dist/mdz_helpers.d.ts +61 -0
- package/dist/mdz_helpers.d.ts.map +1 -1
- package/dist/mdz_helpers.js +74 -2
- package/dist/mdz_lexer.d.ts +12 -0
- package/dist/mdz_lexer.d.ts.map +1 -1
- package/dist/mdz_lexer.js +190 -40
- package/dist/mdz_opcodes.d.ts.map +1 -1
- package/dist/mdz_opcodes_to_nodes.d.ts.map +1 -1
- package/dist/mdz_opcodes_to_nodes.js +22 -17
- package/dist/mdz_stream_parser.d.ts.map +1 -1
- package/dist/mdz_stream_parser.js +8 -8
- package/dist/mdz_stream_parser_block.js +5 -5
- package/dist/mdz_stream_parser_inline.d.ts.map +1 -1
- package/dist/mdz_stream_parser_inline.js +22 -20
- package/dist/mdz_stream_parser_link.d.ts.map +1 -1
- package/dist/mdz_stream_parser_link.js +88 -56
- package/dist/mdz_stream_parser_list.d.ts.map +1 -1
- package/dist/mdz_stream_parser_list.js +23 -9
- package/dist/mdz_stream_parser_state.d.ts +29 -0
- package/dist/mdz_stream_parser_state.d.ts.map +1 -1
- package/dist/mdz_stream_parser_state.js +59 -10
- package/dist/mdz_stream_parser_table.d.ts +3 -2
- package/dist/mdz_stream_parser_table.d.ts.map +1 -1
- package/dist/mdz_stream_parser_table.js +24 -13
- package/dist/mdz_stream_parser_text.d.ts.map +1 -1
- package/dist/mdz_stream_parser_text.js +36 -25
- package/dist/mdz_stream_parser_url.js +7 -7
- package/dist/mdz_stream_state.svelte.d.ts.map +1 -1
- package/dist/mdz_to_svelte.d.ts +1 -1
- package/dist/mdz_to_svelte.d.ts.map +1 -1
- package/dist/mdz_to_svelte.js +28 -31
- package/dist/mdz_token_parser.d.ts.map +1 -1
- package/dist/mdz_token_parser.js +24 -25
- package/dist/svelte_preprocess_mdz.d.ts +2 -2
- package/dist/svelte_preprocess_mdz.d.ts.map +1 -1
- package/dist/svelte_preprocess_mdz.js +41 -47
- package/package.json +28 -40
- package/src/lib/context_helpers.ts +2 -2
- package/src/lib/mdz.ts +11 -2
- package/src/lib/mdz_contexts.ts +5 -5
- package/src/lib/mdz_debug_work.ts +38 -0
- package/src/lib/mdz_helpers.ts +126 -23
- package/src/lib/mdz_lexer.ts +243 -93
- package/src/lib/mdz_opcodes.ts +1 -1
- package/src/lib/mdz_opcodes_to_nodes.ts +35 -30
- package/src/lib/mdz_stream_parser.ts +17 -17
- package/src/lib/mdz_stream_parser_block.ts +14 -14
- package/src/lib/mdz_stream_parser_inline.ts +38 -34
- package/src/lib/mdz_stream_parser_link.ts +99 -66
- package/src/lib/mdz_stream_parser_list.ts +41 -29
- package/src/lib/mdz_stream_parser_state.ts +100 -28
- package/src/lib/mdz_stream_parser_table.ts +47 -36
- package/src/lib/mdz_stream_parser_text.ts +40 -39
- package/src/lib/mdz_stream_parser_url.ts +11 -11
- package/src/lib/mdz_stream_state.svelte.ts +8 -8
- package/src/lib/mdz_to_svelte.ts +38 -46
- package/src/lib/mdz_token_parser.ts +55 -58
- package/src/lib/svelte_preprocess_mdz.ts +92 -89
- package/src/lib/tsdoc_mdz.ts +1 -1
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type {MdzNodeTypeContainer} from './mdz_opcodes.ts';
|
|
8
|
+
import type { MdzNodeTypeContainer } from './mdz_opcodes.ts';
|
|
9
9
|
import {
|
|
10
10
|
A_UPPER,
|
|
11
11
|
LEFT_BRACKET,
|
|
12
12
|
LEFT_PAREN,
|
|
13
|
-
|
|
13
|
+
MAX_INLINE_NESTING_DEPTH,
|
|
14
14
|
RIGHT_ANGLE,
|
|
15
15
|
RIGHT_BRACKET,
|
|
16
16
|
RIGHT_PAREN,
|
|
@@ -21,13 +21,14 @@ import {
|
|
|
21
21
|
is_tag_name_char,
|
|
22
22
|
is_valid_path_char,
|
|
23
23
|
mdz_is_safe_reference,
|
|
24
|
-
mdz_is_url
|
|
24
|
+
mdz_is_url
|
|
25
25
|
} from './mdz_helpers.ts';
|
|
26
26
|
import {
|
|
27
27
|
type MdzStreamParserState,
|
|
28
28
|
type TryResult,
|
|
29
29
|
accumulate_text,
|
|
30
30
|
alloc_id,
|
|
31
|
+
buffer_index_of,
|
|
31
32
|
emit,
|
|
32
33
|
ensure_paragraph,
|
|
33
34
|
flush_text,
|
|
@@ -35,19 +36,28 @@ import {
|
|
|
35
36
|
offset,
|
|
36
37
|
pop_stack_entry,
|
|
37
38
|
push_stack_entry,
|
|
38
|
-
revert_above
|
|
39
|
+
revert_above
|
|
39
40
|
} 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';
|
|
40
43
|
|
|
41
44
|
// -- Links --
|
|
42
45
|
|
|
43
46
|
/** @nodocs */
|
|
44
47
|
export const try_link_open = (state: MdzStreamParserState): boolean => {
|
|
48
|
+
// nesting-depth cap: too deep to open — render `[` literal, matching the
|
|
49
|
+
// sync lexer's `#tokenize_markdown_link` cap (keeps the two parsers agreeing
|
|
50
|
+
// on where deep `[` stops nesting)
|
|
51
|
+
if (state.open_link_tag_depth >= MAX_INLINE_NESTING_DEPTH) {
|
|
52
|
+
consume_delimiter_as_text(state, '[');
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
45
55
|
flush_text(state);
|
|
46
56
|
ensure_paragraph(state);
|
|
47
57
|
|
|
48
58
|
const id = alloc_id(state);
|
|
49
59
|
const link_start = offset(state);
|
|
50
|
-
emit(state, {type: 'open', id, node_type: 'Link', start: link_start});
|
|
60
|
+
emit(state, { type: 'open', id, node_type: 'Link', start: link_start });
|
|
51
61
|
push_stack_entry(state, id, 'Link', link_start, '[');
|
|
52
62
|
state.active_text_id = null;
|
|
53
63
|
state.pos++;
|
|
@@ -66,12 +76,12 @@ export const try_link_open = (state: MdzStreamParserState): boolean => {
|
|
|
66
76
|
export const abort_link_to_text = (
|
|
67
77
|
state: MdzStreamParserState,
|
|
68
78
|
link_stack_idx: number,
|
|
69
|
-
bracket_pos: number
|
|
79
|
+
bracket_pos: number
|
|
70
80
|
): void => {
|
|
71
81
|
flush_text(state);
|
|
72
82
|
revert_above(state, link_stack_idx);
|
|
73
83
|
const entry = pop_stack_entry(state);
|
|
74
|
-
emit(state, {type: 'revert', id: entry.id, replacement_text: '[', start: entry.start});
|
|
84
|
+
emit(state, { type: 'revert', id: entry.id, replacement_text: '[', start: entry.start });
|
|
75
85
|
state.active_text_id = null;
|
|
76
86
|
accumulate_text(state, ']', offset(state, bracket_pos));
|
|
77
87
|
state.pos = bracket_pos + 1;
|
|
@@ -97,65 +107,69 @@ export const try_complete_link = (state: MdzStreamParserState, link_stack_idx: n
|
|
|
97
107
|
return true;
|
|
98
108
|
}
|
|
99
109
|
|
|
100
|
-
// found ]( —
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
end: offset(state, i + 1),
|
|
138
|
-
reference,
|
|
139
|
-
link_type,
|
|
140
|
-
});
|
|
141
|
-
state.active_text_id = null;
|
|
142
|
-
state.pos = i + 1;
|
|
143
|
-
state.column += i + 1 - bracket_pos;
|
|
144
|
-
state.prev_char = RIGHT_PAREN;
|
|
110
|
+
// found ]( — find the reference's terminator: `)` completes, a space or
|
|
111
|
+
// newline aborts. Memoized searches (see `buffer_index_of`) — a link whose
|
|
112
|
+
// URL arrives incrementally re-enters here on every feed with `pos` still
|
|
113
|
+
// at the `]`, and a raw scan would re-cover the growing reference each
|
|
114
|
+
// time, going quadratic on long streamed URLs.
|
|
115
|
+
const ref_start = bracket_pos + 2;
|
|
116
|
+
const close_pos = buffer_index_of(state, ')', ref_start);
|
|
117
|
+
const space_pos = buffer_index_of(state, ' ', ref_start);
|
|
118
|
+
const newline_pos = buffer_index_of(state, '\n', ref_start);
|
|
119
|
+
let invalid_pos = space_pos;
|
|
120
|
+
if (newline_pos !== -1 && (invalid_pos === -1 || newline_pos < invalid_pos)) {
|
|
121
|
+
invalid_pos = newline_pos;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (close_pos === -1) {
|
|
125
|
+
// didn't find ) — need more input or invalid
|
|
126
|
+
if (invalid_pos === -1) return false; // need more input
|
|
127
|
+
// found invalid char before any ) — revert
|
|
128
|
+
abort_link_to_text(state, link_stack_idx, bracket_pos);
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
if (invalid_pos !== -1 && invalid_pos < close_pos) {
|
|
132
|
+
// invalid character in URL (simplified check)
|
|
133
|
+
abort_link_to_text(state, link_stack_idx, bracket_pos);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const i = close_pos;
|
|
138
|
+
const reference = state.buffer.slice(ref_start, i);
|
|
139
|
+
// validate reference: non-empty and only valid path chars
|
|
140
|
+
if (!reference.trim()) {
|
|
141
|
+
abort_link_to_text(state, link_stack_idx, bracket_pos);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
for (let j = 0; j < reference.length; j++) {
|
|
145
|
+
if (!is_valid_path_char(reference.charCodeAt(j))) {
|
|
146
|
+
abort_link_to_text(state, link_stack_idx, bracket_pos);
|
|
145
147
|
return true;
|
|
146
148
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
}
|
|
150
|
+
// Reject unsafe protocols (javascript:, data:, etc.) at parse time.
|
|
151
|
+
// `is_valid_path_char` permits `:` so we have to filter explicitly.
|
|
152
|
+
if (!mdz_is_safe_reference(reference)) {
|
|
153
|
+
abort_link_to_text(state, link_stack_idx, bracket_pos);
|
|
154
|
+
return true;
|
|
152
155
|
}
|
|
153
156
|
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
// success — close the link with reference
|
|
158
|
+
flush_text(state);
|
|
159
|
+
revert_above(state, link_stack_idx);
|
|
160
|
+
const entry = pop_stack_entry(state);
|
|
161
|
+
const link_type = mdz_is_url(reference) ? 'external' : 'internal';
|
|
162
|
+
emit(state, {
|
|
163
|
+
type: 'close',
|
|
164
|
+
id: entry.id,
|
|
165
|
+
end: offset(state, i + 1),
|
|
166
|
+
reference,
|
|
167
|
+
link_type
|
|
168
|
+
});
|
|
169
|
+
state.active_text_id = null;
|
|
170
|
+
state.pos = i + 1;
|
|
171
|
+
state.column += i + 1 - bracket_pos;
|
|
172
|
+
state.prev_char = RIGHT_PAREN;
|
|
159
173
|
return true;
|
|
160
174
|
};
|
|
161
175
|
|
|
@@ -176,7 +190,19 @@ export const try_tag_open = (state: MdzStreamParserState): TryResult => {
|
|
|
176
190
|
return 'not_match';
|
|
177
191
|
}
|
|
178
192
|
|
|
193
|
+
// nesting-depth cap: `<letter…` is a tag open, but we're too deep to nest —
|
|
194
|
+
// render `<` literal, matching the sync lexer's `#tokenize_tag` cap. Checked
|
|
195
|
+
// after the closer/hold cases above (a `</…>` closer still closes; a `<` at
|
|
196
|
+
// the buffer end still holds), so only genuine opens are suppressed.
|
|
197
|
+
if (state.open_link_tag_depth >= MAX_INLINE_NESTING_DEPTH) {
|
|
198
|
+
consume_delimiter_as_text(state, '<');
|
|
199
|
+
return 'consumed';
|
|
200
|
+
}
|
|
201
|
+
|
|
179
202
|
// collect tag name
|
|
203
|
+
// TODO the name/whitespace scans rescan from `start` on every held retry
|
|
204
|
+
// of an unclosed tag's open tail — needs a hold watermark; a length cap would
|
|
205
|
+
// diverge from the sync parser on pathological multi-KB "tag names"
|
|
180
206
|
const name_start = i;
|
|
181
207
|
while (i < state.buffer.length && is_tag_name_char(state.buffer.charCodeAt(i))) {
|
|
182
208
|
i++;
|
|
@@ -204,8 +230,8 @@ export const try_tag_open = (state: MdzStreamParserState): TryResult => {
|
|
|
204
230
|
ensure_paragraph(state);
|
|
205
231
|
const id = alloc_id(state);
|
|
206
232
|
const tag_start = offset(state, start);
|
|
207
|
-
emit(state, {type: 'open', id, node_type, start: tag_start, name});
|
|
208
|
-
emit(state, {type: 'close', id, end: offset(state, i + 2)});
|
|
233
|
+
emit(state, { type: 'open', id, node_type, start: tag_start, name });
|
|
234
|
+
emit(state, { type: 'close', id, end: offset(state, i + 2) });
|
|
209
235
|
state.active_text_id = null;
|
|
210
236
|
state.pos = i + 2;
|
|
211
237
|
state.column += i + 2 - start;
|
|
@@ -228,7 +254,7 @@ export const try_tag_open = (state: MdzStreamParserState): TryResult => {
|
|
|
228
254
|
const id = alloc_id(state);
|
|
229
255
|
const tag_start = offset(state, start);
|
|
230
256
|
const delimiter = state.buffer.slice(start, i); // e.g., "<Alert>"
|
|
231
|
-
emit(state, {type: 'open', id, node_type, start: tag_start, name});
|
|
257
|
+
emit(state, { type: 'open', id, node_type, start: tag_start, name });
|
|
232
258
|
push_stack_entry(state, id, node_type, tag_start, delimiter, name);
|
|
233
259
|
state.active_text_id = null;
|
|
234
260
|
state.pos = i;
|
|
@@ -255,9 +281,16 @@ export const try_close_tag = (state: MdzStreamParserState): TryResult => {
|
|
|
255
281
|
const name = state.buffer.slice(name_start, i);
|
|
256
282
|
i++; // past >
|
|
257
283
|
|
|
284
|
+
// O(1) bail when no tag of this name is open — without it, every `</name>`
|
|
285
|
+
// candidate walks the whole inline stack, which grows one frame per leaked
|
|
286
|
+
// unclosed `<Tag>` and goes quadratic on tag-dense input (the same shape
|
|
287
|
+
// `open_counts` guards for the delimiter inlines)
|
|
288
|
+
if (!state.open_tag_counts?.get(name)) return 'not_match';
|
|
289
|
+
|
|
258
290
|
// find matching open tag on stack
|
|
259
291
|
let found_idx = -1;
|
|
260
292
|
for (let j = state.stack.length - 1; j >= 0; j--) {
|
|
293
|
+
if (mdz_debug_work.enabled) mdz_debug_work.total++; // frames walked (mismatched-tags guard)
|
|
261
294
|
const entry = state.stack[j]!;
|
|
262
295
|
if (
|
|
263
296
|
(entry.node_type === 'Element' || entry.node_type === 'Component') &&
|
|
@@ -274,7 +307,7 @@ export const try_close_tag = (state: MdzStreamParserState): TryResult => {
|
|
|
274
307
|
flush_text(state);
|
|
275
308
|
revert_above(state, found_idx);
|
|
276
309
|
const entry = pop_stack_entry(state);
|
|
277
|
-
emit(state, {type: 'close', id: entry.id, end: offset(state, i)});
|
|
310
|
+
emit(state, { type: 'close', id: entry.id, end: offset(state, i) });
|
|
278
311
|
state.active_text_id = null;
|
|
279
312
|
state.column += i - state.pos;
|
|
280
313
|
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;
|
|
@@ -418,10 +418,16 @@ export const process_list_line = (
|
|
|
418
418
|
|
|
419
419
|
if (marker !== null && marker.empty) {
|
|
420
420
|
// empty item: only at an indent exactly matching an open level of the
|
|
421
|
-
// same marker type — anything else degrades below
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
)
|
|
421
|
+
// same marker type — anything else degrades below (manual loop like
|
|
422
|
+
// `list_continuation_target` — no per-line closure allocation)
|
|
423
|
+
let level_idx = -1;
|
|
424
|
+
for (let li = 0; li < state.list_levels.length; li++) {
|
|
425
|
+
const l = state.list_levels[li]!;
|
|
426
|
+
if (l.indent === indent && l.ordered === marker.ordered) {
|
|
427
|
+
level_idx = li;
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
425
431
|
if (level_idx !== -1) {
|
|
426
432
|
close_list_run(state);
|
|
427
433
|
while (state.list_levels.length - 1 > level_idx) {
|
|
@@ -456,13 +462,19 @@ export const process_list_line = (
|
|
|
456
462
|
}
|
|
457
463
|
const id = alloc_id(state);
|
|
458
464
|
const cb_start = offset(state, j);
|
|
459
|
-
emit(state, {
|
|
465
|
+
emit(state, {
|
|
466
|
+
type: 'open',
|
|
467
|
+
id,
|
|
468
|
+
node_type: 'Codeblock',
|
|
469
|
+
start: cb_start,
|
|
470
|
+
lang: fence.lang
|
|
471
|
+
});
|
|
460
472
|
state.codeblock = {
|
|
461
473
|
id,
|
|
462
474
|
backtick_count: fence.backtick_count,
|
|
463
475
|
text_id: null,
|
|
464
476
|
fence_indent: indent,
|
|
465
|
-
start: cb_start
|
|
477
|
+
start: cb_start
|
|
466
478
|
};
|
|
467
479
|
state.active_text_id = null;
|
|
468
480
|
state.pos = fence.content_start;
|