Skip to content

Commit b0717e7

Browse files
RunDevelopmentmAAdhaTTah
authored andcommitted
Add support for code blocks in Markdown (#1562)
It also supports syntax highlighting! The highlighting is done in two steps: 1. Add an alias `language-****` containing the given language to the `code-block` token. This happens in the `after-tokenize` hook. 2. Highlight the code with the `wrap` hook. This is to get around the encoding (`util.encode`) of tokens in `Prism.highlight`. By using this procedure we get the correct execution of the `before-tokenize`, `after-tokenize`, and `wrap` hook for all included languages.
1 parent 1b81238 commit b0717e7

File tree

3 files changed

+102
-3
lines changed

3 files changed

+102
-3
lines changed

components/prism-markdown.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,24 @@ Prism.languages.insertBefore('markdown', 'prolog', {
1616
// ``code``
1717
pattern: /``.+?``|`[^`\n]+`/,
1818
alias: 'keyword'
19+
},
20+
{
21+
// ```optional language
22+
// code block
23+
// ```
24+
pattern: /^```[\s\S]*?^```$/m,
25+
greedy: true,
26+
inside: {
27+
'code-block': {
28+
pattern: /^(```.*(?:\r?\n|\r))[\s\S]+?(?=(?:\r?\n|\r)^```$)/m,
29+
lookbehind: true
30+
},
31+
'code-language': {
32+
pattern: /^(```).+/,
33+
lookbehind: true
34+
},
35+
'punctuation': /```/
36+
}
1937
}
2038
],
2139
'title': [
@@ -137,3 +155,73 @@ Prism.languages.markdown['italic'].inside['bold'] = Prism.languages.markdown['bo
137155
Prism.languages.markdown['italic'].inside['strike'] = Prism.languages.markdown['strike'];
138156
Prism.languages.markdown['strike'].inside['bold'] = Prism.languages.markdown['bold'];
139157
Prism.languages.markdown['strike'].inside['italic'] = Prism.languages.markdown['italic'];
158+
159+
Prism.hooks.add('after-tokenize', function (env) {
160+
if (env.language !== 'markdown') {
161+
return;
162+
}
163+
164+
function walkTokens(tokens) {
165+
if (!tokens || typeof tokens === 'string') {
166+
return;
167+
}
168+
169+
for (var i = 0, l = tokens.length; i < l; i++) {
170+
var token = tokens[i];
171+
172+
if (token.type !== 'code') {
173+
walkTokens(token.content);
174+
continue;
175+
}
176+
177+
var codeLang = token.content[1];
178+
var codeBlock = token.content[3];
179+
180+
if (codeLang && codeBlock &&
181+
codeLang.type === 'code-language' && codeBlock.type === 'code-block' &&
182+
typeof codeLang.content === 'string') {
183+
184+
// this might be a language that Prism does not support
185+
var alias = 'language-' + codeLang.content.trim().split(/\s+/)[0].toLowerCase();
186+
187+
// add alias
188+
if (!codeBlock.alias) {
189+
codeBlock.alias = [alias];
190+
} else if (typeof codeBlock.alias === 'string') {
191+
codeBlock.alias = [codeBlock.alias, alias];
192+
} else {
193+
codeBlock.alias.push(alias);
194+
}
195+
}
196+
}
197+
}
198+
199+
walkTokens(env.tokens);
200+
});
201+
202+
Prism.hooks.add('wrap', function (env) {
203+
if (env.type !== 'code-block') {
204+
return;
205+
}
206+
207+
var codeLang = '';
208+
for (var i = 0, l = env.classes.length; i < l; i++) {
209+
var cls = env.classes[i];
210+
var match = /language-(\w+)/.exec(cls);
211+
if (match) {
212+
codeLang = match[1];
213+
break;
214+
}
215+
}
216+
217+
var grammar = Prism.languages[codeLang];
218+
219+
if (!grammar) {
220+
return;
221+
}
222+
223+
// reverse Prism.util.encode
224+
var code = env.content.replace(/&lt;/g, '<').replace(/&amp;/g, '&');
225+
226+
env.content = Prism.highlight(code, grammar, codeLang);
227+
});

components/prism-markdown.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/languages/markdown/code_feature.test

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,29 @@
55

66
foobar
77

8+
``` js
9+
var a = 0;
10+
```
11+
812
----------------------------------------------------
913

1014
[
1115
["code", "`foo bar baz`"],
1216
["code", "``foo `bar` baz``"],
1317
["code", " foobar"],
14-
["code", "\tfoobar"]
18+
["code", "\tfoobar"],
19+
20+
["code", [
21+
["punctuation", "```"],
22+
["code-language", " js"],
23+
["code-block", "var a = 0;"],
24+
["punctuation", "```"]
25+
]]
1526
]
1627

1728
----------------------------------------------------
1829

1930
Checks for code blocks and inline code. The first code block is
2031
indented with 4 spaces, the second one is indented with 1 tab.
2132
The initial dot is necessary because of the first part being trimmed
22-
by the test runner.
33+
by the test runner.

0 commit comments

Comments
 (0)