Repository navigation
Conversation
|
Hey! Just wanted to let you know that I haven't gotten around to review this yet, but it sounds promising :)!
I think that's reasonable. As long as without the option, we can produce equivalent output (but not source-identital). |
|
I'm glad it looks promising so far, just let me know if there's anything I can help with. :)
Yes, that's the case right now. The 4 test cases that fail would still render all other details of the link reference definitions (including most whitespace), they would just be missing any line breaks inside the link reference definition itself. |
robinst
left a comment
There was a problem hiding this comment.
Alright, I've given this a first (shallow) review, thoughts in addition to the inline comments:
- I might have missed it but where are the tests?
- Have you run the benchmarks yet and compared to before your changes? I wonder if it makes sense to put the code for this behind an option (which would also automatically enable source positions). I'm not sure it's worth it, just thinking.
- What are your thoughts about users that would like to generate an AST using code and then render to CommonMark? In that case, with the current implementation, users would have to use
setRawto get the desired rendering, otherwise they'd get invalid CommonMark, right (i.e. justsetLiteralwouldn't be enough). - In general, I feel like it's a lot of tricky code additions. If you have any ideas on how to condense some of the parsing code, that would be great. Do you think something like
Scannerfor block parsers would help?
| } | ||
| // BlankLine nodes do _not_ interrupt the current node type's processor, as | ||
| // they are non-standard CommonMark nodes meant only for roundtrip processing | ||
| if(!(openBlockParser.blockParser instanceof BlankLineParser)) { |
There was a problem hiding this comment.
Instead of excluding BlankLineParser here and a few lines further down, could we instead:
- Remove
BlankLineParseraltogether - Instead, just add
BlankLinenodes directly at the point where we'd add aBlankLineParser
The answer might be "no, because ..." which would be useful too - I haven't thought through the implications of this, whereas you might have tried what I suggested already and figured out that it doesn't work.
There was a problem hiding this comment.
I agree this would be good, but I wasn't able to find a way to do it.
Most of the time what is being captured in a BlankLine is occurring in DocumentParser.parseLine(CharSequnce ln). That method never seems to deal with individual blocks, instead capturing whole parsers and deciding whether to add their results to the structure via addChild. The parsers themselves are also not equipped to contain multiple nodes per parser, as they each contain a single object of the type they're supposed to parse (usually called block).
The only place I saw where individual nodes could be added directly was InlineParserImpl.parseInline(), which (aside from inline processing being too late for capturing blank lines) still used Collections.singletonList and thus was not meant for more than one node at a time either.
Is there a place I missed where we might add the BlankLine nodes directly? I agree that it would be cleaner and would likely clear the extra lines out of addChild, it just didn't seem to fit within the existing design.
| // where the literal string begins. Calling this method returns a SourceLine containing | ||
| // only the literal portion of the line (if it is a subset of the line) or the line | ||
| // in full (if they are the same). | ||
| public SourceLine getLiteralLine() { |
There was a problem hiding this comment.
I was a bit confused as to what that "literal" meant in this context when I started reading this PR. Maybe just use sub instead of literal?
There was a problem hiding this comment.
Yeah, it may be wise to standardize this somehow.
I noticed that anytime you wanted to just "get the text", you had a getLiteral method (see HtmlBlock, Code, Text, etc.). This literal value is used by HTML rendering and is rarely unprocessed, as it's always at least post-block-processing and sometimes post-inline as well. I chose to use the term "raw" whenever lines needed to be as unprocessed as possible. That same mindset slipped into a couple other places, so getLiteralLine here as well as the concept of a "literal index" (an offset which, if you ignore the contents prior to that index, you get what the HTML renderer would expect).
I'm open to suggestion on how that might be communicated more clearly. The line you see here does have a specific purpose, but I would want it to be a clear purpose to everyone who isn't me, too!
|
All individual comments addressed, a couple will need your comments.
Other than updating some of the internal tests where they had broken, I hadn't added new ones because most of what I have done are edits to existing work. I do see that I'm missing a test for the new renderer, but in my mind it also happened to be thoroughly tested by making sure all CommonMark tests cases are rendered correctly. (I also figured it would be a bit overkill to include all 652 CommonMark tests.) If you have suggestions for where you'd like to see more test coverage I will be happy to write them and push an update.
Other than the output for how long individual existing tests took, I couldn't see an obvious benchmarking method. Is there a wiki or document for how I could achieve this? I'm open to running the benchmarks, I just want to make sure that whatever I provide is an apples to apples comparison.
I think generating an AST directly, without going through parsing, is already risky without my changes and would continue to be about the same level of risk. Unless someone uses a My guess is that most people attempting to generate an AST would be directly making a tree of nodes. This means they have to be very confident they know things per node like the correct literal values, when lists are tight, when
I agree that much of what is changed in the parsers, at least, is tricky. As for ideas, I will say that I can't think of much which would also preserve this library's original intent. It would likely be much cleaner to start with a roundtrip object and split out what the HTML needs (as HTML rendering is usually heavily distilled from the raw). That would also probably be a ton of work, starting at (To answer your specific question, I think a Scanner for block parsers would be only partially useful from this PR's perspective. CommonMark seems to recommend parsing in a single pass, which means a scanner would be only able to deal with a single block and wouldn't have a good grasp of any relationships between blocks. It might, however, be able to identify the blank lines between blocks in a more intelligent fashion. If a block-level scanner parsing through raw contents knew that a previous block existed there would probably be ways to capture the blank lines between "previous" and "the current block".) |
|
I believe all initial review comments have been addressed for now. I was able to successfully run a No rush, just making sure I close the loop on your original review. |
|
is there someone active on this? I'd really like to see the roundtrip feature deployed |
|
@ToWi87 I did not keep this PR in sync with the main repository, as it seemed like the PR wasn't going to be accepted. |
Seconded! |
|
Hey @seii. You might have seen that I've implemented a I'm sorry this PR didn't work out, closing it now. I think the way forward for improving round-tripping should be a few focused smaller PRs, e.g. one for links, one for escaping, insignificant whitespace, etc. For each of those, the aim needs to be:
|
Fixes #12
Until now, this library could parse Commonmark into other formats, but not back into Commonmark format. This is understandable, as the Commonmark spec itself uses conversions to HTML to depict compliance. However, with the new
CoreCommonMarkNodeRendererit becomes possible to render Commonmark once it has been parsed - a "roundtrip" rendering. Usage is identical toHtmlRenderer.KNOWN ISSUE:
IncludeSourceSpans.NONE(the default) orIncludeSourceSpans.BLOCKS, 4 test cases related to link reference definitions (193, 195, 198, and 217) will fail. In all cases it's due to line breaks, this is the only data structure where I couldn't find another way (besidesSourceSpan) to track the line number if newlines occur. Use ofIncludeSourceSpans.BLOCKS_AND_INLINESis recommended for 100% Commonmark roundtrip compliance.DETAILS:
This is a relatively large PR. I have attempted to keep backwards compatibility whenever possible, but it's uncertain whether this update may break extensions to the core specification. Core specifications are confirmed to all pass.
In order to achieve this roundtrip functionality, new fields have been added to the various Commonmark nodes, and many changes to parsers have been added to populate these new fields. The
commonmark-javalibrary is geared towards conversion to HTML already, so I have chosen to keep that functionality and its optimizations intact and build extra structures on top of that approach rather than trying to rewrite what has already been done.The one change which departs from existing functionality is the addition of a new
BlankLinenode type. This node represents a concept explained in the Commonmark spec (blank lines), but it is not truly defined in the spec as a Commonmark block.BlankLineshould be treated asSourceSpanalready is - a utility class that helps to describe the structure. (Speaking ofSourceSpan, I have elected to keep them tracking the HTML wherever possible rather than the raw text in order to avoid breaking changes.)The
BlankLineclass is specifically ignored in HTML rendering and is only currently used by the roundtrip rendering to fill in gaps which otherwise can't be determined from the parsed AST tree. In this way, existing plugins can render to other formats the same as they always have, but will also have the option of rendering back to Commonmark.