From 7ce64b94773e92bddd1293dd1b242a3b369f832c Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Sun, 2 Apr 2023 15:51:23 +0100 Subject: [PATCH 1/2] Store list item index on the node during rendering, to avoid quadratic performance. --- src/cmark.h | 11 +++++++++++ src/commonmark.c | 8 +------- src/man.c | 8 +------- src/node.c | 25 +++++++++++++++++++++++++ src/render.c | 9 +++++++++ 5 files changed, 47 insertions(+), 14 deletions(-) diff --git a/src/cmark.h b/src/cmark.h index d2021f0ff..285fbdf37 100644 --- a/src/cmark.h +++ b/src/cmark.h @@ -329,6 +329,17 @@ CMARK_EXPORT int cmark_node_get_list_tight(cmark_node *node); */ CMARK_EXPORT int cmark_node_set_list_tight(cmark_node *node, int tight); +/** + * Returns item index of 'node'. This is only used when rendering output + * formats such as commonmark, which need to output the index. It is not + * required for formats such as html or latex. + */ +CMARK_EXPORT int cmark_node_get_item_index(cmark_node *node); + +/** Sets item index of 'node'. Returns 1 on success, 0 on failure. + */ +CMARK_EXPORT int cmark_node_set_item_index(cmark_node *node, int idx); + /** Returns the info string from a fenced code block. */ CMARK_EXPORT const char *cmark_node_get_fence_info(cmark_node *node); diff --git a/src/commonmark.c b/src/commonmark.c index b45906dd3..ba315b095 100644 --- a/src/commonmark.c +++ b/src/commonmark.c @@ -153,7 +153,6 @@ static bool is_autolink(cmark_node *node) { static int S_render_node(cmark_renderer *renderer, cmark_node *node, cmark_event_type ev_type, int options) { - cmark_node *tmp; int list_number; cmark_delim_type list_delim; size_t numticks; @@ -216,13 +215,8 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node, if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) { marker_width = 4; } else { - list_number = cmark_node_get_list_start(node->parent); + list_number = cmark_node_get_item_index(node); list_delim = cmark_node_get_list_delim(node->parent); - tmp = node; - while (tmp->prev) { - tmp = tmp->prev; - list_number += 1; - } // we ensure a width of at least 4 so // we get nice transition from single digits // to double diff --git a/src/man.c b/src/man.c index 65e5c7950..bb73aae24 100644 --- a/src/man.c +++ b/src/man.c @@ -72,7 +72,6 @@ static void S_outc(cmark_renderer *renderer, cmark_escaping escape, int32_t c, static int S_render_node(cmark_renderer *renderer, cmark_node *node, cmark_event_type ev_type, int options) { - cmark_node *tmp; int list_number; bool entering = (ev_type == CMARK_EVENT_ENTER); bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options); @@ -125,12 +124,7 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node, if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) { LIT("\\[bu] 2"); } else { - list_number = cmark_node_get_list_start(node->parent); - tmp = node; - while (tmp->prev) { - tmp = tmp->prev; - list_number += 1; - } + list_number = cmark_node_get_item_index(node); char list_number_s[LIST_NUMBER_SIZE]; snprintf(list_number_s, LIST_NUMBER_SIZE, "\"%d.\" 4", list_number); LIT(list_number_s); diff --git a/src/node.c b/src/node.c index 3b0cf1361..df0de0c48 100644 --- a/src/node.c +++ b/src/node.c @@ -466,6 +466,31 @@ int cmark_node_set_list_tight(cmark_node *node, int tight) { } } +int cmark_node_get_item_index(cmark_node *node) { + if (node == NULL) { + return 0; + } + + if (node->type == CMARK_NODE_ITEM) { + return node->as.list.start; + } else { + return 0; + } +} + +int cmark_node_set_item_index(cmark_node *node, int idx) { + if (node == NULL || idx < 0) { + return 0; + } + + if (node->type == CMARK_NODE_ITEM) { + node->as.list.start = idx; + return 1; + } else { + return 0; + } +} + const char *cmark_node_get_fence_info(cmark_node *node) { if (node == NULL) { return NULL; diff --git a/src/render.c b/src/render.c index f71b048b6..e614dab1a 100644 --- a/src/render.c +++ b/src/render.c @@ -170,6 +170,15 @@ char *cmark_render(cmark_node *root, int options, int width, while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) { cur = cmark_iter_get_node(iter); + if (cur->type == CMARK_NODE_ITEM) { + // Calculate the list item's index, for the benefit of output formats + // like commonmark and plaintext. + if (cur->prev) { + cmark_node_set_item_index(cur, 1 + cmark_node_get_item_index(cur->prev)); + } else { + cmark_node_set_item_index(cur, cmark_node_get_list_start(cur->parent)); + } + } if (!render_node(&renderer, cur, ev_type, options)) { // a false value causes us to skip processing // the node's contents. this is used for From 6193892db9a5885184e896d8866ccb6b625e9b47 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Tue, 18 Apr 2023 14:51:10 +0100 Subject: [PATCH 2/2] Move prototypes to node.h so that they're added to the public API. --- src/cmark.h | 11 ----------- src/node.h | 11 +++++++++++ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/cmark.h b/src/cmark.h index 285fbdf37..d2021f0ff 100644 --- a/src/cmark.h +++ b/src/cmark.h @@ -329,17 +329,6 @@ CMARK_EXPORT int cmark_node_get_list_tight(cmark_node *node); */ CMARK_EXPORT int cmark_node_set_list_tight(cmark_node *node, int tight); -/** - * Returns item index of 'node'. This is only used when rendering output - * formats such as commonmark, which need to output the index. It is not - * required for formats such as html or latex. - */ -CMARK_EXPORT int cmark_node_get_item_index(cmark_node *node); - -/** Sets item index of 'node'. Returns 1 on success, 0 on failure. - */ -CMARK_EXPORT int cmark_node_set_item_index(cmark_node *node, int idx); - /** Returns the info string from a fenced code block. */ CMARK_EXPORT const char *cmark_node_get_fence_info(cmark_node *node); diff --git a/src/node.h b/src/node.h index 1cae5d745..63822e395 100644 --- a/src/node.h +++ b/src/node.h @@ -85,6 +85,17 @@ struct cmark_node { CMARK_EXPORT int cmark_node_check(cmark_node *node, FILE *out); +/** + * Returns item index of 'node'. This is only used when rendering output + * formats such as commonmark, which need to output the index. It is not + * required for formats such as html or latex. + */ +int cmark_node_get_item_index(cmark_node *node); + +/** Sets item index of 'node'. Returns 1 on success, 0 on failure. + */ +int cmark_node_set_item_index(cmark_node *node, int idx); + #ifdef __cplusplus } #endif