twig.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. export var conf = {
  6. wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g,
  7. comments: {
  8. blockComment: ['{#', '#}']
  9. },
  10. brackets: [
  11. ['{#', '#}'],
  12. ['{%', '%}'],
  13. ['{{', '}}'],
  14. ['(', ')'],
  15. ['[', ']'],
  16. // HTML
  17. ['<!--', '-->'],
  18. ['<', '>']
  19. ],
  20. autoClosingPairs: [
  21. { open: '{# ', close: ' #}' },
  22. { open: '{% ', close: ' %}' },
  23. { open: '{{ ', close: ' }}' },
  24. { open: '[', close: ']' },
  25. { open: '(', close: ')' },
  26. { open: '"', close: '"' },
  27. { open: "'", close: "'" }
  28. ],
  29. surroundingPairs: [
  30. { open: '"', close: '"' },
  31. { open: "'", close: "'" },
  32. // HTML
  33. { open: '<', close: '>' }
  34. ]
  35. };
  36. export var language = {
  37. defaultToken: '',
  38. tokenPostfix: '',
  39. ignoreCase: true,
  40. keywords: [
  41. // (opening) tags
  42. 'apply',
  43. 'autoescape',
  44. 'block',
  45. 'deprecated',
  46. 'do',
  47. 'embed',
  48. 'extends',
  49. 'flush',
  50. 'for',
  51. 'from',
  52. 'if',
  53. 'import',
  54. 'include',
  55. 'macro',
  56. 'sandbox',
  57. 'set',
  58. 'use',
  59. 'verbatim',
  60. 'with',
  61. // closing tags
  62. 'endapply',
  63. 'endautoescape',
  64. 'endblock',
  65. 'endembed',
  66. 'endfor',
  67. 'endif',
  68. 'endmacro',
  69. 'endsandbox',
  70. 'endset',
  71. 'endwith',
  72. // literals
  73. 'true',
  74. 'false'
  75. ],
  76. tokenizer: {
  77. root: [
  78. // whitespace
  79. [/\s+/],
  80. // Twig Tag Delimiters
  81. [/{#/, 'comment.twig', '@commentState'],
  82. [/{%[-~]?/, 'delimiter.twig', '@blockState'],
  83. [/{{[-~]?/, 'delimiter.twig', '@variableState'],
  84. // HTML
  85. [/<!DOCTYPE/, 'metatag.html', '@doctype'],
  86. [/<!--/, 'comment.html', '@comment'],
  87. [
  88. /(<)((?:[\w\-]+:)?[\w\-]+)(\s*)(\/>)/,
  89. ['delimiter.html', 'tag.html', '', 'delimiter.html']
  90. ],
  91. [/(<)(script)/, ['delimiter.html', { token: 'tag.html', next: '@script' }]],
  92. [/(<)(style)/, ['delimiter.html', { token: 'tag.html', next: '@style' }]],
  93. [
  94. /(<)((?:[\w\-]+:)?[\w\-]+)/,
  95. ['delimiter.html', { token: 'tag.html', next: '@otherTag' }]
  96. ],
  97. [
  98. /(<\/)((?:[\w\-]+:)?[\w\-]+)/,
  99. ['delimiter.html', { token: 'tag.html', next: '@otherTag' }]
  100. ],
  101. [/</, 'delimiter.html'],
  102. [/[^<]+/] // text
  103. ],
  104. /**
  105. * Comment Tag Handling
  106. */
  107. commentState: [
  108. [/#}/, 'comment.twig', '@pop'],
  109. [/./, 'comment.twig']
  110. ],
  111. /**
  112. * Block Tag Handling
  113. */
  114. blockState: [
  115. [/[-~]?%}/, 'delimiter.twig', '@pop'],
  116. // whitespace
  117. [/\s+/],
  118. // verbatim
  119. // Unlike other blocks, verbatim ehas its own state
  120. // transition to ensure we mark its contents as strings.
  121. [
  122. /(verbatim)(\s*)([-~]?%})/,
  123. ['keyword.twig', '', { token: 'delimiter.twig', next: '@rawDataState' }]
  124. ],
  125. { include: 'expression' }
  126. ],
  127. rawDataState: [
  128. // endverbatim
  129. [
  130. /({%[-~]?)(\s*)(endverbatim)(\s*)([-~]?%})/,
  131. [
  132. 'delimiter.twig',
  133. '',
  134. 'keyword.twig',
  135. '',
  136. { token: 'delimiter.twig', next: '@popall' }
  137. ]
  138. ],
  139. [/./, 'string.twig']
  140. ],
  141. /**
  142. * Variable Tag Handling
  143. */
  144. variableState: [[/[-~]?}}/, 'delimiter.twig', '@pop'], { include: 'expression' }],
  145. stringState: [
  146. // closing double quoted string
  147. [/"/, 'string.twig', '@pop'],
  148. // interpolation start
  149. [/#{\s*/, 'string.twig', '@interpolationState'],
  150. // string part
  151. [/[^#"\\]*(?:(?:\\.|#(?!\{))[^#"\\]*)*/, 'string.twig']
  152. ],
  153. interpolationState: [
  154. // interpolation end
  155. [/}/, 'string.twig', '@pop'],
  156. { include: 'expression' }
  157. ],
  158. /**
  159. * Expression Handling
  160. */
  161. expression: [
  162. // whitespace
  163. [/\s+/],
  164. // operators - math
  165. [/\+|-|\/{1,2}|%|\*{1,2}/, 'operators.twig'],
  166. // operators - logic
  167. [/(and|or|not|b-and|b-xor|b-or)(\s+)/, ['operators.twig', '']],
  168. // operators - comparison (symbols)
  169. [/==|!=|<|>|>=|<=/, 'operators.twig'],
  170. // operators - comparison (words)
  171. [/(starts with|ends with|matches)(\s+)/, ['operators.twig', '']],
  172. // operators - containment
  173. [/(in)(\s+)/, ['operators.twig', '']],
  174. // operators - test
  175. [/(is)(\s+)/, ['operators.twig', '']],
  176. // operators - misc
  177. [/\||~|:|\.{1,2}|\?{1,2}/, 'operators.twig'],
  178. // names
  179. [
  180. /[^\W\d][\w]*/,
  181. {
  182. cases: {
  183. '@keywords': 'keyword.twig',
  184. '@default': 'variable.twig'
  185. }
  186. }
  187. ],
  188. // numbers
  189. [/\d+(\.\d+)?/, 'number.twig'],
  190. // punctuation
  191. [/\(|\)|\[|\]|{|}|,/, 'delimiter.twig'],
  192. // strings
  193. [/"([^#"\\]*(?:\\.[^#"\\]*)*)"|\'([^\'\\]*(?:\\.[^\'\\]*)*)\'/, 'string.twig'],
  194. // opening double quoted string
  195. [/"/, 'string.twig', '@stringState'],
  196. // misc syntactic constructs
  197. // These are not operators per se, but for the purposes of lexical analysis we
  198. // can treat them as such.
  199. // arrow functions
  200. [/=>/, 'operators.twig'],
  201. // assignment
  202. [/=/, 'operators.twig']
  203. ],
  204. /**
  205. * HTML
  206. */
  207. doctype: [
  208. [/[^>]+/, 'metatag.content.html'],
  209. [/>/, 'metatag.html', '@pop']
  210. ],
  211. comment: [
  212. [/-->/, 'comment.html', '@pop'],
  213. [/[^-]+/, 'comment.content.html'],
  214. [/./, 'comment.content.html']
  215. ],
  216. otherTag: [
  217. [/\/?>/, 'delimiter.html', '@pop'],
  218. [/"([^"]*)"/, 'attribute.value.html'],
  219. [/'([^']*)'/, 'attribute.value.html'],
  220. [/[\w\-]+/, 'attribute.name.html'],
  221. [/=/, 'delimiter.html'],
  222. [/[ \t\r\n]+/] // whitespace
  223. ],
  224. // -- BEGIN <script> tags handling
  225. // After <script
  226. script: [
  227. [/type/, 'attribute.name.html', '@scriptAfterType'],
  228. [/"([^"]*)"/, 'attribute.value.html'],
  229. [/'([^']*)'/, 'attribute.value.html'],
  230. [/[\w\-]+/, 'attribute.name.html'],
  231. [/=/, 'delimiter.html'],
  232. [
  233. />/,
  234. {
  235. token: 'delimiter.html',
  236. next: '@scriptEmbedded',
  237. nextEmbedded: 'text/javascript'
  238. }
  239. ],
  240. [/[ \t\r\n]+/],
  241. [
  242. /(<\/)(script\s*)(>)/,
  243. ['delimiter.html', 'tag.html', { token: 'delimiter.html', next: '@pop' }]
  244. ]
  245. ],
  246. // After <script ... type
  247. scriptAfterType: [
  248. [/=/, 'delimiter.html', '@scriptAfterTypeEquals'],
  249. [
  250. />/,
  251. {
  252. token: 'delimiter.html',
  253. next: '@scriptEmbedded',
  254. nextEmbedded: 'text/javascript'
  255. }
  256. ],
  257. [/[ \t\r\n]+/],
  258. [/<\/script\s*>/, { token: '@rematch', next: '@pop' }]
  259. ],
  260. // After <script ... type =
  261. scriptAfterTypeEquals: [
  262. [
  263. /"([^"]*)"/,
  264. {
  265. token: 'attribute.value.html',
  266. switchTo: '@scriptWithCustomType.$1'
  267. }
  268. ],
  269. [
  270. /'([^']*)'/,
  271. {
  272. token: 'attribute.value.html',
  273. switchTo: '@scriptWithCustomType.$1'
  274. }
  275. ],
  276. [
  277. />/,
  278. {
  279. token: 'delimiter.html',
  280. next: '@scriptEmbedded',
  281. nextEmbedded: 'text/javascript'
  282. }
  283. ],
  284. [/[ \t\r\n]+/],
  285. [/<\/script\s*>/, { token: '@rematch', next: '@pop' }]
  286. ],
  287. // After <script ... type = $S2
  288. scriptWithCustomType: [
  289. [
  290. />/,
  291. {
  292. token: 'delimiter.html',
  293. next: '@scriptEmbedded.$S2',
  294. nextEmbedded: '$S2'
  295. }
  296. ],
  297. [/"([^"]*)"/, 'attribute.value.html'],
  298. [/'([^']*)'/, 'attribute.value.html'],
  299. [/[\w\-]+/, 'attribute.name.html'],
  300. [/=/, 'delimiter.html'],
  301. [/[ \t\r\n]+/],
  302. [/<\/script\s*>/, { token: '@rematch', next: '@pop' }]
  303. ],
  304. scriptEmbedded: [
  305. [/<\/script/, { token: '@rematch', next: '@pop', nextEmbedded: '@pop' }],
  306. [/[^<]+/, '']
  307. ],
  308. // -- END <script> tags handling
  309. // -- BEGIN <style> tags handling
  310. // After <style
  311. style: [
  312. [/type/, 'attribute.name.html', '@styleAfterType'],
  313. [/"([^"]*)"/, 'attribute.value.html'],
  314. [/'([^']*)'/, 'attribute.value.html'],
  315. [/[\w\-]+/, 'attribute.name.html'],
  316. [/=/, 'delimiter.html'],
  317. [
  318. />/,
  319. {
  320. token: 'delimiter.html',
  321. next: '@styleEmbedded',
  322. nextEmbedded: 'text/css'
  323. }
  324. ],
  325. [/[ \t\r\n]+/],
  326. [
  327. /(<\/)(style\s*)(>)/,
  328. ['delimiter.html', 'tag.html', { token: 'delimiter.html', next: '@pop' }]
  329. ]
  330. ],
  331. // After <style ... type
  332. styleAfterType: [
  333. [/=/, 'delimiter.html', '@styleAfterTypeEquals'],
  334. [
  335. />/,
  336. {
  337. token: 'delimiter.html',
  338. next: '@styleEmbedded',
  339. nextEmbedded: 'text/css'
  340. }
  341. ],
  342. [/[ \t\r\n]+/],
  343. [/<\/style\s*>/, { token: '@rematch', next: '@pop' }]
  344. ],
  345. // After <style ... type =
  346. styleAfterTypeEquals: [
  347. [
  348. /"([^"]*)"/,
  349. {
  350. token: 'attribute.value.html',
  351. switchTo: '@styleWithCustomType.$1'
  352. }
  353. ],
  354. [
  355. /'([^']*)'/,
  356. {
  357. token: 'attribute.value.html',
  358. switchTo: '@styleWithCustomType.$1'
  359. }
  360. ],
  361. [
  362. />/,
  363. {
  364. token: 'delimiter.html',
  365. next: '@styleEmbedded',
  366. nextEmbedded: 'text/css'
  367. }
  368. ],
  369. [/[ \t\r\n]+/],
  370. [/<\/style\s*>/, { token: '@rematch', next: '@pop' }]
  371. ],
  372. // After <style ... type = $S2
  373. styleWithCustomType: [
  374. [
  375. />/,
  376. {
  377. token: 'delimiter.html',
  378. next: '@styleEmbedded.$S2',
  379. nextEmbedded: '$S2'
  380. }
  381. ],
  382. [/"([^"]*)"/, 'attribute.value.html'],
  383. [/'([^']*)'/, 'attribute.value.html'],
  384. [/[\w\-]+/, 'attribute.name.html'],
  385. [/=/, 'delimiter.html'],
  386. [/[ \t\r\n]+/],
  387. [/<\/style\s*>/, { token: '@rematch', next: '@pop' }]
  388. ],
  389. styleEmbedded: [
  390. [/<\/style/, { token: '@rematch', next: '@pop', nextEmbedded: '@pop' }],
  391. [/[^<]+/, '']
  392. ]
  393. }
  394. };