smarty.js 5.8 KB

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