markdown.js 4.8 KB

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