react-native-codepad-lang-markdown 0.0.0-stage → 0.1.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 (34) hide show
  1. package/CodepadLangMarkdown.podspec +28 -0
  2. package/README.md +50 -2
  3. package/android/CMakeLists.txt +19 -0
  4. package/android/build.gradle +61 -0
  5. package/android/consumer-rules.pro +2 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/assets/codepad-languages/markdown +1 -0
  8. package/android/src/main/assets/codepad-languages/markdown_inline +1 -0
  9. package/android/src/main/cpp/lang.c +17 -0
  10. package/android/src/main/java/me/sectiontn/codepad/lang/markdown/CodepadLangMarkdownPackage.kt +13 -0
  11. package/android/src/main/java/me/sectiontn/codepad/lang/markdown/MarkdownLanguage.kt +169 -0
  12. package/android/src/main/java/me/sectiontn/codepad/lang/markdown_inline/MarkdownInlineLanguage.kt +156 -0
  13. package/grammar/LICENSE +21 -0
  14. package/grammar/tree-sitter-markdown/src/parser.c +59787 -0
  15. package/grammar/tree-sitter-markdown/src/scanner.c +1602 -0
  16. package/grammar/tree-sitter-markdown/src/tree_sitter/alloc.h +54 -0
  17. package/grammar/tree-sitter-markdown/src/tree_sitter/array.h +330 -0
  18. package/grammar/tree-sitter-markdown/src/tree_sitter/parser.h +286 -0
  19. package/grammar/tree-sitter-markdown-inline/src/parser.c +75709 -0
  20. package/grammar/tree-sitter-markdown-inline/src/scanner.c +397 -0
  21. package/grammar/tree-sitter-markdown-inline/src/tree_sitter/alloc.h +54 -0
  22. package/grammar/tree-sitter-markdown-inline/src/tree_sitter/array.h +330 -0
  23. package/grammar/tree-sitter-markdown-inline/src/tree_sitter/parser.h +286 -0
  24. package/ios/CPLangMarkdown.h +11 -0
  25. package/ios/CPLangMarkdown.swift +162 -0
  26. package/ios/CPLangMarkdownInline.h +11 -0
  27. package/ios/CPLangMarkdownInline.swift +149 -0
  28. package/package.json +40 -4
  29. package/queries/markdown/highlights.scm +123 -0
  30. package/queries/markdown/indents.scm +1 -0
  31. package/queries/markdown/injections.scm +25 -0
  32. package/queries/markdown_inline/highlights.scm +128 -0
  33. package/queries/markdown_inline/injections.scm +7 -0
  34. package/src/index.ts +2 -0
