markly 0.16.0 → 0.18.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fda5c52f4ac015dde2e9f22e2a76467ddcb655d197897f7bbd76c7da235f2991
4
- data.tar.gz: 936fdce7bd2fde2339e48bf43a9b4f9ae8c4b552844de6cf69bd445965477a3f
3
+ metadata.gz: a6df3d157ca441e13f840de336bf04408669aae72ff791d35be091ee008da87c
4
+ data.tar.gz: 682356cc37b69d2a486624ffd0e0c02f75a717b07f32c0a41d838e2032730223
5
5
  SHA512:
6
- metadata.gz: 74eae720741b8b4aee9e4d01d92d25d53cbd92f46066a4eb341881907a9c5291102b1cb10c4c59c81b2192adc4156bd6eaf619e51836ef661be03dde450f4415
7
- data.tar.gz: 1080d1c3a72e238325e017974e34c1ad7ed56174811adcbf1c97d6b860566dd4886ebb71a9a634e8bc05826afd71045b4ef72edb622740ff72af8d6b887534ae
6
+ metadata.gz: 70315346cac721d22cbc67c0efaee8bd6a035198fec894388a827136c3710d60844c24e29a26a4374a899af01212618412d929acfd840db03ce887f79e2b80ef
7
+ data.tar.gz: 895682bb5cd75d95306c2e60b9bb446b4459ade9800d8a29e5ecd13b4fb9f8e1caa33518ac1d3f6779143af9f213d50a0c17460ded2fc94eea57872e7aba9c26
checksums.yaml.gz.sig CHANGED
Binary file
@@ -42,7 +42,9 @@ Markly accepts integer flags which control how the Markdown is parsed and render
42
42
  | ------------------------------------ | -----------
43
43
  | `Markly::DEFAULT` | The default parsing system.
44
44
  | `Markly::UNSAFE` | Allow raw/custom HTML and unsafe links.
45
+ | `Markly::FRONT_MATTER` | Parse front matter at the start of the document.
45
46
  | `Markly::FOOTNOTES` | Parse footnotes.
47
+ | `Markly::INLINE_CODE_INFO` | Parse language prefixes such as `ruby:` on inline code spans.
46
48
  | `Markly::LIBERAL_HTML_TAG` | Support liberal parsing of inline HTML tags.
47
49
  | `Markly::SMART` | Use smart punctuation (curly quotes, etc.).
