github.com/elliott5/community@v0.14.1-0.20160709191136-823126fb026a/app/public/codemirror/mode/soy/soy.js (about)

     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  });