Fix: merge conflict
[myslice.git] / third-party / codemirror-3.15 / mode / smartymixed / smartymixed.js
1 /**
2 * @file smartymixed.js
3 * @brief Smarty Mixed Codemirror mode (Smarty + Mixed HTML)
4 * @author Ruslan Osmanov <rrosmanov at gmail dot com>
5 * @version 3.0
6 * @date 05.07.2013
7 */
8 CodeMirror.defineMode("smartymixed", function(config) {
9   var settings, regs, helpers, parsers,
10   htmlMixedMode = CodeMirror.getMode(config, "htmlmixed"),
11   smartyMode = CodeMirror.getMode(config, "smarty"),
12
13   settings = {
14     rightDelimiter: '}',
15     leftDelimiter: '{'
16   };
17
18   if (config.hasOwnProperty("leftDelimiter")) {
19     settings.leftDelimiter = config.leftDelimiter;
20   }
21   if (config.hasOwnProperty("rightDelimiter")) {
22     settings.rightDelimiter = config.rightDelimiter;
23   }
24
25   regs = {
26     smartyComment: new RegExp("^" + settings.leftDelimiter + "\\*"),
27     literalOpen: new RegExp(settings.leftDelimiter + "literal" + settings.rightDelimiter),
28     literalClose: new RegExp(settings.leftDelimiter + "\/literal" + settings.rightDelimiter),
29     hasLeftDelimeter: new RegExp(".*" + settings.leftDelimiter),
30     htmlHasLeftDelimeter: new RegExp("[^<>]*" + settings.leftDelimiter)
31   };
32
33   helpers = {
34     chain: function(stream, state, parser) {
35       state.tokenize = parser;
36       return parser(stream, state);
37     },
38
39     cleanChain: function(stream, state, parser) {
40       state.tokenize = null;
41       state.localState = null;
42       state.localMode = null;
43       return (typeof parser == "string") ? (parser ? parser : null) : parser(stream, state);
44     },
45
46     maybeBackup: function(stream, pat, style) {
47       var cur = stream.current();
48       var close = cur.search(pat),
49       m;
50       if (close > - 1) stream.backUp(cur.length - close);
51       else if (m = cur.match(/<\/?$/)) {
52         stream.backUp(cur.length);
53         if (!stream.match(pat, false)) stream.match(cur[0]);
54       }
55       return style;
56     }
57   };
58
59   parsers = {
60     html: function(stream, state) {
61       if (!state.inLiteral && stream.match(regs.htmlHasLeftDelimeter, false)) {
62         state.tokenize = parsers.smarty;
63         state.localMode = smartyMode;
64         state.localState = smartyMode.startState(htmlMixedMode.indent(state.htmlMixedState, ""));
65         return helpers.maybeBackup(stream, settings.leftDelimiter, smartyMode.token(stream, state.localState));
66       }
67       return htmlMixedMode.token(stream, state.htmlMixedState);
68     },
69
70     smarty: function(stream, state) {
71       if (stream.match(settings.leftDelimiter, false)) {
72         if (stream.match(regs.smartyComment, false)) {
73           return helpers.chain(stream, state, parsers.inBlock("comment", "*" + settings.rightDelimiter));
74         }
75       } else if (stream.match(settings.rightDelimiter, false)) {
76         stream.eat(settings.rightDelimiter);
77         state.tokenize = parsers.html;
78         state.localMode = htmlMixedMode;
79         state.localState = state.htmlMixedState;
80         return "tag";
81       }
82
83       return helpers.maybeBackup(stream, settings.rightDelimiter, smartyMode.token(stream, state.localState));
84     },
85
86     inBlock: function(style, terminator) {
87       return function(stream, state) {
88         while (!stream.eol()) {
89           if (stream.match(terminator)) {
90             helpers.cleanChain(stream, state, "");
91             break;
92           }
93           stream.next();
94         }
95         return style;
96       };
97     }
98   };
99
100   return {
101     startState: function() {
102       var state = htmlMixedMode.startState();
103       return {
104         token: parsers.html,
105         localMode: null,
106         localState: null,
107         htmlMixedState: state,
108         tokenize: null,
109         inLiteral: false
110       };
111     },
112
113     copyState: function(state) {
114       var local = null, tok = (state.tokenize || state.token);
115       if (state.localState) {
116         local = CodeMirror.copyState((tok != parsers.html ? smartyMode : htmlMixedMode), state.localState);
117       }
118       return {
119         token: state.token,
120         tokenize: state.tokenize,
121         localMode: state.localMode,
122         localState: local,
123         htmlMixedState: CodeMirror.copyState(htmlMixedMode, state.htmlMixedState),
124         inLiteral: state.inLiteral
125       };
126     },
127
128     token: function(stream, state) {
129       if (stream.match(settings.leftDelimiter, false)) {
130         if (!state.inLiteral && stream.match(regs.literalOpen, true)) {
131           state.inLiteral = true;
132           return "keyword";
133         } else if (state.inLiteral && stream.match(regs.literalClose, true)) {
134           state.inLiteral = false;
135           return "keyword";
136         }
137       }
138       if (state.inLiteral && state.localState != state.htmlMixedState) {
139         state.tokenize = parsers.html;
140         state.localMode = htmlMixedMode;
141         state.localState = state.htmlMixedState;
142       }
143
144       var style = (state.tokenize || state.token)(stream, state);
145       return style;
146     },
147
148     indent: function(state, textAfter) {
149       if (state.localMode == smartyMode
150           || (state.inLiteral && !state.localMode)
151          || regs.hasLeftDelimeter.test(textAfter)) {
152         return CodeMirror.Pass;
153       }
154       return htmlMixedMode.indent(state.htmlMixedState, textAfter);
155     },
156
157     electricChars: "/{}:",
158
159     innerMode: function(state) {
160       return {
161         state: state.localState || state.htmlMixedState,
162         mode: state.localMode || htmlMixedMode
163       };
164     }
165   };
166 },
167 "htmlmixed");
168
169 CodeMirror.defineMIME("text/x-smarty", "smartymixed");
170 // vim: et ts=2 sts=2 sw=2