Commit | Line | Data |
---|---|---|
44f4c339 | 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others |
373d1232 | 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE |
44f4c339 | 3 | |
837afb80 TD |
4 | /** |
5 | * Smarty 2 and 3 mode. | |
6 | */ | |
7 | ||
8 | (function(mod) { | |
9 | if (typeof exports == "object" && typeof module == "object") // CommonJS | |
10 | mod(require("../../lib/codemirror")); | |
11 | else if (typeof define == "function" && define.amd) // AMD | |
12 | define(["../../lib/codemirror"], mod); | |
13 | else // Plain browser env | |
14 | mod(CodeMirror); | |
15 | })(function(CodeMirror) { | |
837afb80 TD |
16 | "use strict"; |
17 | ||
44f4c339 AE |
18 | CodeMirror.defineMode("smarty", function(config, parserConf) { |
19 | var rightDelimiter = parserConf.rightDelimiter || "}"; | |
20 | var leftDelimiter = parserConf.leftDelimiter || "{"; | |
21 | var version = parserConf.version || 2; | |
22 | var baseMode = CodeMirror.getMode(config, parserConf.baseMode || "null"); | |
23 | ||
24 | var keyFunctions = ["debug", "extends", "function", "include", "literal"]; | |
25 | var regs = { | |
26 | operatorChars: /[+\-*&%=<>!?]/, | |
27 | validIdentifier: /[a-zA-Z0-9_]/, | |
28 | stringChar: /['"]/ | |
29 | }; | |
30 | ||
31 | var last; | |
32 | function cont(style, lastType) { | |
837afb80 TD |
33 | last = lastType; |
34 | return style; | |
44f4c339 AE |
35 | } |
36 | ||
37 | function chain(stream, state, parser) { | |
77b7b761 TD |
38 | state.tokenize = parser; |
39 | return parser(stream, state); | |
40 | } | |
77b7b761 | 41 | |
44f4c339 AE |
42 | // Smarty 3 allows { and } surrounded by whitespace to NOT slip into Smarty mode |
43 | function doesNotCount(stream, pos) { | |
44 | if (pos == null) pos = stream.pos; | |
45 | return version === 3 && leftDelimiter == "{" && | |
46 | (pos == stream.string.length || /\s/.test(stream.string.charAt(pos))); | |
47 | } | |
77b7b761 | 48 | |
44f4c339 AE |
49 | function tokenTop(stream, state) { |
50 | var string = stream.string; | |
51 | for (var scan = stream.pos;;) { | |
52 | var nextMatch = string.indexOf(leftDelimiter, scan); | |
53 | scan = nextMatch + leftDelimiter.length; | |
54 | if (nextMatch == -1 || !doesNotCount(stream, nextMatch + leftDelimiter.length)) break; | |
55 | } | |
56 | if (nextMatch == stream.pos) { | |
57 | stream.match(leftDelimiter); | |
837afb80 | 58 | if (stream.eat("*")) { |
44f4c339 | 59 | return chain(stream, state, tokenBlock("comment", "*" + rightDelimiter)); |
837afb80 | 60 | } else { |
837afb80 | 61 | state.depth++; |
44f4c339 AE |
62 | state.tokenize = tokenSmarty; |
63 | last = "startTag"; | |
64 | return "tag"; | |
77b7b761 | 65 | } |
77b7b761 | 66 | } |
44f4c339 AE |
67 | |
68 | if (nextMatch > -1) stream.string = string.slice(0, nextMatch); | |
69 | var token = baseMode.token(stream, state.base); | |
70 | if (nextMatch > -1) stream.string = string; | |
71 | return token; | |
72 | } | |
77b7b761 | 73 | |
837afb80 | 74 | // parsing Smarty content |
44f4c339 AE |
75 | function tokenSmarty(stream, state) { |
76 | if (stream.match(rightDelimiter, true)) { | |
77 | if (version === 3) { | |
837afb80 TD |
78 | state.depth--; |
79 | if (state.depth <= 0) { | |
44f4c339 | 80 | state.tokenize = tokenTop; |
837afb80 TD |
81 | } |
82 | } else { | |
44f4c339 | 83 | state.tokenize = tokenTop; |
77b7b761 | 84 | } |
44f4c339 | 85 | return cont("tag", null); |
77b7b761 | 86 | } |
837afb80 | 87 | |
44f4c339 | 88 | if (stream.match(leftDelimiter, true)) { |
837afb80 | 89 | state.depth++; |
44f4c339 | 90 | return cont("tag", "startTag"); |
77b7b761 | 91 | } |
77b7b761 | 92 | |
837afb80 TD |
93 | var ch = stream.next(); |
94 | if (ch == "$") { | |
95 | stream.eatWhile(regs.validIdentifier); | |
44f4c339 | 96 | return cont("variable-2", "variable"); |
837afb80 | 97 | } else if (ch == "|") { |
44f4c339 | 98 | return cont("operator", "pipe"); |
837afb80 | 99 | } else if (ch == ".") { |
44f4c339 | 100 | return cont("operator", "property"); |
837afb80 | 101 | } else if (regs.stringChar.test(ch)) { |
44f4c339 AE |
102 | state.tokenize = tokenAttribute(ch); |
103 | return cont("string", "string"); | |
837afb80 TD |
104 | } else if (regs.operatorChars.test(ch)) { |
105 | stream.eatWhile(regs.operatorChars); | |
44f4c339 | 106 | return cont("operator", "operator"); |
837afb80 | 107 | } else if (ch == "[" || ch == "]") { |
44f4c339 | 108 | return cont("bracket", "bracket"); |
837afb80 | 109 | } else if (ch == "(" || ch == ")") { |
44f4c339 | 110 | return cont("bracket", "operator"); |
837afb80 TD |
111 | } else if (/\d/.test(ch)) { |
112 | stream.eatWhile(/\d/); | |
44f4c339 | 113 | return cont("number", "number"); |
837afb80 TD |
114 | } else { |
115 | ||
116 | if (state.last == "variable") { | |
117 | if (ch == "@") { | |
118 | stream.eatWhile(regs.validIdentifier); | |
44f4c339 | 119 | return cont("property", "property"); |
837afb80 TD |
120 | } else if (ch == "|") { |
121 | stream.eatWhile(regs.validIdentifier); | |
44f4c339 | 122 | return cont("qualifier", "modifier"); |
837afb80 TD |
123 | } |
124 | } else if (state.last == "pipe") { | |
125 | stream.eatWhile(regs.validIdentifier); | |
44f4c339 | 126 | return cont("qualifier", "modifier"); |
837afb80 TD |
127 | } else if (state.last == "whitespace") { |
128 | stream.eatWhile(regs.validIdentifier); | |
44f4c339 | 129 | return cont("attribute", "modifier"); |
837afb80 TD |
130 | } if (state.last == "property") { |
131 | stream.eatWhile(regs.validIdentifier); | |
44f4c339 | 132 | return cont("property", null); |
837afb80 TD |
133 | } else if (/\s/.test(ch)) { |
134 | last = "whitespace"; | |
135 | return null; | |
77b7b761 | 136 | } |
77b7b761 | 137 | |
837afb80 TD |
138 | var str = ""; |
139 | if (ch != "/") { | |
140 | str += ch; | |
77b7b761 | 141 | } |
837afb80 TD |
142 | var c = null; |
143 | while (c = stream.eat(regs.validIdentifier)) { | |
144 | str += c; | |
145 | } | |
146 | for (var i=0, j=keyFunctions.length; i<j; i++) { | |
147 | if (keyFunctions[i] == str) { | |
44f4c339 | 148 | return cont("keyword", "keyword"); |
837afb80 TD |
149 | } |
150 | } | |
151 | if (/\s/.test(ch)) { | |
152 | return null; | |
153 | } | |
44f4c339 | 154 | return cont("tag", "tag"); |
77b7b761 | 155 | } |
44f4c339 | 156 | } |
837afb80 | 157 | |
44f4c339 | 158 | function tokenAttribute(quote) { |
837afb80 TD |
159 | return function(stream, state) { |
160 | var prevChar = null; | |
161 | var currChar = null; | |
162 | while (!stream.eol()) { | |
163 | currChar = stream.peek(); | |
164 | if (stream.next() == quote && prevChar !== '\\') { | |
44f4c339 | 165 | state.tokenize = tokenSmarty; |
837afb80 TD |
166 | break; |
167 | } | |
168 | prevChar = currChar; | |
169 | } | |
170 | return "string"; | |
171 | }; | |
44f4c339 | 172 | } |
837afb80 | 173 | |
44f4c339 | 174 | function tokenBlock(style, terminator) { |
837afb80 TD |
175 | return function(stream, state) { |
176 | while (!stream.eol()) { | |
177 | if (stream.match(terminator)) { | |
44f4c339 | 178 | state.tokenize = tokenTop; |
837afb80 TD |
179 | break; |
180 | } | |
181 | stream.next(); | |
182 | } | |
183 | return style; | |
184 | }; | |
185 | } | |
837afb80 | 186 | |
44f4c339 AE |
187 | return { |
188 | startState: function() { | |
189 | return { | |
190 | base: CodeMirror.startState(baseMode), | |
191 | tokenize: tokenTop, | |
192 | last: null, | |
193 | depth: 0 | |
194 | }; | |
195 | }, | |
196 | copyState: function(state) { | |
197 | return { | |
198 | base: CodeMirror.copyState(baseMode, state.base), | |
199 | tokenize: state.tokenize, | |
200 | last: state.last, | |
201 | depth: state.depth | |
202 | }; | |
203 | }, | |
204 | innerMode: function(state) { | |
205 | if (state.tokenize == tokenTop) | |
206 | return {mode: baseMode, state: state.base}; | |
207 | }, | |
208 | token: function(stream, state) { | |
209 | var style = state.tokenize(stream, state); | |
210 | state.last = last; | |
211 | return style; | |
212 | }, | |
373d1232 | 213 | indent: function(state, text, line) { |
44f4c339 | 214 | if (state.tokenize == tokenTop && baseMode.indent) |
373d1232 | 215 | return baseMode.indent(state.base, text, line); |
44f4c339 AE |
216 | else |
217 | return CodeMirror.Pass; | |
218 | }, | |
219 | blockCommentStart: leftDelimiter + "*", | |
220 | blockCommentEnd: "*" + rightDelimiter | |
221 | }; | |
222 | }); | |
223 | ||
224 | CodeMirror.defineMIME("text/x-smarty", "smarty"); | |
837afb80 | 225 | }); |