Подсветка синтаксиса 1С в Syntax Highlighter

09 октября 2015

Посмотреть последнюю версию раскраски и темы оформления на GitHub

Предисловие

Пакет SyntaxHighlighter для Composite C1В этом блоге большая часть материалов о разработке на платформе 1С:Предприятие, поэтому оказалось просто необходимо делать подсветку синтаксиса кода 1С в статьях и для других материалов. Весь сайт создан на базе Composite C1 CMS, поэтому самым простым способом решения этой задачи была установка пакета "Composite.Web.Html.SyntaxHighlighter".

Но вот незадача, по умолчанию пакет не поддерживает синтаксис платформы 1С!

Что не удивительно, конечно же =)

Решим эту проблему!

Как это устроено

Файлы пакета Syntax HighlighterУстановив пакет "Syntax Highlighter", в состав веб-сайта добавляются скрипты JavaScript, которые обрабатывают размещенный на странице код, добавляя для него разметку. То есть подстветка кода выполняется не на сервере, а на стороне клиента после загрузки контента.

Эти скрипты, а также CSS-стили для подсветки синтаксиса, находятся в каталоге сайта "~/Frontend/Composite/Web/Html/SyntaxHighlighter".

Кроме базовых скриптов "shCore.js", "shLegacy.js" и некоторых других добавлены отдельные скрипты для каждого поддерживаемого синтаксиса языка. На скриншоте Вы можете видеть список скриптов для таких язвков как C, C++, C#, PHP и др.

Как уже было сказано, среди них нет поддержки для синтаксиса языка платформы 1С:Предприятие. Если Вы попытаетесь использовать один из существующих вариантов, например для C#, то результат не будет отличатся от подстветки обычного текста:

Процедура Тест()

КонецПроцедуры

Для того, чтобы разобраться, что, как и куда добавлять или изменять, откроем Razor-страницу "SyntaxHighlighter.cshtml". Именно здесь содержится код, описывающий работу со всеми поддержваемыми синтаксисами, указание на соответствующие скрипты JS и стили CSS.

