fix(HTMLParser): fix ghCodeBlocks being parsed inside code tags

When using html pre/code tags to wrap github's fenced code block syntax,
showdown would parsed them instead of treating them like plain code.

Closes #229
This commit is contained in:
Estevão Soares dos Santos 2016-01-25 01:04:06 +00:00
parent e8852a83bb
commit 7d0436d210
14 changed files with 178 additions and 49 deletions

61
dist/showdown.js vendored
View File

@ -1,4 +1,4 @@
;/*! showdown 02-01-2016 */
;/*! showdown 25-01-2016 */
(function(){
/**
* Created by Tivie on 13-07-2015.
@ -929,7 +929,8 @@ showdown.Converter = function (converterOptions) {
hashLinkCounts: {},
langExtensions: langExtensions,
outputModifiers: outputModifiers,
converter: this
converter: this,
ghCodeBlocks: []
};
// attacklab: Replace ~ with ~T
@ -1628,6 +1629,7 @@ showdown.subParser('githubCodeBlocks', function (text, options, globals) {
text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g, function (wholeMatch, language, codeblock) {
var end = (options.omitExtraWLInCodeBlocks) ? '' : '\n';
// First parse the github code block
codeblock = showdown.subParser('encodeCode')(codeblock);
codeblock = showdown.subParser('detab')(codeblock);
codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
@ -1635,15 +1637,18 @@ showdown.subParser('githubCodeBlocks', function (text, options, globals) {
codeblock = '<pre><code' + (language ? ' class="' + language + ' language-' + language + '"' : '') + '>' + codeblock + end + '</code></pre>';
return showdown.subParser('hashBlock')(codeblock, options, globals);
codeblock = showdown.subParser('hashBlock')(codeblock, options, globals);
// Since GHCodeblocks can be false positives, we need to
// store the primitive text and the parsed text in a global var,
// and then return a token
return '\n\n~G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n';
});
// attacklab: strip sentinel
text = text.replace(/~0/, '');
text = globals.converter._dispatch('githubCodeBlocks.after', text, options);
return text;
return globals.converter._dispatch('githubCodeBlocks.after', text, options);
});
showdown.subParser('hashBlock', function (text, options, globals) {
@ -2143,11 +2148,10 @@ showdown.subParser('paragraphs', function (text, options, globals) {
for (var i = 0; i < end; i++) {
var str = grafs[i];
// if this is an HTML marker, copy it
if (str.search(/~K(\d+)K/g) >= 0) {
if (str.search(/~(K|G)(\d+)\1/g) >= 0) {
grafsOut.push(str);
} else if (str.search(/\S/) >= 0) {
} else {
str = showdown.subParser('spanGamut')(str, options, globals);
str = str.replace(/^([ \t]*)/g, '<p>');
str += '</p>';
@ -2157,18 +2161,39 @@ showdown.subParser('paragraphs', function (text, options, globals) {
/** Unhashify HTML blocks */
end = grafsOut.length;
console.log(text);
for (i = 0; i < end; i++) {
var blockText = '';
var blockText = '',
grafsOutIt = grafsOut[i],
child = false,
codeFlag = false;
// if this is a marker for an html block...
while (grafsOut[i].search(/~K(\d+)K/) >= 0) {
blockText = globals.gHtmlBlocks[RegExp.$1];
blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs
grafsOut[i] = grafsOut[i].replace(/~K\d+K/, blockText);
}
}
while (grafsOutIt.search(/~(K|G)(\d+)\1/) >= 0) {
var delim = RegExp.$1,
num = RegExp.$2;
text = globals.converter._dispatch('paragraphs.after', text, options);
return grafsOut.join('\n\n');
if (delim === 'K') {
blockText = globals.gHtmlBlocks[num];
} else {
// we need to check if ghBlock is a false positive
blockText = (codeFlag) ? globals.ghCodeBlocks[num].text : globals.ghCodeBlocks[num].codeblock;
}
blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs
grafsOutIt = grafsOutIt.replace(/(\n\n)?~(K|G)\d+\2(\n\n)?/, blockText);
// Check if grafsOutIt is a pre->code
if (/^<pre\b[^>]*>\s*<code\b[^>]*>/.test(grafsOutIt)) {
codeFlag = true;
}
child = true;
}
grafsOut[i] = grafsOutIt;
}
text = grafsOut.join('\n\n');
// Strip leading and trailing lines:
text = text.replace(/^\n+/g, '');
text = text.replace(/\n+$/g, '');
return globals.converter._dispatch('paragraphs.after', text, options);
});
/**

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -242,7 +242,8 @@ showdown.Converter = function (converterOptions) {
hashLinkCounts: {},
langExtensions: langExtensions,
outputModifiers: outputModifiers,
converter: this
converter: this,
ghCodeBlocks: []
};
// attacklab: Replace ~ with ~T

View File

@ -23,6 +23,7 @@ showdown.subParser('githubCodeBlocks', function (text, options, globals) {
text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g, function (wholeMatch, language, codeblock) {
var end = (options.omitExtraWLInCodeBlocks) ? '' : '\n';
// First parse the github code block
codeblock = showdown.subParser('encodeCode')(codeblock);
codeblock = showdown.subParser('detab')(codeblock);
codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
@ -30,13 +31,16 @@ showdown.subParser('githubCodeBlocks', function (text, options, globals) {
codeblock = '<pre><code' + (language ? ' class="' + language + ' language-' + language + '"' : '') + '>' + codeblock + end + '</code></pre>';
return showdown.subParser('hashBlock')(codeblock, options, globals);
codeblock = showdown.subParser('hashBlock')(codeblock, options, globals);
// Since GHCodeblocks can be false positives, we need to
// store the primitive text and the parsed text in a global var,
// and then return a token
return '\n\n~G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n';
});
// attacklab: strip sentinel
text = text.replace(/~0/, '');
text = globals.converter._dispatch('githubCodeBlocks.after', text, options);
return text;
return globals.converter._dispatch('githubCodeBlocks.after', text, options);
});

View File

@ -15,11 +15,10 @@ showdown.subParser('paragraphs', function (text, options, globals) {
for (var i = 0; i < end; i++) {
var str = grafs[i];
// if this is an HTML marker, copy it
if (str.search(/~K(\d+)K/g) >= 0) {
if (str.search(/~(K|G)(\d+)\1/g) >= 0) {
grafsOut.push(str);
} else if (str.search(/\S/) >= 0) {
} else {
str = showdown.subParser('spanGamut')(str, options, globals);
str = str.replace(/^([ \t]*)/g, '<p>');
str += '</p>';
@ -29,16 +28,37 @@ showdown.subParser('paragraphs', function (text, options, globals) {
/** Unhashify HTML blocks */
end = grafsOut.length;
console.log(text);
for (i = 0; i < end; i++) {
var blockText = '';
var blockText = '',
grafsOutIt = grafsOut[i],
child = false,
codeFlag = false;
// if this is a marker for an html block...
while (grafsOut[i].search(/~K(\d+)K/) >= 0) {
blockText = globals.gHtmlBlocks[RegExp.$1];
blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs
grafsOut[i] = grafsOut[i].replace(/~K\d+K/, blockText);
}
}
while (grafsOutIt.search(/~(K|G)(\d+)\1/) >= 0) {
var delim = RegExp.$1,
num = RegExp.$2;
text = globals.converter._dispatch('paragraphs.after', text, options);
return grafsOut.join('\n\n');
if (delim === 'K') {
blockText = globals.gHtmlBlocks[num];
} else {
// we need to check if ghBlock is a false positive
blockText = (codeFlag) ? globals.ghCodeBlocks[num].text : globals.ghCodeBlocks[num].codeblock;
}
blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs
grafsOutIt = grafsOutIt.replace(/(\n\n)?~(K|G)\d+\2(\n\n)?/, blockText);
// Check if grafsOutIt is a pre->code
if (/^<pre\b[^>]*>\s*<code\b[^>]*>/.test(grafsOutIt)) {
codeFlag = true;
}
child = true;
}
grafsOut[i] = grafsOutIt;
}
text = grafsOut.join('\n\n');
// Strip leading and trailing lines:
text = text.replace(/^\n+/g, '');
text = text.replace(/\n+$/g, '');
return globals.converter._dispatch('paragraphs.after', text, options);
});