48
50
  | `Markly::STRIKETHROUGH_DOUBLE_TILDE` | Parse strikethroughs by double tildes (compatibility with [redcarpet](https://github.com/vmg/redcarpet))
@@ -76,6 +78,40 @@ To have multiple options applied, `|` (or) the flags together:
76
78
  Markly.render_html("\"'Shelob' is my name.\"", flags: Markly::HARD_BREAKS|Markly::SOURCE_POSITION)
77
79
  ```
78
80
 
81
+ Inline code language prefixes are opt-in. The language is available through
82
+ `Node#code_info` and is rendered as a `language-...` class:
83
+
84
+ ``` ruby
85
+ document = Markly.parse("ruby:`Object.new`", flags: Markly::INLINE_CODE_INFO)
86
+ code = document.first_child.first_child
87
+
88
+ code.code_info
89
+ # => "ruby"
90
+
91
+ code.code_language
92
+ # => "ruby"
93
+
94
+ document.to_html
95
+ # => <p><code class="language-ruby">Object.new</code></p>
96
+ ```
97
+
98
+ `Node#code_info` is also the general info-string accessor for fenced code
99
+ blocks and front matter. `Node#code_language` returns the first token of that
100
+ info string, while `Node#fence_info` remains available for compatibility on
101
+ those block nodes.
102
+
103
+ For a fenced code block, `Node#fence` returns a `Node::Fence` structure with
104
+ the fence character, length, and indentation:
105
+
106
+ ``` ruby
107
+ block = Markly.parse(" ~~~~ ruby\n Object.new\n ~~~~").first_child
108
+
109
+ block.fence
110
+ # => #<struct Markly::Node::Fence character="~", length=4, indent=2>
111
+ ```
112
+
113
+ Indented code blocks and other node types return `nil`.
114
+
79
115
  ## Extensions
80
116
 
81
117
  Both `render_html` and `parse` take an optional `extensions:` argument defining the extensions you want enabled as your CommonMark document is being processed:
@@ -264,6 +264,10 @@ typedef void (*cmark_opaque_free_func) (cmark_syntax_extension *extension,
264
264
  cmark_mem *mem,
265
265
  cmark_node *node);
266
266
 
267
+ typedef void (*cmark_opaque_copy_func)(cmark_syntax_extension *extension,
268
+ cmark_mem *mem, cmark_node *node,
269
+ cmark_node *source);
270
+
267
271
  /** Free a cmark_syntax_extension.
268
272
  */
269
273
  CMARK_GFM_EXPORT
@@ -406,6 +410,12 @@ CMARK_GFM_EXPORT
406
410
  void cmark_syntax_extension_set_opaque_free_func(cmark_syntax_extension *extension,
407
411
  cmark_opaque_free_func func);
408
412
 
413
+ /** See the documentation for 'cmark_syntax_extension'
414
+ */
415
+ CMARK_GFM_EXPORT
416
+ void cmark_syntax_extension_set_opaque_copy_func(
417
+ cmark_syntax_extension *extension, cmark_opaque_copy_func func);
418
+
409
419
  /** See the documentation for 'cmark_syntax_extension'
410
420
  */
411
421
  CMARK_GFM_EXPORT
@@ -196,6 +196,12 @@ CMARK_GFM_EXPORT cmark_node *cmark_node_new_with_mem_and_ext(cmark_node_type typ
196
196
  cmark_mem *mem,
197
197
  cmark_syntax_extension *extension);
198
198
 
199
+ /** Creates an independent deep copy of 'node' and all its children.
200
+ * User data is not copied. Returns NULL when extension-specific node data
201
+ * cannot be copied.
202
+ */
203
+ CMARK_GFM_EXPORT cmark_node *cmark_node_clone(cmark_node *node);
204
+
199
205
  /** Frees the memory allocated for a node and any children.
200
206
  */
201
207
  CMARK_GFM_EXPORT void cmark_node_free(cmark_node *node);
@@ -425,6 +431,18 @@ CMARK_GFM_EXPORT int cmark_node_get_item_index(cmark_node *node);
425
431
  */
426
432
  CMARK_GFM_EXPORT int cmark_node_set_item_index(cmark_node *node, int idx);
427
433
 
434
+ /** Returns the info string from a code block or the language identifier from
435
+ * an inline code span.
436
+ */
437
+ CMARK_GFM_EXPORT const char *cmark_node_get_code_info(cmark_node *node);
438
+
439
+ /** Sets the info string in a code block or the language identifier on an
440
+ * inline code span, returning 1 on success and 0 on failure. Inline code
441
+ * language identifiers must begin with an alphanumeric character and may
442
+ * additionally contain '_', '-', '+', '#', and '.'.
443
+ */
444
+ CMARK_GFM_EXPORT int cmark_node_set_code_info(cmark_node *node, const char *info);
445
+
428
446
  /** Returns the info string from a fenced code block.
429
447
  */
430
448
  CMARK_GFM_EXPORT const char *cmark_node_get_fence_info(cmark_node *node);
@@ -776,6 +794,10 @@ char *cmark_render_latex_with_mem(cmark_node *root, int options, int width, cmar
776
794
  */
777
795
  #define CMARK_OPT_FRONT_MATTER (1 << 18)
778
796
 
797
+ /** Parse inline code language prefixes, e.g. ruby:`code`.
798
+ */
799
+ #define CMARK_OPT_INLINE_CODE_INFO (1 << 19)
800
+
779
801
  /**
780
802
  * ## Version information
781
803
  */
@@ -33,6 +33,7 @@
33
33
  # define CMARK_GFM_DEPRECATED_NO_EXPORT CMARK_GFM_NO_EXPORT CMARK_GFM_DEPRECATED
34
34
  #endif
35
35
 
36
+ /* NOLINTNEXTLINE(readability-avoid-unconditional-preprocessor-if) */
36
37
  #if 0 /* DEFINE_NO_DEPRECATED */
37
38
  # ifndef CMARK_GFM_NO_DEPRECATED
38
39
  # define CMARK_GFM_NO_DEPRECATED
@@ -1,7 +1,7 @@
1
1
  #ifndef CMARK_GFM_VERSION_H
2
2
  #define CMARK_GFM_VERSION_H
3
3
 
4
- #define CMARK_GFM_VERSION ((0 << 24) | (29 << 16) | (0 << 8) | 2)
5
- #define CMARK_GFM_VERSION_STRING "0.29.0.gfm.2"
4
+ #define CMARK_GFM_VERSION ((0 << 24) | (29 << 16) | (0 << 8) | 13)
5
+ #define CMARK_GFM_VERSION_STRING "0.29.0.gfm.13"
6
6
 
7
7
  #endif
@@ -42,3 +42,12 @@ int cmark_isalnum(char c) {
42
42
  int cmark_isdigit(char c) { return cmark_ctype_class[(uint8_t)c] == 3; }
43
43
 
44
44
  int cmark_isalpha(char c) { return cmark_ctype_class[(uint8_t)c] == 4; }
45
+
46
+ int cmark_is_inline_code_info_start_char(char c) {
47
+ return cmark_isalnum(c);
48
+ }
49
+
50
+ int cmark_is_inline_code_info_char(char c) {
51
+ return cmark_is_inline_code_info_start_char(c) || c == '_' || c == '-' ||
52
+ c == '+' || c == '#' || c == '.';
53
+ }
@@ -26,6 +26,11 @@ int cmark_isdigit(char c);
26
26
  CMARK_GFM_EXPORT
27
27
  int cmark_isalpha(char c);
28
28
 
29
+ /* Character classes for inline code info identifiers. */
30
+ int cmark_is_inline_code_info_start_char(char c);
31
+
32
+ int cmark_is_inline_code_info_char(char c);
33
+
29
34
  #ifdef __cplusplus
30
35
  }
31
36
  #endif
@@ -271,7 +271,7 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
271
271
  if (!first_in_list_item) {
272
272
  BLANKLINE();
273
273
  }
274
- info = cmark_node_get_fence_info(node);
274
+ info = cmark_node_get_code_info(node);
275
275
  info_len = strlen(info);
276
276
  fencechar[0] = strchr(info, '`') == NULL ? '`' : '~';
277
277
  code = cmark_node_get_literal(node);
@@ -363,6 +363,10 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
363
363
  extra_spaces = code_len == 0 ||
364
364
  code[0] == '`' || code[code_len - 1] == '`' ||
365
365
  code[0] == ' ' || code[code_len - 1] == ' ';
366
+ if (node->as.code.info.len > 0) {
367
+ OUT(cmark_node_get_code_info(node), false, LITERAL);
368
+ LIT(":");
369
+ }
366
370
  for (i = 0; i < numticks; i++) {
367
371
  LIT("`");
368
372
  }
@@ -494,10 +498,13 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
494
498
 
495
499
  case CMARK_NODE_FRONT_MATTER:
496
500
  if (entering) {
497
- const char *info = cmark_node_get_fence_info(node);
501
+ const char *front_matter_info = cmark_node_get_fence_info(node);
498
502
  BLANKLINE();
499
503
  LIT("---");
500
- if (info && *info) { LIT(" "); OUT(info, false, LITERAL); }
504
+ if (front_matter_info && *front_matter_info) {
505
+ LIT(" ");
506
+ OUT(front_matter_info, false, LITERAL);
507
+ }
501
508
  LIT("\n");
502
509
  OUT(cmark_node_get_literal(node), false, LITERAL);
503
510
  LIT("---\n");
data/ext/markly/config.h CHANGED
@@ -5,9 +5,7 @@
5
5
  extern "C" {
6
6
  #endif
7
7
 
8
- #ifndef HAVE_STDBOOL_H
9
8
  #define HAVE_STDBOOL_H
10
- #endif
11
9
 
12
10
  #ifdef HAVE_STDBOOL_H
13
11
  #include <stdbool.h>
@@ -71,8 +69,6 @@ CMARK_INLINE int c99_snprintf(char *outBuf, size_t size, const char *format, ...
71
69
 
72
70
  #endif
73
71
 
74
- #define CMARK_DEBUG_NODES 0
75
-
76
72
  #ifdef __cplusplus
77
73
  }
78
74
  #endif
@@ -842,6 +842,29 @@ static void opaque_free(cmark_syntax_extension *self, cmark_mem *mem, cmark_node
842
842
  }
843
843
  }
844
844
 
845
+ static void opaque_copy(cmark_syntax_extension *self, cmark_mem *mem,
846
+ cmark_node *node, cmark_node *source) {
847
+ if (node->type == CMARK_NODE_TABLE) {
848
+ node_table *table = (node_table *)node->as.opaque;
849
+ node_table *source_table = (node_table *)source->as.opaque;
850
+
851
+ table->n_columns = source_table->n_columns;
852
+ table->n_rows = source_table->n_rows;
853
+ table->n_nonempty_cells = source_table->n_nonempty_cells;
854
+
855
+ if (source_table->alignments) {
856
+ table->alignments = mem->calloc(source_table->n_columns, sizeof(uint8_t));
857
+ memcpy(table->alignments, source_table->alignments,
858
+ source_table->n_columns * sizeof(uint8_t));
859
+ }
860
+ } else if (node->type == CMARK_NODE_TABLE_ROW) {
861
+ *(node_table_row *)node->as.opaque = *(node_table_row *)source->as.opaque;
862
+ } else if (node->type == CMARK_NODE_TABLE_CELL) {
863
+ mem->free(node->as.opaque);
864
+ node->as.cell_index = source->as.cell_index;
865
+ }
866
+ }
867
+
845
868
  static int escape(cmark_syntax_extension *self, cmark_node *node, int c) {
846
869
  return
847
870
  node->type != CMARK_NODE_TABLE &&
@@ -867,6 +890,7 @@ cmark_syntax_extension *create_table_extension(void) {
867
890
  cmark_syntax_extension_set_html_render_func(self, html_render);
868
891
  cmark_syntax_extension_set_opaque_alloc_func(self, opaque_alloc);
869
892
  cmark_syntax_extension_set_opaque_free_func(self, opaque_free);
893
+ cmark_syntax_extension_set_opaque_copy_func(self, opaque_copy);
870
894
  cmark_syntax_extension_set_commonmark_escape_func(self, escape);
871
895
  CMARK_NODE_TABLE = cmark_syntax_extension_add_node(0);
872
896
  CMARK_NODE_TABLE_ROW = cmark_syntax_extension_add_node(0);
data/ext/markly/html.c CHANGED
@@ -18,6 +18,34 @@ static void escape_html(cmark_strbuf *dest, const unsigned char *source,
18
18
  houdini_escape_html0(dest, source, length, 0);
19
19
  }
20
20
 
21
+ static bufsize_t first_code_info_tag(cmark_chunk *info) {
22
+ bufsize_t first_tag = 0;
23
+ while (first_tag < info->len && !cmark_isspace(info->data[first_tag])) {
24
+ first_tag += 1;
25
+ }
26
+ return first_tag;
27
+ }
28
+
29
+ static void render_code_info_attrs(cmark_strbuf *html, cmark_chunk *info,
30
+ int options, bool pre_lang) {
31
+ bufsize_t first_tag = first_code_info_tag(info);
32
+
33
+ if (pre_lang) {
34
+ cmark_strbuf_puts(html, " lang=\"");
35
+ } else {
36
+ cmark_strbuf_puts(html, " class=\"language-");
37
+ }
38
+
39
+ escape_html(html, info->data, first_tag);
40
+
41
+ if (first_tag < info->len && (options & CMARK_OPT_FULL_INFO_STRING)) {
42
+ cmark_strbuf_puts(html, "\" data-meta=\"");
43
+ escape_html(html, info->data + first_tag + 1, info->len - first_tag - 1);
44
+ }
45
+
46
+ cmark_strbuf_putc(html, '"');
47
+ }
48
+
21
49
  static void filter_html_block(cmark_html_renderer *renderer, uint8_t *data, size_t len) {
22
50
  cmark_strbuf *html = renderer->html;
23
51
  cmark_llist *it;
@@ -125,7 +153,11 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
125
153
  case CMARK_NODE_TEXT:
126
154
  case CMARK_NODE_CODE:
127
155
  case CMARK_NODE_HTML_INLINE:
128
- escape_html(html, node->as.literal.data, node->as.literal.len);
156
+ if (node->type == CMARK_NODE_CODE) {
157
+ escape_html(html, node->as.code.literal.data, node->as.code.literal.len);
158
+ } else {
159
+ escape_html(html, node->as.literal.data, node->as.literal.len);
160
+ }
129
161
  break;
130
162
 
131
163
  case CMARK_NODE_LINEBREAK:
@@ -220,32 +252,17 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
220
252
  cmark_html_render_sourcepos(node, html, options);
221
253
  cmark_strbuf_puts(html, "><code>");
222
254
  } else {
223
- bufsize_t first_tag = 0;
224
- while (first_tag < node->as.code.info.len &&
225
- !cmark_isspace(node->as.code.info.data[first_tag])) {
226
- first_tag += 1;
227
- }
228
-
229
255
  if (options & CMARK_OPT_GITHUB_PRE_LANG) {
230
256
  cmark_strbuf_puts(html, "<pre");
231
257
  cmark_html_render_sourcepos(node, html, options);
232
- cmark_strbuf_puts(html, " lang=\"");
233
- escape_html(html, node->as.code.info.data, first_tag);
234
- if (first_tag < node->as.code.info.len && (options & CMARK_OPT_FULL_INFO_STRING)) {
235
- cmark_strbuf_puts(html, "\" data-meta=\"");
236
- escape_html(html, node->as.code.info.data + first_tag + 1, node->as.code.info.len - first_tag - 1);
237
- }
238
- cmark_strbuf_puts(html, "\"><code>");
258
+ render_code_info_attrs(html, &node->as.code.info, options, true);
259
+ cmark_strbuf_puts(html, "><code>");
239
260
  } else {
240
261
  cmark_strbuf_puts(html, "<pre");
241
262
  cmark_html_render_sourcepos(node, html, options);
242
- cmark_strbuf_puts(html, "><code class=\"language-");
243
- escape_html(html, node->as.code.info.data, first_tag);
244
- if (first_tag < node->as.code.info.len && (options & CMARK_OPT_FULL_INFO_STRING)) {
245
- cmark_strbuf_puts(html, "\" data-meta=\"");
246
- escape_html(html, node->as.code.info.data + first_tag + 1, node->as.code.info.len - first_tag - 1);
247
- }
248
- cmark_strbuf_puts(html, "\">");
263
+ cmark_strbuf_puts(html, "><code");
264
+ render_code_info_attrs(html, &node->as.code.info, options, false);
265
+ cmark_strbuf_putc(html, '>');
249
266
  }
250
267
  }
251
268
 
@@ -286,7 +303,7 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
286
303
 
287
304
  case CMARK_NODE_PARAGRAPH:
288
305
  parent = cmark_node_parent(node);
289
- grandparent = cmark_node_parent(parent);
306
+ grandparent = parent ? cmark_node_parent(parent) : NULL;
290
307
  if (grandparent != NULL && grandparent->type == CMARK_NODE_LIST) {
291
308
  tight = grandparent->as.list.tight;
292
309
  } else {
@@ -299,7 +316,8 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
299
316
  cmark_html_render_sourcepos(node, html, options);
300
317
  cmark_strbuf_putc(html, '>');
301
318
  } else {
302
- if (parent->type == CMARK_NODE_FOOTNOTE_DEFINITION && node->next == NULL) {
319
+ if (parent && parent->type == CMARK_NODE_FOOTNOTE_DEFINITION &&
320
+ node->next == NULL) {
303
321
  cmark_strbuf_putc(html, ' ');
304
322
  S_put_footnote_backref(renderer, html, parent);
305
323
  }
@@ -327,8 +345,12 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
327
345
  break;
328
346
 
329
347
  case CMARK_NODE_CODE:
330
- cmark_strbuf_puts(html, "<code>");
331
- escape_html(html, node->as.literal.data, node->as.literal.len);
348
+ cmark_strbuf_puts(html, "<code");
349
+ if (node->as.code.info.len > 0) {
350
+ render_code_info_attrs(html, &node->as.code.info, options, false);
351
+ }
352
+ cmark_strbuf_putc(html, '>');
353
+ escape_html(html, node->as.code.literal.data, node->as.code.literal.len);
332
354
  cmark_strbuf_puts(html, "</code>");
333
355
  break;
334
356
 
data/ext/markly/inlines.c CHANGED
@@ -24,7 +24,6 @@ static const char *RIGHTSINGLEQUOTE = "\xE2\x80\x99";
24
24
 
25
25
  // Macros for creating various kinds of simple.
26
26
  #define make_str(subj, sc, ec, s) make_literal(subj, CMARK_NODE_TEXT, sc, ec, s)
27
- #define make_code(subj, sc, ec, s) make_literal(subj, CMARK_NODE_CODE, sc, ec, s)
28
27
  #define make_raw_html(subj, sc, ec, s) make_literal(subj, CMARK_NODE_HTML_INLINE, sc, ec, s)
29
28
  #define make_linebreak(mem) make_simple(mem, CMARK_NODE_LINEBREAK)
30
29
  #define make_softbreak(mem) make_simple(mem, CMARK_NODE_SOFTBREAK)
@@ -104,6 +103,18 @@ static CMARK_INLINE cmark_node *make_simple(cmark_mem *mem, cmark_node_type t) {
104
103
  return e;
105
104
  }
106
105
 
106
+ static CMARK_INLINE cmark_node *make_code(subject *subj, int start_column,
107
+ int end_column, cmark_chunk literal) {
108
+ cmark_node *e = (cmark_node *)subj->mem->calloc(1, sizeof(*e));
109
+ cmark_strbuf_init(subj->mem, &e->content, 0);
110
+ e->type = CMARK_NODE_CODE;
111
+ e->as.code.literal = literal;
112
+ e->start_line = e->end_line = subj->line;
113
+ e->start_column = start_column + 1 + subj->column_offset + subj->block_offset;
114
+ e->end_column = end_column + 1 + subj->column_offset + subj->block_offset;
115
+ return e;
116
+ }
117
+
107
118
  // Like make_str, but parses entities.
108
119
  static cmark_node *make_str_with_entities(subject *subj,
109
120
  int start_column, int end_column,
@@ -385,6 +396,39 @@ static void S_normalize_code(cmark_strbuf *s) {
385
396
 
386
397
  }
387
398
 
399
+ static bufsize_t scan_inline_code_info_prefix(subject *subj, bufsize_t start) {
400
+ if (start >= subj->input.len ||
401
+ !cmark_is_inline_code_info_start_char((char)subj->input.data[start]) ||
402
+ (start > 0 && cmark_is_inline_code_info_char(
403
+ (char)subj->input.data[start - 1]))) {
404
+ return 0;
405
+ }
406
+
407
+ bufsize_t pos = start + 1;
408
+ while (pos < subj->input.len && cmark_is_inline_code_info_char(
409
+ (char)subj->input.data[pos])) {
410
+ pos++;
411
+ }
412
+
413
+ if (pos + 1 < subj->input.len && subj->input.data[pos] == ':' &&
414
+ subj->input.data[pos + 1] == '`') {
415
+ return pos;
416
+ }
417
+
418
+ return 0;
419
+ }
420
+
421
+ static bufsize_t find_inline_code_info_prefix(subject *subj, bufsize_t start,
422
+ bufsize_t limit) {
423
+ while (start < limit) {
424
+ if (scan_inline_code_info_prefix(subj, start)) {
425
+ return start;
426
+ }
427
+ start++;
428
+ }
429
+
430
+ return 0;
431
+ }
388
432
 
389
433
  // Parse backtick code section or raw backticks, return an inline.
390
434
  // Assumes that the subject has a backtick at the current position.
@@ -403,12 +447,35 @@ static cmark_node *handle_backticks(subject *subj, int options) {
403
447
  endpos - startpos - openticks.len);
404
448
  S_normalize_code(&buf);
405
449
 
406
- cmark_node *node = make_code(subj, startpos, endpos - openticks.len - 1, cmark_chunk_buf_detach(&buf));
407
- adjust_subj_node_newlines(subj, node, endpos - startpos, openticks.len, options);
450
+ cmark_node *node = make_code(subj, startpos, endpos - openticks.len - 1,
451
+ cmark_chunk_buf_detach(&buf));
452
+ adjust_subj_node_newlines(subj, node, endpos - startpos, openticks.len,
453
+ options);
408
454
  return node;
409
455
  }
410
456
  }
411
457
 
458
+ static cmark_node *handle_inline_code_info(subject *subj, int options) {
459
+ bufsize_t startpos = subj->pos;
460
+ bufsize_t colonpos = scan_inline_code_info_prefix(subj, startpos);
461
+ if (colonpos == 0) {
462
+ return NULL;
463
+ }
464
+
465
+ cmark_chunk info =
466
+ cmark_chunk_dup(&subj->input, startpos, colonpos - startpos);
467
+ subj->pos = colonpos + 1;
468
+
469
+ cmark_node *node = handle_backticks(subj, options);
470
+ if (node->type != CMARK_NODE_CODE) {
471
+ cmark_node_free(node);
472
+ subj->pos = startpos;
473
+ return NULL;
474
+ }
475
+
476
+ node->as.code.info = chunk_clone(subj->mem, &info);
477
+ return node;
478
+ }
412
479
 
413
480
  // Scan ***, **, or * and return number scanned, or 0.
414
481
  // Advances position.
@@ -1458,6 +1525,14 @@ static int parse_inline(cmark_parser *parser, subject *subj, cmark_node *parent,
1458
1525
  if (c == 0) {
1459
1526
  return 0;
1460
1527
  }
1528
+ if (options & CMARK_OPT_INLINE_CODE_INFO) {
1529
+ new_inl = handle_inline_code_info(subj, options);
1530
+ }
1531
+ if (new_inl != NULL) {
1532
+ append_child(parent, new_inl);
1533
+ return 1;
1534
+ }
1535
+
1461
1536
  switch (c) {
1462
1537
  case '\r':
1463
1538
  case '\n':
@@ -1511,6 +1586,13 @@ static int parse_inline(cmark_parser *parser, subject *subj, cmark_node *parent,
1511
1586
  break;
1512
1587
 
1513
1588
  endpos = subject_find_special_char(subj, options);
1589
+ if (options & CMARK_OPT_INLINE_CODE_INFO) {
1590
+ bufsize_t info_start =
1591
+ find_inline_code_info_prefix(subj, subj->pos + 1, endpos);
1592
+ if (info_start > 0 && info_start < endpos) {
1593
+ endpos = info_start;
1594
+ }
1595
+ }
1514
1596
  contents = cmark_chunk_dup(&subj->input, subj->pos, endpos - subj->pos);
1515
1597
  startpos = subj->pos;
1516
1598
  subj->pos = endpos;
@@ -139,10 +139,15 @@ void cmark_node_own(cmark_node *root) {
139
139
  switch (cur->type) {
140
140
  case CMARK_NODE_TEXT:
141
141
  case CMARK_NODE_HTML_INLINE:
142
- case CMARK_NODE_CODE:
143
142
  case CMARK_NODE_HTML_BLOCK:
144
143
  cmark_chunk_to_cstr(iter->mem, &cur->as.literal);
145
144
  break;
145
+ case CMARK_NODE_CODE:
146
+ cmark_chunk_to_cstr(iter->mem, &cur->as.code.literal);
147
+ if (cur->as.code.info.len > 0) {
148
+ cmark_chunk_to_cstr(iter->mem, &cur->as.code.info);
149
+ }
150
+ break;
146
151
  case CMARK_NODE_LINK:
147
152
  cmark_chunk_to_cstr(iter->mem, &cur->as.link.url);
148
153
  cmark_chunk_to_cstr(iter->mem, &cur->as.link.title);