@@ -33,12 +33,18 @@ static const char *RIGHTSINGLEQUOTE = "\xE2\x80\x99";
33
33
34
34
#define MAXBACKTICKS 80
35
35
36
+ typedef enum {
37
+ LINK ,
38
+ IMAGE ,
39
+ ATTRIBUTE
40
+ } bracket_type ;
41
+
36
42
typedef struct bracket {
37
43
struct bracket * previous ;
38
44
struct delimiter * previous_delimiter ;
39
45
cmark_node * inl_text ;
40
46
bufsize_t position ;
41
- bool image ;
47
+ bracket_type type ;
42
48
bool active ;
43
49
bool bracket_after ;
44
50
} bracket ;
@@ -513,12 +519,12 @@ static void push_delimiter(subject *subj, unsigned char c, bool can_open,
513
519
subj -> last_delim = delim ;
514
520
}
515
521
516
- static void push_bracket (subject * subj , bool image , cmark_node * inl_text ) {
522
+ static void push_bracket (subject * subj , bracket_type type , cmark_node * inl_text ) {
517
523
bracket * b = (bracket * )subj -> mem -> calloc (1 , sizeof (bracket ));
518
524
if (subj -> last_bracket != NULL ) {
519
525
subj -> last_bracket -> bracket_after = true;
520
526
}
521
- b -> image = image ;
527
+ b -> type = type ;
522
528
b -> active = true;
523
529
b -> inl_text = inl_text ;
524
530
b -> previous = subj -> last_bracket ;
@@ -1032,7 +1038,91 @@ static bufsize_t manual_scan_link_url(cmark_chunk *input, bufsize_t offset,
1032
1038
return i - offset ;
1033
1039
}
1034
1040
1035
- // Return a link, an image, or a literal close bracket.
1041
+ static bufsize_t manual_scan_attribute_attributes (cmark_chunk * input , bufsize_t offset ,
1042
+ cmark_chunk * output ) {
1043
+ bufsize_t i = offset ;
1044
+ size_t nb_p = 0 ;
1045
+
1046
+ while (i < input -> len ) {
1047
+ if (input -> data [i ] == '\\' &&
1048
+ i + 1 < input -> len &&
1049
+ cmark_ispunct (input -> data [i + 1 ]))
1050
+ i += 2 ;
1051
+ else if (input -> data [i ] == '(' ) {
1052
+ ++ nb_p ;
1053
+ ++ i ;
1054
+ if (nb_p > 32 )
1055
+ return -1 ;
1056
+ } else if (input -> data [i ] == ')' ) {
1057
+ if (nb_p == 0 )
1058
+ break ;
1059
+ -- nb_p ;
1060
+ ++ i ;
1061
+ } else {
1062
+ ++ i ;
1063
+ }
1064
+ }
1065
+
1066
+ if (i >= input -> len )
1067
+ return -1 ;
1068
+
1069
+ {
1070
+ cmark_chunk result = {input -> data + offset , i - offset , 0 };
1071
+ * output = result ;
1072
+ }
1073
+ return i - offset ;
1074
+ }
1075
+
1076
+ static cmark_node * handle_close_bracket_attribute (cmark_parser * parser , subject * subj , bracket * opener ) {
1077
+ bufsize_t startattributes , endattributes ;
1078
+ cmark_chunk attributes ;
1079
+ bufsize_t n ;
1080
+ cmark_node * inl ;
1081
+ cmark_chunk raw_label ;
1082
+ int found_label ;
1083
+ cmark_node * tmp , * tmpnext ;
1084
+
1085
+ // ^name[content](attributes)
1086
+ // TODO: support name. we will not even enter this with a name because we fail the match first
1087
+
1088
+ startattributes = subj -> pos + 1 ;
1089
+
1090
+ if (peek_char (subj ) == '(' &&
1091
+ ((n = manual_scan_attribute_attributes (& subj -> input , subj -> pos + 1 ,
1092
+ & attributes )) > -1 )) {
1093
+
1094
+ endattributes = subj -> pos + 1 + n ;
1095
+
1096
+ if (peek_at (subj , endattributes ) == ')' ) {
1097
+ subj -> pos = endattributes + 1 ;
1098
+ attributes = cmark_chunk_dup (& subj -> input , startattributes , endattributes - startattributes );
1099
+ }
1100
+ }
1101
+
1102
+ inl = make_simple (subj -> mem , CMARK_NODE_ATTRIBUTE );
1103
+ inl -> as .attribute .attributes = attributes ;
1104
+ inl -> start_line = inl -> end_line = subj -> line ;
1105
+ inl -> start_column = opener -> inl_text -> start_column ;
1106
+ inl -> end_column = subj -> pos + subj -> column_offset + subj -> block_offset ;
1107
+ cmark_node_insert_before (opener -> inl_text , inl );
1108
+ // Add content text:
1109
+ tmp = opener -> inl_text -> next ;
1110
+ while (tmp ) {
1111
+ tmpnext = tmp -> next ;
1112
+ cmark_node_append_child (inl , tmp );
1113
+ tmp = tmpnext ;
1114
+ }
1115
+
1116
+ // Free the bracket ^[:
1117
+ cmark_node_free (opener -> inl_text );
1118
+
1119
+ process_emphasis (parser , subj , opener -> previous_delimiter );
1120
+ pop_bracket (subj );
1121
+
1122
+ return NULL ;
1123
+ }
1124
+
1125
+ // Return a link, an image, an attribute, or a literal close bracket.
1036
1126
static cmark_node * handle_close_bracket (cmark_parser * parser , subject * subj ) {
1037
1127
bufsize_t initial_pos , after_link_text_pos ;
1038
1128
bufsize_t endurl , starttitle , endtitle , endall ;
@@ -1050,7 +1140,7 @@ static cmark_node *handle_close_bracket(cmark_parser *parser, subject *subj) {
1050
1140
advance (subj ); // advance past ]
1051
1141
initial_pos = subj -> pos ;
1052
1142
1053
- // get last [ or ![
1143
+ // get last [ or ![ or ^[
1054
1144
opener = subj -> last_bracket ;
1055
1145
1056
1146
if (opener == NULL ) {
@@ -1063,9 +1153,13 @@ static cmark_node *handle_close_bracket(cmark_parser *parser, subject *subj) {
1063
1153
return make_str (subj , subj -> pos - 1 , subj -> pos - 1 , cmark_chunk_literal ("]" ));
1064
1154
}
1065
1155
1156
+ if (opener -> type == ATTRIBUTE ) {
1157
+ return handle_close_bracket_attribute (parser , subj , opener );
1158
+ }
1159
+
1066
1160
// If we got here, we matched a potential link/image text.
1067
1161
// Now we check to see if it's a link/image.
1068
- is_image = opener -> image ;
1162
+ is_image = opener -> type == IMAGE ;
1069
1163
1070
1164
after_link_text_pos = subj -> pos ;
1071
1165
@@ -1188,7 +1282,7 @@ static cmark_node *handle_close_bracket(cmark_parser *parser, subject *subj) {
1188
1282
if (!is_image ) {
1189
1283
opener = subj -> last_bracket ;
1190
1284
while (opener != NULL ) {
1191
- if (! opener -> image ) {
1285
+ if (opener -> type == LINK ) {
1192
1286
if (!opener -> active ) {
1193
1287
break ;
1194
1288
} else {
@@ -1341,7 +1435,7 @@ static int parse_inline(cmark_parser *parser, subject *subj, cmark_node *parent,
1341
1435
case '[' :
1342
1436
advance (subj );
1343
1437
new_inl = make_str (subj , subj -> pos - 1 , subj -> pos - 1 , cmark_chunk_literal ("[" ));
1344
- push_bracket (subj , false , new_inl );
1438
+ push_bracket (subj , LINK , new_inl );
1345
1439
break ;
1346
1440
case ']' :
1347
1441
new_inl = handle_close_bracket (parser , subj );
@@ -1351,11 +1445,22 @@ static int parse_inline(cmark_parser *parser, subject *subj, cmark_node *parent,
1351
1445
if (peek_char (subj ) == '[' && peek_char_n (subj , 1 ) != '^' ) {
1352
1446
advance (subj );
1353
1447
new_inl = make_str (subj , subj -> pos - 2 , subj -> pos - 1 , cmark_chunk_literal ("![" ));
1354
- push_bracket (subj , true , new_inl );
1448
+ push_bracket (subj , IMAGE , new_inl );
1355
1449
} else {
1356
1450
new_inl = make_str (subj , subj -> pos - 1 , subj -> pos - 1 , cmark_chunk_literal ("!" ));
1357
1451
}
1358
1452
break ;
1453
+ case '^' :
1454
+ advance (subj );
1455
+ // TODO: Support a name between ^ and [
1456
+ if (peek_char (subj ) == '[' ) {
1457
+ advance (subj );
1458
+ new_inl = make_str (subj , subj -> pos - 2 , subj -> pos - 1 , cmark_chunk_literal ("^[" ));
1459
+ push_bracket (subj , ATTRIBUTE , new_inl );
1460
+ } else {
1461
+ new_inl = make_str (subj , subj -> pos - 1 , subj -> pos - 1 , cmark_chunk_literal ("^" ));
1462
+ }
1463
+ break ;
1359
1464
default :
1360
1465
new_inl = try_extensions (parser , parent , c , subj );
1361
1466
if (new_inl != NULL )
@@ -1604,10 +1709,19 @@ cmark_chunk *cmark_inline_parser_get_chunk(cmark_inline_parser *parser) {
1604
1709
return & parser -> input ;
1605
1710
}
1606
1711
1607
- int cmark_inline_parser_in_bracket (cmark_inline_parser * parser , int image ) {
1608
- for (bracket * b = parser -> last_bracket ; b ; b = b -> previous )
1609
- if (b -> active && b -> image == (image != 0 ))
1610
- return 1 ;
1712
+ int cmark_inline_parser_in_bracket (cmark_inline_parser * parser , int type ) {
1713
+ for (bracket * b = parser -> last_bracket ; b ; b = b -> previous ) {
1714
+ if (b -> active ) {
1715
+ switch (type ) {
1716
+ case 0 :
1717
+ return b -> type == LINK ;
1718
+ case 1 :
1719
+ return b -> type == IMAGE ;
1720
+ case 2 :
1721
+ return b -> type == ATTRIBUTE ;
1722
+ }
1723
+ }
1724
+ }
1611
1725
return 0 ;
1612
1726
}
1613
1727
0 commit comments