apex-ruby 1.0.37 → 1.0.39
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.
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f037626c97e2d9d1da593da4e5cfe66b74bac805d7bad91e8f5547ff676cb394
|
|
4
|
+
data.tar.gz: a1ef5604a103f328a970246606c4e9c79b2ea6ffece769532250da7499dc287d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b6f994047113301b183aa7529089d6342f6847ace386ceb281cd80675b5ef79afc6a7c423318357533c03102b24916e8b7e20405c750b903189674a2cb321380
|
|
7
|
+
data.tar.gz: 90101aea55503ce808d9cc4f6018c466ff9a03791deac096f4e13e85858536beb5abe6dabc7d90e89a0f2fe91b7fde7bdd9d054bc841d99b54fd45679b98d344
|
|
@@ -21,8 +21,8 @@ typedef struct cmark_node cmark_node;
|
|
|
21
21
|
|
|
22
22
|
#define APEX_VERSION_MAJOR 1
|
|
23
23
|
#define APEX_VERSION_MINOR 1
|
|
24
|
-
#define APEX_VERSION_PATCH
|
|
25
|
-
#define APEX_VERSION_STRING "1.1.
|
|
24
|
+
#define APEX_VERSION_PATCH 41
|
|
25
|
+
#define APEX_VERSION_STRING "1.1.41"
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Processor compatibility modes
|
|
@@ -278,23 +278,490 @@ void apex_process_critic_markup_in_tree(cmark_node *node, critic_mode_t mode) {
|
|
|
278
278
|
}
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
+
static const char *critic_next_line(const char *p) {
|
|
282
|
+
while (*p && *p != '\n' && *p != '\r') p++;
|
|
283
|
+
if (*p == '\r' && p[1] == '\n') return p + 2;
|
|
284
|
+
return *p ? p + 1 : p;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/* Length of a ``` or ~~~ fence run opening the line, or 0. */
|
|
288
|
+
static size_t critic_fence_run(const char *line, char *fence_char, const char **after_run) {
|
|
289
|
+
const char *p = line;
|
|
290
|
+
while (*p == ' ' || *p == '\t' || *p == '>') p++;
|
|
291
|
+
if (*p != '`' && *p != '~') return 0;
|
|
292
|
+
char c = *p;
|
|
293
|
+
const char *run = p;
|
|
294
|
+
while (*p == c) p++;
|
|
295
|
+
if (p - run < 3) return 0;
|
|
296
|
+
*fence_char = c;
|
|
297
|
+
*after_run = p;
|
|
298
|
+
return (size_t)(p - run);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/* Bytes at pos that are a fenced block or code span and must be copied
|
|
302
|
+
* through untouched. Returns 0 when pos does not start code. */
|
|
303
|
+
static size_t critic_code_region_len(const char *base, const char *pos) {
|
|
304
|
+
bool line_start = (pos == base || pos[-1] == '\n' || pos[-1] == '\r');
|
|
305
|
+
if (line_start) {
|
|
306
|
+
char fence_char;
|
|
307
|
+
const char *after;
|
|
308
|
+
size_t open_len = critic_fence_run(pos, &fence_char, &after);
|
|
309
|
+
if (open_len) {
|
|
310
|
+
bool valid = true;
|
|
311
|
+
if (fence_char == '`') {
|
|
312
|
+
for (const char *q = after; *q && *q != '\n' && *q != '\r'; q++) {
|
|
313
|
+
if (*q == '`') { valid = false; break; }
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (valid) {
|
|
317
|
+
const char *line = critic_next_line(pos);
|
|
318
|
+
while (*line) {
|
|
319
|
+
char close_char;
|
|
320
|
+
const char *close_after;
|
|
321
|
+
size_t close_len = critic_fence_run(line, &close_char, &close_after);
|
|
322
|
+
if (close_len >= open_len && close_char == fence_char) {
|
|
323
|
+
while (*close_after == ' ' || *close_after == '\t') close_after++;
|
|
324
|
+
if (!*close_after || *close_after == '\n' || *close_after == '\r') {
|
|
325
|
+
return (size_t)(critic_next_line(line) - pos);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
line = critic_next_line(line);
|
|
329
|
+
}
|
|
330
|
+
/* Unclosed fence: protect only the opening line */
|
|
331
|
+
return (size_t)(critic_next_line(pos) - pos);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (*pos != '`') return 0;
|
|
337
|
+
|
|
338
|
+
size_t backslashes = 0;
|
|
339
|
+
for (const char *b = pos; b > base && b[-1] == '\\'; b--) backslashes++;
|
|
340
|
+
if (backslashes % 2) return 1;
|
|
341
|
+
|
|
342
|
+
const char *p = pos;
|
|
343
|
+
while (*p == '`') p++;
|
|
344
|
+
size_t open_len = (size_t)(p - pos);
|
|
345
|
+
|
|
346
|
+
/* Code spans may wrap lines but end at a blank line */
|
|
347
|
+
while (*p) {
|
|
348
|
+
if (*p == '\n' || *p == '\r') {
|
|
349
|
+
const char *next = critic_next_line(p);
|
|
350
|
+
const char *q = next;
|
|
351
|
+
while (*q == ' ' || *q == '\t' || *q == '>') q++;
|
|
352
|
+
if (!*q || *q == '\n' || *q == '\r') break;
|
|
353
|
+
p = next;
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
if (*p == '`') {
|
|
357
|
+
const char *run = p;
|
|
358
|
+
while (*p == '`') p++;
|
|
359
|
+
if ((size_t)(p - run) == open_len) return (size_t)(p - pos);
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
p++;
|
|
363
|
+
}
|
|
364
|
+
return open_len;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/* Indented code blocks follow the CommonMark rules: four spaces or a tab
|
|
368
|
+
* at the current container, and they cannot interrupt a paragraph. List
|
|
369
|
+
* continuations and paragraph lines stay eligible for critic markup. */
|
|
370
|
+
|
|
371
|
+
typedef enum {
|
|
372
|
+
CRITIC_BOX_QUOTE,
|
|
373
|
+
CRITIC_BOX_ITEM
|
|
374
|
+
} critic_box_kind;
|
|
375
|
+
|
|
376
|
+
typedef struct {
|
|
377
|
+
critic_box_kind kind;
|
|
378
|
+
int need; /* ITEM: columns required to continue */
|
|
379
|
+
bool has_content; /* ITEM has a child block */
|
|
380
|
+
} critic_box;
|
|
381
|
+
|
|
382
|
+
typedef struct {
|
|
383
|
+
critic_box stack[32];
|
|
384
|
+
int depth;
|
|
385
|
+
bool in_paragraph;
|
|
386
|
+
bool in_code;
|
|
387
|
+
} critic_blocks;
|
|
388
|
+
|
|
389
|
+
static int critic_col_add(int col, char c) {
|
|
390
|
+
if (c == '\t') return col + (4 - (col % 4));
|
|
391
|
+
return col + 1;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
static bool critic_line_blank(const char *p) {
|
|
395
|
+
while (*p == ' ' || *p == '\t') p++;
|
|
396
|
+
return *p == '\0' || *p == '\n' || *p == '\r';
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
static const char *critic_skip_indent(const char *p, int *col, int *indent) {
|
|
400
|
+
int start = *col;
|
|
401
|
+
while (*p == ' ' || *p == '\t') {
|
|
402
|
+
*col = critic_col_add(*col, *p);
|
|
403
|
+
p++;
|
|
404
|
+
}
|
|
405
|
+
*indent = *col - start;
|
|
406
|
+
return p;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/* Consume exactly `columns` of indent. A tab may be only partly consumed. */
|
|
410
|
+
static const char *critic_advance_columns(const char *p, int *col, int columns) {
|
|
411
|
+
int target = *col + columns;
|
|
412
|
+
while (*p && *p != '\n' && *p != '\r' && *col < target) {
|
|
413
|
+
if (*p == '\t') {
|
|
414
|
+
int next = critic_col_add(*col, '\t');
|
|
415
|
+
if (next > target) {
|
|
416
|
+
*col = target;
|
|
417
|
+
return p;
|
|
418
|
+
}
|
|
419
|
+
*col = next;
|
|
420
|
+
p++;
|
|
421
|
+
} else {
|
|
422
|
+
*col += 1;
|
|
423
|
+
p++;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return p;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
static bool critic_match_quote(const char *p, int col, const char **out, int *out_col) {
|
|
430
|
+
int c = col;
|
|
431
|
+
int indent = 0;
|
|
432
|
+
const char *q = critic_skip_indent(p, &c, &indent);
|
|
433
|
+
if (indent > 3 || *q != '>') return false;
|
|
434
|
+
q++;
|
|
435
|
+
c += 1;
|
|
436
|
+
if (*q == ' ' || *q == '\t') {
|
|
437
|
+
c = critic_col_add(c, *q);
|
|
438
|
+
q++;
|
|
439
|
+
}
|
|
440
|
+
*out = q;
|
|
441
|
+
*out_col = c;
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/* 1 = bullet, 2 = ordered. len is the marker without trailing space. */
|
|
446
|
+
static int critic_list_marker(const char *p, int *len, int *ordered_start) {
|
|
447
|
+
if (*p == '-' || *p == '+' || *p == '*') {
|
|
448
|
+
char n = p[1];
|
|
449
|
+
if (n == ' ' || n == '\t' || n == '\n' || n == '\r' || n == '\0') {
|
|
450
|
+
*len = 1;
|
|
451
|
+
*ordered_start = 0;
|
|
452
|
+
return 1;
|
|
453
|
+
}
|
|
454
|
+
return 0;
|
|
455
|
+
}
|
|
456
|
+
if (*p >= '1' && *p <= '9') {
|
|
457
|
+
int i = 0;
|
|
458
|
+
int num = 0;
|
|
459
|
+
while (i < 9 && p[i] >= '0' && p[i] <= '9') {
|
|
460
|
+
num = num * 10 + (p[i] - '0');
|
|
461
|
+
i++;
|
|
462
|
+
}
|
|
463
|
+
if (i > 0 && (p[i] == '.' || p[i] == ')')) {
|
|
464
|
+
char n = p[i + 1];
|
|
465
|
+
if (n == ' ' || n == '\t' || n == '\n' || n == '\r' || n == '\0') {
|
|
466
|
+
*len = i + 1;
|
|
467
|
+
*ordered_start = num;
|
|
468
|
+
return 2;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return 0;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
static bool critic_is_atx(const char *p) {
|
|
476
|
+
int n = 0;
|
|
477
|
+
while (p[n] == '#' && n < 7) n++;
|
|
478
|
+
if (n < 1 || n > 6) return false;
|
|
479
|
+
return p[n] == ' ' || p[n] == '\t' || p[n] == '\n' || p[n] == '\r' || p[n] == '\0';
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
static bool critic_is_fence_opener(const char *p) {
|
|
483
|
+
char c = *p;
|
|
484
|
+
if (c != '`' && c != '~') return false;
|
|
485
|
+
int n = 0;
|
|
486
|
+
while (p[n] == c) n++;
|
|
487
|
+
if (n < 3) return false;
|
|
488
|
+
if (c == '`') {
|
|
489
|
+
for (int i = n; p[i] && p[i] != '\n' && p[i] != '\r'; i++) {
|
|
490
|
+
if (p[i] == '`') return false;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return true;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/* Thematic break, or a setext underline when a paragraph is open. */
|
|
497
|
+
static bool critic_is_rule(const char *p, bool in_paragraph) {
|
|
498
|
+
char kind = 0;
|
|
499
|
+
int count = 0;
|
|
500
|
+
bool spaced = false;
|
|
501
|
+
for (const char *q = p; *q && *q != '\n' && *q != '\r'; q++) {
|
|
502
|
+
if (*q == ' ' || *q == '\t') {
|
|
503
|
+
if (count > 0) spaced = true;
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
if (*q != '-' && *q != '_' && *q != '*' && *q != '=') return false;
|
|
507
|
+
if (!kind) kind = *q;
|
|
508
|
+
else if (*q != kind) return false;
|
|
509
|
+
count++;
|
|
510
|
+
}
|
|
511
|
+
if (count < 1 || !kind) return false;
|
|
512
|
+
if (!spaced && in_paragraph && (kind == '-' || kind == '=') && count >= 1) return true;
|
|
513
|
+
if (!spaced && count >= 3 && (kind == '-' || kind == '_' || kind == '*')) return true;
|
|
514
|
+
if (spaced && count >= 3 && (kind == '-' || kind == '_' || kind == '*')) return true;
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
static int critic_match_boxes(const char *line, const critic_blocks *st,
|
|
519
|
+
const char **p, int *col) {
|
|
520
|
+
*p = line;
|
|
521
|
+
*col = 0;
|
|
522
|
+
int matched = 0;
|
|
523
|
+
bool blank = critic_line_blank(line);
|
|
524
|
+
for (int i = 0; i < st->depth; i++) {
|
|
525
|
+
if (st->stack[i].kind == CRITIC_BOX_QUOTE) {
|
|
526
|
+
const char *q;
|
|
527
|
+
int c;
|
|
528
|
+
if (!critic_match_quote(*p, *col, &q, &c)) break;
|
|
529
|
+
*p = q;
|
|
530
|
+
*col = c;
|
|
531
|
+
matched++;
|
|
532
|
+
} else if (blank) {
|
|
533
|
+
if (!st->stack[i].has_content) break;
|
|
534
|
+
matched++;
|
|
535
|
+
} else {
|
|
536
|
+
int indent = 0;
|
|
537
|
+
int c = *col;
|
|
538
|
+
critic_skip_indent(*p, &c, &indent);
|
|
539
|
+
if (indent < st->stack[i].need) break;
|
|
540
|
+
c = *col;
|
|
541
|
+
*p = critic_advance_columns(*p, &c, st->stack[i].need);
|
|
542
|
+
*col = c;
|
|
543
|
+
matched++;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
return matched;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
static void critic_mark_content(critic_blocks *st) {
|
|
550
|
+
for (int i = 0; i < st->depth; i++) {
|
|
551
|
+
if (st->stack[i].kind == CRITIC_BOX_ITEM) st->stack[i].has_content = true;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/* Would this line open a block that closes an open paragraph? */
|
|
556
|
+
static bool critic_interrupts_paragraph(const char *p, int col) {
|
|
557
|
+
int indent = 0;
|
|
558
|
+
int c = col;
|
|
559
|
+
const char *q = critic_skip_indent(p, &c, &indent);
|
|
560
|
+
if (indent >= 4) return false; /* indented code cannot interrupt */
|
|
561
|
+
if (*q == '>') return true;
|
|
562
|
+
int marker_len = 0;
|
|
563
|
+
int ordered_start = 0;
|
|
564
|
+
int kind = critic_list_marker(q, &marker_len, &ordered_start);
|
|
565
|
+
if (kind == 1) return true;
|
|
566
|
+
if (kind == 2 && ordered_start == 1) return true;
|
|
567
|
+
if (critic_is_atx(q) || critic_is_fence_opener(q) || critic_is_rule(q, true)) return true;
|
|
568
|
+
return false;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/* Interpret the remainder of a line after open containers have been matched.
|
|
572
|
+
* Returns true when the line is indented code and must be copied verbatim. */
|
|
573
|
+
static bool critic_interpret_line(critic_blocks *st, const char *p, int col) {
|
|
574
|
+
for (;;) {
|
|
575
|
+
int indent = 0;
|
|
576
|
+
int c = col;
|
|
577
|
+
const char *q = critic_skip_indent(p, &c, &indent);
|
|
578
|
+
if (*q == '\0' || *q == '\n' || *q == '\r') {
|
|
579
|
+
st->in_paragraph = false;
|
|
580
|
+
return false;
|
|
581
|
+
}
|
|
582
|
+
if (indent >= 4) {
|
|
583
|
+
if (!st->in_paragraph) {
|
|
584
|
+
st->in_code = true;
|
|
585
|
+
critic_mark_content(st);
|
|
586
|
+
return true;
|
|
587
|
+
}
|
|
588
|
+
return false;
|
|
589
|
+
}
|
|
590
|
+
if (*q == '>') {
|
|
591
|
+
if (st->depth < 32) {
|
|
592
|
+
st->stack[st->depth].kind = CRITIC_BOX_QUOTE;
|
|
593
|
+
st->stack[st->depth].need = 0;
|
|
594
|
+
st->stack[st->depth].has_content = false;
|
|
595
|
+
st->depth++;
|
|
596
|
+
}
|
|
597
|
+
q++;
|
|
598
|
+
c += 1;
|
|
599
|
+
if (*q == ' ' || *q == '\t') {
|
|
600
|
+
c = critic_col_add(c, *q);
|
|
601
|
+
q++;
|
|
602
|
+
}
|
|
603
|
+
p = q;
|
|
604
|
+
col = c;
|
|
605
|
+
st->in_paragraph = false;
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
int marker_len = 0;
|
|
609
|
+
int ordered_start = 0;
|
|
610
|
+
int kind = critic_list_marker(q, &marker_len, &ordered_start);
|
|
611
|
+
if (kind && !(st->in_paragraph && kind == 2 && ordered_start != 1)) {
|
|
612
|
+
int marker_offset = indent;
|
|
613
|
+
const char *after = q + marker_len;
|
|
614
|
+
int after_col = c + marker_len;
|
|
615
|
+
int save_col = after_col;
|
|
616
|
+
const char *s = after;
|
|
617
|
+
int sc = after_col;
|
|
618
|
+
while (*s == ' ' || *s == '\t') {
|
|
619
|
+
if (sc - save_col > 5) break;
|
|
620
|
+
int next = critic_col_add(sc, *s);
|
|
621
|
+
if (sc - save_col <= 5) {
|
|
622
|
+
sc = next;
|
|
623
|
+
s++;
|
|
624
|
+
if (sc - save_col > 5) break;
|
|
625
|
+
} else {
|
|
626
|
+
break;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
int i = sc - save_col;
|
|
630
|
+
int padding;
|
|
631
|
+
const char *content;
|
|
632
|
+
int content_col;
|
|
633
|
+
if (i >= 5 || i < 1 || *s == '\0' || *s == '\n' || *s == '\r') {
|
|
634
|
+
padding = marker_len + 1;
|
|
635
|
+
content_col = after_col;
|
|
636
|
+
content = after;
|
|
637
|
+
if (i > 0) content = critic_advance_columns(after, &content_col, 1);
|
|
638
|
+
} else {
|
|
639
|
+
padding = marker_len + i;
|
|
640
|
+
content = s;
|
|
641
|
+
content_col = sc;
|
|
642
|
+
}
|
|
643
|
+
if (st->depth < 32) {
|
|
644
|
+
st->stack[st->depth].kind = CRITIC_BOX_ITEM;
|
|
645
|
+
st->stack[st->depth].need = marker_offset + padding;
|
|
646
|
+
st->stack[st->depth].has_content = false;
|
|
647
|
+
st->depth++;
|
|
648
|
+
}
|
|
649
|
+
p = content;
|
|
650
|
+
col = content_col;
|
|
651
|
+
st->in_paragraph = false;
|
|
652
|
+
st->in_code = false;
|
|
653
|
+
continue;
|
|
654
|
+
}
|
|
655
|
+
if (critic_is_fence_opener(q) || critic_is_atx(q) || critic_is_rule(q, st->in_paragraph)) {
|
|
656
|
+
st->in_paragraph = false;
|
|
657
|
+
st->in_code = false;
|
|
658
|
+
critic_mark_content(st);
|
|
659
|
+
return false;
|
|
660
|
+
}
|
|
661
|
+
st->in_paragraph = true;
|
|
662
|
+
st->in_code = false;
|
|
663
|
+
critic_mark_content(st);
|
|
664
|
+
return false;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/* True when this line is indented code and should be copied unchanged.
|
|
669
|
+
* Updates block state either way. Call only at the start of a line. */
|
|
670
|
+
static bool critic_indented_code_line(const char *line, critic_blocks *st) {
|
|
671
|
+
const char *p;
|
|
672
|
+
int col;
|
|
673
|
+
int matched = critic_match_boxes(line, st, &p, &col);
|
|
674
|
+
bool blank = critic_line_blank(line);
|
|
675
|
+
|
|
676
|
+
if (matched < st->depth) {
|
|
677
|
+
if (st->in_paragraph && !blank && !critic_interrupts_paragraph(p, col)) {
|
|
678
|
+
return false; /* lazy continuation */
|
|
679
|
+
}
|
|
680
|
+
st->depth = matched;
|
|
681
|
+
st->in_code = false;
|
|
682
|
+
st->in_paragraph = false;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
if (blank) {
|
|
686
|
+
st->in_paragraph = false;
|
|
687
|
+
if (matched < st->depth) st->in_code = false;
|
|
688
|
+
return false;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (st->in_code) {
|
|
692
|
+
int indent = 0;
|
|
693
|
+
int c = col;
|
|
694
|
+
critic_skip_indent(p, &c, &indent);
|
|
695
|
+
if (indent >= 4) return true;
|
|
696
|
+
st->in_code = false;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
return critic_interpret_line(st, p, col);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/* remaining counts bytes available before the terminating NUL. */
|
|
703
|
+
static bool critic_append(char **buf, char **write, size_t *remaining,
|
|
704
|
+
const char *src, size_t len) {
|
|
705
|
+
if (len >= *remaining) {
|
|
706
|
+
size_t written = (size_t)(*write - *buf);
|
|
707
|
+
size_t new_cap = (written + len + 1) * 2;
|
|
708
|
+
char *grown = realloc(*buf, new_cap);
|
|
709
|
+
if (!grown) return false;
|
|
710
|
+
*buf = grown;
|
|
711
|
+
*write = grown + written;
|
|
712
|
+
*remaining = new_cap - written - 1;
|
|
713
|
+
}
|
|
714
|
+
if (len) {
|
|
715
|
+
memcpy(*write, src, len);
|
|
716
|
+
*write += len;
|
|
717
|
+
*remaining -= len;
|
|
718
|
+
}
|
|
719
|
+
return true;
|
|
720
|
+
}
|
|
721
|
+
|
|
281
722
|
/**
|
|
282
723
|
* Process Critic Markup in raw text (preprocessing approach)
|
|
283
|
-
* This is better than postprocessing because it avoids smart typography interference
|
|
724
|
+
* This is better than postprocessing because it avoids smart typography interference.
|
|
725
|
+
* Code spans, fenced blocks, and indented code blocks are copied through
|
|
726
|
+
* so the syntax can be shown literally.
|
|
284
727
|
*/
|
|
285
728
|
char *apex_process_critic_markup_text(const char *text, critic_mode_t mode) {
|
|
286
729
|
if (!text) return NULL;
|
|
287
730
|
|
|
288
731
|
size_t len = strlen(text);
|
|
289
|
-
size_t output_capacity = len * 2
|
|
732
|
+
size_t output_capacity = len * 2 + 1;
|
|
290
733
|
char *output = malloc(output_capacity);
|
|
291
734
|
if (!output) return NULL;
|
|
292
735
|
|
|
293
736
|
const char *read_pos = text;
|
|
294
737
|
char *write_pos = output;
|
|
295
|
-
size_t remaining_capacity = output_capacity;
|
|
738
|
+
size_t remaining_capacity = output_capacity - 1;
|
|
739
|
+
critic_blocks blocks;
|
|
740
|
+
memset(&blocks, 0, sizeof(blocks));
|
|
296
741
|
|
|
297
742
|
while (*read_pos) {
|
|
743
|
+
bool line_start = (read_pos == text || read_pos[-1] == '\n' ||
|
|
744
|
+
(read_pos[-1] == '\r' && *read_pos != '\n'));
|
|
745
|
+
if (line_start && critic_indented_code_line(read_pos, &blocks)) {
|
|
746
|
+
const char *next = critic_next_line(read_pos);
|
|
747
|
+
if (!critic_append(&output, &write_pos, &remaining_capacity, read_pos, (size_t)(next - read_pos))) {
|
|
748
|
+
free(output);
|
|
749
|
+
return NULL;
|
|
750
|
+
}
|
|
751
|
+
read_pos = next;
|
|
752
|
+
continue;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
size_t code_len = critic_code_region_len(text, read_pos);
|
|
756
|
+
if (code_len > 0) {
|
|
757
|
+
if (!critic_append(&output, &write_pos, &remaining_capacity, read_pos, code_len)) {
|
|
758
|
+
free(output);
|
|
759
|
+
return NULL;
|
|
760
|
+
}
|
|
761
|
+
read_pos += code_len;
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
|
|
298
765
|
/* Look for { */
|
|
299
766
|
if (*read_pos == '{') {
|
|
300
767
|
int consumed;
|
|
@@ -310,11 +777,10 @@ char *apex_process_critic_markup_text(const char *text, critic_mode_t mode) {
|
|
|
310
777
|
/* Found valid critic markup - convert to HTML */
|
|
311
778
|
char *html = critic_to_html(type, content, content_len, old_text, old_len, mode);
|
|
312
779
|
if (html) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
remaining_capacity -= html_len;
|
|
780
|
+
if (!critic_append(&output, &write_pos, &remaining_capacity, html, strlen(html))) {
|
|
781
|
+
free(html);
|
|
782
|
+
free(output);
|
|
783
|
+
return NULL;
|
|
318
784
|
}
|
|
319
785
|
free(html);
|
|
320
786
|
}
|
|
@@ -324,9 +790,9 @@ char *apex_process_critic_markup_text(const char *text, critic_mode_t mode) {
|
|
|
324
790
|
}
|
|
325
791
|
|
|
326
792
|
/* Not critic markup, copy character */
|
|
327
|
-
if (remaining_capacity
|
|
328
|
-
|
|
329
|
-
|
|
793
|
+
if (!critic_append(&output, &write_pos, &remaining_capacity, read_pos, 1)) {
|
|
794
|
+
free(output);
|
|
795
|
+
return NULL;
|
|
330
796
|
}
|
|
331
797
|
read_pos++;
|
|
332
798
|
}
|
|
@@ -470,7 +470,9 @@ static void collect_nodes_with_attrs_recursive(cmark_node *node, attr_node **lis
|
|
|
470
470
|
/* Increment counter for this element type */
|
|
471
471
|
int elem_idx = -1;
|
|
472
472
|
if (type == CMARK_NODE_PARAGRAPH) elem_idx = counters->para_count++;
|
|
473
|
-
|
|
473
|
+
/* One node type for every heading level. HEADING+1..+5 are thematic
|
|
474
|
+
* breaks, footnotes, and the GFM table nodes, not h2-h6. */
|
|
475
|
+
else if (type == CMARK_NODE_HEADING) elem_idx = counters->heading_count++;
|
|
474
476
|
else if (type == CMARK_NODE_TABLE) {
|
|
475
477
|
/* For tables, increment the counter first, then use (count - 1) as the index */
|
|
476
478
|
/* This ensures the index matches the HTML renderer's count of <table> tags */
|
|
@@ -655,8 +657,7 @@ char *apex_render_html_with_attributes(cmark_node *document, int options) {
|
|
|
655
657
|
for (attr_node *a = attr_list; a; a = a->next, check_idx++) {
|
|
656
658
|
/* Check by element type and index for removal */
|
|
657
659
|
if (!used[check_idx] &&
|
|
658
|
-
|
|
659
|
-
(elem_type == CMARK_NODE_HEADING && a->node_type >= CMARK_NODE_HEADING && a->node_type <= CMARK_NODE_HEADING + 5)) &&
|
|
660
|
+
a->node_type == elem_type &&
|
|
660
661
|
a->element_index == elem_idx) {
|
|
661
662
|
if (strstr(a->attrs, "data-remove")) {
|
|
662
663
|
should_remove = true;
|
|
@@ -846,9 +847,7 @@ char *apex_render_html_with_attributes(cmark_node *document, int options) {
|
|
|
846
847
|
for (attr_node *a = attr_list; a; a = a->next, idx++) {
|
|
847
848
|
if (used[idx]) continue;
|
|
848
849
|
|
|
849
|
-
|
|
850
|
-
bool type_match = (a->node_type == elem_type ||
|
|
851
|
-
(elem_type == CMARK_NODE_HEADING && a->node_type >= CMARK_NODE_HEADING && a->node_type <= CMARK_NODE_HEADING + 5));
|
|
850
|
+
bool type_match = (a->node_type == elem_type);
|
|
852
851
|
|
|
853
852
|
if (!type_match) continue;
|
|
854
853
|
|
data/lib/apex/version.rb
CHANGED