Subversion-Projekte lars-tiefland.ci

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
776 lars 1
// CodeMirror, copyright (c) by Marijn Haverbeke and others
2
// Distributed under an MIT license: http://codemirror.net/LICENSE
3
 
4
(function(mod) {
5
  if (typeof exports == "object" && typeof module == "object") // CommonJS
6
    mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"));
7
  else if (typeof define == "function" && define.amd) // AMD
8
    define(["../../lib/codemirror", "../htmlmixed/htmlmixed"], mod);
9
  else // Plain browser env
10
    mod(CodeMirror);
11
})(function(CodeMirror) {
12
  "use strict";
13
 
14
  var indentingTags = ["template", "literal", "msg", "fallbackmsg", "let", "if", "elseif",
15
                       "else", "switch", "case", "default", "foreach", "ifempty", "for",
16
                       "call", "param", "deltemplate", "delcall", "log"];
17
 
18
  CodeMirror.defineMode("soy", function(config) {
19
    var textMode = CodeMirror.getMode(config, "text/plain");
20
    var modes = {
21
      html: CodeMirror.getMode(config, {name: "text/html", multilineTagIndentFactor: 2, multilineTagIndentPastTag: false}),
22
      attributes: textMode,
23
      text: textMode,
24
      uri: textMode,
25
      css: CodeMirror.getMode(config, "text/css"),
26
      js: CodeMirror.getMode(config, {name: "text/javascript", statementIndent: 2 * config.indentUnit})
27
    };
28
 
29
    function last(array) {
30
      return array[array.length - 1];
31
    }
32
 
33
    function tokenUntil(stream, state, untilRegExp) {
34
      var oldString = stream.string;
35
      var match = untilRegExp.exec(oldString.substr(stream.pos));
36
      if (match) {
37
        // We don't use backUp because it backs up just the position, not the state.
38
        // This uses an undocumented API.
39
        stream.string = oldString.substr(0, stream.pos + match.index);
40
      }
41
      var result = stream.hideFirstChars(state.indent, function() {
42
        return state.localMode.token(stream, state.localState);
43
      });
44
      stream.string = oldString;
45
      return result;
46
    }
47
 
48
    return {
49
      startState: function() {
50
        return {
51
          kind: [],
52
          kindTag: [],
53
          soyState: [],
54
          indent: 0,
55
          localMode: modes.html,
56
          localState: CodeMirror.startState(modes.html)
57
        };
58
      },
59
 
60
      copyState: function(state) {
61
        return {
62
          tag: state.tag, // Last seen Soy tag.
63
          kind: state.kind.concat([]), // Values of kind="" attributes.
64
          kindTag: state.kindTag.concat([]), // Opened tags with kind="" attributes.
65
          soyState: state.soyState.concat([]),
66
          indent: state.indent, // Indentation of the following line.
67
          localMode: state.localMode,
68
          localState: CodeMirror.copyState(state.localMode, state.localState)
69
        };
70
      },
71
 
72
      token: function(stream, state) {
73
        var match;
74
 
75
        switch (last(state.soyState)) {
76
          case "comment":
77
            if (stream.match(/^.*?\*\//)) {
78
              state.soyState.pop();
79
            } else {
80
              stream.skipToEnd();
81
            }
82
            return "comment";
83
 
84
          case "variable":
85
            if (stream.match(/^}/)) {
86
              state.indent -= 2 * config.indentUnit;
87
              state.soyState.pop();
88
              return "variable-2";
89
            }
90
            stream.next();
91
            return null;
92
 
93
          case "tag":
94
            if (stream.match(/^\/?}/)) {
95
              if (state.tag == "/template" || state.tag == "/deltemplate") state.indent = 0;
96
              else state.indent -= (stream.current() == "/}" || indentingTags.indexOf(state.tag) == -1 ? 2 : 1) * config.indentUnit;
97
              state.soyState.pop();
98
              return "keyword";
99
            } else if (stream.match(/^([\w?]+)(?==)/)) {
100
              if (stream.current() == "kind" && (match = stream.match(/^="([^"]+)/, false))) {
101
                var kind = match[1];
102
                state.kind.push(kind);
103
                state.kindTag.push(state.tag);
104
                state.localMode = modes[kind] || modes.html;
105
                state.localState = CodeMirror.startState(state.localMode);
106
              }
107
              return "attribute";
108
            } else if (stream.match(/^"/)) {
109
              state.soyState.push("string");
110
              return "string";
111
            }
112
            stream.next();
113
            return null;
114
 
115
          case "literal":
116
            if (stream.match(/^(?=\{\/literal})/)) {
117
              state.indent -= config.indentUnit;
118
              state.soyState.pop();
119
              return this.token(stream, state);
120
            }
121
            return tokenUntil(stream, state, /\{\/literal}/);
122
 
123
          case "string":
124
            if (stream.match(/^.*?"/)) {
125
              state.soyState.pop();
126
            } else {
127
              stream.skipToEnd();
128
            }
129
            return "string";
130
        }
131
 
132
        if (stream.match(/^\/\*/)) {
133
          state.soyState.push("comment");
134
          return "comment";
135
        } else if (stream.match(stream.sol() ? /^\s*\/\/.*/ : /^\s+\/\/.*/)) {
136
          return "comment";
137
        } else if (stream.match(/^\{\$[\w?]*/)) {
138
          state.indent += 2 * config.indentUnit;
139
          state.soyState.push("variable");
140
          return "variable-2";
141
        } else if (stream.match(/^\{literal}/)) {
142
          state.indent += config.indentUnit;
143
          state.soyState.push("literal");
144
          return "keyword";
145
        } else if (match = stream.match(/^\{([\/@\\]?[\w?]*)/)) {
146
          if (match[1] != "/switch")
147
            state.indent += (/^(\/|(else|elseif|case|default)$)/.test(match[1]) && state.tag != "switch" ? 1 : 2) * config.indentUnit;
148
          state.tag = match[1];
149
          if (state.tag == "/" + last(state.kindTag)) {
150
            // We found the tag that opened the current kind="".
151
            state.kind.pop();
152
            state.kindTag.pop();
153
            state.localMode = modes[last(state.kind)] || modes.html;
154
            state.localState = CodeMirror.startState(state.localMode);
155
          }
156
          state.soyState.push("tag");
157
          return "keyword";
158
        }
159
 
160
        return tokenUntil(stream, state, /\{|\s+\/\/|\/\*/);
161
      },
162
 
163
      indent: function(state, textAfter) {
164
        var indent = state.indent, top = last(state.soyState);
165
        if (top == "comment") return CodeMirror.Pass;
166
 
167
        if (top == "literal") {
168
          if (/^\{\/literal}/.test(textAfter)) indent -= config.indentUnit;
169
        } else {
170
          if (/^\s*\{\/(template|deltemplate)\b/.test(textAfter)) return 0;
171
          if (/^\{(\/|(fallbackmsg|elseif|else|ifempty)\b)/.test(textAfter)) indent -= config.indentUnit;
172
          if (state.tag != "switch" && /^\{(case|default)\b/.test(textAfter)) indent -= config.indentUnit;
173
          if (/^\{\/switch\b/.test(textAfter)) indent -= config.indentUnit;
174
        }
175
        if (indent && state.localMode.indent)
176
          indent += state.localMode.indent(state.localState, textAfter);
177
        return indent;
178
      },
179
 
180
      innerMode: function(state) {
181
        if (state.soyState.length && last(state.soyState) != "literal") return null;
182
        else return {state: state.localState, mode: state.localMode};
183
      },
184
 
185
      electricInput: /^\s*\{(\/|\/template|\/deltemplate|\/switch|fallbackmsg|elseif|else|case|default|ifempty|\/literal\})$/,
186
      lineComment: "//",
187
      blockCommentStart: "/*",
188
      blockCommentEnd: "*/",
189
      blockCommentContinue: " * ",
190
      fold: "indent"
191
    };
192
  }, "htmlmixed");
193
 
194
  CodeMirror.registerHelper("hintWords", "soy", indentingTags.concat(
195
      ["delpackage", "namespace", "alias", "print", "css", "debugger"]));
196
 
197
  CodeMirror.defineMIME("text/x-soy", "soy");
198
});