@@ -0,0 +1,1602 @@
1
+ #include "tree_sitter/parser.h"
2
+ #include <assert.h>
3
+ #include <ctype.h>
4
+ #include <string.h>
5
+ #include <wchar.h>
6
+ #include <wctype.h>
7
+
8
+ // For explanation of the tokens see grammar.js
9
+ typedef enum {
10
+ LINE_ENDING,
11
+ SOFT_LINE_ENDING,
12
+ BLOCK_CLOSE,
13
+ BLOCK_CONTINUATION,
14
+ BLOCK_QUOTE_START,
15
+ INDENTED_CHUNK_START,
16
+ ATX_H1_MARKER,
17
+ ATX_H2_MARKER,
18
+ ATX_H3_MARKER,
19
+ ATX_H4_MARKER,
20
+ ATX_H5_MARKER,
21
+ ATX_H6_MARKER,
22
+ SETEXT_H1_UNDERLINE,
23
+ SETEXT_H2_UNDERLINE,
24
+ THEMATIC_BREAK,
25
+ LIST_MARKER_MINUS,
26
+ LIST_MARKER_PLUS,
27
+ LIST_MARKER_STAR,
28
+ LIST_MARKER_PARENTHESIS,
29
+ LIST_MARKER_DOT,
30
+ LIST_MARKER_MINUS_DONT_INTERRUPT,
31
+ LIST_MARKER_PLUS_DONT_INTERRUPT,
32
+ LIST_MARKER_STAR_DONT_INTERRUPT,
33
+ LIST_MARKER_PARENTHESIS_DONT_INTERRUPT,
34
+ LIST_MARKER_DOT_DONT_INTERRUPT,
35
+ FENCED_CODE_BLOCK_START_BACKTICK,
36
+ FENCED_CODE_BLOCK_START_TILDE,
37
+ BLANK_LINE_START,
38
+ FENCED_CODE_BLOCK_END_BACKTICK,
39
+ FENCED_CODE_BLOCK_END_TILDE,
40
+ HTML_BLOCK_1_START,
41
+ HTML_BLOCK_1_END,
42
+ HTML_BLOCK_2_START,
43
+ HTML_BLOCK_3_START,
44
+ HTML_BLOCK_4_START,
45
+ HTML_BLOCK_5_START,
46
+ HTML_BLOCK_6_START,
47
+ HTML_BLOCK_7_START,
48
+ CLOSE_BLOCK,
49
+ NO_INDENTED_CHUNK,
50
+ ERROR,
51
+ TRIGGER_ERROR,
52
+ TOKEN_EOF,
53
+ MINUS_METADATA,
54
+ PLUS_METADATA,
55
+ PIPE_TABLE_START,
56
+ PIPE_TABLE_LINE_ENDING,
57
+ } TokenType;
58
+
59
+ // Description of a block on the block stack.
60
+ //
61
+ // LIST_ITEM is a list item with minimal indentation (content begins at indent
62
+ // level 2) while LIST_ITEM_MAX_INDENTATION represents a list item with maximal
63
+ // indentation without being considered a indented code block.
64
+ //
65
+ // ANONYMOUS represents any block that whose close is not handled by the
66
+ // external s.
67
+ typedef enum {
68
+ BLOCK_QUOTE,
69
+ INDENTED_CODE_BLOCK,
70
+ LIST_ITEM,
71
+ LIST_ITEM_1_INDENTATION,
72
+ LIST_ITEM_2_INDENTATION,
73
+ LIST_ITEM_3_INDENTATION,
74
+ LIST_ITEM_4_INDENTATION,
75
+ LIST_ITEM_5_INDENTATION,
76
+ LIST_ITEM_6_INDENTATION,
77
+ LIST_ITEM_7_INDENTATION,
78
+ LIST_ITEM_8_INDENTATION,
79
+ LIST_ITEM_9_INDENTATION,
80
+ LIST_ITEM_10_INDENTATION,
81
+ LIST_ITEM_11_INDENTATION,
82
+ LIST_ITEM_12_INDENTATION,
83
+ LIST_ITEM_13_INDENTATION,
84
+ LIST_ITEM_14_INDENTATION,
85
+ LIST_ITEM_MAX_INDENTATION,
86
+ FENCED_CODE_BLOCK,
87
+ ANONYMOUS,
88
+ } Block;
89
+
90
+ // Determines if a character is punctuation as defined by the markdown spec.
91
+ static bool is_punctuation(char chr) {
92
+ return (chr >= '!' && chr <= '/') || (chr >= ':' && chr <= '@') ||
93
+ (chr >= '[' && chr <= '`') || (chr >= '{' && chr <= '~');
94
+ }
95
+
96
+ // Returns the indentation level which lines of a list item should have at
97
+ // minimum. Should only be called with blocks for which `is_list_item` returns
98
+ // true.
99
+ static uint8_t list_item_indentation(Block block) {
100
+ return (uint8_t)(block - LIST_ITEM + 2);
101
+ }
102
+
103
+ #define NUM_HTML_TAG_NAMES_RULE_1 3
104
+
105
+ static const char *const HTML_TAG_NAMES_RULE_1[NUM_HTML_TAG_NAMES_RULE_1] = {
106
+ "pre", "script", "style"};
107
+
108
+ #define NUM_HTML_TAG_NAMES_RULE_7 62
109
+
110
+ static const char *const HTML_TAG_NAMES_RULE_7[NUM_HTML_TAG_NAMES_RULE_7] = {
111
+ "address", "article", "aside", "base", "basefont", "blockquote",
112
+ "body", "caption", "center", "col", "colgroup", "dd",
113
+ "details", "dialog", "dir", "div", "dl", "dt",
114
+ "fieldset", "figcaption", "figure", "footer", "form", "frame",
115
+ "frameset", "h1", "h2", "h3", "h4", "h5",
116
+ "h6", "head", "header", "hr", "html", "iframe",
117
+ "legend", "li", "link", "main", "menu", "menuitem",
118
+ "nav", "noframes", "ol", "optgroup", "option", "p",
119
+ "param", "section", "source", "summary", "table", "tbody",
120
+ "td", "tfoot", "th", "thead", "title", "tr",
121
+ "track", "ul"};
122
+
123
+ // For explanation of the tokens see grammar.js
124
+ static const bool paragraph_interrupt_symbols[] = {
125
+ false, // LINE_ENDING,
126
+ false, // SOFT_LINE_ENDING,
127
+ false, // BLOCK_CLOSE,
128
+ false, // BLOCK_CONTINUATION,
129
+ true, // BLOCK_QUOTE_START,
130
+ false, // INDENTED_CHUNK_START,
131
+ true, // ATX_H1_MARKER,
132
+ true, // ATX_H2_MARKER,
133
+ true, // ATX_H3_MARKER,
134
+ true, // ATX_H4_MARKER,
135
+ true, // ATX_H5_MARKER,
136
+ true, // ATX_H6_MARKER,
137
+ true, // SETEXT_H1_UNDERLINE,
138
+ true, // SETEXT_H2_UNDERLINE,
139
+ true, // THEMATIC_BREAK,
140
+ true, // LIST_MARKER_MINUS,
141
+ true, // LIST_MARKER_PLUS,
142
+ true, // LIST_MARKER_STAR,
143
+ true, // LIST_MARKER_PARENTHESIS,
144
+ true, // LIST_MARKER_DOT,
145
+ false, // LIST_MARKER_MINUS_DONT_INTERRUPT,
146
+ false, // LIST_MARKER_PLUS_DONT_INTERRUPT,
147
+ false, // LIST_MARKER_STAR_DONT_INTERRUPT,
148
+ false, // LIST_MARKER_PARENTHESIS_DONT_INTERRUPT,
149
+ false, // LIST_MARKER_DOT_DONT_INTERRUPT,
150
+ true, // FENCED_CODE_BLOCK_START_BACKTICK,
151
+ true, // FENCED_CODE_BLOCK_START_TILDE,
152
+ true, // BLANK_LINE_START,
153
+ false, // FENCED_CODE_BLOCK_END_BACKTICK,
154
+ false, // FENCED_CODE_BLOCK_END_TILDE,
155
+ true, // HTML_BLOCK_1_START,
156
+ false, // HTML_BLOCK_1_END,
157
+ true, // HTML_BLOCK_2_START,
158
+ true, // HTML_BLOCK_3_START,
159
+ true, // HTML_BLOCK_4_START,
160
+ true, // HTML_BLOCK_5_START,
161
+ true, // HTML_BLOCK_6_START,
162
+ false, // HTML_BLOCK_7_START,
163
+ false, // CLOSE_BLOCK,
164
+ false, // NO_INDENTED_CHUNK,
165
+ false, // ERROR,
166
+ false, // TRIGGER_ERROR,
167
+ false, // EOF,
168
+ false, // MINUS_METADATA,
169
+ false, // PLUS_METADATA,
170
+ true, // PIPE_TABLE_START,
171
+ false, // PIPE_TABLE_LINE_ENDING,
172
+ };
173
+
174
+ // State bitflags used with `Scanner.state`
175
+
176
+ // Currently matching (at the beginning of a line)
177
+ static const uint8_t STATE_MATCHING = 0x1 << 0;
178
+ // Last line break was inside a paragraph
179
+ static const uint8_t STATE_WAS_SOFT_LINE_BREAK = 0x1 << 1;
180
+ // Block should be closed after next line break
181
+ static const uint8_t STATE_CLOSE_BLOCK = 0x1 << 4;
182
+
183
+ static size_t roundup_32(size_t x) {
184
+ x--;
185
+
186
+ x |= x >> 1;
187
+ x |= x >> 2;
188
+ x |= x >> 4;
189
+ x |= x >> 8;
190
+ x |= x >> 16;
191
+
192
+ x++;
193
+
194
+ return x;
195
+ }
196
+
197
+ typedef struct {
198
+ // A stack of open blocks in the current parse state
199
+ struct {
200
+ size_t size;
201
+ size_t capacity;
202
+ Block *items;
203
+ } open_blocks;
204
+
205
+ // Parser state flags
206
+ uint8_t state;
207
+ // Number of blocks that have been matched so far. Only changes during
208
+ // matching and is reset after every line ending
209
+ uint8_t matched;
210
+ // Consumed but "unused" indentation. Sometimes a tab needs to be "split" to
211
+ // be used in multiple tokens.
212
+ uint8_t indentation;
213
+ // The current column. Used to decide how many spaces a tab should equal
214
+ uint8_t column;
215
+ // The delimiter length of the currently open fenced code block
216
+ uint8_t fenced_code_block_delimiter_length;
217
+
218
+ bool simulate;
219
+ } Scanner;
220
+
221
+ static void push_block(Scanner *s, Block b) {
222
+ if (s->open_blocks.size == s->open_blocks.capacity) {
223
+ s->open_blocks.capacity =
224
+ s->open_blocks.capacity ? s->open_blocks.capacity << 1 : 8;
225
+ void *tmp = realloc(s->open_blocks.items,
226
+ sizeof(Block) * s->open_blocks.capacity);
227
+ assert(tmp != NULL);
228
+ s->open_blocks.items = tmp;
229
+ }
230
+
231
+ s->open_blocks.items[s->open_blocks.size++] = b;
232
+ }
233
+
234
+ static inline Block pop_block(Scanner *s) {
235
+ return s->open_blocks.items[--s->open_blocks.size];
236
+ }
237
+
238
+ // Write the whole state of a Scanner to a byte buffer
239
+ static unsigned serialize(Scanner *s, char *buffer) {
240
+ unsigned size = 0;
241
+ buffer[size++] = (char)s->state;
242
+ buffer[size++] = (char)s->matched;
243
+ buffer[size++] = (char)s->indentation;
244
+ buffer[size++] = (char)s->column;
245
+ buffer[size++] = (char)s->fenced_code_block_delimiter_length;
246
+ size_t blocks_count = s->open_blocks.size;
247
+ if (blocks_count > 0) {
248
+ memcpy(&buffer[size], s->open_blocks.items,
249
+ blocks_count * sizeof(Block));
250
+ size += blocks_count * sizeof(Block);
251
+ }
252
+ return size;
253
+ }
254
+
255
+ // Read the whole state of a Scanner from a byte buffer
256
+ // `serizalize` and `deserialize` should be fully symmetric.
257
+ static void deserialize(Scanner *s, const char *buffer, unsigned length) {
258
+ s->open_blocks.size = 0;
259
+ s->open_blocks.capacity = 0;
260
+ s->state = 0;
261
+ s->matched = 0;
262
+ s->indentation = 0;
263
+ s->column = 0;
264
+ s->fenced_code_block_delimiter_length = 0;
265
+ if (length > 0) {
266
+ size_t size = 0;
267
+ s->state = (uint8_t)buffer[size++];
268
+ s->matched = (uint8_t)buffer[size++];
269
+ s->indentation = (uint8_t)buffer[size++];
270
+ s->column = (uint8_t)buffer[size++];
271
+ s->fenced_code_block_delimiter_length = (uint8_t)buffer[size++];
272
+ size_t blocks_size = length - size;
273
+ if (blocks_size > 0) {
274
+ size_t blocks_count = blocks_size / sizeof(Block);
275
+
276
+ // ensure open blocks has enough room
277
+ if (s->open_blocks.capacity < blocks_count) {
278
+ size_t capacity = roundup_32(blocks_count);
279
+ void *tmp = realloc(s->open_blocks.items,
280
+ sizeof(Block) * capacity);
281
+ assert(tmp != NULL);
282
+ s->open_blocks.items = tmp;
283
+ s->open_blocks.capacity = capacity;
284
+ }
285
+ memcpy(s->open_blocks.items, &buffer[size], blocks_size);
286
+ s->open_blocks.size = blocks_count;
287
+ }
288
+ }
289
+ }
290
+
291
+ static void mark_end(Scanner *s, TSLexer *lexer) {
292
+ if (!s->simulate) {
293
+ lexer->mark_end(lexer);
294
+ }
295
+ }
296
+
297
+ // Convenience function to emit the error token. This is done to stop invalid
298
+ // parse branches. Specifically:
299
+ // 1. When encountering a newline after a line break that ended a paragraph, and
300
+ // no new block
301
+ // has been opened.
302
+ // 2. When encountering a new block after a soft line break.
303
+ // 3. When a `$._trigger_error` token is valid, which is used to stop parse
304
+ // branches through
305
+ // normal tree-sitter grammar rules.
306
+ //
307
+ // See also the `$._soft_line_break` and `$._paragraph_end_newline` tokens in
308
+ // grammar.js
309
+ static bool error(TSLexer *lexer) {
310
+ lexer->result_symbol = ERROR;
311
+ return true;
312
+ }
313
+
314
+ // Advance the lexer one character
315
+ // Also keeps track of the current column, counting tabs as spaces with tab stop
316
+ // 4 See https://github.github.com/gfm/#tabs
317
+ static size_t advance(Scanner *s, TSLexer *lexer) {
318
+ size_t size = 1;
319
+ if (lexer->lookahead == '\t') {
320
+ size = 4 - s->column;
321
+ s->column = 0;
322
+ } else {
323
+ s->column = (s->column + 1) % 4;
324
+ }
325
+ lexer->advance(lexer, false);
326
+ return size;
327
+ }
328
+
329
+ // Try to match the given block, i.e. consume all tokens that belong to the
330
+ // block. These are
331
+ // 1. indentation for list items and indented code blocks
332
+ // 2. '>' for block quotes
333
+ // Returns true if the block is matched and false otherwise
334
+ static bool match(Scanner *s, TSLexer *lexer, Block block) {
335
+ switch (block) {
336
+ case INDENTED_CODE_BLOCK:
337
+ while (s->indentation < 4) {
338
+ if (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
339
+ s->indentation += advance(s, lexer);
340
+ } else {
341
+ break;
342
+ }
343
+ }
344
+ if (s->indentation >= 4 && lexer->lookahead != '\n' &&
345
+ lexer->lookahead != '\r') {
346
+ s->indentation -= 4;
347
+ return true;
348
+ }
349
+ break;
350
+ case LIST_ITEM:
351
+ case LIST_ITEM_1_INDENTATION:
352
+ case LIST_ITEM_2_INDENTATION:
353
+ case LIST_ITEM_3_INDENTATION:
354
+ case LIST_ITEM_4_INDENTATION:
355
+ case LIST_ITEM_5_INDENTATION:
356
+ case LIST_ITEM_6_INDENTATION:
357
+ case LIST_ITEM_7_INDENTATION:
358
+ case LIST_ITEM_8_INDENTATION:
359
+ case LIST_ITEM_9_INDENTATION:
360
+ case LIST_ITEM_10_INDENTATION:
361
+ case LIST_ITEM_11_INDENTATION:
362
+ case LIST_ITEM_12_INDENTATION:
363
+ case LIST_ITEM_13_INDENTATION:
364
+ case LIST_ITEM_14_INDENTATION:
365
+ case LIST_ITEM_MAX_INDENTATION:
366
+ while (s->indentation < list_item_indentation(block)) {
367
+ if (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
368
+ s->indentation += advance(s, lexer);
369
+ } else {
370
+ break;
371
+ }
372
+ }
373
+ if (s->indentation >= list_item_indentation(block)) {
374
+ s->indentation -= list_item_indentation(block);
375
+ return true;
376
+ }
377
+ if (lexer->lookahead == '\n' || lexer->lookahead == '\r') {
378
+ s->indentation = 0;
379
+ return true;
380
+ }
381
+ break;
382
+ case BLOCK_QUOTE:
383
+ while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
384
+ s->indentation += advance(s, lexer);
385
+ }
386
+ if (lexer->lookahead == '>') {
387
+ advance(s, lexer);
388
+ s->indentation = 0;
389
+ if (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
390
+ s->indentation += advance(s, lexer) - 1;
391
+ }
392
+ return true;
393
+ }
394
+ break;
395
+ case FENCED_CODE_BLOCK:
396
+ case ANONYMOUS:
397
+ return true;
398
+ }
399
+ return false;
400
+ }
401
+
402
+ static bool parse_fenced_code_block(Scanner *s, const char delimiter,
403
+ TSLexer *lexer, const bool *valid_symbols) {
404
+ // count the number of backticks
405
+ uint8_t level = 0;
406
+ while (lexer->lookahead == delimiter) {
407
+ advance(s, lexer);
408
+ level++;
409
+ }
410
+ mark_end(s, lexer);
411
+ // If this is able to close a fenced code block then that is the only valid
412
+ // interpretation. It can only close a fenced code block if the number of
413
+ // backticks is at least the number of backticks of the opening delimiter.
414
+ // Also it cannot be indented more than 3 spaces.
415
+ if ((delimiter == '`' ? valid_symbols[FENCED_CODE_BLOCK_END_BACKTICK]
416
+ : valid_symbols[FENCED_CODE_BLOCK_END_TILDE]) &&
417
+ s->indentation < 4 && level >= s->fenced_code_block_delimiter_length) {
418
+ while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
419
+ advance(s, lexer);
420
+ }
421
+ if (lexer->lookahead == '\n' || lexer->lookahead == '\r') {
422
+ s->fenced_code_block_delimiter_length = 0;
423
+ lexer->result_symbol = delimiter == '`'
424
+ ? FENCED_CODE_BLOCK_END_BACKTICK
425
+ : FENCED_CODE_BLOCK_END_TILDE;
426
+ return true;
427
+ }
428
+ }
429
+ // If this could be the start of a fenced code block, check if the info
430
+ // string contains any backticks.
431
+ if ((delimiter == '`' ? valid_symbols[FENCED_CODE_BLOCK_START_BACKTICK]
432
+ : valid_symbols[FENCED_CODE_BLOCK_START_TILDE]) &&
433
+ level >= 3) {
434
+ bool info_string_has_backtick = false;
435
+ if (delimiter == '`') {
436
+ while (lexer->lookahead != '\n' && lexer->lookahead != '\r' &&
437
+ !lexer->eof(lexer)) {
438
+ if (lexer->lookahead == '`') {
439
+ info_string_has_backtick = true;
440
+ break;
441
+ }
442
+ advance(s, lexer);
443
+ }
444
+ }
445
+ // If it does not then choose to interpret this as the start of a fenced
446
+ // code block.
447
+ if (!info_string_has_backtick) {
448
+ lexer->result_symbol = delimiter == '`'
449
+ ? FENCED_CODE_BLOCK_START_BACKTICK
450
+ : FENCED_CODE_BLOCK_START_TILDE;
451
+ if (!s->simulate)
452
+ push_block(s, FENCED_CODE_BLOCK);
453
+ // Remember the length of the delimiter for later, since we need it
454
+ // to decide whether a sequence of backticks can close the block.
455
+ s->fenced_code_block_delimiter_length = level;
456
+ s->indentation = 0;
457
+ return true;
458
+ }
459
+ }
460
+ return false;
461
+ }
462
+
463
+ static bool parse_star(Scanner *s, TSLexer *lexer, const bool *valid_symbols) {
464
+ advance(s, lexer);
465
+ mark_end(s, lexer);
466
+ // Otherwise count the number of stars permitting whitespaces between them.
467
+ size_t star_count = 1;
468
+ // Also remember how many stars there are before the first whitespace...
469
+ // ...and how many spaces follow the first star.
470
+ uint8_t extra_indentation = 0;
471
+ for (;;) {
472
+ if (lexer->lookahead == '*') {
473
+ if (star_count == 1 && extra_indentation >= 1 &&
474
+ valid_symbols[LIST_MARKER_STAR]) {
475
+ // If we get to this point then the token has to be at least
476
+ // this long. We need to call `mark_end` here in case we decide
477
+ // later that this is a list item.
478
+ mark_end(s, lexer);
479
+ }
480
+ star_count++;
481
+ advance(s, lexer);
482
+ } else if (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
483
+ if (star_count == 1) {
484
+ extra_indentation += advance(s, lexer);
485
+ } else {
486
+ advance(s, lexer);
487
+ }
488
+ } else {
489
+ break;
490
+ }
491
+ }
492
+ bool line_end = lexer->lookahead == '\n' || lexer->lookahead == '\r';
493
+ bool dont_interrupt = false;
494
+ if (star_count == 1 && line_end) {
495
+ extra_indentation = 1;
496
+ // line is empty so don't interrupt paragraphs if this is a list marker
497
+ dont_interrupt = s->matched == s->open_blocks.size;
498
+ }
499
+ // If there were at least 3 stars then this could be a thematic break
500
+ bool thematic_break = star_count >= 3 && line_end;
501
+ // If there was a star and at least one space after that star then this
502
+ // could be a list marker.
503
+ bool list_marker_star = star_count >= 1 && extra_indentation >= 1;
504
+ if (valid_symbols[THEMATIC_BREAK] && thematic_break && s->indentation < 4) {
505
+ // If a thematic break is valid then it takes precedence
506
+ lexer->result_symbol = THEMATIC_BREAK;
507
+ mark_end(s, lexer);
508
+ s->indentation = 0;
509
+ return true;
510
+ }
511
+ if ((dont_interrupt ? valid_symbols[LIST_MARKER_STAR_DONT_INTERRUPT]
512
+ : valid_symbols[LIST_MARKER_STAR]) &&
513
+ list_marker_star) {
514
+ // List markers take precedence over emphasis markers
515
+ // If star_count > 1 then we already called mark_end at the right point.
516
+ // Otherwise the token should go until this point.
517
+ if (star_count == 1) {
518
+ mark_end(s, lexer);
519
+ }
520
+ // Not counting one space...
521
+ extra_indentation--;
522
+ // ... check if the list item begins with an indented code block
523
+ if (extra_indentation <= 3) {
524
+ // If not then calculate the indentation level of the list item
525
+ // content as indentation of list marker + indentation after list
526
+ // marker - 1
527
+ extra_indentation += s->indentation;
528
+ s->indentation = 0;
529
+ } else {
530
+ // Otherwise the indentation level is just the indentation of the
531
+ // list marker. We keep the indentation after the list marker for
532
+ // later blocks.
533
+ uint8_t temp = s->indentation;
534
+ s->indentation = extra_indentation;
535
+ extra_indentation = temp;
536
+ }
537
+ if (!s->simulate)
538
+ push_block(s, (Block)(LIST_ITEM + extra_indentation));
539
+ lexer->result_symbol =
540
+ dont_interrupt ? LIST_MARKER_STAR_DONT_INTERRUPT : LIST_MARKER_STAR;
541
+ return true;
542
+ }
543
+ return false;
544
+ }
545
+
546
+ static bool parse_thematic_break_underscore(Scanner *s, TSLexer *lexer,
547
+ const bool *valid_symbols) {
548
+ advance(s, lexer);
549
+ mark_end(s, lexer);
550
+ size_t underscore_count = 1;
551
+ for (;;) {
552
+ if (lexer->lookahead == '_') {
553
+ underscore_count++;
554
+ advance(s, lexer);
555
+ } else if (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
556
+ advance(s, lexer);
557
+ } else {
558
+ break;
559
+ }
560
+ }
561
+ bool line_end = lexer->lookahead == '\n' || lexer->lookahead == '\r';
562
+ if (underscore_count >= 3 && line_end && valid_symbols[THEMATIC_BREAK]) {
563
+ lexer->result_symbol = THEMATIC_BREAK;
564
+ mark_end(s, lexer);
565
+ s->indentation = 0;
566
+ return true;
567
+ }
568
+ return false;
569
+ }
570
+
571
+ static bool parse_block_quote(Scanner *s, TSLexer *lexer,
572
+ const bool *valid_symbols) {
573
+ if (valid_symbols[BLOCK_QUOTE_START]) {
574
+ advance(s, lexer);
575
+ s->indentation = 0;
576
+ if (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
577
+ s->indentation += advance(s, lexer) - 1;
578
+ }
579
+ lexer->result_symbol = BLOCK_QUOTE_START;
580
+ if (!s->simulate)
581
+ push_block(s, BLOCK_QUOTE);
582
+ return true;
583
+ }
584
+ return false;
585
+ }
586
+
587
+ static bool parse_atx_heading(Scanner *s, TSLexer *lexer,
588
+ const bool *valid_symbols) {
589
+ if (valid_symbols[ATX_H1_MARKER] && s->indentation <= 3) {
590
+ mark_end(s, lexer);
591
+ uint16_t level = 0;
592
+ while (lexer->lookahead == '#' && level <= 6) {
593
+ advance(s, lexer);
594
+ level++;
595
+ }
596
+ if (level <= 6 &&
597
+ (lexer->lookahead == ' ' || lexer->lookahead == '\t' ||
598
+ lexer->lookahead == '\n' || lexer->lookahead == '\r')) {
599
+ lexer->result_symbol = ATX_H1_MARKER + (level - 1);
600
+ s->indentation = 0;
601
+ mark_end(s, lexer);
602
+ return true;
603
+ }
604
+ }
605
+ return false;
606
+ }
607
+
608
+ static bool parse_setext_underline(Scanner *s, TSLexer *lexer,
609
+ const bool *valid_symbols) {
610
+ if (valid_symbols[SETEXT_H1_UNDERLINE] &&
611
+ s->matched == s->open_blocks.size) {
612
+ mark_end(s, lexer);
613
+ while (lexer->lookahead == '=') {
614
+ advance(s, lexer);
615
+ }
616
+ while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
617
+ advance(s, lexer);
618
+ }
619
+ if (lexer->lookahead == '\n' || lexer->lookahead == '\r') {
620
+ lexer->result_symbol = SETEXT_H1_UNDERLINE;
621
+ mark_end(s, lexer);
622
+ return true;
623
+ }
624
+ }
625
+ return false;
626
+ }
627
+
628
+ static bool parse_plus(Scanner *s, TSLexer *lexer, const bool *valid_symbols) {
629
+ if (s->indentation <= 3 &&
630
+ (valid_symbols[LIST_MARKER_PLUS] ||
631
+ valid_symbols[LIST_MARKER_PLUS_DONT_INTERRUPT] ||
632
+ valid_symbols[PLUS_METADATA])) {
633
+ advance(s, lexer);
634
+ if (valid_symbols[PLUS_METADATA] && lexer->lookahead == '+') {
635
+ advance(s, lexer);
636
+ if (lexer->lookahead != '+') {
637
+ return false;
638
+ }
639
+ advance(s, lexer);
640
+ while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
641
+ advance(s, lexer);
642
+ }
643
+ if (lexer->lookahead != '\n' && lexer->lookahead != '\r') {
644
+ return false;
645
+ }
646
+ for (;;) {
647
+ // advance over newline
648
+ if (lexer->lookahead == '\r') {
649
+ advance(s, lexer);
650
+ if (lexer->lookahead == '\n') {
651
+ advance(s, lexer);
652
+ }
653
+ } else {
654
+ advance(s, lexer);
655
+ }
656
+ // check for pluses
657
+ size_t plus_count = 0;
658
+ while (lexer->lookahead == '+') {
659
+ plus_count++;
660
+ advance(s, lexer);
661
+ }
662
+ if (plus_count == 3) {
663
+ // if exactly 3 check if next symbol (after eventual
664
+ // whitespace) is newline
665
+ while (lexer->lookahead == ' ' ||
666
+ lexer->lookahead == '\t') {
667
+ advance(s, lexer);
668
+ }
669
+ if (lexer->lookahead == '\r' || lexer->lookahead == '\n') {
670
+ // if so also consume newline
671
+ if (lexer->lookahead == '\r') {
672
+ advance(s, lexer);
673
+ if (lexer->lookahead == '\n') {
674
+ advance(s, lexer);
675
+ }
676
+ } else {
677
+ advance(s, lexer);
678
+ }
679
+ mark_end(s, lexer);
680
+ lexer->result_symbol = PLUS_METADATA;
681
+ return true;
682
+ }
683
+ }
684
+ // otherwise consume rest of line
685
+ while (lexer->lookahead != '\n' && lexer->lookahead != '\r' &&
686
+ !lexer->eof(lexer)) {
687
+ advance(s, lexer);
688
+ }
689
+ // if end of file is reached, then this is not metadata
690
+ if (lexer->eof(lexer)) {
691
+ break;
692
+ }
693
+ }
694
+ } else {
695
+ uint8_t extra_indentation = 0;
696
+ while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
697
+ extra_indentation += advance(s, lexer);
698
+ }
699
+ bool dont_interrupt = false;
700
+ if (lexer->lookahead == '\r' || lexer->lookahead == '\n') {
701
+ extra_indentation = 1;
702
+ dont_interrupt = true;
703
+ }
704
+ dont_interrupt =
705
+ dont_interrupt && s->matched == s->open_blocks.size;
706
+ if (extra_indentation >= 1 &&
707
+ (dont_interrupt ? valid_symbols[LIST_MARKER_PLUS_DONT_INTERRUPT]
708
+ : valid_symbols[LIST_MARKER_PLUS])) {
709
+ lexer->result_symbol = dont_interrupt
710
+ ? LIST_MARKER_PLUS_DONT_INTERRUPT
711
+ : LIST_MARKER_PLUS;
712
+ extra_indentation--;
713
+ if (extra_indentation <= 3) {
714
+ extra_indentation += s->indentation;
715
+ s->indentation = 0;
716
+ } else {
717
+ uint8_t temp = s->indentation;
718
+ s->indentation = extra_indentation;
719
+ extra_indentation = temp;
720
+ }
721
+ if (!s->simulate)
722
+ push_block(s, (Block)(LIST_ITEM + extra_indentation));
723
+ return true;
724
+ }
725
+ }
726
+ }
727
+ return false;
728
+ }
729
+
730
+ static bool parse_ordered_list_marker(Scanner *s, TSLexer *lexer,
731
+ const bool *valid_symbols) {
732
+ if (s->indentation <= 3 &&
733
+ (valid_symbols[LIST_MARKER_PARENTHESIS] ||
734
+ valid_symbols[LIST_MARKER_DOT] ||
735
+ valid_symbols[LIST_MARKER_PARENTHESIS_DONT_INTERRUPT] ||
736
+ valid_symbols[LIST_MARKER_DOT_DONT_INTERRUPT])) {
737
+ size_t digits = 1;
738
+ bool dont_interrupt = !isdigit(lexer->lookahead);
739
+ advance(s, lexer);
740
+ while (isdigit(lexer->lookahead)) {
741
+ dont_interrupt = true;
742
+ digits++;
743
+ advance(s, lexer);
744
+ }
745
+ if (digits >= 1 && digits <= 9) {
746
+ bool dot = false;
747
+ bool parenthesis = false;
748
+ if (lexer->lookahead == '.') {
749
+ advance(s, lexer);
750
+ dot = true;
751
+ } else if (lexer->lookahead == ')') {
752
+ advance(s, lexer);
753
+ parenthesis = true;
754
+ }
755
+ if (dot || parenthesis) {
756
+ uint8_t extra_indentation = 0;
757
+ while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
758
+ extra_indentation += advance(s, lexer);
759
+ }
760
+ bool line_end =
761
+ lexer->lookahead == '\n' || lexer->lookahead == '\r';
762
+ if (line_end) {
763
+ extra_indentation = 1;
764
+ dont_interrupt = true;
765
+ }
766
+ dont_interrupt =
767
+ dont_interrupt && s->matched == s->open_blocks.size;
768
+ if (extra_indentation >= 1 &&
769
+ (dot ? (dont_interrupt
770
+ ? valid_symbols[LIST_MARKER_DOT_DONT_INTERRUPT]
771
+ : valid_symbols[LIST_MARKER_DOT])
772
+ : (dont_interrupt
773
+ ? valid_symbols
774
+ [LIST_MARKER_PARENTHESIS_DONT_INTERRUPT]
775
+ : valid_symbols[LIST_MARKER_PARENTHESIS]))) {
776
+ lexer->result_symbol =
777
+ dot ? LIST_MARKER_DOT : LIST_MARKER_PARENTHESIS;
778
+ extra_indentation--;
779
+ if (extra_indentation <= 3) {
780
+ extra_indentation += s->indentation;
781
+ s->indentation = 0;
782
+ } else {
783
+ uint8_t temp = s->indentation;
784
+ s->indentation = extra_indentation;
785
+ extra_indentation = temp;
786
+ }
787
+ if (!s->simulate)
788
+ push_block(
789
+ s, (Block)(LIST_ITEM + extra_indentation + digits));
790
+ return true;
791
+ }
792
+ }
793
+ }
794
+ }
795
+ return false;
796
+ }
797
+
798
+ static bool parse_minus(Scanner *s, TSLexer *lexer, const bool *valid_symbols) {
799
+ if (s->indentation <= 3 &&
800
+ (valid_symbols[LIST_MARKER_MINUS] ||
801
+ valid_symbols[LIST_MARKER_MINUS_DONT_INTERRUPT] ||
802
+ valid_symbols[SETEXT_H2_UNDERLINE] || valid_symbols[THEMATIC_BREAK] ||
803
+ valid_symbols[MINUS_METADATA])) {
804
+ mark_end(s, lexer);
805
+ bool whitespace_after_minus = false;
806
+ bool minus_after_whitespace = false;
807
+ size_t minus_count = 0;
808
+ uint8_t extra_indentation = 0;
809
+
810
+ for (;;) {
811
+ if (lexer->lookahead == '-') {
812
+ if (minus_count == 1 && extra_indentation >= 1) {
813
+ mark_end(s, lexer);
814
+ }
815
+ minus_count++;
816
+ advance(s, lexer);
817
+ minus_after_whitespace = whitespace_after_minus;
818
+ } else if (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
819
+ if (minus_count == 1) {
820
+ extra_indentation += advance(s, lexer);
821
+ } else {
822
+ advance(s, lexer);
823
+ }
824
+ whitespace_after_minus = true;
825
+ } else {
826
+ break;
827
+ }
828
+ }
829
+ bool line_end = lexer->lookahead == '\n' || lexer->lookahead == '\r';
830
+ bool dont_interrupt = false;
831
+ if (minus_count == 1 && line_end) {
832
+ extra_indentation = 1;
833
+ dont_interrupt = true;
834
+ }
835
+ dont_interrupt = dont_interrupt && s->matched == s->open_blocks.size;
836
+ bool thematic_break = minus_count >= 3 && line_end;
837
+ bool underline =
838
+ minus_count >= 1 && !minus_after_whitespace && line_end &&
839
+ s->matched ==
840
+ s->open_blocks
841
+ .size; // setext heading can not break lazy continuation
842
+ bool list_marker_minus = minus_count >= 1 && extra_indentation >= 1;
843
+ bool success = false;
844
+ if (valid_symbols[SETEXT_H2_UNDERLINE] && underline) {
845
+ lexer->result_symbol = SETEXT_H2_UNDERLINE;
846
+ mark_end(s, lexer);
847
+ s->indentation = 0;
848
+ success = true;
849
+ } else if (valid_symbols[THEMATIC_BREAK] &&
850
+ thematic_break) { // underline is false if list_marker_minus
851
+ // is true
852
+ lexer->result_symbol = THEMATIC_BREAK;
853
+ mark_end(s, lexer);
854
+ s->indentation = 0;
855
+ success = true;
856
+ } else if ((dont_interrupt
857
+ ? valid_symbols[LIST_MARKER_MINUS_DONT_INTERRUPT]
858
+ : valid_symbols[LIST_MARKER_MINUS]) &&
859
+ list_marker_minus) {
860
+ if (minus_count == 1) {
861
+ mark_end(s, lexer);
862
+ }
863
+ extra_indentation--;
864
+ if (extra_indentation <= 3) {
865
+ extra_indentation += s->indentation;
866
+ s->indentation = 0;
867
+ } else {
868
+ uint8_t temp = s->indentation;
869
+ s->indentation = extra_indentation;
870
+ extra_indentation = temp;
871
+ }
872
+ if (!s->simulate)
873
+ push_block(s, (Block)(LIST_ITEM + extra_indentation));
874
+ lexer->result_symbol = dont_interrupt
875
+ ? LIST_MARKER_MINUS_DONT_INTERRUPT
876
+ : LIST_MARKER_MINUS;
877
+ return true;
878
+ }
879
+ if (minus_count == 3 && (!minus_after_whitespace) && line_end &&
880
+ valid_symbols[MINUS_METADATA]) {
881
+ for (;;) {
882
+ // advance over newline
883
+ if (lexer->lookahead == '\r') {
884
+ advance(s, lexer);
885
+ if (lexer->lookahead == '\n') {
886
+ advance(s, lexer);
887
+ }
888
+ } else {
889
+ advance(s, lexer);
890
+ }
891
+ // check for minuses
892
+ minus_count = 0;
893
+ while (lexer->lookahead == '-') {
894
+ minus_count++;
895
+ advance(s, lexer);
896
+ }
897
+ if (minus_count == 3) {
898
+ // if exactly 3 check if next symbol (after eventual
899
+ // whitespace) is newline
900
+ while (lexer->lookahead == ' ' ||
901
+ lexer->lookahead == '\t') {
902
+ advance(s, lexer);
903
+ }
904
+ if (lexer->lookahead == '\r' || lexer->lookahead == '\n') {
905
+ // if so also consume newline
906
+ if (lexer->lookahead == '\r') {
907
+ advance(s, lexer);
908
+ if (lexer->lookahead == '\n') {
909
+ advance(s, lexer);
910
+ }
911
+ } else {
912
+ advance(s, lexer);
913
+ }
914
+ mark_end(s, lexer);
915
+ lexer->result_symbol = MINUS_METADATA;
916
+ return true;
917
+ }
918
+ }
919
+ // otherwise consume rest of line
920
+ while (lexer->lookahead != '\n' && lexer->lookahead != '\r' &&
921
+ !lexer->eof(lexer)) {
922
+ advance(s, lexer);
923
+ }
924
+ // if end of file is reached, then this is not metadata
925
+ if (lexer->eof(lexer)) {
926
+ break;
927
+ }
928
+ }
929
+ }
930
+ if (success) {
931
+ return true;
932
+ }
933
+ }
934
+ return false;
935
+ }
936
+
937
+ static bool parse_html_block(Scanner *s, TSLexer *lexer,
938
+ const bool *valid_symbols) {
939
+ if (!(valid_symbols[HTML_BLOCK_1_START] ||
940
+ valid_symbols[HTML_BLOCK_1_END] ||
941
+ valid_symbols[HTML_BLOCK_2_START] ||
942
+ valid_symbols[HTML_BLOCK_3_START] ||
943
+ valid_symbols[HTML_BLOCK_4_START] ||
944
+ valid_symbols[HTML_BLOCK_5_START] ||
945
+ valid_symbols[HTML_BLOCK_6_START] ||
946
+ valid_symbols[HTML_BLOCK_7_START])) {
947
+ return false;
948
+ }
949
+ advance(s, lexer);
950
+ if (lexer->lookahead == '?' && valid_symbols[HTML_BLOCK_3_START]) {
951
+ advance(s, lexer);
952
+ lexer->result_symbol = HTML_BLOCK_3_START;
953
+ if (!s->simulate)
954
+ push_block(s, ANONYMOUS);
955
+ return true;
956
+ }
957
+ if (lexer->lookahead == '!') {
958
+ // could be block 2
959
+ advance(s, lexer);
960
+ if (lexer->lookahead == '-') {
961
+ advance(s, lexer);
962
+ if (lexer->lookahead == '-' && valid_symbols[HTML_BLOCK_2_START]) {
963
+ advance(s, lexer);
964
+ lexer->result_symbol = HTML_BLOCK_2_START;
965
+ if (!s->simulate)
966
+ push_block(s, ANONYMOUS);
967
+ return true;
968
+ }
969
+ } else if ('A' <= lexer->lookahead && lexer->lookahead <= 'Z' &&
970
+ valid_symbols[HTML_BLOCK_4_START]) {
971
+ advance(s, lexer);
972
+ lexer->result_symbol = HTML_BLOCK_4_START;
973
+ if (!s->simulate)
974
+ push_block(s, ANONYMOUS);
975
+ return true;
976
+ } else if (lexer->lookahead == '[') {
977
+ advance(s, lexer);
978
+ if (lexer->lookahead == 'C') {
979
+ advance(s, lexer);
980
+ if (lexer->lookahead == 'D') {
981
+ advance(s, lexer);
982
+ if (lexer->lookahead == 'A') {
983
+ advance(s, lexer);
984
+ if (lexer->lookahead == 'T') {
985
+ advance(s, lexer);
986
+ if (lexer->lookahead == 'A') {
987
+ advance(s, lexer);
988
+ if (lexer->lookahead == '[' &&
989
+ valid_symbols[HTML_BLOCK_5_START]) {
990
+ advance(s, lexer);
991
+ lexer->result_symbol = HTML_BLOCK_5_START;
992
+ if (!s->simulate)
993
+ push_block(s, ANONYMOUS);
994
+ return true;
995
+ }
996
+ }
997
+ }
998
+ }
999
+ }
1000
+ }
1001
+ }
1002
+ }
1003
+ bool starting_slash = lexer->lookahead == '/';
1004
+ if (starting_slash) {
1005
+ advance(s, lexer);
1006
+ }
1007
+ char name[11];
1008
+ size_t name_length = 0;
1009
+ while (iswalpha((wint_t)lexer->lookahead)) {
1010
+ if (name_length < 10) {
1011
+ name[name_length++] = (char)towlower((wint_t)lexer->lookahead);
1012
+ } else {
1013
+ name_length = 12;
1014
+ }
1015
+ advance(s, lexer);
1016
+ }
1017
+ if (name_length == 0) {
1018
+ return false;
1019
+ }
1020
+ bool tag_closed = false;
1021
+ if (name_length < 11) {
1022
+ name[name_length] = 0;
1023
+ bool next_symbol_valid =
1024
+ lexer->lookahead == ' ' || lexer->lookahead == '\t' ||
1025
+ lexer->lookahead == '\n' || lexer->lookahead == '\r' ||
1026
+ lexer->lookahead == '>';
1027
+ if (next_symbol_valid) {
1028
+ // try block 1 names
1029
+ for (size_t i = 0; i < NUM_HTML_TAG_NAMES_RULE_1; i++) {
1030
+ if (strcmp(name, HTML_TAG_NAMES_RULE_1[i]) == 0) {
1031
+ if (starting_slash) {
1032
+ if (valid_symbols[HTML_BLOCK_1_END]) {
1033
+ lexer->result_symbol = HTML_BLOCK_1_END;
1034
+ return true;
1035
+ }
1036
+ } else if (valid_symbols[HTML_BLOCK_1_START]) {
1037
+ lexer->result_symbol = HTML_BLOCK_1_START;
1038
+ if (!s->simulate)
1039
+ push_block(s, ANONYMOUS);
1040
+ return true;
1041
+ }
1042
+ }
1043
+ }
1044
+ }
1045
+ if (!next_symbol_valid && lexer->lookahead == '/') {
1046
+ advance(s, lexer);
1047
+ if (lexer->lookahead == '>') {
1048
+ advance(s, lexer);
1049
+ tag_closed = true;
1050
+ }
1051
+ }
1052
+ if (next_symbol_valid || tag_closed) {
1053
+ // try block 2 names
1054
+ for (size_t i = 0; i < NUM_HTML_TAG_NAMES_RULE_7; i++) {
1055
+ if (strcmp(name, HTML_TAG_NAMES_RULE_7[i]) == 0 &&
1056
+ valid_symbols[HTML_BLOCK_6_START]) {
1057
+ lexer->result_symbol = HTML_BLOCK_6_START;
1058
+ if (!s->simulate)
1059
+ push_block(s, ANONYMOUS);
1060
+ return true;
1061
+ }
1062
+ }
1063
+ }
1064
+ }
1065
+
1066
+ if (!valid_symbols[HTML_BLOCK_7_START]) {
1067
+ return false;
1068
+ }
1069
+
1070
+ if (!tag_closed) {
1071
+ // tag name (continued)
1072
+ while (iswalnum((wint_t)lexer->lookahead) || lexer->lookahead == '-') {
1073
+ advance(s, lexer);
1074
+ }
1075
+ if (!starting_slash) {
1076
+ // attributes
1077
+ bool had_whitespace = false;
1078
+ for (;;) {
1079
+ // whitespace
1080
+ while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
1081
+ had_whitespace = true;
1082
+ advance(s, lexer);
1083
+ }
1084
+ if (lexer->lookahead == '/') {
1085
+ advance(s, lexer);
1086
+ break;
1087
+ }
1088
+ if (lexer->lookahead == '>') {
1089
+ break;
1090
+ }
1091
+ // attribute name
1092
+ if (!had_whitespace) {
1093
+ return false;
1094
+ }
1095
+ if (!iswalpha((wint_t)lexer->lookahead) &&
1096
+ lexer->lookahead != '_' && lexer->lookahead != ':') {
1097
+ return false;
1098
+ }
1099
+ had_whitespace = false;
1100
+ advance(s, lexer);
1101
+ while (iswalnum((wint_t)lexer->lookahead) ||
1102
+ lexer->lookahead == '_' || lexer->lookahead == '.' ||
1103
+ lexer->lookahead == ':' || lexer->lookahead == '-') {
1104
+ advance(s, lexer);
1105
+ }
1106
+ // attribute value specification
1107
+ // optional whitespace
1108
+ while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
1109
+ had_whitespace = true;
1110
+ advance(s, lexer);
1111
+ }
1112
+ // =
1113
+ if (lexer->lookahead == '=') {
1114
+ advance(s, lexer);
1115
+ had_whitespace = false;
1116
+ // optional whitespace
1117
+ while (lexer->lookahead == ' ' ||
1118
+ lexer->lookahead == '\t') {
1119
+ advance(s, lexer);
1120
+ }
1121
+ // attribute value
1122
+ if (lexer->lookahead == '\'' || lexer->lookahead == '"') {
1123
+ char delimiter = (char)lexer->lookahead;
1124
+ advance(s, lexer);
1125
+ while (lexer->lookahead != delimiter &&
1126
+ lexer->lookahead != '\n' &&
1127
+ lexer->lookahead != '\r' && !lexer->eof(lexer)) {
1128
+ advance(s, lexer);
1129
+ }
1130
+ if (lexer->lookahead != delimiter) {
1131
+ return false;
1132
+ }
1133
+ advance(s, lexer);
1134
+ } else {
1135
+ // unquoted attribute value
1136
+ bool had_one = false;
1137
+ while (lexer->lookahead != ' ' &&
1138
+ lexer->lookahead != '\t' &&
1139
+ lexer->lookahead != '"' &&
1140
+ lexer->lookahead != '\'' &&
1141
+ lexer->lookahead != '=' &&
1142
+ lexer->lookahead != '<' &&
1143
+ lexer->lookahead != '>' &&
1144
+ lexer->lookahead != '`' &&
1145
+ lexer->lookahead != '\n' &&
1146
+ lexer->lookahead != '\r' && !lexer->eof(lexer)) {
1147
+ advance(s, lexer);
1148
+ had_one = true;
1149
+ }
1150
+ if (!had_one) {
1151
+ return false;
1152
+ }
1153
+ }
1154
+ }
1155
+ }
1156
+ } else {
1157
+ while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
1158
+ advance(s, lexer);
1159
+ }
1160
+ }
1161
+ if (lexer->lookahead != '>') {
1162
+ return false;
1163
+ }
1164
+ advance(s, lexer);
1165
+ }
1166
+ while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
1167
+ advance(s, lexer);
1168
+ }
1169
+ if (lexer->lookahead == '\r' || lexer->lookahead == '\n') {
1170
+ lexer->result_symbol = HTML_BLOCK_7_START;
1171
+ if (!s->simulate)
1172
+ push_block(s, ANONYMOUS);
1173
+ return true;
1174
+ }
1175
+ return false;
1176
+ }
1177
+
1178
+ static bool parse_pipe_table(Scanner *s, TSLexer *lexer,
1179
+ const bool *valid_symbols) {
1180
+
1181
+ // unused
1182
+ (void)(valid_symbols);
1183
+
1184
+ // PIPE_TABLE_START is zero width
1185
+ mark_end(s, lexer);
1186
+ // count number of cells
1187
+ size_t cell_count = 0;
1188
+ // also remember if we see starting and ending pipes, as empty headers have
1189
+ // to have both
1190
+ bool starting_pipe = false;
1191
+ bool ending_pipe = false;
1192
+ bool empty = true;
1193
+ if (lexer->lookahead == '|') {
1194
+ starting_pipe = true;
1195
+ advance(s, lexer);
1196
+ }
1197
+ while (lexer->lookahead != '\r' && lexer->lookahead != '\n' &&
1198
+ !lexer->eof(lexer)) {
1199
+ if (lexer->lookahead == '|') {
1200
+ cell_count++;
1201
+ ending_pipe = true;
1202
+ advance(s, lexer);
1203
+ } else {
1204
+ if (lexer->lookahead != ' ' && lexer->lookahead != '\t') {
1205
+ ending_pipe = false;
1206
+ }
1207
+ if (lexer->lookahead == '\\') {
1208
+ advance(s, lexer);
1209
+ if (is_punctuation((char)lexer->lookahead)) {
1210
+ advance(s, lexer);
1211
+ }
1212
+ } else {
1213
+ advance(s, lexer);
1214
+ }
1215
+ }
1216
+ }
1217
+ if (empty && cell_count == 0 && !(starting_pipe && ending_pipe)) {
1218
+ return false;
1219
+ }
1220
+ if (!ending_pipe) {
1221
+ cell_count++;
1222
+ }
1223
+
1224
+ // check the following line for a delimiter row
1225
+ // parse a newline
1226
+ if (lexer->lookahead == '\n') {
1227
+ advance(s, lexer);
1228
+ } else if (lexer->lookahead == '\r') {
1229
+ advance(s, lexer);
1230
+ if (lexer->lookahead == '\n') {
1231
+ advance(s, lexer);
1232
+ }
1233
+ } else {
1234
+ return false;
1235
+ }
1236
+ s->indentation = 0;
1237
+ s->column = 0;
1238
+ for (;;) {
1239
+ if (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
1240
+ s->indentation += advance(s, lexer);
1241
+ } else {
1242
+ break;
1243
+ }
1244
+ }
1245
+ s->simulate = true;
1246
+ uint8_t matched_temp = 0;
1247
+ while (matched_temp < (uint8_t)s->open_blocks.size) {
1248
+ if (match(s, lexer, s->open_blocks.items[matched_temp])) {
1249
+ matched_temp++;
1250
+ } else {
1251
+ return false;
1252
+ }
1253
+ }
1254
+
1255
+ // check if delimiter row has the same number of cells and at least one pipe
1256
+ size_t delimiter_cell_count = 0;
1257
+ if (lexer->lookahead == '|') {
1258
+ advance(s, lexer);
1259
+ }
1260
+ for (;;) {
1261
+ while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
1262
+ advance(s, lexer);
1263
+ }
1264
+ if (lexer->lookahead == '|') {
1265
+ delimiter_cell_count++;
1266
+ advance(s, lexer);
1267
+ continue;
1268
+ }
1269
+ if (lexer->lookahead == ':') {
1270
+ advance(s, lexer);
1271
+ if (lexer->lookahead != '-') {
1272
+ return false;
1273
+ }
1274
+ }
1275
+ bool had_one_minus = false;
1276
+ while (lexer->lookahead == '-') {
1277
+ had_one_minus = true;
1278
+ advance(s, lexer);
1279
+ }
1280
+ if (had_one_minus) {
1281
+ delimiter_cell_count++;
1282
+ }
1283
+ if (lexer->lookahead == ':') {
1284
+ if (!had_one_minus) {
1285
+ return false;
1286
+ }
1287
+ advance(s, lexer);
1288
+ }
1289
+ while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
1290
+ advance(s, lexer);
1291
+ }
1292
+ if (lexer->lookahead == '|') {
1293
+ if (!had_one_minus) {
1294
+ delimiter_cell_count++;
1295
+ }
1296
+ advance(s, lexer);
1297
+ continue;
1298
+ }
1299
+ if (lexer->lookahead != '\r' && lexer->lookahead != '\n') {
1300
+ return false;
1301
+ } else {
1302
+ break;
1303
+ }
1304
+ }
1305
+ // if the cell counts are not equal then this is not a table
1306
+ if (cell_count != delimiter_cell_count) {
1307
+ return false;
1308
+ }
1309
+
1310
+ lexer->result_symbol = PIPE_TABLE_START;
1311
+ return true;
1312
+ }
1313
+
1314
+ static bool scan(Scanner *s, TSLexer *lexer, const bool *valid_symbols) {
1315
+ // A normal tree-sitter rule decided that the current branch is invalid and
1316
+ // now "requests" an error to stop the branch
1317
+ if (valid_symbols[TRIGGER_ERROR]) {
1318
+ return error(lexer);
1319
+ }
1320
+
1321
+ // Close the inner most block after the next line break as requested. See
1322
+ // `$._close_block` in grammar.js
1323
+ if (valid_symbols[CLOSE_BLOCK]) {
1324
+ s->state |= STATE_CLOSE_BLOCK;
1325
+ lexer->result_symbol = CLOSE_BLOCK;
1326
+ return true;
1327
+ }
1328
+
1329
+ // if we are at the end of the file and there are still open blocks close
1330
+ // them all
1331
+ if (lexer->eof(lexer)) {
1332
+ if (valid_symbols[TOKEN_EOF]) {
1333
+ lexer->result_symbol = TOKEN_EOF;
1334
+ return true;
1335
+ }
1336
+ if (s->open_blocks.size > 0) {
1337
+ lexer->result_symbol = BLOCK_CLOSE;
1338
+ if (!s->simulate)
1339
+ pop_block(s);
1340
+ return true;
1341
+ }
1342
+ return false;
1343
+ }
1344
+
1345
+ if (!(s->state & STATE_MATCHING)) {
1346
+ // Parse any preceeding whitespace and remember its length. This makes a
1347
+ // lot of parsing quite a bit easier.
1348
+ for (;;) {
1349
+ if (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
1350
+ s->indentation += advance(s, lexer);
1351
+ } else {
1352
+ break;
1353
+ }
1354
+ }
1355
+ // We are not matching. This is where the parsing logic for most
1356
+ // "normal" token is. Most importantly parsing logic for the start of
1357
+ // new blocks.
1358
+ if (valid_symbols[INDENTED_CHUNK_START] &&
1359
+ !valid_symbols[NO_INDENTED_CHUNK]) {
1360
+ if (s->indentation >= 4 && lexer->lookahead != '\n' &&
1361
+ lexer->lookahead != '\r') {
1362
+ lexer->result_symbol = INDENTED_CHUNK_START;
1363
+ if (!s->simulate)
1364
+ push_block(s, INDENTED_CODE_BLOCK);
1365
+ s->indentation -= 4;
1366
+ return true;
1367
+ }
1368
+ }
1369
+ // Decide which tokens to consider based on the first non-whitespace
1370
+ // character
1371
+ switch (lexer->lookahead) {
1372
+ case '\r':
1373
+ case '\n':
1374
+ if (valid_symbols[BLANK_LINE_START]) {
1375
+ // A blank line token is actually just 0 width, so do not
1376
+ // consume the characters
1377
+ lexer->result_symbol = BLANK_LINE_START;
1378
+ return true;
1379
+ }
1380
+ break;
1381
+ case '`':
1382
+ // A backtick could mark the beginning or ending of a fenced
1383
+ // code block.
1384
+ return parse_fenced_code_block(s, '`', lexer, valid_symbols);
1385
+ case '~':
1386
+ // A tilde could mark the beginning or ending of a fenced code
1387
+ // block.
1388
+ return parse_fenced_code_block(s, '~', lexer, valid_symbols);
1389
+ case '*':
1390
+ // A star could either mark a list item or a thematic break.
1391
+ // This code is similar to the code for '_' and '+'.
1392
+ return parse_star(s, lexer, valid_symbols);
1393
+ case '_':
1394
+ return parse_thematic_break_underscore(s, lexer, valid_symbols);
1395
+ case '>':
1396
+ // A '>' could mark the beginning of a block quote
1397
+ return parse_block_quote(s, lexer, valid_symbols);
1398
+ case '#':
1399
+ // A '#' could mark a atx heading
1400
+ return parse_atx_heading(s, lexer, valid_symbols);
1401
+ case '=':
1402
+ // A '=' could mark a setext underline
1403
+ return parse_setext_underline(s, lexer, valid_symbols);
1404
+ case '+':
1405
+ // A '+' could be a list marker
1406
+ return parse_plus(s, lexer, valid_symbols);
1407
+ case '0':
1408
+ case '1':
1409
+ case '2':
1410
+ case '3':
1411
+ case '4':
1412
+ case '5':
1413
+ case '6':
1414
+ case '7':
1415
+ case '8':
1416
+ case '9':
1417
+ // A number could be a list marker (if followed by a dot or a
1418
+ // parenthesis)
1419
+ return parse_ordered_list_marker(s, lexer, valid_symbols);
1420
+ case '-':
1421
+ // A minus could mark a list marker, a thematic break or a
1422
+ // setext underline
1423
+ return parse_minus(s, lexer, valid_symbols);
1424
+ case '<':
1425
+ // A < could mark the beginning of a html block
1426
+ return parse_html_block(s, lexer, valid_symbols);
1427
+ }
1428
+ if (lexer->lookahead != '\r' && lexer->lookahead != '\n' &&
1429
+ valid_symbols[PIPE_TABLE_START]) {
1430
+ return parse_pipe_table(s, lexer, valid_symbols);
1431
+ }
1432
+ } else { // we are in the state of trying to match all currently open blocks
1433
+ bool partial_success = false;
1434
+ while (s->matched < (uint8_t)s->open_blocks.size) {
1435
+ if (s->matched == (uint8_t)s->open_blocks.size - 1 &&
1436
+ (s->state & STATE_CLOSE_BLOCK)) {
1437
+ if (!partial_success)
1438
+ s->state &= ~STATE_CLOSE_BLOCK;
1439
+ break;
1440
+ }
1441
+ if (match(s, lexer, s->open_blocks.items[s->matched])) {
1442
+ partial_success = true;
1443
+ s->matched++;
1444
+ } else {
1445
+ if (s->state & STATE_WAS_SOFT_LINE_BREAK) {
1446
+ s->state &= (~STATE_MATCHING);
1447
+ }
1448
+ break;
1449
+ }
1450
+ }
1451
+ if (partial_success) {
1452
+ if (s->matched == s->open_blocks.size) {
1453
+ s->state &= (~STATE_MATCHING);
1454
+ }
1455
+ lexer->result_symbol = BLOCK_CONTINUATION;
1456
+ return true;
1457
+ }
1458
+
1459
+ if (!(s->state & STATE_WAS_SOFT_LINE_BREAK)) {
1460
+ lexer->result_symbol = BLOCK_CLOSE;
1461
+ pop_block(s);
1462
+ if (s->matched == s->open_blocks.size) {
1463
+ s->state &= (~STATE_MATCHING);
1464
+ }
1465
+ return true;
1466
+ }
1467
+ }
1468
+
1469
+ // The parser just encountered a line break. Setup the state correspondingly
1470
+ if ((valid_symbols[LINE_ENDING] || valid_symbols[SOFT_LINE_ENDING] ||
1471
+ valid_symbols[PIPE_TABLE_LINE_ENDING]) &&
1472
+ (lexer->lookahead == '\n' || lexer->lookahead == '\r')) {
1473
+ if (lexer->lookahead == '\r') {
1474
+ advance(s, lexer);
1475
+ if (lexer->lookahead == '\n') {
1476
+ advance(s, lexer);
1477
+ }
1478
+ } else {
1479
+ advance(s, lexer);
1480
+ }
1481
+ s->indentation = 0;
1482
+ s->column = 0;
1483
+ if (!(s->state & STATE_CLOSE_BLOCK) &&
1484
+ (valid_symbols[SOFT_LINE_ENDING] ||
1485
+ valid_symbols[PIPE_TABLE_LINE_ENDING])) {
1486
+ lexer->mark_end(lexer);
1487
+ for (;;) {
1488
+ if (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
1489
+ s->indentation += advance(s, lexer);
1490
+ } else {
1491
+ break;
1492
+ }
1493
+ }
1494
+ s->simulate = true;
1495
+ uint8_t matched_temp = s->matched;
1496
+ s->matched = 0;
1497
+ bool one_will_be_matched = false;
1498
+ while (s->matched < (uint8_t)s->open_blocks.size) {
1499
+ if (match(s, lexer, s->open_blocks.items[s->matched])) {
1500
+ s->matched++;
1501
+ one_will_be_matched = true;
1502
+ } else {
1503
+ break;
1504
+ }
1505
+ }
1506
+ bool all_will_be_matched = s->matched == s->open_blocks.size;
1507
+ if (!lexer->eof(lexer) &&
1508
+ !scan(s, lexer, paragraph_interrupt_symbols)) {
1509
+ s->matched = matched_temp;
1510
+ // If the last line break ended a paragraph and no new block
1511
+ // opened, the last line break should have been a soft line
1512
+ // break Reset the counter for matched blocks
1513
+ s->matched = 0;
1514
+ s->indentation = 0;
1515
+ s->column = 0;
1516
+ // If there is at least one open block, we should be in the
1517
+ // matching state. Also set the matching flag if a
1518
+ // `$._soft_line_break_marker` can be emitted so it does get
1519
+ // emitted.
1520
+ if (one_will_be_matched) {
1521
+ s->state |= STATE_MATCHING;
1522
+ } else {
1523
+ s->state &= (~STATE_MATCHING);
1524
+ }
1525
+ if (valid_symbols[PIPE_TABLE_LINE_ENDING]) {
1526
+ if (all_will_be_matched) {
1527
+ lexer->result_symbol = PIPE_TABLE_LINE_ENDING;
1528
+ return true;
1529
+ }
1530
+ } else {
1531
+ lexer->result_symbol = SOFT_LINE_ENDING;
1532
+ // reset some state variables
1533
+ s->state |= STATE_WAS_SOFT_LINE_BREAK;
1534
+ return true;
1535
+ }
1536
+ } else {
1537
+ s->matched = matched_temp;
1538
+ }
1539
+ s->indentation = 0;
1540
+ s->column = 0;
1541
+ }
1542
+ if (valid_symbols[LINE_ENDING]) {
1543
+ // If the last line break ended a paragraph and no new block opened,
1544
+ // the last line break should have been a soft line break Reset the
1545
+ // counter for matched blocks
1546
+ s->matched = 0;
1547
+ // If there is at least one open block, we should be in the matching
1548
+ // state. Also set the matching flag if a
1549
+ // `$._soft_line_break_marker` can be emitted so it does get
1550
+ // emitted.
1551
+ if (s->open_blocks.size > 0) {
1552
+ s->state |= STATE_MATCHING;
1553
+ } else {
1554
+ s->state &= (~STATE_MATCHING);
1555
+ }
1556
+ // reset some state variables
1557
+ s->state &= (~STATE_WAS_SOFT_LINE_BREAK);
1558
+ lexer->result_symbol = LINE_ENDING;
1559
+ return true;
1560
+ }
1561
+ }
1562
+ return false;
1563
+ }
1564
+
1565
+ void *tree_sitter_markdown_external_scanner_create(void) {
1566
+ Scanner *s = (Scanner *)malloc(sizeof(Scanner));
1567
+ s->open_blocks.items = (Block *)calloc(1, sizeof(Block));
1568
+ #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
1569
+ _Static_assert(ATX_H6_MARKER == ATX_H1_MARKER + 5, "");
1570
+ #else
1571
+ assert(ATX_H6_MARKER == ATX_H1_MARKER + 5);
1572
+ #endif
1573
+ deserialize(s, NULL, 0);
1574
+
1575
+ return s;
1576
+ }
1577
+
1578
+ bool tree_sitter_markdown_external_scanner_scan(void *payload, TSLexer *lexer,
1579
+ const bool *valid_symbols) {
1580
+ Scanner *scanner = (Scanner *)payload;
1581
+ scanner->simulate = false;
1582
+ return scan(scanner, lexer, valid_symbols);
1583
+ }
1584
+
1585
+ unsigned tree_sitter_markdown_external_scanner_serialize(void *payload,
1586
+ char *buffer) {
1587
+ Scanner *scanner = (Scanner *)payload;
1588
+ return serialize(scanner, buffer);
1589
+ }
1590
+
1591
+ void tree_sitter_markdown_external_scanner_deserialize(void *payload,
1592
+ const char *buffer,
1593
+ unsigned length) {
1594
+ Scanner *scanner = (Scanner *)payload;
1595
+ deserialize(scanner, buffer, length);
1596
+ }
1597
+
1598
+ void tree_sitter_markdown_external_scanner_destroy(void *payload) {
1599
+ Scanner *scanner = (Scanner *)payload;
1600
+ free(scanner->open_blocks.items);
1601
+ free(scanner);
1602
+ }