Skip to content

Commit f040422

Browse files
Store list item index on the node during rendering, to avoid quadratic performance.
1 parent 36f8112 commit f040422

File tree

6 files changed

+48
-21
lines changed

6 files changed

+48
-21
lines changed

src/cmark-gfm.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,17 @@ CMARK_GFM_EXPORT int cmark_node_get_list_tight(cmark_node *node);
423423
*/
424424
CMARK_GFM_EXPORT int cmark_node_set_list_tight(cmark_node *node, int tight);
425425

426+
/**
427+
* Returns item index of 'node'. This is only used when rendering output
428+
* formats such as commonmark, which need to output the index. It is not
429+
* required for formats such as html or latex.
430+
*/
431+
CMARK_GFM_EXPORT int cmark_node_get_item_index(cmark_node *node);
432+
433+
/** Sets item index of 'node'. Returns 1 on success, 0 on failure.
434+
*/
435+
CMARK_GFM_EXPORT int cmark_node_set_item_index(cmark_node *node, int idx);
436+
426437
/** Returns the info string from a fenced code block.
427438
*/
428439
CMARK_GFM_EXPORT const char *cmark_node_get_fence_info(cmark_node *node);

src/commonmark.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@ static bool is_autolink(cmark_node *node) {
155155

156156
static int S_render_node(cmark_renderer *renderer, cmark_node *node,
157157
cmark_event_type ev_type, int options) {
158-
cmark_node *tmp;
159158
int list_number;
160159
cmark_delim_type list_delim;
161160
int numticks;
@@ -223,13 +222,8 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
223222
if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
224223
marker_width = 4;
225224
} else {
226-
list_number = cmark_node_get_list_start(node->parent);
225+
list_number = cmark_node_get_item_index(node);
227226
list_delim = cmark_node_get_list_delim(node->parent);
228-
tmp = node;
229-
while (tmp->prev) {
230-
tmp = tmp->prev;
231-
list_number += 1;
232-
}
233227
// we ensure a width of at least 4 so
234228
// we get nice transition from single digits
235229
// to double

src/man.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ static void S_outc(cmark_renderer *renderer, cmark_node *node,
7474

7575
static int S_render_node(cmark_renderer *renderer, cmark_node *node,
7676
cmark_event_type ev_type, int options) {
77-
cmark_node *tmp;
7877
int list_number;
7978
bool entering = (ev_type == CMARK_EVENT_ENTER);
8079
bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options);
@@ -123,12 +122,7 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
123122
if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
124123
LIT("\\[bu] 2");
125124
} else {
126-
list_number = cmark_node_get_list_start(node->parent);
127-
tmp = node;
128-
while (tmp->prev) {
129-
tmp = tmp->prev;
130-
list_number += 1;
131-
}
125+
list_number = cmark_node_get_item_index(node);
132126
char list_number_s[LIST_NUMBER_SIZE];
133127
snprintf(list_number_s, LIST_NUMBER_SIZE, "\"%d.\" 4", list_number);
134128
LIT(list_number_s);

src/node.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,31 @@ int cmark_node_set_list_tight(cmark_node *node, int tight) {
564564
}
565565
}
566566

567+
int cmark_node_get_item_index(cmark_node *node) {
568+
if (node == NULL) {
569+
return 0;
570+
}
571+
572+
if (node->type == CMARK_NODE_ITEM) {
573+
return node->as.list.start;
574+
} else {
575+
return 0;
576+
}
577+
}
578+
579+
int cmark_node_set_item_index(cmark_node *node, int idx) {
580+
if (node == NULL || idx < 0) {
581+
return 0;
582+
}
583+
584+
if (node->type == CMARK_NODE_ITEM) {
585+
node->as.list.start = idx;
586+
return 1;
587+
} else {
588+
return 0;
589+
}
590+
}
591+
567592
const char *cmark_node_get_fence_info(cmark_node *node) {
568593
if (node == NULL) {
569594
return NULL;

src/plaintext.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ static CMARK_INLINE void outc(cmark_renderer *renderer, cmark_node *node,
1818

1919
static int S_render_node(cmark_renderer *renderer, cmark_node *node,
2020
cmark_event_type ev_type, int options) {
21-
cmark_node *tmp;
2221
int list_number;
2322
cmark_delim_type list_delim;
2423
int i;
@@ -68,13 +67,8 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
6867
if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
6968
marker_width = 4;
7069
} else {
71-
list_number = cmark_node_get_list_start(node->parent);
70+
list_number = cmark_node_get_item_index(node);
7271
list_delim = cmark_node_get_list_delim(node->parent);
73-
tmp = node;
74-
while (tmp->prev) {
75-
tmp = tmp->prev;
76-
list_number += 1;
77-
}
7872
// we ensure a width of at least 4 so
7973
// we get nice transition from single digits
8074
// to double

src/render.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,15 @@ char *cmark_render(cmark_mem *mem, cmark_node *root, int options, int width,
181181
} else if (cur->parent) {
182182
cur->ancestor_extension = cur->parent->ancestor_extension;
183183
}
184+
if (cur->type == CMARK_NODE_ITEM) {
185+
// Calculate the list item's index, for the benefit of output formats
186+
// like commonmark and plaintext.
187+
if (cur->prev) {
188+
cmark_node_set_item_index(cur, 1 + cmark_node_get_item_index(cur->prev));
189+
} else {
190+
cmark_node_set_item_index(cur, cmark_node_get_list_start(cur->parent));
191+
}
192+
}
184193
if (!render_node(&renderer, cur, ev_type, options)) {
185194
// a false value causes us to skip processing
186195
// the node's contents. this is used for

0 commit comments

Comments
 (0)