prism-textile.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. (function (Prism) {
  2. // We don't allow for pipes inside parentheses
  3. // to not break table pattern |(. foo |). bar |
  4. var modifierRegex = /\([^|()\n]+\)|\[[^\]\n]+\]|\{[^}\n]+\}/.source;
  5. // Opening and closing parentheses which are not a modifier
  6. // This pattern is necessary to prevent exponential backtracking
  7. var parenthesesRegex = /\)|\((?![^|()\n]+\))/.source;
  8. /**
  9. * @param {string} source
  10. * @param {string} [flags]
  11. */
  12. function withModifier(source, flags) {
  13. return RegExp(
  14. source
  15. .replace(/<MOD>/g, function () { return '(?:' + modifierRegex + ')'; })
  16. .replace(/<PAR>/g, function () { return '(?:' + parenthesesRegex + ')'; }),
  17. flags || '');
  18. }
  19. var modifierTokens = {
  20. 'css': {
  21. pattern: /\{[^{}]+\}/,
  22. inside: {
  23. rest: Prism.languages.css
  24. }
  25. },
  26. 'class-id': {
  27. pattern: /(\()[^()]+(?=\))/,
  28. lookbehind: true,
  29. alias: 'attr-value'
  30. },
  31. 'lang': {
  32. pattern: /(\[)[^\[\]]+(?=\])/,
  33. lookbehind: true,
  34. alias: 'attr-value'
  35. },
  36. // Anything else is punctuation (the first pattern is for row/col spans inside tables)
  37. 'punctuation': /[\\\/]\d+|\S/
  38. };
  39. var textile = Prism.languages.textile = Prism.languages.extend('markup', {
  40. 'phrase': {
  41. pattern: /(^|\r|\n)\S[\s\S]*?(?=$|\r?\n\r?\n|\r\r)/,
  42. lookbehind: true,
  43. inside: {
  44. // h1. Header 1
  45. 'block-tag': {
  46. pattern: withModifier(/^[a-z]\w*(?:<MOD>|<PAR>|[<>=])*\./.source),
  47. inside: {
  48. 'modifier': {
  49. pattern: withModifier(/(^[a-z]\w*)(?:<MOD>|<PAR>|[<>=])+(?=\.)/.source),
  50. lookbehind: true,
  51. inside: modifierTokens
  52. },
  53. 'tag': /^[a-z]\w*/,
  54. 'punctuation': /\.$/
  55. }
  56. },
  57. // # List item
  58. // * List item
  59. 'list': {
  60. pattern: withModifier(/^[*#]+<MOD>*\s+\S.*/.source, 'm'),
  61. inside: {
  62. 'modifier': {
  63. pattern: withModifier(/(^[*#]+)<MOD>+/.source),
  64. lookbehind: true,
  65. inside: modifierTokens
  66. },
  67. 'punctuation': /^[*#]+/
  68. }
  69. },
  70. // | cell | cell | cell |
  71. 'table': {
  72. // Modifiers can be applied to the row: {color:red}.|1|2|3|
  73. // or the cell: |{color:red}.1|2|3|
  74. pattern: withModifier(/^(?:(?:<MOD>|<PAR>|[<>=^~])+\.\s*)?(?:\|(?:(?:<MOD>|<PAR>|[<>=^~_]|[\\/]\d+)+\.|(?!(?:<MOD>|<PAR>|[<>=^~_]|[\\/]\d+)+\.))[^|]*)+\|/.source, 'm'),
  75. inside: {
  76. 'modifier': {
  77. // Modifiers for rows after the first one are
  78. // preceded by a pipe and a line feed
  79. pattern: withModifier(/(^|\|(?:\r?\n|\r)?)(?:<MOD>|<PAR>|[<>=^~_]|[\\/]\d+)+(?=\.)/.source),
  80. lookbehind: true,
  81. inside: modifierTokens
  82. },
  83. 'punctuation': /\||^\./
  84. }
  85. },
  86. 'inline': {
  87. // eslint-disable-next-line regexp/no-super-linear-backtracking
  88. pattern: withModifier(/(^|[^a-zA-Z\d])(\*\*|__|\?\?|[*_%@+\-^~])<MOD>*.+?\2(?![a-zA-Z\d])/.source),
  89. lookbehind: true,
  90. inside: {
  91. // Note: superscripts and subscripts are not handled specifically
  92. // *bold*, **bold**
  93. 'bold': {
  94. // eslint-disable-next-line regexp/no-super-linear-backtracking
  95. pattern: withModifier(/(^(\*\*?)<MOD>*).+?(?=\2)/.source),
  96. lookbehind: true
  97. },
  98. // _italic_, __italic__
  99. 'italic': {
  100. // eslint-disable-next-line regexp/no-super-linear-backtracking
  101. pattern: withModifier(/(^(__?)<MOD>*).+?(?=\2)/.source),
  102. lookbehind: true
  103. },
  104. // ??cite??
  105. 'cite': {
  106. // eslint-disable-next-line regexp/no-super-linear-backtracking
  107. pattern: withModifier(/(^\?\?<MOD>*).+?(?=\?\?)/.source),
  108. lookbehind: true,
  109. alias: 'string'
  110. },
  111. // @code@
  112. 'code': {
  113. // eslint-disable-next-line regexp/no-super-linear-backtracking
  114. pattern: withModifier(/(^@<MOD>*).+?(?=@)/.source),
  115. lookbehind: true,
  116. alias: 'keyword'
  117. },
  118. // +inserted+
  119. 'inserted': {
  120. // eslint-disable-next-line regexp/no-super-linear-backtracking
  121. pattern: withModifier(/(^\+<MOD>*).+?(?=\+)/.source),
  122. lookbehind: true
  123. },
  124. // -deleted-
  125. 'deleted': {
  126. // eslint-disable-next-line regexp/no-super-linear-backtracking
  127. pattern: withModifier(/(^-<MOD>*).+?(?=-)/.source),
  128. lookbehind: true
  129. },
  130. // %span%
  131. 'span': {
  132. // eslint-disable-next-line regexp/no-super-linear-backtracking
  133. pattern: withModifier(/(^%<MOD>*).+?(?=%)/.source),
  134. lookbehind: true
  135. },
  136. 'modifier': {
  137. pattern: withModifier(/(^\*\*|__|\?\?|[*_%@+\-^~])<MOD>+/.source),
  138. lookbehind: true,
  139. inside: modifierTokens
  140. },
  141. 'punctuation': /[*_%?@+\-^~]+/
  142. }
  143. },
  144. // [alias]http://example.com
  145. 'link-ref': {
  146. pattern: /^\[[^\]]+\]\S+$/m,
  147. inside: {
  148. 'string': {
  149. pattern: /(^\[)[^\]]+(?=\])/,
  150. lookbehind: true
  151. },
  152. 'url': {
  153. pattern: /(^\])\S+$/,
  154. lookbehind: true
  155. },
  156. 'punctuation': /[\[\]]/
  157. }
  158. },
  159. // "text":http://example.com
  160. // "text":link-ref
  161. 'link': {
  162. // eslint-disable-next-line regexp/no-super-linear-backtracking
  163. pattern: withModifier(/"<MOD>*[^"]+":.+?(?=[^\w/]?(?:\s|$))/.source),
  164. inside: {
  165. 'text': {
  166. // eslint-disable-next-line regexp/no-super-linear-backtracking
  167. pattern: withModifier(/(^"<MOD>*)[^"]+(?=")/.source),
  168. lookbehind: true
  169. },
  170. 'modifier': {
  171. pattern: withModifier(/(^")<MOD>+/.source),
  172. lookbehind: true,
  173. inside: modifierTokens
  174. },
  175. 'url': {
  176. pattern: /(:).+/,
  177. lookbehind: true
  178. },
  179. 'punctuation': /[":]/
  180. }
  181. },
  182. // !image.jpg!
  183. // !image.jpg(Title)!:http://example.com
  184. 'image': {
  185. pattern: withModifier(/!(?:<MOD>|<PAR>|[<>=])*(?![<>=])[^!\s()]+(?:\([^)]+\))?!(?::.+?(?=[^\w/]?(?:\s|$)))?/.source),
  186. inside: {
  187. 'source': {
  188. pattern: withModifier(/(^!(?:<MOD>|<PAR>|[<>=])*)(?![<>=])[^!\s()]+(?:\([^)]+\))?(?=!)/.source),
  189. lookbehind: true,
  190. alias: 'url'
  191. },
  192. 'modifier': {
  193. pattern: withModifier(/(^!)(?:<MOD>|<PAR>|[<>=])+/.source),
  194. lookbehind: true,
  195. inside: modifierTokens
  196. },
  197. 'url': {
  198. pattern: /(:).+/,
  199. lookbehind: true
  200. },
  201. 'punctuation': /[!:]/
  202. }
  203. },
  204. // Footnote[1]
  205. 'footnote': {
  206. pattern: /\b\[\d+\]/,
  207. alias: 'comment',
  208. inside: {
  209. 'punctuation': /\[|\]/
  210. }
  211. },
  212. // CSS(Cascading Style Sheet)
  213. 'acronym': {
  214. pattern: /\b[A-Z\d]+\([^)]+\)/,
  215. inside: {
  216. 'comment': {
  217. pattern: /(\()[^()]+(?=\))/,
  218. lookbehind: true
  219. },
  220. 'punctuation': /[()]/
  221. }
  222. },
  223. // Prism(C)
  224. 'mark': {
  225. pattern: /\b\((?:C|R|TM)\)/,
  226. alias: 'comment',
  227. inside: {
  228. 'punctuation': /[()]/
  229. }
  230. }
  231. }
  232. }
  233. });
  234. var phraseInside = textile['phrase'].inside;
  235. var nestedPatterns = {
  236. 'inline': phraseInside['inline'],
  237. 'link': phraseInside['link'],
  238. 'image': phraseInside['image'],
  239. 'footnote': phraseInside['footnote'],
  240. 'acronym': phraseInside['acronym'],
  241. 'mark': phraseInside['mark']
  242. };
  243. // Only allow alpha-numeric HTML tags, not XML tags
  244. textile.tag.pattern = /<\/?(?!\d)[a-z0-9]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i;
  245. // Allow some nesting
  246. var phraseInlineInside = phraseInside['inline'].inside;
  247. phraseInlineInside['bold'].inside = nestedPatterns;
  248. phraseInlineInside['italic'].inside = nestedPatterns;
  249. phraseInlineInside['inserted'].inside = nestedPatterns;
  250. phraseInlineInside['deleted'].inside = nestedPatterns;
  251. phraseInlineInside['span'].inside = nestedPatterns;
  252. // Allow some styles inside table cells
  253. var phraseTableInside = phraseInside['table'].inside;
  254. phraseTableInside['inline'] = nestedPatterns['inline'];
  255. phraseTableInside['link'] = nestedPatterns['link'];
  256. phraseTableInside['image'] = nestedPatterns['image'];
  257. phraseTableInside['footnote'] = nestedPatterns['footnote'];
  258. phraseTableInside['acronym'] = nestedPatterns['acronym'];
  259. phraseTableInside['mark'] = nestedPatterns['mark'];
  260. }(Prism));