handlebars.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. import { languages } from '../fillers/monaco-editor-core.js';
  6. var EMPTY_ELEMENTS = [
  7. 'area',
  8. 'base',
  9. 'br',
  10. 'col',
  11. 'embed',
  12. 'hr',
  13. 'img',
  14. 'input',
  15. 'keygen',
  16. 'link',
  17. 'menuitem',
  18. 'meta',
  19. 'param',
  20. 'source',
  21. 'track',
  22. 'wbr'
  23. ];
  24. export var conf = {
  25. wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g,
  26. comments: {
  27. blockComment: ['{{!--', '--}}']
  28. },
  29. brackets: [
  30. ['<!--', '-->'],
  31. ['<', '>'],
  32. ['{{', '}}'],
  33. ['{', '}'],
  34. ['(', ')']
  35. ],
  36. autoClosingPairs: [
  37. { open: '{', close: '}' },
  38. { open: '[', close: ']' },
  39. { open: '(', close: ')' },
  40. { open: '"', close: '"' },
  41. { open: "'", close: "'" }
  42. ],
  43. surroundingPairs: [
  44. { open: '<', close: '>' },
  45. { open: '"', close: '"' },
  46. { open: "'", close: "'" }
  47. ],
  48. onEnterRules: [
  49. {
  50. beforeText: new RegExp("<(?!(?:" + EMPTY_ELEMENTS.join('|') + "))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$", 'i'),
  51. afterText: /^<\/(\w[\w\d]*)\s*>$/i,
  52. action: {
  53. indentAction: languages.IndentAction.IndentOutdent
  54. }
  55. },
  56. {
  57. beforeText: new RegExp("<(?!(?:" + EMPTY_ELEMENTS.join('|') + "))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$", 'i'),
  58. action: { indentAction: languages.IndentAction.Indent }
  59. }
  60. ]
  61. };
  62. export var language = {
  63. defaultToken: '',
  64. tokenPostfix: '',
  65. // ignoreCase: true,
  66. // The main tokenizer for our languages
  67. tokenizer: {
  68. root: [
  69. [/\{\{!--/, 'comment.block.start.handlebars', '@commentBlock'],
  70. [/\{\{!/, 'comment.start.handlebars', '@comment'],
  71. [/\{\{/, { token: '@rematch', switchTo: '@handlebarsInSimpleState.root' }],
  72. [/<!DOCTYPE/, 'metatag.html', '@doctype'],
  73. [/<!--/, 'comment.html', '@commentHtml'],
  74. [/(<)(\w+)(\/>)/, ['delimiter.html', 'tag.html', 'delimiter.html']],
  75. [/(<)(script)/, ['delimiter.html', { token: 'tag.html', next: '@script' }]],
  76. [/(<)(style)/, ['delimiter.html', { token: 'tag.html', next: '@style' }]],
  77. [/(<)([:\w]+)/, ['delimiter.html', { token: 'tag.html', next: '@otherTag' }]],
  78. [/(<\/)(\w+)/, ['delimiter.html', { token: 'tag.html', next: '@otherTag' }]],
  79. [/</, 'delimiter.html'],
  80. [/\{/, 'delimiter.html'],
  81. [/[^<{]+/] // text
  82. ],
  83. doctype: [
  84. [
  85. /\{\{/,
  86. {
  87. token: '@rematch',
  88. switchTo: '@handlebarsInSimpleState.comment'
  89. }
  90. ],
  91. [/[^>]+/, 'metatag.content.html'],
  92. [/>/, 'metatag.html', '@pop']
  93. ],
  94. comment: [
  95. [/\}\}/, 'comment.end.handlebars', '@pop'],
  96. [/./, 'comment.content.handlebars']
  97. ],
  98. commentBlock: [
  99. [/--\}\}/, 'comment.block.end.handlebars', '@pop'],
  100. [/./, 'comment.content.handlebars']
  101. ],
  102. commentHtml: [
  103. [
  104. /\{\{/,
  105. {
  106. token: '@rematch',
  107. switchTo: '@handlebarsInSimpleState.comment'
  108. }
  109. ],
  110. [/-->/, 'comment.html', '@pop'],
  111. [/[^-]+/, 'comment.content.html'],
  112. [/./, 'comment.content.html']
  113. ],
  114. otherTag: [
  115. [
  116. /\{\{/,
  117. {
  118. token: '@rematch',
  119. switchTo: '@handlebarsInSimpleState.otherTag'
  120. }
  121. ],
  122. [/\/?>/, 'delimiter.html', '@pop'],
  123. [/"([^"]*)"/, 'attribute.value'],
  124. [/'([^']*)'/, 'attribute.value'],
  125. [/[\w\-]+/, 'attribute.name'],
  126. [/=/, 'delimiter'],
  127. [/[ \t\r\n]+/] // whitespace
  128. ],
  129. // -- BEGIN <script> tags handling
  130. // After <script
  131. script: [
  132. [
  133. /\{\{/,
  134. {
  135. token: '@rematch',
  136. switchTo: '@handlebarsInSimpleState.script'
  137. }
  138. ],
  139. [/type/, 'attribute.name', '@scriptAfterType'],
  140. [/"([^"]*)"/, 'attribute.value'],
  141. [/'([^']*)'/, 'attribute.value'],
  142. [/[\w\-]+/, 'attribute.name'],
  143. [/=/, 'delimiter'],
  144. [
  145. />/,
  146. {
  147. token: 'delimiter.html',
  148. next: '@scriptEmbedded.text/javascript',
  149. nextEmbedded: 'text/javascript'
  150. }
  151. ],
  152. [/[ \t\r\n]+/],
  153. [
  154. /(<\/)(script\s*)(>)/,
  155. ['delimiter.html', 'tag.html', { token: 'delimiter.html', next: '@pop' }]
  156. ]
  157. ],
  158. // After <script ... type
  159. scriptAfterType: [
  160. [
  161. /\{\{/,
  162. {
  163. token: '@rematch',
  164. switchTo: '@handlebarsInSimpleState.scriptAfterType'
  165. }
  166. ],
  167. [/=/, 'delimiter', '@scriptAfterTypeEquals'],
  168. [
  169. />/,
  170. {
  171. token: 'delimiter.html',
  172. next: '@scriptEmbedded.text/javascript',
  173. nextEmbedded: 'text/javascript'
  174. }
  175. ],
  176. [/[ \t\r\n]+/],
  177. [/<\/script\s*>/, { token: '@rematch', next: '@pop' }]
  178. ],
  179. // After <script ... type =
  180. scriptAfterTypeEquals: [
  181. [
  182. /\{\{/,
  183. {
  184. token: '@rematch',
  185. switchTo: '@handlebarsInSimpleState.scriptAfterTypeEquals'
  186. }
  187. ],
  188. [
  189. /"([^"]*)"/,
  190. {
  191. token: 'attribute.value',
  192. switchTo: '@scriptWithCustomType.$1'
  193. }
  194. ],
  195. [
  196. /'([^']*)'/,
  197. {
  198. token: 'attribute.value',
  199. switchTo: '@scriptWithCustomType.$1'
  200. }
  201. ],
  202. [
  203. />/,
  204. {
  205. token: 'delimiter.html',
  206. next: '@scriptEmbedded.text/javascript',
  207. nextEmbedded: 'text/javascript'
  208. }
  209. ],
  210. [/[ \t\r\n]+/],
  211. [/<\/script\s*>/, { token: '@rematch', next: '@pop' }]
  212. ],
  213. // After <script ... type = $S2
  214. scriptWithCustomType: [
  215. [
  216. /\{\{/,
  217. {
  218. token: '@rematch',
  219. switchTo: '@handlebarsInSimpleState.scriptWithCustomType.$S2'
  220. }
  221. ],
  222. [
  223. />/,
  224. {
  225. token: 'delimiter.html',
  226. next: '@scriptEmbedded.$S2',
  227. nextEmbedded: '$S2'
  228. }
  229. ],
  230. [/"([^"]*)"/, 'attribute.value'],
  231. [/'([^']*)'/, 'attribute.value'],
  232. [/[\w\-]+/, 'attribute.name'],
  233. [/=/, 'delimiter'],
  234. [/[ \t\r\n]+/],
  235. [/<\/script\s*>/, { token: '@rematch', next: '@pop' }]
  236. ],
  237. scriptEmbedded: [
  238. [
  239. /\{\{/,
  240. {
  241. token: '@rematch',
  242. switchTo: '@handlebarsInEmbeddedState.scriptEmbedded.$S2',
  243. nextEmbedded: '@pop'
  244. }
  245. ],
  246. [/<\/script/, { token: '@rematch', next: '@pop', nextEmbedded: '@pop' }]
  247. ],
  248. // -- END <script> tags handling
  249. // -- BEGIN <style> tags handling
  250. // After <style
  251. style: [
  252. [
  253. /\{\{/,
  254. {
  255. token: '@rematch',
  256. switchTo: '@handlebarsInSimpleState.style'
  257. }
  258. ],
  259. [/type/, 'attribute.name', '@styleAfterType'],
  260. [/"([^"]*)"/, 'attribute.value'],
  261. [/'([^']*)'/, 'attribute.value'],
  262. [/[\w\-]+/, 'attribute.name'],
  263. [/=/, 'delimiter'],
  264. [
  265. />/,
  266. {
  267. token: 'delimiter.html',
  268. next: '@styleEmbedded.text/css',
  269. nextEmbedded: 'text/css'
  270. }
  271. ],
  272. [/[ \t\r\n]+/],
  273. [
  274. /(<\/)(style\s*)(>)/,
  275. ['delimiter.html', 'tag.html', { token: 'delimiter.html', next: '@pop' }]
  276. ]
  277. ],
  278. // After <style ... type
  279. styleAfterType: [
  280. [
  281. /\{\{/,
  282. {
  283. token: '@rematch',
  284. switchTo: '@handlebarsInSimpleState.styleAfterType'
  285. }
  286. ],
  287. [/=/, 'delimiter', '@styleAfterTypeEquals'],
  288. [
  289. />/,
  290. {
  291. token: 'delimiter.html',
  292. next: '@styleEmbedded.text/css',
  293. nextEmbedded: 'text/css'
  294. }
  295. ],
  296. [/[ \t\r\n]+/],
  297. [/<\/style\s*>/, { token: '@rematch', next: '@pop' }]
  298. ],
  299. // After <style ... type =
  300. styleAfterTypeEquals: [
  301. [
  302. /\{\{/,
  303. {
  304. token: '@rematch',
  305. switchTo: '@handlebarsInSimpleState.styleAfterTypeEquals'
  306. }
  307. ],
  308. [
  309. /"([^"]*)"/,
  310. {
  311. token: 'attribute.value',
  312. switchTo: '@styleWithCustomType.$1'
  313. }
  314. ],
  315. [
  316. /'([^']*)'/,
  317. {
  318. token: 'attribute.value',
  319. switchTo: '@styleWithCustomType.$1'
  320. }
  321. ],
  322. [
  323. />/,
  324. {
  325. token: 'delimiter.html',
  326. next: '@styleEmbedded.text/css',
  327. nextEmbedded: 'text/css'
  328. }
  329. ],
  330. [/[ \t\r\n]+/],
  331. [/<\/style\s*>/, { token: '@rematch', next: '@pop' }]
  332. ],
  333. // After <style ... type = $S2
  334. styleWithCustomType: [
  335. [
  336. /\{\{/,
  337. {
  338. token: '@rematch',
  339. switchTo: '@handlebarsInSimpleState.styleWithCustomType.$S2'
  340. }
  341. ],
  342. [
  343. />/,
  344. {
  345. token: 'delimiter.html',
  346. next: '@styleEmbedded.$S2',
  347. nextEmbedded: '$S2'
  348. }
  349. ],
  350. [/"([^"]*)"/, 'attribute.value'],
  351. [/'([^']*)'/, 'attribute.value'],
  352. [/[\w\-]+/, 'attribute.name'],
  353. [/=/, 'delimiter'],
  354. [/[ \t\r\n]+/],
  355. [/<\/style\s*>/, { token: '@rematch', next: '@pop' }]
  356. ],
  357. styleEmbedded: [
  358. [
  359. /\{\{/,
  360. {
  361. token: '@rematch',
  362. switchTo: '@handlebarsInEmbeddedState.styleEmbedded.$S2',
  363. nextEmbedded: '@pop'
  364. }
  365. ],
  366. [/<\/style/, { token: '@rematch', next: '@pop', nextEmbedded: '@pop' }]
  367. ],
  368. // -- END <style> tags handling
  369. handlebarsInSimpleState: [
  370. [/\{\{\{?/, 'delimiter.handlebars'],
  371. [/\}\}\}?/, { token: 'delimiter.handlebars', switchTo: '@$S2.$S3' }],
  372. { include: 'handlebarsRoot' }
  373. ],
  374. handlebarsInEmbeddedState: [
  375. [/\{\{\{?/, 'delimiter.handlebars'],
  376. [
  377. /\}\}\}?/,
  378. {
  379. token: 'delimiter.handlebars',
  380. switchTo: '@$S2.$S3',
  381. nextEmbedded: '$S3'
  382. }
  383. ],
  384. { include: 'handlebarsRoot' }
  385. ],
  386. handlebarsRoot: [
  387. [/"[^"]*"/, 'string.handlebars'],
  388. [/[#/][^\s}]+/, 'keyword.helper.handlebars'],
  389. [/else\b/, 'keyword.helper.handlebars'],
  390. [/[\s]+/],
  391. [/[^}]/, 'variable.parameter.handlebars']
  392. ]
  393. }
  394. };