UNPKG

5.04 kBJavaScriptView Raw
1/*
2Language: Markdown
3Requires: xml.js
4Author: John Crepezzi <john.crepezzi@gmail.com>
5Website: https://daringfireball.net/projects/markdown/
6Category: common, markup
7*/
8
9function markdown(hljs) {
10 const regex = hljs.regex;
11 const INLINE_HTML = {
12 begin: /<\/?[A-Za-z_]/,
13 end: '>',
14 subLanguage: 'xml',
15 relevance: 0
16 };
17 const HORIZONTAL_RULE = {
18 begin: '^[-\\*]{3,}',
19 end: '$'
20 };
21 const CODE = {
22 className: 'code',
23 variants: [
24 // TODO: fix to allow these to work with sublanguage also
25 { begin: '(`{3,})[^`](.|\\n)*?\\1`*[ ]*' },
26 { begin: '(~{3,})[^~](.|\\n)*?\\1~*[ ]*' },
27 // needed to allow markdown as a sublanguage to work
28 {
29 begin: '```',
30 end: '```+[ ]*$'
31 },
32 {
33 begin: '~~~',
34 end: '~~~+[ ]*$'
35 },
36 { begin: '`.+?`' },
37 {
38 begin: '(?=^( {4}|\\t))',
39 // use contains to gobble up multiple lines to allow the block to be whatever size
40 // but only have a single open/close tag vs one per line
41 contains: [
42 {
43 begin: '^( {4}|\\t)',
44 end: '(\\n)$'
45 }
46 ],
47 relevance: 0
48 }
49 ]
50 };
51 const LIST = {
52 className: 'bullet',
53 begin: '^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)',
54 end: '\\s+',
55 excludeEnd: true
56 };
57 const LINK_REFERENCE = {
58 begin: /^\[[^\n]+\]:/,
59 returnBegin: true,
60 contains: [
61 {
62 className: 'symbol',
63 begin: /\[/,
64 end: /\]/,
65 excludeBegin: true,
66 excludeEnd: true
67 },
68 {
69 className: 'link',
70 begin: /:\s*/,
71 end: /$/,
72 excludeBegin: true
73 }
74 ]
75 };
76 const URL_SCHEME = /[A-Za-z][A-Za-z0-9+.-]*/;
77 const LINK = {
78 variants: [
79 // too much like nested array access in so many languages
80 // to have any real relevance
81 {
82 begin: /\[.+?\]\[.*?\]/,
83 relevance: 0
84 },
85 // popular internet URLs
86 {
87 begin: /\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/,
88 relevance: 2
89 },
90 {
91 begin: regex.concat(/\[.+?\]\(/, URL_SCHEME, /:\/\/.*?\)/),
92 relevance: 2
93 },
94 // relative urls
95 {
96 begin: /\[.+?\]\([./?&#].*?\)/,
97 relevance: 1
98 },
99 // whatever else, lower relevance (might not be a link at all)
100 {
101 begin: /\[.*?\]\(.*?\)/,
102 relevance: 0
103 }
104 ],
105 returnBegin: true,
106 contains: [
107 {
108 // empty strings for alt or link text
109 match: /\[(?=\])/ },
110 {
111 className: 'string',
112 relevance: 0,
113 begin: '\\[',
114 end: '\\]',
115 excludeBegin: true,
116 returnEnd: true
117 },
118 {
119 className: 'link',
120 relevance: 0,
121 begin: '\\]\\(',
122 end: '\\)',
123 excludeBegin: true,
124 excludeEnd: true
125 },
126 {
127 className: 'symbol',
128 relevance: 0,
129 begin: '\\]\\[',
130 end: '\\]',
131 excludeBegin: true,
132 excludeEnd: true
133 }
134 ]
135 };
136 const BOLD = {
137 className: 'strong',
138 contains: [], // defined later
139 variants: [
140 {
141 begin: /_{2}/,
142 end: /_{2}/
143 },
144 {
145 begin: /\*{2}/,
146 end: /\*{2}/
147 }
148 ]
149 };
150 const ITALIC = {
151 className: 'emphasis',
152 contains: [], // defined later
153 variants: [
154 {
155 begin: /\*(?!\*)/,
156 end: /\*/
157 },
158 {
159 begin: /_(?!_)/,
160 end: /_/,
161 relevance: 0
162 }
163 ]
164 };
165
166 // 3 level deep nesting is not allowed because it would create confusion
167 // in cases like `***testing***` because where we don't know if the last
168 // `***` is starting a new bold/italic or finishing the last one
169 const BOLD_WITHOUT_ITALIC = hljs.inherit(BOLD, { contains: [] });
170 const ITALIC_WITHOUT_BOLD = hljs.inherit(ITALIC, { contains: [] });
171 BOLD.contains.push(ITALIC_WITHOUT_BOLD);
172 ITALIC.contains.push(BOLD_WITHOUT_ITALIC);
173
174 let CONTAINABLE = [
175 INLINE_HTML,
176 LINK
177 ];
178
179 [
180 BOLD,
181 ITALIC,
182 BOLD_WITHOUT_ITALIC,
183 ITALIC_WITHOUT_BOLD
184 ].forEach(m => {
185 m.contains = m.contains.concat(CONTAINABLE);
186 });
187
188 CONTAINABLE = CONTAINABLE.concat(BOLD, ITALIC);
189
190 const HEADER = {
191 className: 'section',
192 variants: [
193 {
194 begin: '^#{1,6}',
195 end: '$',
196 contains: CONTAINABLE
197 },
198 {
199 begin: '(?=^.+?\\n[=-]{2,}$)',
200 contains: [
201 { begin: '^[=-]*$' },
202 {
203 begin: '^',
204 end: "\\n",
205 contains: CONTAINABLE
206 }
207 ]
208 }
209 ]
210 };
211
212 const BLOCKQUOTE = {
213 className: 'quote',
214 begin: '^>\\s+',
215 contains: CONTAINABLE,
216 end: '$'
217 };
218
219 return {
220 name: 'Markdown',
221 aliases: [
222 'md',
223 'mkdown',
224 'mkd'
225 ],
226 contains: [
227 HEADER,
228 INLINE_HTML,
229 LIST,
230 BOLD,
231 ITALIC,
232 BLOCKQUOTE,
233 CODE,
234 HORIZONTAL_RULE,
235 LINK,
236 LINK_REFERENCE
237 ]
238 };
239}
240
241module.exports = markdown;