smarty.js 6.3 KB

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