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