twig.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. // CodeMirror, copyright (c) by Marijn Haverbeke and others
  2. // Distributed under an MIT license: https://codemirror.net/LICENSE
  3. ;(function (mod) {
  4. if (typeof exports == 'object' && typeof module == 'object')
  5. // CommonJS
  6. mod(require('../../lib/codemirror'), require('../../addon/mode/multiplex'))
  7. else if (typeof define == 'function' && define.amd)
  8. // AMD
  9. define(['../../lib/codemirror', '../../addon/mode/multiplex'], mod)
  10. // Plain browser env
  11. else mod(CodeMirror)
  12. })(function (CodeMirror) {
  13. 'use strict'
  14. CodeMirror.defineMode('twig:inner', function () {
  15. var keywords = [
  16. 'and',
  17. 'as',
  18. 'autoescape',
  19. 'endautoescape',
  20. 'block',
  21. 'do',
  22. 'endblock',
  23. 'else',
  24. 'elseif',
  25. 'extends',
  26. 'for',
  27. 'endfor',
  28. 'embed',
  29. 'endembed',
  30. 'filter',
  31. 'endfilter',
  32. 'flush',
  33. 'from',
  34. 'if',
  35. 'endif',
  36. 'in',
  37. 'is',
  38. 'include',
  39. 'import',
  40. 'not',
  41. 'or',
  42. 'set',
  43. 'spaceless',
  44. 'endspaceless',
  45. 'with',
  46. 'endwith',
  47. 'trans',
  48. 'endtrans',
  49. 'blocktrans',
  50. 'endblocktrans',
  51. 'macro',
  52. 'endmacro',
  53. 'use',
  54. 'verbatim',
  55. 'endverbatim',
  56. ],
  57. operator = /^[+\-*&%=<>!?|~^]/,
  58. sign = /^[:\[\(\{]/,
  59. atom = ['true', 'false', 'null', 'empty', 'defined', 'divisibleby', 'divisible by', 'even', 'odd', 'iterable', 'sameas', 'same as'],
  60. number = /^(\d[+\-\*\/])?\d+(\.\d+)?/
  61. keywords = new RegExp('((' + keywords.join(')|(') + '))\\b')
  62. atom = new RegExp('((' + atom.join(')|(') + '))\\b')
  63. function tokenBase(stream, state) {
  64. var ch = stream.peek()
  65. //Comment
  66. if (state.incomment) {
  67. if (!stream.skipTo('#}')) {
  68. stream.skipToEnd()
  69. } else {
  70. stream.eatWhile(/\#|}/)
  71. state.incomment = false
  72. }
  73. return 'comment'
  74. //Tag
  75. } else if (state.intag) {
  76. //After operator
  77. if (state.operator) {
  78. state.operator = false
  79. if (stream.match(atom)) {
  80. return 'atom'
  81. }
  82. if (stream.match(number)) {
  83. return 'number'
  84. }
  85. }
  86. //After sign
  87. if (state.sign) {
  88. state.sign = false
  89. if (stream.match(atom)) {
  90. return 'atom'
  91. }
  92. if (stream.match(number)) {
  93. return 'number'
  94. }
  95. }
  96. if (state.instring) {
  97. if (ch == state.instring) {
  98. state.instring = false
  99. }
  100. stream.next()
  101. return 'string'
  102. } else if (ch == "'" || ch == '"') {
  103. state.instring = ch
  104. stream.next()
  105. return 'string'
  106. } else if (stream.match(state.intag + '}') || (stream.eat('-') && stream.match(state.intag + '}'))) {
  107. state.intag = false
  108. return 'tag'
  109. } else if (stream.match(operator)) {
  110. state.operator = true
  111. return 'operator'
  112. } else if (stream.match(sign)) {
  113. state.sign = true
  114. } else {
  115. if (stream.eat(' ') || stream.sol()) {
  116. if (stream.match(keywords)) {
  117. return 'keyword'
  118. }
  119. if (stream.match(atom)) {
  120. return 'atom'
  121. }
  122. if (stream.match(number)) {
  123. return 'number'
  124. }
  125. if (stream.sol()) {
  126. stream.next()
  127. }
  128. } else {
  129. stream.next()
  130. }
  131. }
  132. return 'variable'
  133. } else if (stream.eat('{')) {
  134. if (stream.eat('#')) {
  135. state.incomment = true
  136. if (!stream.skipTo('#}')) {
  137. stream.skipToEnd()
  138. } else {
  139. stream.eatWhile(/\#|}/)
  140. state.incomment = false
  141. }
  142. return 'comment'
  143. //Open tag
  144. } else if ((ch = stream.eat(/\{|%/))) {
  145. //Cache close tag
  146. state.intag = ch
  147. if (ch == '{') {
  148. state.intag = '}'
  149. }
  150. stream.eat('-')
  151. return 'tag'
  152. }
  153. }
  154. stream.next()
  155. }
  156. return {
  157. startState: function () {
  158. return {}
  159. },
  160. token: function (stream, state) {
  161. return tokenBase(stream, state)
  162. },
  163. }
  164. })
  165. CodeMirror.defineMode('twig', function (config, parserConfig) {
  166. var twigInner = CodeMirror.getMode(config, 'twig:inner')
  167. if (!parserConfig || !parserConfig.base) return twigInner
  168. return CodeMirror.multiplexingMode(CodeMirror.getMode(config, parserConfig.base), {
  169. open: /\{[{#%]/,
  170. close: /[}#%]\}/,
  171. mode: twigInner,
  172. parseDelimiters: true,
  173. })
  174. })
  175. CodeMirror.defineMIME('text/x-twig', 'twig')
  176. })