View File

@ -2,6 +2,5 @@
<li><p>A list item with code:</p>
<pre><code>alert('Hello world!');
</code></pre>
</li>
</code></pre></li>
</ul>

View File

@ -5,14 +5,12 @@
<pre><code class="sh language-sh">$ git clone thing.git
dfgdfg
</code></pre>
</li>
</code></pre></li>
<li>
<p>I am another thing!</p>
<pre><code class="sh language-sh">$ git clone other-thing.git
foobar
</code></pre>
</li>
</code></pre></li>
</ol>

View File

@ -0,0 +1,25 @@
<pre lang="no-highlight"><code>
foo
```javascript
var s = "JavaScript syntax highlighting";
alert(s);
```
bar
</code></pre>
<p>this is a long paragraph</p>
<p>this is another long paragraph</p>
<pre lang="no-highlight"><code>```javascript
var s = "JavaScript syntax highlighting";
alert(s);
```
```python
s = "Python syntax highlighting"
print s
```
</code></pre>

View File

@ -0,0 +1,25 @@
<pre lang="no-highlight"><code>
foo
```javascript
var s = "JavaScript syntax highlighting";
alert(s);
```
bar
</code></pre>
this is a long paragraph
this is another long paragraph
<pre lang="no-highlight"><code>```javascript
var s = "JavaScript syntax highlighting";
alert(s);
```
```python
s = "Python syntax highlighting"
print s
```
</code></pre>

View File

@ -0,0 +1,16 @@
<pre lang="no-highlight"><code>
```javascript
var s = "JavaScript syntax highlighting";
alert(s);
```
```python
s = "Python syntax highlighting"
print s
```
```
No language indicated, so no syntax highlighting.
But let's throw in a <b>tag</b>.
```
</code></pre>

View File

@ -0,0 +1,16 @@
<pre lang="no-highlight"><code>
```javascript
var s = "JavaScript syntax highlighting";
alert(s);
```
```python
s = "Python syntax highlighting"
print s
```
```
No language indicated, so no syntax highlighting.
But let's throw in a <b>tag</b>.
```
</code></pre>

View File

@ -1,8 +1,8 @@
<ul>
<li><p>a list containing a block of code</p>
<li>
<p>a list containing a block of code</p>
<pre><code>10 PRINT HELLO INFINITE
<pre><code>10 PRINT HELLO INFINITE
20 GOTO 10
</code></pre>
</li>
</code></pre></li>
</ul>