Fix quadratic behavior involving get_containing_block

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.
This commit is contained in:
Nick Wellnhofer 2021-12-03 12:42:37 +01:00 committed by John MacFarlane
parent 99b48963a2
commit 2dca096d8e
2 changed files with 15 additions and 24 deletions

View File

@ -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;
@ -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) {

View File

@ -111,6 +111,9 @@ pathological = {
}
pathological_cmark = {
"nested inlines":
("*" * 40000 + "a" + "*" * 40000,
re.compile("^\*+a\*+$")),
}
whitespace_re = re.compile('/s+/')