UNPKG

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