Редактор кода JavaScript в браузере с поддержкой подсветки синтаксиса для шаблонных тегов Smarty? - PullRequest
1 голос
/ 25 января 2012

Я искал в Интернете все выше и ниже, и нашел несколько действительно потрясающих редакторов кода JS с подсветкой синтаксиса и отступами и многим другим ... но, похоже, никто не поддерживает Smarty тегов шаблонов пока нет.

Лучше всего подойдет новый режим Smarty для CodeMirror , но я буду использовать другой редактор, если потребуется.

Я нашел эту запись в блоге ... но она ОЧЕНЬ проста, и я хотел бы по-прежнему поддерживать смешанную подсветку HTML / CSS / JS, например режим PHP для CodeMirror.

Я просто подумал, что я проверю разум Улья перед тем, как начать свой собственный режим CodeMirror. Если я сделаю новый режим (и доберусь куда-нибудь с ним), я выложу его здесь.

Спасибо!

Ответы [ 3 ]

2 голосов
/ 29 июня 2012

Я сделал несколько попыток получить смешанный режим с помощью smarty, и хотя моя работа не идеальна, пока он работает достаточно хорошо для меня. Я начал с de htmlmixedmode, чтобы добавить умный режим:

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

Переключение в режим Smarty производится только в функции токена, но ... Вы также должны пропатчить другие основные режимы (css, javascript & xml), чтобы остановить их на символе {, чтобы можно было использовать функцию токена для проверки ее на регулярное выражение ({за которым следует непустой символ).

1 голос
/ 01 июля 2012

Вторая часть ответа: патч в файле 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 работать, как и раньше.

1 голос
/ 14 марта 2012

Это может помочь.Я написал режим Smarty для CodeMirror2 в эти выходные.См .: http://www.benjaminkeen.com/misc/CodeMirror2/mode/smarty/

Я также разложил проект CodeMirror со своими изменениями здесь: https://github.com/benkeen/CodeMirror2

Всего наилучшего -

Бен

[РЕДАКТИРОВАТЬ: теперь это часть основного сценария.Я скоро добавлю режим Smarty / HTML / CSS / JS].

...