Skip to content

Commit 97fdfb1

Browse files
Merge pull request commonmark#322 from kevinbackhouse/list-item-index
Fix quadratic list indexing in commonmark/man/plaintext output formats
2 parents e8b5864 + f040422 commit 97fdfb1

File tree

7 files changed

+50
-9
lines changed

7 files changed

+50
-9
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 & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,14 +216,13 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
216216
LIT("<!-- end list -->");
217217
BLANKLINE();
218218
}
219-
renderer->list_number = cmark_node_get_list_start(node);
220219
break;
221220

222221
case CMARK_NODE_ITEM:
223222
if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
224223
marker_width = 4;
225224
} else {
226-
list_number = renderer->list_number++;
225+
list_number = cmark_node_get_item_index(node);
227226
list_delim = cmark_node_get_list_delim(node->parent);
228227
// we ensure a width of at least 4 so
229228
// we get nice transition from single digits

src/man.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,6 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
113113
break;
114114

115115
case CMARK_NODE_LIST:
116-
renderer->list_number = cmark_node_get_list_start(node);
117116
break;
118117

119118
case CMARK_NODE_ITEM:
@@ -123,7 +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 = renderer->list_number++;
125+
list_number = cmark_node_get_item_index(node);
127126
char list_number_s[LIST_NUMBER_SIZE];
128127
snprintf(list_number_s, LIST_NUMBER_SIZE, "\"%d.\" 4", list_number);
129128
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 & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,13 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
6161
node->next->type == CMARK_NODE_LIST)) {
6262
CR();
6363
}
64-
renderer->list_number = cmark_node_get_list_start(node);
6564
break;
6665

6766
case CMARK_NODE_ITEM:
6867
if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
6968
marker_width = 4;
7069
} else {
71-
list_number = renderer->list_number++;
70+
list_number = cmark_node_get_item_index(node);
7271
list_delim = cmark_node_get_list_delim(node->parent);
7372
// we ensure a width of at least 4 so
7473
// we get nice transition from single digits

src/render.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,8 @@ char *cmark_render(cmark_mem *mem, cmark_node *root, int options, int width,
171171

172172
cmark_renderer renderer = {mem, &buf, &pref, 0, width,
173173
0, 0, true, true, false,
174-
false, 0, outc, S_cr, S_blankline,
175-
S_out, 0};
174+
false, outc, S_cr, S_blankline, S_out,
175+
0};
176176

177177
while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
178178
cur = cmark_iter_get_node(iter);
@@ -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

src/render.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ struct cmark_renderer {
2323
bool begin_content;
2424
bool no_linebreaks;
2525
bool in_tight_list_item;
26-
int list_number;
2726
void (*outc)(struct cmark_renderer *, cmark_node *, cmark_escaping, int32_t, unsigned char);
2827
void (*cr)(struct cmark_renderer *);
2928
void (*blankline)(struct cmark_renderer *);

0 commit comments

Comments
 (0)