дизайн подсветки синтаксиса - PullRequest
0 голосов
/ 23 ноября 2010

Я пишу свою собственную подсветку синтаксиса в javascript для забавы и вижу пару подходов, но у них обоих есть свои плюсы и некоторые довольно серьезные минусы, которые я не могу обойти.Что вы, ребята, думаете об этих подходах и есть ли лучшие методы, которые я упускаю?

Предположение

Код для выделения существует в одной строке.

Подходы

  1. Обрабатывать код в виде строки и использовать регулярные выражения для поиска шаблонов.
    Плюсы
    Простота определения и поиска шаблонов
    Минусы
    Трудно игнорировать ключевые слова внутри кавычек или комментариев

  2. Разделить строку пробелами и переносами строк и выполнить цикл по массиву.
    Плюсы
    Легко отслеживать область действия
    Минусы
    Трудно отслеживать пробелы и переносы строк после разделения

EDIT: Лексический анализ
Итак, если я понимаю, с помощью Lexical Analysis вы разбиваете строку на токены.Это как-то похоже на подход № 2?Как вы подходите для сборки токенов в исходную строку?

1 Ответ

1 голос
/ 24 ноября 2010

Примечание: здесь используется jQuery.Если хотите, его вполне можно переписать для работы с прямым javascript.

Я на самом деле написал небольшой плагин для забавы, который делает это:

(function($) {
 $.fn.codeBlock = function(blockComment) {

  // Setup keyword regex
   var keywords = /(abstract|boolean|break|byte|case|catch|char|class|const|continue|debugger|default|delete|do|double|else|enum|export|extends|final|finally|float|for|function|goto|if|implements|import|in|instanceof|int|interface|long|native|new|package|private|protected|public|return|short|static|super|switch|synchronized|this|throw|throws|transient|try|typeof|var|void|volatile|while|with|true|false|prototype)(?!\w|=)/gi;

  // Booleans to toggle comment, regex, quote exclusions
   var comment = false;
   var quote = false;
   var regex = false;

  /*  Array used to store values of regular expressions, quotes, etc.
   so they can be used to ID locations to be skipped durring keyword
   regexing.
  */
   var locator = new Array();
   var locatorIndex = 0;

   if (blockComment) locator[locatorIndex++] = 0;

  var text = $(this).html();
  var continuation;
  var numerals = /[0-9]/;

  var arr = ($(this).html()).split("");
  var outhtml = "";

  for (key in arr) {
   // Assign three variables common 'lookup' values for faster aquisition
    var keyd = key;
    var val = arr[keyd];
    var nVal = arr[keyd - 1];
    var pVal = arr[++keyd];

   if ((val == "\"" || val == "'") && nVal != "\\") {
    if (quote == false) {
     quote = true;
     outhtml += val;
    }
    else {
     outhtml += val;
     quote = false;
    }
    locator[locatorIndex++] = parseInt(key);
   }
   else if (numerals.test(val) && quote == false && blockComment == false && regex == false) {
    outhtml += '<span class="num">' + val + '</span>';
   }
   else if (val == "/" && nVal != "<") {
    var keys = key;
    if (pVal == "/") {
     comment = true;
     continuation = key;
     break;
    }
    else if (pVal == "*") {
     outhtml += "/";
     blockComment = true;
     locator[locatorIndex++] = parseInt(key);
    }
    else if (nVal == "*") {
     outhtml += "/";
     blockComment = false;
     locator[locatorIndex++] = parseInt(key);
    }
    else if (pVal == "[" && regex == false) {
     outhtml += "<span class='res'>/";
     regex = true;
    }
    else {
     outhtml += "/";
    }
   }
   else if (val == "," || val == ";" && regex == true) {
    outhtml += "</span>" + val;
    regex = false;
   }
   else {
    outhtml += val;
   }
  }

  if (comment == true) {
   outhtml = outhtml.replace(keywords, "<span class='res'>$1</span>");
   outhtml += '<span class="com">';
   outhtml += text.substring(continuation, text.length);
   outhtml += '</span>';
  }
  else {
   if ((locator.length % 2) != 0) locator[locator.length] = (text.length - 1);

   if (locator.length != 0) {
    text = outhtml;

    outhtml  = text.substring(0, locator[0]).replace(keywords, "<span class=\"res\">$1</span>");

    for (var i = 0; i < locator.length;) {
     qTest = text.substring(locator[i], locator[i] + 1);
     if (qTest == "'" || qTest == "\"") outhtml += "<span class=\"quo\">";
     else outhtml += "<span class=\"com\">";

     outhtml += text.substring(locator[i], locator[++i] + 1) + "</span>";

     outhtml += text.substring(locator[i] + 1, locator[++i]).replace(keywords, "<span class=\"res\">$1</span>");
    }
   }
   else {
    outhtml = outhtml.replace(keywords, "<span class=\"res\">$1</span>");
   }
  }

  text = outhtml;
  $(this).html(text);
  return blockComment;
 }
})(jQuery);

Я не собираюсь требовать этогоэто самый эффективный способ сделать это или лучше, но это работает.Вероятно, есть еще несколько ошибок, которые я еще не идентифицировал (и 1, о которых я знаю, но не удосужился исправить), но это должно дать вам представление о том, как вы могли пойтиоб этом, если хотите.

Я рекомендую реализовать это, чтобы создать textarea или что-то в этом роде и запустить плагин, когда вы нажимаете кнопку или что-то в этом духе (для тестирования это неплохая идея)) и, конечно, вы можете установить для текста в текстовой области некоторый начальный код, чтобы убедиться, что он работает (Совет: вы можете поместить теги между тегом <textarea>, и он будет отображаться как текст, а не как HTML).

Кроме того, blockComment является логическим значением, обязательно передайте false, потому что true вызовет цитирование блока.Если вы решили анализировать что-то построчно, например:

<a>code</a>
<a>some more code</a>

Сделайте что-то вроде:

blockComment = false;
$("a").each(function() {
  blockComment = $(this).codeBlock(blockComment);
});
...