Стандартно в скрипте поддерживаются следующие типы кода:

    private static Dictionary<string, Tuple<string, string>> CodeTypes = new Dictionary<string, Tuple<string, string>> {    
    {"shBrushAS3", new Tuple<string, string> ("ActionScript3", "as3") },
    {"shBrushBash", new Tuple<string, string> ("Bash/shell", "bash")},
    {"shBrushColdFusion", new Tuple<string, string> ("ColdFusion", "coldfusion") },
    {"shBrushCSharp", new Tuple<string, string> ("C#", "csharp")},
    {"shBrushCpp", new Tuple<string, string> ("C++", "cpp")},
    {"shBrushCss", new Tuple<string, string> ("CSS", "css")},
    {"shBrushDelphi", new Tuple<string, string> ("Delphi", "delphi") },
    {"shBrushDiff", new Tuple<string, string> ("Diff", "diff") },
    {"shBrushErlang", new Tuple<string, string> ("Erlang", "erlang") },
    {"shBrushGroovy", new Tuple<string, string> ("Groovy", "groovy") },
    {"shBrushJScript", new Tuple<string, string> ("JavaScript", "js")},
    {"shBrushJava", new Tuple<string, string> ("Java", "java")},
    {"shBrushJavaFX", new Tuple<string, string> ("JavaFX", "javafx")},
    {"shBrushPerl", new Tuple<string, string> ("Perl", "perl")},
    {"shBrushPhp", new Tuple<string, string> ("PHP", "php")},
    {"shBrushPlain", new Tuple<string, string> ("Plain Text", "text")},
    {"shBrushPowerShell", new Tuple<string, string> ("PowerShell", "ps")},
    {"shBrushPython", new Tuple<string, string> ("Python", "python")},
    {"shBrushRuby", new Tuple<string, string> ("Ruby", "ruby")},
    {"shBrushScala", new Tuple<string, string> ("Scala", "scala")},
    {"shBrushSql", new Tuple<string, string> ("SQL", "sql")},
    {"shBrushVb", new Tuple<string, string> ("Visual Basic", "vb")},
    {"shBrushXml", new Tuple<string, string> ("XML", "xml")}

Здесь для каждого вида синтаксиса кода задается соответстующий скрипт (расположены в папке "scripts"), название синтаксиса и имя класса, который затем используется скриптами при поиске блоков на странице для подсветки синтаксиса.

Параметры подсветки синтаксиса

Чтобы добавить собственный скрипт подсветки синтаксиса необходимо добавить его в папку "scripts" (как это сделать речь пойдет чуть позже) и затем прописать его соответствие на этой Razor-странице.

Также на этой Razor-странице содержится описание виджета для выбора таблицы стилей для оформления код. Таблицы стилей хранятся в папке "styles" и их имена переисляются в тексте описания виджета:

    private const string themeWidgetMarkup = @"<f:widgetfunction xmlns:f=""http://www.composite.net/ns/function/1.0"" name=""Composite.Widgets.Selector"">
		<f:param name=""Options"">
			<f:function name=""Composite.Utils.String.Split"">
				<f:param name=""String"" value=""shThemeDefault.css,shThemeDjango.css,shThemeEclipse.css,shThemeEmacs.css,shThemeFadeToGrey.css,shThemeMidnight.css,shThemeRDark.css"" />
				<f:param name=""Separator"" value="","" />
			</f:function>
		</f:param>
	</f:widgetfunction>";
Из листинга выше мы видим, что поумолчанию поддерживаются такие стили оформления как:

  • shThemeDefault.css
  • shThemeEclipse.css
  • shThemeEmacs.css
  • и другие.

Если нужно добавить свой стиль, то сначала добавляем его в папку "styles", а затем в тексте описания виджета дописываем название файла с стилями оформления.

Поддержка нового синтаксиса

И так, теперь мы знаем все необходимое, чтобы добавить собственную поддержку синтаксиса кода 1С:Предприятия в поставляемый виджет Syntax Highlighter для Composite C1 CMS. Первое, что мы сделаем - это добавим скрипт "shBrushOce.js" в папку "scripts" и опишем в нем регулярные выражения для ключевых слов, тегов, чисел, знаков пунктуации и строк. Кодировка файла обязательно должна быть UTF-8, иначе при поиске ключевых слов на кириллице регулярными выражениями подсветка может не работать, т.к. они просто не будут найдены. Вот листинг скрипта, который был создан мной на момент написания статьи:

/* version of 1C module for SyntaxHighliter + Composite C1 CMS */
/* created by Permitin Y.A. aka YPermitin */
/* http://www.develplatform.ru */

; (function () {
    // CommonJS
    typeof (require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;

    function Brush() {
        // Формируем регулярное выражение для поиска ключевых слов        
        var keywords = '';
        var keyWordsList = ['Пока', 'Цикл', 'КонецЦикла', 'Для', 'По', 'Каждого', 'Знач',
                            'Новый', 'Функция', 'Возврат', 'КонецФункции', 'Или', 'Перем', 'Экспорт', 'Процедура',
                            'КонецПроцедуры', 'Если', 'Тогда', 'ИначеЕсли', 'КонецЕсли', 'Из', 'Не', 'Истина', 'Ложь',
                            'Попытка', 'Исключение', 'КонецПопытки', 'Иначе', 'NULL', 'Неопределено', 'ВызватьИсключение',
                            'While', 'For', 'Each',  'In', 'Do', 'To', 'New', 'Function', 'Return', 'EndDo', 
                            'EndFunction', 'Or', 'Var', 'Export', 'Procedure', 'EndProcedure', 'If', 'Then', 
                            'Else', 'ElsIf', 'EndIf', 'Not', 'True', 'False', 'Val',
                            'Try', 'Except', 'Raise', 'EndTry', 'Undefined'
                           ];
        for (index = 0; index < keyWordsList.length; ++index) {
            keywords = keywords + keyWordsList[index];
            if (index == keyWordsList.length-1)
                keywords = keywords;
            else
                keywords = keywords + '|';
        }
        keywords = '(^|\\s|[;()])(' + keywords + ')(?=\\s|$|[;()])';

        this.regexList = [
            { regex: SyntaxHighlighter.regexLib.singleLineCComments, css: 'comments' },		                        // Однострочный комментарий
            { regex: /(["'])(?:(?!\1)[^\\]|\\|\\.)*\1/gi, css: 'string' },		                                    // Строка в двойных кавычках
            { regex: new RegExp('\'(?:\\?.)*?\'', 'gi'), css: 'string' },		                                    // Строка в одинарных кавычках
            { regex: new RegExp('(^|\\s|\,)([\\d]+(\\.[\\d]+)?|0x[a-f0-9]+)(?=\\s|$|;|\,)', 'gi'), css: 'number' },	// Числа
            { regex: /\+|\)|\(|\.|\,|\\|\*|=|\:|;|\&lt;|\&gt;|\[|\]|\?/g, css: 'punctuation' },		                // Пунктуация
            { regex: new RegExp(keywords, 'gi'), css: 'keyword' },		                                            // Ключевые слова
            { regex: /^\s*#.*/gm, css: 'preprocessor' },		                                                    // Теги препроцессора вида #Область и #КонецОбласти
            { regex: /^\s*&.*/gm, css: 'preprocessor' },		                                                    // Теги препроцессора вида &Клиент and &Сервер
        ];
    }

    Brush.prototype = new SyntaxHighlighter.Highlighter();
    Brush.aliases = ['oce']; // Имя класса блока на странице, для которого будет использован этот скрипт

    SyntaxHighlighter.brushes.Bash = Brush;

    // CommonJS
    typeof (exports) != 'undefined' ? exports.Brush = Brush : null;
})();

В листинге прокомментировал основные моменты, с которыми возникли вопросы. Класс для нового типа кода назвал "oce" (One C Enterprise). Теперь добавим файл со стилями в каталог "styles". Новый файл я создал копированием стилей по умолчанию ("shCoreDefault.css") и назвал его "shTheme1C.css". В нем остается поправить оформление только для следующих классов:

.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
  color: blue !important;
}
.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
  color: #008200 !important;
}

.syntaxhighlighter .string, .syntaxhighlighter .string a {
  color: black !important;
}

.syntaxhighlighter .line.highlighted.number {
  color: black !important;
}

.syntaxhighlighter .punctuation {
  color: red !important;
}

.syntaxhighlighter .keyword {
  color: red !important;
}

.syntaxhighlighter .keyword {
  font-weight: normal !important;
}

.syntaxhighlighter .preprocessor {
  color: #963200 !important;
}

Почти все готово! Осталось внести изменения в Razor-страницу "SyntaxHighlighter.cshtml":

  1. Добавим соответствие для нового типа кода:
        private static Dictionary<string, Tuple<string, string>> CodeTypes = new Dictionary<string, Tuple<string, string>> {
    	{"shBrushOce", new Tuple<string, string> ("1C", "oce")},
        {"shBrushAS3", new Tuple<string, string> ("ActionScript3", "as3") },
        {"shBrushBash", new Tuple<string, string> ("Bash/shell", "bash")},
    	// ...
  2. Включим в виджет новый стиль оформления:
        private const string themeWidgetMarkup = @"<f:widgetfunction xmlns:f=""http://www.composite.net/ns/function/1.0"" name=""Composite.Widgets.Selector"">
    		<f:param name=""Options"">
    			<f:function name=""Composite.Utils.String.Split"">
    				<f:param name=""String"" value=""shThemeDefault.css,shTheme1C.css, etc... "" />
    				<f:param name=""Separator"" value="","" />
    			</f:function>
    		</f:param>
    	</f:widgetfunction>";

Время тестирования!

Посмотрим на результат внесенных нами изменения. Добавим функцию "SyntaxHighlighter" на любую страницу. Мы увидим, что теперь доступен тип синтаксиса "1С":

Поддержка синтаксиса 1С

Также доступен и новый стиль оформления, аналогичный тому, что используется в конфигураторе платформы 1С:Предприятия:

Стиль оформления кода 1С:Предприятия

Непосредственную работу нового скрипта Вы можете посмотреть на странице статьи про 1С:Предприятие.

Листинге большего размера Вы можете найти в статьях моего блога по 1С:Предприятию, а файл скрипта и таблицу стилей скачать по ссылкам в верху страницы.

Спасибо за внимание! =)


comments powered by Disqus