UNPKG

8.98 kBJavaScriptView Raw
1(function (Prism) {
2
3 // Allow only one line break
4 var inner = /(?:\\.|[^\\\n\r]|(?:\n|\r\n?)(?!\n|\r\n?))/.source;
5
6 /**
7 * This function is intended for the creation of the bold or italic pattern.
8 *
9 * This also adds a lookbehind group to the given pattern to ensure that the pattern is not backslash-escaped.
10 *
11 * _Note:_ Keep in mind that this adds a capturing group.
12 *
13 * @param {string} pattern
14 * @param {boolean} starAlternative Whether to also add an alternative where all `_`s are replaced with `*`s.
15 * @returns {RegExp}
16 */
17 function createInline(pattern, starAlternative) {
18 pattern = pattern.replace(/<inner>/g, function () { return inner; });
19 if (starAlternative) {
20 pattern = pattern + '|' + pattern.replace(/_/g, '\\*');
21 }
22 return RegExp(/((?:^|[^\\])(?:\\{2})*)/.source + '(?:' + pattern + ')');
23 }
24
25
26 var tableCell = /(?:\\.|``.+?``|`[^`\r\n]+`|[^\\|\r\n`])+/.source;
27 var tableRow = /\|?__(?:\|__)+\|?(?:(?:\n|\r\n?)|$)/.source.replace(/__/g, function () { return tableCell; });
28 var tableLine = /\|?[ \t]*:?-{3,}:?[ \t]*(?:\|[ \t]*:?-{3,}:?[ \t]*)+\|?(?:\n|\r\n?)/.source;
29
30
31 Prism.languages.markdown = Prism.languages.extend('markup', {});
32 Prism.languages.insertBefore('markdown', 'prolog', {
33 'blockquote': {
34 // > ...
35 pattern: /^>(?:[\t ]*>)*/m,
36 alias: 'punctuation'
37 },
38 'table': {
39 pattern: RegExp('^' + tableRow + tableLine + '(?:' + tableRow + ')*', 'm'),
40 inside: {
41 'table-data-rows': {
42 pattern: RegExp('^(' + tableRow + tableLine + ')(?:' + tableRow + ')*$'),
43 lookbehind: true,
44 inside: {
45 'table-data': {
46 pattern: RegExp(tableCell),
47 inside: Prism.languages.markdown
48 },
49 'punctuation': /\|/
50 }
51 },
52 'table-line': {
53 pattern: RegExp('^(' + tableRow + ')' + tableLine + '$'),
54 lookbehind: true,
55 inside: {
56 'punctuation': /\||:?-{3,}:?/
57 }
58 },
59 'table-header-row': {
60 pattern: RegExp('^' + tableRow + '$'),
61 inside: {
62 'table-header': {
63 pattern: RegExp(tableCell),
64 alias: 'important',
65 inside: Prism.languages.markdown
66 },
67 'punctuation': /\|/
68 }
69 }
70 }
71 },
72 'code': [
73 {
74 // Prefixed by 4 spaces or 1 tab and preceded by an empty line
75 pattern: /((?:^|\n)[ \t]*\n|(?:^|\r\n?)[ \t]*\r\n?)(?: {4}|\t).+(?:(?:\n|\r\n?)(?: {4}|\t).+)*/,
76 lookbehind: true,
77 alias: 'keyword'
78 },
79 {
80 // `code`
81 // ``code``
82 pattern: /``.+?``|`[^`\r\n]+`/,
83 alias: 'keyword'
84 },
85 {
86 // ```optional language
87 // code block
88 // ```
89 pattern: /^```[\s\S]*?^```$/m,
90 greedy: true,
91 inside: {
92 'code-block': {
93 pattern: /^(```.*(?:\n|\r\n?))[\s\S]+?(?=(?:\n|\r\n?)^```$)/m,
94 lookbehind: true
95 },
96 'code-language': {
97 pattern: /^(```).+/,
98 lookbehind: true
99 },
100 'punctuation': /```/
101 }
102 }
103 ],
104 'title': [
105 {
106 // title 1
107 // =======
108
109 // title 2
110 // -------
111 pattern: /\S.*(?:\n|\r\n?)(?:==+|--+)(?=[ \t]*$)/m,
112 alias: 'important',
113 inside: {
114 punctuation: /==+$|--+$/
115 }
116 },
117 {
118 // # title 1
119 // ###### title 6
120 pattern: /(^\s*)#+.+/m,
121 lookbehind: true,
122 alias: 'important',
123 inside: {
124 punctuation: /^#+|#+$/
125 }
126 }
127 ],
128 'hr': {
129 // ***
130 // ---
131 // * * *
132 // -----------
133 pattern: /(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,
134 lookbehind: true,
135 alias: 'punctuation'
136 },
137 'list': {
138 // * item
139 // + item
140 // - item
141 // 1. item
142 pattern: /(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,
143 lookbehind: true,
144 alias: 'punctuation'
145 },
146 'url-reference': {
147 // [id]: http://example.com "Optional title"
148 // [id]: http://example.com 'Optional title'
149 // [id]: http://example.com (Optional title)
150 // [id]: <http://example.com> "Optional title"
151 pattern: /!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,
152 inside: {
153 'variable': {
154 pattern: /^(!?\[)[^\]]+/,
155 lookbehind: true
156 },
157 'string': /(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,
158 'punctuation': /^[\[\]!:]|[<>]/
159 },
160 alias: 'url'
161 },
162 'bold': {
163 // **strong**
164 // __strong__
165
166 // allow one nested instance of italic text using the same delimiter
167 pattern: createInline(/__(?:(?!_)<inner>|_(?:(?!_)<inner>)+_)+__/.source, true),
168 lookbehind: true,
169 greedy: true,
170 inside: {
171 'content': {
172 pattern: /(^..)[\s\S]+(?=..$)/,
173 lookbehind: true,
174 inside: {} // see below
175 },
176 'punctuation': /\*\*|__/
177 }
178 },
179 'italic': {
180 // *em*
181 // _em_
182
183 // allow one nested instance of bold text using the same delimiter
184 pattern: createInline(/_(?:(?!_)<inner>|__(?:(?!_)<inner>)+__)+_/.source, true),
185 lookbehind: true,
186 greedy: true,
187 inside: {
188 'content': {
189 pattern: /(^.)[\s\S]+(?=.$)/,
190 lookbehind: true,
191 inside: {} // see below
192 },
193 'punctuation': /[*_]/
194 }
195 },
196 'strike': {
197 // ~~strike through~~
198 // ~strike~
199 pattern: createInline(/(~~?)(?:(?!~)<inner>)+?\2/.source, false),
200 lookbehind: true,
201 greedy: true,
202 inside: {
203 'content': {
204 pattern: /(^~~?)[\s\S]+(?=\1$)/,
205 lookbehind: true,
206 inside: {} // see below
207 },
208 'punctuation': /~~?/
209 }
210 },
211 'url': {
212 // [example](http://example.com "Optional title")
213 // [example][id]
214 // [example] [id]
215 pattern: createInline(/!?\[(?:(?!\])<inner>)+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)| ?\[(?:(?!\])<inner>)+\])/.source, false),
216 lookbehind: true,
217 greedy: true,
218 inside: {
219 'variable': {
220 pattern: /(\[)[^\]]+(?=\]$)/,
221 lookbehind: true
222 },
223 'content': {
224 pattern: /(^!?\[)[^\]]+(?=\])/,
225 lookbehind: true,
226 inside: {} // see below
227 },
228 'string': {
229 pattern: /"(?:\\.|[^"\\])*"(?=\)$)/
230 }
231 }
232 }
233 });
234
235 ['url', 'bold', 'italic', 'strike'].forEach(function (token) {
236 ['url', 'bold', 'italic', 'strike'].forEach(function (inside) {
237 if (token !== inside) {
238 Prism.languages.markdown[token].inside.content.inside[inside] = Prism.languages.markdown[inside];
239 }
240 });
241 });
242
243 Prism.hooks.add('after-tokenize', function (env) {
244 if (env.language !== 'markdown' && env.language !== 'md') {
245 return;
246 }
247
248 function walkTokens(tokens) {
249 if (!tokens || typeof tokens === 'string') {
250 return;
251 }
252
253 for (var i = 0, l = tokens.length; i < l; i++) {
254 var token = tokens[i];
255
256 if (token.type !== 'code') {
257 walkTokens(token.content);
258 continue;
259 }
260
261 /*
262 * Add the correct `language-xxxx` class to this code block. Keep in mind that the `code-language` token
263 * is optional. But the grammar is defined so that there is only one case we have to handle:
264 *
265 * token.content = [
266 * <span class="punctuation">```</span>,
267 * <span class="code-language">xxxx</span>,
268 * '\n', // exactly one new lines (\r or \n or \r\n)
269 * <span class="code-block">...</span>,
270 * '\n', // exactly one new lines again
271 * <span class="punctuation">```</span>
272 * ];
273 */
274
275 var codeLang = token.content[1];
276 var codeBlock = token.content[3];
277
278 if (codeLang && codeBlock &&
279 codeLang.type === 'code-language' && codeBlock.type === 'code-block' &&
280 typeof codeLang.content === 'string') {
281
282 // this might be a language that Prism does not support
283
284 // do some replacements to support C++, C#, and F#
285 var lang = codeLang.content.replace(/\b#/g, 'sharp').replace(/\b\+\+/g, 'pp')
286 // only use the first word
287 lang = (/[a-z][\w-]*/i.exec(lang) || [''])[0].toLowerCase();
288 var alias = 'language-' + lang;
289
290 // add alias
291 if (!codeBlock.alias) {
292 codeBlock.alias = [alias];
293 } else if (typeof codeBlock.alias === 'string') {
294 codeBlock.alias = [codeBlock.alias, alias];
295 } else {
296 codeBlock.alias.push(alias);
297 }
298 }
299 }
300 }
301
302 walkTokens(env.tokens);
303 });
304
305 Prism.hooks.add('wrap', function (env) {
306 if (env.type !== 'code-block') {
307 return;
308 }
309
310 var codeLang = '';
311 for (var i = 0, l = env.classes.length; i < l; i++) {
312 var cls = env.classes[i];
313 var match = /language-(.+)/.exec(cls);
314 if (match) {
315 codeLang = match[1];
316 break;
317 }
318 }
319
320 var grammar = Prism.languages[codeLang];
321
322 if (!grammar) {
323 if (codeLang && codeLang !== 'none' && Prism.plugins.autoloader) {
324 var id = 'md-' + new Date().valueOf() + '-' + Math.floor(Math.random() * 1e16);
325 env.attributes['id'] = id;
326
327 Prism.plugins.autoloader.loadLanguages(codeLang, function () {
328 var ele = document.getElementById(id);
329 if (ele) {
330 ele.innerHTML = Prism.highlight(ele.textContent, Prism.languages[codeLang], codeLang);
331 }
332 });
333 }
334 } else {
335 // reverse Prism.util.encode
336 var code = env.content.replace(/&lt;/g, '<').replace(/&amp;/g, '&');
337
338 env.content = Prism.highlight(code, grammar, codeLang);
339 }
340 });
341
342 Prism.languages.md = Prism.languages.markdown;
343
344}(Prism));