Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bump dependencies to support tasklists #94

Merged
merged 13 commits into from
Apr 3, 2019
Merged

Conversation

gjtorikian
Copy link
Owner

Re: #64 (comment)

Something is a bit missing when it comes to walking the nodes around a task list...it ends up in a recursion. Not sure if this is a problem here or upstream just yet.

@kivikakk
Copy link
Collaborator

kivikakk commented Feb 8, 2019

oh good lord

not my problem!
@gjtorikian
Copy link
Owner Author

@kivikakk
Copy link
Collaborator

kivikakk commented Feb 8, 2019

image

@gjtorikian
Copy link
Owner Author

gjtorikian commented Feb 14, 2019

@kivikakk I believe something upstream is not quite right, but I haven't identified what it is just yet.

In this gem, parsing a file as

doc = CommonMarker.render_doc(text, :DEFAULT, %i[tasklist])
doc.to_html

works precisely as expected. to_html relies on the C lib's cmark_render_html function.

But when attempting to step through and render individual nodes:

CommonMarker::HtmlRenderer.new.render(doc)

the generation hangs.

That renderer depends on a node's type string to understand what to do. According to just puts node.type_string.to_s, this emits list, not an expected tasklist. Would you be able to help me sleuth this, or, if you're pressed for time, point out to me where I can go to look a bit further?

@kivikakk
Copy link
Collaborator

Ugh, I cannot believe what has happened here:

  • commonmarker uses cmark_node_set_user_data and cmark_node_get_user_data to store a cache of the Ruby Node against the cmark node.
  • the tasklist extension uses the same functions to store CMARK_TASKLIST_CHECKED and CMARK_TASKLIST_NOCHECKED, which are an enum.
  • this means when storing CMARK_TASKLIST_NOCHECKED, the cached node is forgotten. (memory leak!)
  • when storing CMARK_TASKLIST_CHECKED, we return it immediately as a VALUE in rb_node_to_value. The VALUE 1 is the Fixnum 0.
  • in CommonMarker::Node#each, we get the first child and then repeatedly call next on it.
  • Fixnum#next returns the next integer, i.e. it sits there looping through all integers starting at zero.

I don't have time to work on a fix for this atm, but hopefully this is enough of a pointer.

@kivikakk
Copy link
Collaborator

Relevant sections of code:

static VALUE rb_node_to_value(cmark_node *node) {
void *user_data;
RUBY_DATA_FUNC free_func;
VALUE val;
if (node == NULL)
return Qnil;
user_data = cmark_node_get_user_data(node);
if (user_data)
return (VALUE)user_data;
/* Only free tree roots. */
free_func = cmark_node_parent(node) ? NULL : rb_free_c_struct;
val = Data_Wrap_Struct(rb_mNode, rb_mark_c_struct, free_func, node);
cmark_node_set_user_data(node, (void *)val);
return val;
}

# Public: Iterate over the children (if any) of the current pointer.
def each(&block)
return enum_for(:each) unless block_given?
child = first_child
while child
nextchild = child.next
yield child
child = nextchild
end
end

@kivikakk
Copy link
Collaborator

And:

typedef enum {
CMARK_TASKLIST_NOCHECKED,
CMARK_TASKLIST_CHECKED,
} cmark_tasklist_type;

long userdata;
if (strstr((char*)input, "[x]")) {
userdata = CMARK_TASKLIST_CHECKED;
} else {
userdata = CMARK_TASKLIST_NOCHECKED;
}
cmark_node_set_user_data(parent_container, (void*)userdata);

@kivikakk
Copy link
Collaborator

If possible, the tasklist code in cmark-gfm should use the node opaque area like the table extension does. (I haven't looked into this yet and didn't write the tasklist extension, so not sure if that's something that'll work or not.)

@gjtorikian
Copy link
Owner Author

but hopefully this is enough of a pointer

I C what you did there hyuck hyuck

Thanks a bunch! This helps a ton.

@kivikakk
Copy link
Collaborator

but hopefully this is enough of a pointer

I C what you did there hyuck hyuck

I can't even admit to doing that on purpose /o\

@gjtorikian gjtorikian merged commit ae64cf4 into master Apr 3, 2019
@gjtorikian gjtorikian deleted the bump-dependencies branch April 3, 2019 14:57
@kivikakk
Copy link
Collaborator

kivikakk commented Apr 3, 2019

🎉

@oliverguenther
Copy link

Great stuff, thanks for working on this! 🙇‍♂️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants