【问题标题】:In-browser JavaScript code editor with syntax highlighting support for Smarty template tags?浏览器内 JavaScript 代码编辑器是否支持 Smarty 模板标签的语法高亮显示?
【发布时间】:2012-01-25 00:50:13
【问题描述】:

我在 Interwebs 上到处搜索,发现了一些 真的 很棒的 JS code editors,带有语法高亮和缩进等等……但似乎没有一个支持 Smarty 模板标签还没有。

CodeMirror 的新 Smarty 模式将是最好的,但如果需要,我会使用不同的编辑器。

我确实找到了this blog post...,但它非常简单,我希望仍然支持混合的 HTML/CSS/JS 突出显示,例如 CodeMirror 的 PHP 模式。

我只是想在开始推出自己的 CodeMirror 模式之前先检查一下 SO Hive 的想法。如果我确实制作了一个新模式(并且可以使用它),我会在这里发布。

谢谢!

【问题讨论】:

    标签: javascript editor smarty syntax-highlighting codemirror


    【解决方案1】:

    我尝试使用 smarty 实现混合模式,虽然我的工作并不完美,但到目前为止它对我来说已经足够好了。我从 de htmlmixedmode 开始添加 smarty 模式:

      CodeMirror.defineMode("smartymixed", function(config, parserConfig) {
      var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
      var smartyMode = CodeMirror.getMode(config, "smarty");
      var jsMode = CodeMirror.getMode(config, "javascript");
      var cssMode = CodeMirror.getMode(config, "css");
    
    
    
      function html(stream, state) {
        var style = htmlMode.token(stream, state.htmlState);
        if (style == "tag" && stream.current() == ">" && state.htmlState.context) {
          if (/^script$/i.test(state.htmlState.context.tagName)) {
            state.token = javascript;
            state.localState = jsMode.startState(htmlMode.indent(state.htmlState, ""));
            state.mode = "javascript";
          }
          else if (/^style$/i.test(state.htmlState.context.tagName)) {
            state.token = css;
            state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
            state.mode = "css";
          }
        }
    
        return style;
      }
      function maybeBackup(stream, pat, style) {
        var cur = stream.current();
        var close = cur.search(pat);
        if (close > -1) stream.backUp(cur.length - close);
        return style;
      }
      function javascript(stream, state) {
        if (stream.match(/^<\/\s*script\s*>/i, false)) {
          state.token = html;
          state.localState = null;
          state.mode = "html";
          return html(stream, state);
        }
        return maybeBackup(stream, /<\/\s*script\s*>/,
                           jsMode.token(stream, state.localState));
      }
      function css(stream, state) {
        if (stream.match(/^<\/\s*style\s*>/i, false)) {
          state.token = html;
          state.localState = null;
          state.mode = "html";
          return html(stream, state);
        }
        return maybeBackup(stream, /<\/\s*style\s*>/,
                           cssMode.token(stream, state.localState));
      }
    
      function smarty(stream, state) {
         style =  smartyMode.token(stream, state.localState);
         if ( state.localState.tokenize == null ) 
              { // back to anything from smarty
              state.token = state.htmlState.tokens.pop();
              state.mode = state.htmlState.modes.pop();
              state.localState = state.htmlState.states.pop(); // state.htmlState;
              }
          return(style);
          }
    
      return {
    
        startState: function() {
          var state = htmlMode.startState();
          state.modes = [];
          state.tokens = [];
          state.states = [];
          return {token: html, localState: null, mode: "html", htmlState: state};
        },
    
        copyState: function(state) { 
          if (state.localState)
            var local = CodeMirror.copyState( 
                ( state.token == css ) ? cssMode : (( state.token == javascript ) ? jsMode : smartyMode ), 
                state.localState);
          return {token: state.token, localState: local, mode: state.mode,
                  htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
        },
    
        token: function(stream, state) {
    
          if ( stream.match(/^{[^ ]{1}/,false) )
              { // leaving anything to smarty
              state.htmlState.states.push(state.localState);
              state.htmlState.tokens.push(state.token);
              state.htmlState.modes.push(state.mode);
              state.token = smarty;
                  state.localState = smartyMode.startState();
                  state.mode = "smarty";
              }
    
          return state.token(stream, state);
        },
    
    
        compareStates: function(a, b) {
          if (a.mode != b.mode) return false;
          if (a.localState) return CodeMirror.Pass;
          return htmlMode.compareStates(a.htmlState, b.htmlState);
        },
    
        electricChars: "/{}:"
      }
    }, "xml", "javascript", "css", "smarty");
    
    CodeMirror.defineMIME("text/html", "smartymixed");
    

    智能模式的切换仅在令牌功能中进行,但... 您还必须修补其他基本模式(css、javascript 和 xml)以在 { 字符上停止它们,以便您可以退回到令牌函数中以针对正则表达式({ 后跟非空白字符)进行测试。

    【讨论】:

    • 下次,只需使用版主标志。看到答案下方的“标志”链接了吗?除了答案之外,不要使用答案。发布答案时唯一收到通知的人是问题所有者。
    【解决方案2】:

    这可能会有所帮助。这个周末我为 CodeMirror2 写了一个 Smarty 模式。看: http://www.benjaminkeen.com/misc/CodeMirror2/mode/smarty/

    我还在这里更改了 CodeMirror 项目: https://github.com/benkeen/CodeMirror2

    万事如意——

    [编辑:这现在是主脚本的一部分。我将很快添加 Smarty/HTML/CSS/JS 模式]。

    【讨论】:

    • Sweet,核心中的 Smarty 模式似乎运行良好。不过,我在你的叉子中尝试了混合 smarty 模式,但遇到了一些麻烦。我会留意更新。谢谢!
    • 刚刚尝试了 olivier 在此处发布的混合智能模式 - 如果您想查看它并将其融入核心,似乎可以正常工作。
    【解决方案3】:

    答案的第二部分:benjamin smarty 文件中的一个补丁,以便能够离开它并退回到 smartymixed 模式。所以这里是 mode/smarty/smarty.js 的补丁版本

    CodeMirror.defineMode("smarty", function(config, parserConfig) {
      var breakOnSmarty = ( config.mode == "smartymixed" ) ? true : false; // we are called in a "smartymixed" context
      var keyFuncs = ["debug", "extends", "function", "include", "literal"];
      var last;
      var regs = {
        operatorChars: /[+\-*&%=<>!?]/,
        validIdentifier: /[a-zA-Z0-9\_]/,
        stringChar: /[\'\"]/
      }
      var leftDelim = (typeof config.mode.leftDelimiter != 'undefined') ? config.mode.leftDelimiter : "{";
      var rightDelim = (typeof config.mode.rightDelimiter != 'undefined') ? config.mode.rightDelimiter : "}";
      function ret(style, lst) { last = lst; return style; }
    
    
      function tokenizer(stream, state) {
        function chain(parser) {
          state.tokenize = parser;
          return parser(stream, state);
        }
    
        if (stream.match(leftDelim, true)) {
          if (stream.eat("*")) {
            return chain(inBlock("comment", "*" + rightDelim));
          }
          else {
            state.tokenize = inSmarty;
            return ( breakOnSmarty == true ) ? "bracket" : "tag";
          }
        }
        else {
          // I'd like to do an eatWhile() here, but I can't get it to eat only up to the rightDelim string/char
          stream.next();
          return null;
        }
      }
    
      function inSmarty(stream, state) {
        if (stream.match(rightDelim, true)) {
          state.tokenize = ( breakOnSmarty ) ? null : tokenizer;
          return ( breakOnSmarty == true ) ? ret("bracket", null) : ret("tag", null);
        }
    
        var ch = stream.next();
        if (ch == "$") {
          stream.eatWhile(regs.validIdentifier);
          return ret("variable-2", "variable");
        }
        else if (ch == ".") {
          return ret("operator", "property");
        }
        else if (regs.stringChar.test(ch)) {
          state.tokenize = inAttribute(ch);
          return ret("string", "string");
        }
        else if (regs.operatorChars.test(ch)) {
          stream.eatWhile(regs.operatorChars);
          return ret("operator", "operator");
        }
        else if (ch == "[" || ch == "]") {
          return ret("bracket", "bracket");
        }
        else if (/\d/.test(ch)) {
          stream.eatWhile(/\d/);
          return ret("number", "number");
        }
        else {
          if (state.last == "variable") {
            if (ch == "@") {
              stream.eatWhile(regs.validIdentifier);
              return ret("property", "property");
            }
            else if (ch == "|") {
              stream.eatWhile(regs.validIdentifier);
              return ret("qualifier", "modifier");
            }
          }
          else if (state.last == "whitespace") {
            stream.eatWhile(regs.validIdentifier);
            return ret("attribute", "modifier");
          }
          else if (state.last == "property") {
            stream.eatWhile(regs.validIdentifier);
            return ret("property", null);
          }
          else if (/\s/.test(ch)) {
            last = "whitespace";
            return null;
          }
    
          var str = "";
          if (ch != "/") {
            str += ch;
          }
          var c = "";
          while ((c = stream.eat(regs.validIdentifier))) {
            str += c;
          }
          var i, j;
          for (i=0, j=keyFuncs.length; i<j; i++) {
            if (keyFuncs[i] == str) {
              return ret("keyword", "keyword");
            }
          }
          if (/\s/.test(ch)) {
            return null;
          }
          return ret("tag", "tag");
        }
      }
    
      function inAttribute(quote) {
        return function(stream, state) {
          while (!stream.eol()) {
            if (stream.next() == quote) {
              state.tokenize = inSmarty;
              break;
            }
          }
          return "string";
        };
      }
    
      function inBlock(style, terminator) {
        return function(stream, state) {
          while (!stream.eol()) {
            if (stream.match(terminator)) {
              state.tokenize = ( breakOnSmarty == true ) ? null : tokenizer;
              break;
            }
            stream.next();
          }
          return style;
        };
      }
    
      return {
        startState: function() {
          return { tokenize: tokenizer, mode: "smarty", last: null };
        },
        token: function(stream, state) {
          var style = state.tokenize(stream, state);
          state.last = last;
          return style;
        },
        electricChars: ""
      }
    });
    
    CodeMirror.defineMIME("text/x-smarty", "smarty");
    

    第一行检查我们是否被 smartymixed 模式调用,并在此条件下进行测试,允许 smarty 模式像以前一样运行。

    【讨论】:

      猜你喜欢
      • 2010-11-02
      • 1970-01-01
      • 2016-10-27
      • 2021-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-16
      • 1970-01-01
      相关资源
      最近更新 更多