You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
It would be nice to be able to talk about the comments this tool provides inside of code blocks. I'm pretty sure that with a simple modification to the regex you should be able to support this.
Change the regex in _uncommented from .*<!---.*---> to [^( )]<!---.--->, and similarly for the multiline start comment (note that there is supposed to be exactly 4 spaces inside the parens). The idea is that ^ is "not", and then ``( ) is "tilde and at least four spaces" (again, supposed to be exactly 4 spaces in the parens), so the two common ways of starting code blocks. Making these changes myself, the input
`<!--- test --->`
` <!---- test -->` testing quote-single-space
some text for testing `<!--- test -->` more text
inline testing <!--- test --> and more testing
I thought this would fail but it doesn't <!--- test --> more text
<!--- testing single space --> this works too, hurray
<!--- test --> testing code blocks
<!--- test --> testing 8 spaces
<!--- test --> testing 5 spaces
produces the output:
<!--- test -->
<!--- test --> testing quote-single-space
some text for testing <!--- test --> more text
inline testing and more testing
I thought this would fail but it doesn't more text
this works too, hurray
<!--- test --> testing code blocks
<!--- test --> testing 8 spaces
<!--- test --> testing 5 spaces
So it seems to work even in cases where I thought it wouldn't; I'm not really sure why that is to be honest. I didn't try running the test cases with this modification, but I think if you tried they would work fine. I don't particularly feel like cloning and making the pull request, or testing the multiline comments, but I think this would be a good addition to the plugin.
``( )` is "tilde and at least four spaces" (again, supposed to be exactly 4 spaces in the parens), so the two common ways of starting code blocks.
According to markdown spec:
To produce a code block in Markdown, simply indent every line of the block by at least 4 spaces or 1 tab.
So we should match a tab or spaces (something like ^( {4}|\t)).
C
I'm not sure what your brackets are doing. I thought those were only for ranges (i.e. [a-z]). I think we'll need something more like (?<!...) (negative lookbehind).
D
In order to make sure we're not in an inline code block, we'll need to make sure there aren't an odd number of `'s prior to the match. Something like
(.*`.*`).*`.*
E
Putting this together, I would expect a regex something more like
Regarding ^ as start-of-line or not, as well as regarding [] for ranges, see the section on brackets in the python regex docs you linked; in particular:
Used to indicate a set of characters. In a set: ... Characters that are not within a range can be matched by complementing the set. If the first character of the set is '^', all the characters that are not in the set will be matched. For example, [^5] will match any character except '5', and [^^] will match any character except '^'. ^ has no special meaning if it’s not the first character in the set.
The brackets are just a way to specify a union of characters without making a capture group like () does. The fact that ranges work in them is also a property of sets.
Regarding odd/even backticks: The Markdown spec doesn't really specify what you should do with multiple backticks right next to each other, like ``. (I'm interested in seeing how Github parses it. EDIT: it leaves them as is, you don't have to escape two backticks next to each other, they just show up) It does specify that you can use "multiple" backticks and optionally space-pad the inner content with exactly one space on either side:
To include a literal backtick character within a code span, you can use multiple backticks as the opening and closing delimiters:
``There is a literal backtick (`) here.``
`` This produces the same thing with the backtick (`) ``
So if we were interested in being thorough, we would have to check for that case. That's why I have the second test case with the backtick-space and then the comment; it seems like the original regex handles it fine, although it's unclear to me why that is.
I had written a lot more stuff but then I realized that inside sets, special characters generally lose their meaning other than certain uses of - and ^. I didn't know this, which makes me wonder why my regex works at all (it did and does work in the cases I tested, although I'm certain it errs on the side of leaving comments there when it should remove them). Negative lookbehind sounds good, but according to the python docs it cannot (should not?) contain strings that aren't fixed-length:
The contained pattern must only match strings of some fixed length, meaning that abc or a|b are allowed, but a* and a{3,4} are not.
So something like
(?<!`|\t| {4})
might work? It wouldn't work in all cases, but it would in the most usual... Perhaps you could combine it with a start-of-line plus some negative lookahead to handle that better? The trick would be that it would probably match the wrong portion of the line, ie, the part you wanted to keep.
Regular expressions are tricky!
It looks like my original says "match any number of anything before this except parenthesis, spaces, and backticks". So you could change it to [^ \t]*` and it would be a little bit more accurate. I think the reason the original one works is because of how the regex decides to do matching (greedy or non-greedy)? It's subtle and pretty coincidental for sure, and not as trivial as I hoped, at least to handle all cases.
Sounds right all around. Your regex-foo is certainly sharper than mine.
Regarding ^ as start-of-line or not, as well as regarding [] for ranges...
Cool. I get it now.
Regarding odd/even backticks...
Yeah, that's tricky and the markdown spec is rather vague. Of course, the only relevant thing to test would be python-markdown itself, which generally relies upon php-markdown as the reference implementation.
But your implementation is probably fine for 99% of use cases. (There are so many use cases for talking about python-markdown-comments, haha).
Negative lookbehind sounds good, but according to the python docs it cannot (should not?) contain strings that aren't fixed-length
You're right. I've been enjoying grep --perl-regexp too much. Perl doesn't have that limitation.
I'm not sure if there's any way to do the capture in python with a single regex, but we shouldn't even need a look-behind for the match. We could do something like:
[^^\t(^ {4})((.*`.*`)*.*`)].*<!---.*-->
Of course, that still doesn't do anything to address the corner cases of backtick literals/escaping, but it's a proof of concept.
Another consideration is the performance implications of lengthening the regex's. If timeit shows a significant slowdown, then perhaps this should be a feature enabled by a meta=Truesetting. Alongside the overhead of python-markdown itself though, there's a real possibility it will be undetectable.
The first part uses non-consuming negative look-ahead to return False if any of the conditions of being a code block/line are met. The second should return True only if it is a markdown comment.
It occurred to me that it might not be desirable to allow meta-comments in code blocks. It seems more plausible that one would want an actual comment in a code block than a meta-comment and I'm not sure how that ambiguity could be resolved. It might make sense to only support inline meta-comments.
I suppose, yeah. The only use case I can think of for wanting comments to appear in code blocks is if you're writing about Markdown code in general. My reasoning was that I wanted to be consistent; it seemed reasonable that if you put a meta-comment anywhere that would generate code, it would be interpreted as code as opposed to a comment.
Your regex is missing a couple parenthesis, but I don't think it works. If there's any ```s in front of a comment, all the ones prior don't match I think we need negative look-behind instead, maybe something like... Oh, you know what?
Why don't we just say that if you put a \ in front of it, it doesn't get converted? Then the regex is just
(?<!\\)<!---.*-->
And we don't have to guess what the user wants.
I think the code would have to be changed slightly to replace the \<!--- with <!---, that would be the only change.
I think if we wanted to do it the other way we would need multiple regex's. I don't think the conditions we're searching for form a regular language, I think they're context-free.
I can't seem to test it because I think it's still using the old version... I'm not exactly clear on where I'm supposed to install this plugin. I eventually gave up and dropped it into the markdown.extensions package, but now there's no .pyc for it now so I have no idea where it's compiling to.
Sorry, I haven't actually tried any of those they were just proofs of concept.
Regarding your suggested escape syntax, aren't you just creating a meta-meta-comment? How would one write about meta-meta-comments? At some point we have to arbitrarily choose among the ambiguities. I don't think there's any way to avoid that.
Regarding installation, the most reasonable method I'm aware of is to just copy the file into your project. I would not try to patch it into python-markdown.
Python-markdown replaces html blocks with placeholders early in the process and puts them back late in the process. So all it would take to fix this would be having the comment regex run while code blocks are not present. (Instead of before the process, which is what currently happens.)
So I forgot that the html is replaced with placeholders by a preprocessor, so making markdown-comments run immediately afterwards would be as simple as:
Unfortunately, since markdown-comments are valid html comments, they would all be replaced with placeholders by markdown and our regex's would find nothing.
The only solution that comes to mind requires three parsers:
Preprocessor before html_block that searches for markdown-comments and modifies them so they're not valid html.
Preprocessor after html_block that removes any markdown-comments found. (The ones in html will have been replaced with placeholders by markdown.)
Postprocessor after raw_html (which replaces placeholders with raw html) that modifies the markdown comments in raw html back to their original state before they were munged by the first preprocessor.
$ python
>>> import markdown, mkdcomments
>>> md = markdown.Markdown(extensions=["mkdcomments:CommentsExtension"])
>>> md.convert("some text `<!--- inline comment -->` more text example `<pre>` tag")
u'<p>some text <code>` more text example</code><pre>` tag</p>'
To be fair, this isn't what the comments were designed to handle, and I figured out what Markdown is seeing, but that's still not at all what I'd expect to see from that.
Additionally, after some in-browser testing, although your test you added does pass, it doesn't do what you'd expect. HTML is still processed inside a <pre> block, so the comment is inserted into the HTML and promptly stripped by your browser. I think the RawCommentReplacer needs to escape the opening (and probably closing) angle bracket. The test should probably be changed to
def test_comments_in_html(self):
""" See issue #2. """
md_input = """\
<pre>
<!--- test --> testing code blocks
<!--- test --> testing 8 spaces
<!--- test --> testing 5 spaces
<!--- testing multiline comments
more
more
-->
</pre>"""
md_output = """\
<pre>
<!--- test --> testing code blocks
<!--- test --> testing 8 spaces
<!--- test --> testing 5 spaces
<!--- testing multiline comments
more
more
-->
</pre>"""
self.assertExpectedMarkdown(md_input, md_output)
as well (but making sure the number of spaces on the multiline stuff is right).
that's still not at all what I'd expect to see from that.
I'm not sure what the output you'd expect to see is, but I think this extension is doing the right thing. From the spec:
Unlike block-level HTML tags, Markdown syntax is processed within span-level tags.
Therefore it makes sense to me that:
>>>assert (
... markdowner.convert("some text `<!--- inline comment -->` more text example `<pre>` tag")
... ==
... markdowner.convert("some text `` more text example `<pre>` tag")
... )
Additionally, after some in-browser testing, although your test you added does pass, it doesn't do what you'd expect. HTML is still processed inside a <pre> block, so the comment is inserted into the HTML and promptly stripped by your browser. I think the RawCommentReplacer needs to escape the opening (and probably closing) angle bracket.
Correct, the comments will not render because they are valid html comments. And yes, the brackets would need to be escaped in order for them to be rendered.
The question is whether that's the responsibility of this extension or the user. I would be inclined to defer to what python-markdown does in this situation. That is, if you wanted to display regular html comments in a <pre> block you'd have to escape them as well, which markdown does not. Eg.:
<pre><!-- test --> testing code blocks
<!-- test --> testing 8 spaces
<!-- test --> testing 5 spaces
</pre>
vs.
<pre>
<!-- test --> testing code blocks
<!-- test --> testing 8 spaces
<!-- test --> testing 5 spaces
</pre>
I suppose that's fair enough. I had written a page and my comment wasn't showing up in my <pre> block and it was very confusing, and then I remembered that it was turning into an HTML comment. It just seems like that defeats the point of not removing them in the source, because if I wanted to talk about the comments I could have written
Correct, the changes don't really affect any real-world use cases. But the extension no longer violates the contractual promise of the markdown spec not to meddle with the contents of html blocks and I consider that a worthy improvement.
Activity
ryneeverett commentedon Feb 25, 2015
Thanks for the suggestion. I haven't tested it out yet, but here's some initial thoughts. (All snippets are untested/pseudocode.)
A
I'm pretty sure caret matches the start of the line. https://docs.python.org/3.4/library/re.html#regular-expression-syntax
B
According to markdown spec:
So we should match a tab or spaces (something like
^( {4}|\t)).C
I'm not sure what your brackets are doing. I thought those were only for ranges (i.e.
[a-z]). I think we'll need something more like(?<!...)(negative lookbehind).D
In order to make sure we're not in an inline code block, we'll need to make sure there aren't an odd number of `'s prior to the match. Something like
E
Putting this together, I would expect a regex something more like
snoozbuster commentedon Feb 25, 2015
Regarding
^as start-of-line or not, as well as regarding[]for ranges, see the section on brackets in the python regex docs you linked; in particular:The brackets are just a way to specify a union of characters without making a capture group like
()does. The fact that ranges work in them is also a property of sets.Regarding odd/even backticks: The Markdown spec doesn't really specify what you should do with multiple backticks right next to each other, like ``. (I'm interested in seeing how Github parses it. EDIT: it leaves them as is, you don't have to escape two backticks next to each other, they just show up) It does specify that you can use "multiple" backticks and optionally space-pad the inner content with exactly one space on either side:
So if we were interested in being thorough, we would have to check for that case. That's why I have the second test case with the backtick-space and then the comment; it seems like the original regex handles it fine, although it's unclear to me why that is.
I had written a lot more stuff but then I realized that inside sets, special characters generally lose their meaning other than certain uses of
-and^. I didn't know this, which makes me wonder why my regex works at all (it did and does work in the cases I tested, although I'm certain it errs on the side of leaving comments there when it should remove them). Negative lookbehind sounds good, but according to the python docs it cannot (should not?) contain strings that aren't fixed-length:So something like
might work? It wouldn't work in all cases, but it would in the most usual... Perhaps you could combine it with a start-of-line plus some negative lookahead to handle that better? The trick would be that it would probably match the wrong portion of the line, ie, the part you wanted to keep.
Regular expressions are tricky!
It looks like my original says "match any number of anything before this except parenthesis, spaces, and backticks". So you could change it to
[^ \t]*` and it would be a little bit more accurate. I think the reason the original one works is because of how the regex decides to do matching (greedy or non-greedy)? It's subtle and pretty coincidental for sure, and not as trivial as I hoped, at least to handle all cases.ryneeverett commentedon Feb 25, 2015
Sounds right all around. Your regex-foo is certainly sharper than mine.
Cool. I get it now.
Yeah, that's tricky and the markdown spec is rather vague. Of course, the only relevant thing to test would be python-markdown itself, which generally relies upon php-markdown as the reference implementation.
But your implementation is probably fine for 99% of use cases. (There are so many use cases for talking about python-markdown-comments, haha).
You're right. I've been enjoying
grep --perl-regexptoo much. Perl doesn't have that limitation.I'm not sure if there's any way to do the capture in python with a single regex, but we shouldn't even need a look-behind for the match. We could do something like:
Of course, that still doesn't do anything to address the corner cases of backtick literals/escaping, but it's a proof of concept.
Another consideration is the performance implications of lengthening the regex's. If
timeitshows a significant slowdown, then perhaps this should be a feature enabled by ameta=Truesetting. Alongside the overhead of python-markdown itself though, there's a real possibility it will be undetectable.snoozbuster commentedon Feb 25, 2015
The trouble with
[^^\t(^ {4})((.*.)*.*)].` is that inside the brackets, all of those characters just mean the literal characters.ryneeverett commentedon Feb 25, 2015
Ah.
It also occurred to me that
.*is will eat tilde's, so I'm thinking those would have to be[^]*`.So maybe:
So far, we've been checking whether the line meets the comment conditions:
Another option is to check whether it meets the not-a-comment conditions:
I'd think we could do this with a negative lookahead. So we'd expect our negative lookahead to return
Trueif our line is:ryneeverett commentedon Feb 25, 2015
Oh wait,
is still matching positive. I'm not sure how that could be negative matched.
Maybe the only way to do this is to check the opposite with a positive lookahead (is a code block/line) and negative lookahead (not a comment match).
ryneeverett commentedon Feb 26, 2015
Every time I look at this it smells different.
The first part uses non-consuming negative look-ahead to return
Falseif any of the conditions of being a code block/line are met. The second should returnTrueonly if it is a markdown comment.snoozbuster commentedon Feb 26, 2015
Test it out in one of those online regex matchers. Or, alternatively, I'll give it a shot when I go in to work in about an hour.
ryneeverett commentedon Feb 26, 2015
It occurred to me that it might not be desirable to allow meta-comments in code blocks. It seems more plausible that one would want an actual comment in a code block than a meta-comment and I'm not sure how that ambiguity could be resolved. It might make sense to only support inline meta-comments.
snoozbuster commentedon Feb 26, 2015
I suppose, yeah. The only use case I can think of for wanting comments to appear in code blocks is if you're writing about Markdown code in general. My reasoning was that I wanted to be consistent; it seemed reasonable that if you put a meta-comment anywhere that would generate code, it would be interpreted as code as opposed to a comment.
snoozbuster commentedon Feb 26, 2015
Your regex is missing a couple parenthesis, but I don't think it works. If there's any ```s in front of a comment, all the ones prior don't match I think we need negative look-behind instead, maybe something like... Oh, you know what?
Why don't we just say that if you put a
\in front of it, it doesn't get converted? Then the regex is justAnd we don't have to guess what the user wants.
I think the code would have to be changed slightly to replace the
\<!---with<!---, that would be the only change.I think if we wanted to do it the other way we would need multiple regex's. I don't think the conditions we're searching for form a regular language, I think they're context-free.
snoozbuster commentedon Feb 26, 2015
This should work:
I can't seem to test it because I think it's still using the old version... I'm not exactly clear on where I'm supposed to install this plugin. I eventually gave up and dropped it into the
markdown.extensionspackage, but now there's no.pycfor it now so I have no idea where it's compiling to.ryneeverett commentedon Feb 26, 2015
Sorry, I haven't actually tried any of those they were just proofs of concept.
Regarding your suggested escape syntax, aren't you just creating a meta-meta-comment? How would one write about meta-meta-comments? At some point we have to arbitrarily choose among the ambiguities. I don't think there's any way to avoid that.
Regarding installation, the most reasonable method I'm aware of is to just copy the file into your project. I would not try to patch it into python-markdown.
ryneeverett commentedon Feb 26, 2015
I just added an
__init__.pyfile, so another option would be to include the repo as agit subtree, assuming you're using git.9 remaining items
snoozbuster commentedon Feb 28, 2015
Oops.
ryneeverett commentedon Mar 1, 2015
I'm talking about inline html.
Inlinetesting quote-single-spacesome text for testingmore textExpected:<\p></p><\p>testing quote-single-space</p><\p>some text for testingmore text</\p>Block
Should return the same string.
snoozbuster commentedon Mar 1, 2015
Oh, right, okay. Wouldn't the
<and>have to be escaped inside the<code>and<pre>blocks, though?ryneeverett commentedon Mar 1, 2015
Python-markdown replaces html blocks with placeholders early in the process and puts them back late in the process. So all it would take to fix this would be having the comment regex run while code blocks are not present. (Instead of before the process, which is what currently happens.)
ryneeverett commentedon Mar 2, 2015
So I forgot that the html is replaced with placeholders by a preprocessor, so making markdown-comments run immediately afterwards would be as simple as:
Unfortunately, since markdown-comments are valid html comments, they would all be replaced with placeholders by markdown and our regex's would find nothing.
The only solution that comes to mind requires three parsers:
html_blockthat searches for markdown-comments and modifies them so they're not valid html.html_blockthat removes any markdown-comments found. (The ones in html will have been replaced with placeholders by markdown.)raw_html(which replaces placeholders with raw html) that modifies the markdown comments in raw html back to their original state before they were munged by the first preprocessor.snoozbuster commentedon Mar 2, 2015
This is turning out to be a much larger project than I initially anticipated.
ryneeverett commentedon Mar 4, 2015
I think that strategy worked. My description of inline html in markdown above was incorrect. From the spec:
snoozbuster commentedon Mar 4, 2015
Sweet.
snoozbuster commentedon Mar 5, 2015
To be fair, this isn't what the comments were designed to handle, and I figured out what Markdown is seeing, but that's still not at all what I'd expect to see from that.
Additionally, after some in-browser testing, although your test you added does pass, it doesn't do what you'd expect. HTML is still processed inside a
<pre>block, so the comment is inserted into the HTML and promptly stripped by your browser. I think theRawCommentReplacerneeds to escape the opening (and probably closing) angle bracket. The test should probably be changed toas well (but making sure the number of spaces on the multiline stuff is right).
ryneeverett commentedon Mar 5, 2015
I'm not sure what the output you'd expect to see is, but I think this extension is doing the right thing. From the spec:
Therefore it makes sense to me that:
ryneeverett commentedon Mar 5, 2015
Correct, the comments will not render because they are valid html comments. And yes, the brackets would need to be escaped in order for them to be rendered.
The question is whether that's the responsibility of this extension or the user. I would be inclined to defer to what python-markdown does in this situation. That is, if you wanted to display regular html comments in a
<pre>block you'd have to escape them as well, which markdown does not. Eg.:vs.
snoozbuster commentedon Mar 6, 2015
I suppose that's fair enough. I had written a page and my comment wasn't showing up in my
<pre>block and it was very confusing, and then I remembered that it was turning into an HTML comment. It just seems like that defeats the point of not removing them in the source, because if I wanted to talk about the comments I could have writtenwithout any of this work.
ryneeverett commentedon Mar 6, 2015
Correct, the changes don't really affect any real-world use cases. But the extension no longer violates the contractual promise of the markdown spec not to meddle with the contents of html blocks and I consider that a worthy improvement.
README: add section on meta markdown comments, per issue #2.