twig.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. /*
  2. Language: Twig
  3. Requires: xml.js
  4. Author: Luke Holder <lukemh@gmail.com>
  5. Description: Twig is a templating language for PHP
  6. Website: https://twig.symfony.com
  7. Category: template
  8. */
  9. function twig(hljs) {
  10. const regex = hljs.regex;
  11. const FUNCTION_NAMES = [
  12. "absolute_url",
  13. "asset|0",
  14. "asset_version",
  15. "attribute",
  16. "block",
  17. "constant",
  18. "controller|0",
  19. "country_timezones",
  20. "csrf_token",
  21. "cycle",
  22. "date",
  23. "dump",
  24. "expression",
  25. "form|0",
  26. "form_end",
  27. "form_errors",
  28. "form_help",
  29. "form_label",
  30. "form_rest",
  31. "form_row",
  32. "form_start",
  33. "form_widget",
  34. "html_classes",
  35. "include",
  36. "is_granted",
  37. "logout_path",
  38. "logout_url",
  39. "max",
  40. "min",
  41. "parent",
  42. "path|0",
  43. "random",
  44. "range",
  45. "relative_path",
  46. "render",
  47. "render_esi",
  48. "source",
  49. "template_from_string",
  50. "url|0"
  51. ];
  52. const FILTERS = [
  53. "abs",
  54. "abbr_class",
  55. "abbr_method",
  56. "batch",
  57. "capitalize",
  58. "column",
  59. "convert_encoding",
  60. "country_name",
  61. "currency_name",
  62. "currency_symbol",
  63. "data_uri",
  64. "date",
  65. "date_modify",
  66. "default",
  67. "escape",
  68. "file_excerpt",
  69. "file_link",
  70. "file_relative",
  71. "filter",
  72. "first",
  73. "format",
  74. "format_args",
  75. "format_args_as_text",
  76. "format_currency",
  77. "format_date",
  78. "format_datetime",
  79. "format_file",
  80. "format_file_from_text",
  81. "format_number",
  82. "format_time",
  83. "html_to_markdown",
  84. "humanize",
  85. "inky_to_html",
  86. "inline_css",
  87. "join",
  88. "json_encode",
  89. "keys",
  90. "language_name",
  91. "last",
  92. "length",
  93. "locale_name",
  94. "lower",
  95. "map",
  96. "markdown",
  97. "markdown_to_html",
  98. "merge",
  99. "nl2br",
  100. "number_format",
  101. "raw",
  102. "reduce",
  103. "replace",
  104. "reverse",
  105. "round",
  106. "slice",
  107. "slug",
  108. "sort",
  109. "spaceless",
  110. "split",
  111. "striptags",
  112. "timezone_name",
  113. "title",
  114. "trans",
  115. "transchoice",
  116. "trim",
  117. "u|0",
  118. "upper",
  119. "url_encode",
  120. "yaml_dump",
  121. "yaml_encode"
  122. ];
  123. let TAG_NAMES = [
  124. "apply",
  125. "autoescape",
  126. "block",
  127. "cache",
  128. "deprecated",
  129. "do",
  130. "embed",
  131. "extends",
  132. "filter",
  133. "flush",
  134. "for",
  135. "form_theme",
  136. "from",
  137. "if",
  138. "import",
  139. "include",
  140. "macro",
  141. "sandbox",
  142. "set",
  143. "stopwatch",
  144. "trans",
  145. "trans_default_domain",
  146. "transchoice",
  147. "use",
  148. "verbatim",
  149. "with"
  150. ];
  151. TAG_NAMES = TAG_NAMES.concat(TAG_NAMES.map(t => `end${t}`));
  152. const STRING = {
  153. scope: 'string',
  154. variants: [
  155. {
  156. begin: /'/,
  157. end: /'/
  158. },
  159. {
  160. begin: /"/,
  161. end: /"/
  162. },
  163. ]
  164. };
  165. const NUMBER = {
  166. scope: "number",
  167. match: /\d+/
  168. };
  169. const PARAMS = {
  170. begin: /\(/,
  171. end: /\)/,
  172. excludeBegin: true,
  173. excludeEnd: true,
  174. contains: [
  175. STRING,
  176. NUMBER
  177. ]
  178. };
  179. const FUNCTIONS = {
  180. beginKeywords: FUNCTION_NAMES.join(" "),
  181. keywords: { name: FUNCTION_NAMES },
  182. relevance: 0,
  183. contains: [ PARAMS ]
  184. };
  185. const FILTER = {
  186. match: /\|(?=[A-Za-z_]+:?)/,
  187. beginScope: "punctuation",
  188. relevance: 0,
  189. contains: [
  190. {
  191. match: /[A-Za-z_]+:?/,
  192. keywords: FILTERS
  193. },
  194. ]
  195. };
  196. const tagNamed = (tagnames, { relevance }) => {
  197. return {
  198. beginScope: {
  199. 1: 'template-tag',
  200. 3: 'name'
  201. },
  202. relevance: relevance || 2,
  203. endScope: 'template-tag',
  204. begin: [
  205. /\{%/,
  206. /\s*/,
  207. regex.either(...tagnames)
  208. ],
  209. end: /%\}/,
  210. keywords: "in",
  211. contains: [
  212. FILTER,
  213. FUNCTIONS,
  214. STRING,
  215. NUMBER
  216. ]
  217. };
  218. };
  219. const CUSTOM_TAG_RE = /[a-z_]+/;
  220. const TAG = tagNamed(TAG_NAMES, { relevance: 2 });
  221. const CUSTOM_TAG = tagNamed([ CUSTOM_TAG_RE ], { relevance: 1 });
  222. return {
  223. name: 'Twig',
  224. aliases: [ 'craftcms' ],
  225. case_insensitive: true,
  226. subLanguage: 'xml',
  227. contains: [
  228. hljs.COMMENT(/\{#/, /#\}/),
  229. TAG,
  230. CUSTOM_TAG,
  231. {
  232. className: 'template-variable',
  233. begin: /\{\{/,
  234. end: /\}\}/,
  235. contains: [
  236. 'self',
  237. FILTER,
  238. FUNCTIONS,
  239. STRING,
  240. NUMBER
  241. ]
  242. }
  243. ]
  244. };
  245. }
  246. export { twig as default };