Skip to content

Commit

Permalink
Fix quadratic behavior involving get_containing_block
Browse files Browse the repository at this point in the history
Fix quadratic behavior in the Commonmark renderer when determining the
tight list status in deeply nested inlines.

Instead of searching for the containing block, update the tight list
status when

- entering a child of a list item
- exiting a list

Fixes #431.
  • Loading branch information
nwellnhof authored and jgm committed Dec 3, 2021
1 parent 99b4896 commit 2dca096
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 24 deletions.
36 changes: 12 additions & 24 deletions src/commonmark.c
Original file line number Diff line number Diff line change
Expand Up @@ -150,21 +150,6 @@ static bool is_autolink(cmark_node *node) {
strcmp((const char *)url, (char *)link_text->data) == 0;
}

// if node is a block node, returns node.
// otherwise returns first block-level node that is an ancestor of node.
// if there is no block-level ancestor, returns NULL.
static cmark_node *get_containing_block(cmark_node *node) {
while (node) {
if (node->type >= CMARK_NODE_FIRST_BLOCK &&
node->type <= CMARK_NODE_LAST_BLOCK) {
return node;
} else {
node = node->parent;
}
}
return NULL;
}

static int S_render_node(cmark_renderer *renderer, cmark_node *node,
cmark_event_type ev_type, int options) {
cmark_node *tmp;
Expand All @@ -186,16 +171,19 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
!(CMARK_OPT_HARDBREAKS & options);

// Don't adjust tight list status til we've started the list.
// Otherwise we loose the blank line between a paragraph and
// Otherwise we lose the blank line between a paragraph and
// a following list.
if (!(node->type == CMARK_NODE_ITEM && node->prev == NULL && entering)) {
tmp = get_containing_block(node);
renderer->in_tight_list_item =
tmp && // tmp might be NULL if there is no containing block
((tmp->type == CMARK_NODE_ITEM &&
cmark_node_get_list_tight(tmp->parent)) ||
(tmp && tmp->parent && tmp->parent->type == CMARK_NODE_ITEM &&
cmark_node_get_list_tight(tmp->parent->parent)));
if (entering) {
if (node->parent && node->parent->type == CMARK_NODE_ITEM) {
renderer->in_tight_list_item = node->parent->parent->as.list.tight;
}
} else {
if (node->type == CMARK_NODE_LIST) {
renderer->in_tight_list_item =
node->parent &&
node->parent->type == CMARK_NODE_ITEM &&
node->parent->parent->as.list.tight;
}
}

switch (node->type) {
Expand Down
3 changes: 3 additions & 0 deletions test/pathological_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ def badhash(ref):
}

pathological_cmark = {
"nested inlines":
("*" * 40000 + "a" + "*" * 40000,
re.compile("^\*+a\*+$")),
}

whitespace_re = re.compile('/s+/')
Expand Down

0 comments on commit 2dca096

Please sign in to comment.