Хорошо оформленные вставки кода и правильно подобранные иллюстрации - это залог здоровья любой статьи. В том смысле, что их отсутствие может даже отличную статью весомо подпортить (но не наоборот).
Дак вот, именно поэтому довольно куцая подсветка кода в Sandcastle Help File Builder (далее SHFB), меня не слишком обрадовала (про Sandcastle я совсем недавно писал, это утилита для генерации документации в MSDN-стиле).
И вот, когда засел писать документацию в SHFB уже в третий раз (кстати, для SharePoint Fluent Ribbon), мне все-таки удалось эту проблему победить! Теперь моя документация выглядит всем на зависть и на заглядение:
И между прочим, сделать это совсем несложно. В этой статье я расскажу, как.
Настройки Code Block Component
Оказывается, для подключения подсветки в SHFB, нужно перейти в свойства проекта, найти в самой верхней группе настроек (Build) свойство ComponentConfigurations, и нажать на кнопку с троеточием.
Появится вот такое окошко:
Если жмякнуть на Code Block Component, внизу можно прочитать, что это такое:
This build component is used to search for <code> tags within reference XML comments and conceptual content topics and colorize the code within them. It can also include code from an external file or a region within the file.Отлично! Теперь добавим его с помощью кнопки Add, и затем нажмем Configure, чтобы задать для него настройки.
Появится очередное окошко, на этот раз намного более интересное:
Я сразу же включил line numbers и collapsible #regions, а также выставил Default Tab Size в 4. Уже лучше, уже намного интереснее. Но, пока еще не идеально. Потому что названия классов не подсвечиваются.
Подсветка названий классов - это вообще больное место для многих Syntax Highlighter'ов. К примеру, на StackOverflow классы подсвечиваются, очевидно, то ли по каким-то хитрым правилам, то ли по очень большому словарю - в общем, эта система очень часто дает сбои, и обеспечивает большое число лишних подсветок.
В Ooki.FormatC для подсветки классов нужно прописывать названия этих классов каждый раз, когда хочешь подсветить какой-нибудь фрагмент кода. Это самая качественная подсветка, но в то же время для больших фрагментов - замучаешься прописывать все эти классы...
А чаще всего, названия классов не подсвечиваются вовсе, и тут был именно такой случай. Что мне, конечно же, категорически не нравилось (на самом деле для блога, Buzz'а и для CodePlex'а, я пользуюсь именно Ooki.FormatC, только не онлайн-версией, а немного доработанным оффлайн-приложением).
Но давайте вернемся к нашим баранам. Именно на странице настроек Code Block Component я обнаружил очень интересные настройки - Language syntax configuration file, и XSLT style transformation file. Мне захотелось посмотреть, что же они собой представляют, и что там можно интересного поправить...
Исследуем файлы настроек
Я перешел в каталог {@SHFBFolder}, коим оказался на самом деле "C:\Program Files (x86)\EWSoftware\Sandcastle Help File Builder\", и внимательно исследовал папку Colorizer. Там оказалось сразу несколько интересных файлов:
1. highlight.css, который содержал стили. Т.е. в случае необходимости, с помощью этого файлика всегда можно подогнать цвета, которые не соответствуют вашим представлениям о правильных цветах для подсветки.
2. highlight.xsl, который содержал трансформации. А именно, некие особые тэги превращал в обычные span'ы, к которым прикреплялись соответствующие стили.
3. highlight.xml, который содержал довольно много разной информации, ну в целом можно сказать, что он описывал алгоритм подсветки, причем делал это с помощью интересной системы контекстов.
Мне очень понравилась эта система контекстов, которая использовалась в файлике highlight.xml, поэтому позволю себе остановится на ней немного поподробнее.
Итак, для каждого языка в файлике объявлена секция <contexts>, с указанием Id контекста по умолчанию, и внутри идут уже описания контекстов. Внутри каждого из контекстов, своеобразные правила, которые делятся на несколько типов, в том числе это может быть regex и keyword. Каждое правило вытаскивает некий фрагмент из обрабатываемого текста. И здесь, внимание! Правила эти делают две вещи: во-первых, назначают вытащенному ими фрагменту атрибут для подсветки, а во-вторых, могут переключать контексты (!). Т.е. к примеру, ключевые слова ловиться в контексте комментариев уже не будут.
В общем, имеем дело с описанием неких состояний анализатора подсветок. Конечный автомат, блин :)
Да, чтобы было понятно, о чем я говорю, вот вам пример секции (для питона, поскольку секция поменьше):
<!-- Python language specification -->
<language id="python" tabSize="4" name="Python">
<!-- Code contexts: default (most common) is code. -->
<contexts default="code">
<!-- basic source code context -->
<context id="code" attribute="code">
<!-- Single line of comment -->
<regexp id="python-comment" attribute="comment" context="code" expression="(?<=\W|^)(#.*)([\r\n]{1,2}|$)" />
<!-- " " literals -->
<regexp id="dqliteral" attribute="literal" context="code" expression="("")|(@"(.|[\r\n])*?"|"(.|\\"|\\\r\n)*?((\\\\)+"|[^\\]{1}"))" />
<!-- ' ' literals -->
<regexp id="sqliteral" attribute="literal" context="code" expression="('')|('(.|\\'|\\\r\n)*?((\\\\)+'|[^\\]{1}'))" />
<!-- numbers -->
<regexp id="number" attribute="number" context="code" expression="0x[0-9a-fA-F]*|(((?<=[^0-9])(\+|-))?)\b[0-9][0-9]*(\.[0-9]+)?" />
<!-- keyword Python-like -->
<keyword attribute="keyword" context="code" family="kwpython-keywords" />
</context>
<!-- line comment // ... -->
<context id="linecomment" attribute="cpp-linecomment">
<!-- finish line of comment end of line -->
<linecontinue attribute="hidden" context="code" />
</context>
</contexts>
</language>
Очень интересная и гибкая системка. Качество подсветки, судя по тому что я пока видел - на высоте. И насколько я понял, вся подсветка идет исключительно на основе конфига, т.е. можно задать правила для любого гипотетического языка, и оно будет работать.
Вы скажете - да есть куча других хайлайтеров, которые также работают. А вот и нет, не куча! Самый известный хайлайтер для программистов какой? Scintilla. В свое время я очень скурпулезно изучал файлы настроек подсветки Scintilla, и к сожалению, они не обеспечивают полное описание подсветок (т.е. частично подсветки осуществляются кодом, а не на основе конфига).
Кроме того, с Notepad++ могу сказать, тоже нехорошая история. У него конфиги для подсветок вроде бы и универсальные, но вот качество описаний подсветок - хреновое. Т.е. в итоге, в подсветках очень много брака.
Вот такая вот складывается история...
Добавляем список названий классов
Перед внесением изменений, рекомендую сделать копию папки Colorizer, положив её в папку своего, внимание, проекта документации! Т.е. для каждого проекта будем задавать свои правила подсветок.
Да, и еще один совет: сразу же смените в настройках Code Block Component путь к файлам подсветок, а то потом можно забыть и долго пытаться понять, "что же я делаю не так" :)
Первым делом, определимся, что нам нужно. Мы должны подсвечивать некий список слов. Значит, этот список нужно как-то добавить. Например, можно написать regex, содержащий такой список, но это будет слишком бестолково. Подумав, я решил, что нам вполне подойдет механизм, который используется для keyword.
В начале файла highlight.xml можно увидеть описания списков ключевых слов (keywordlist). Например, список может выглядеть так:
<!-- Common C-like language keywords (C, C++, C#) -->
<keywordlist id="kwclang-keywords">
<kw>break</kw>
<kw>case</kw>
<kw>char</kw>
<kw>const</kw>
<!-- ... -->
</keywordlist>
Отлично! Давайте создадим собственный подобный список. У меня получилось следующее:
<keywordlist id="kwcs-types">
<kw>String</kw>
<kw>Boolean</kw>
<kw>DateTime</kw>
<kw>Guid</kw>
<kw>IEnumerable</kw>
<kw>IList</kw>
<kw>ICollection</kw>
<kw>IDictionary</kw>
<kw>List</kw>
<kw>Dictionary</kw>
<kw>SPWeb</kw>
<kw>SPSite</kw>
<!-- ... -->
</keywordlist>
Запоминаем Id, присвоенный нами списку ключевых слов, и идем искать секцию контекстов, ответственную за обработку языка C#. Через несколько секунд, обнаружим следующее:
<!-- C# language specification -->
<language id="cs" tabSize="4" name="C#">
В этой секции видим всего 3 контекста: code, linecomment и blockcomment. Очевидно, нам нужен первый. Внутри этого контекста, помимо всяких regex'ов, видим также 3 правила, для выцепляния ключевых слов:
<!-- keyword C-like -->
<keyword attribute="keyword" context="code" family="kwclang-keywords" />
<!-- keyword C++-like -->
<keyword attribute="keyword" context="code" family="kwclangoo-keywords" />
<!-- keyword C# -->
<keyword attribute="keyword" context="code" family="kwcs-keywords" />
Легко догадаться, что мы должны добавить сюда еще один блок, подставив в атрибут family значение kwcs-types.
Но если на этом все и закончить, наши типы будут подсвечиваться синим, как ключевики, а это неверно.
Как вы уже догадались, нам нужно будет изменить атрибут attribute, присвоив ему собственное значение. Именно это я и проделал:
<!-- C# types -->
<keyword attribute="csharp-type" context="code" family="kwcs-types" />
Отлично, теперь перечисленные нами названия классов будут помечаться атрибутом csharp-type. Дальше я предположил, что оформление для csharp-type можно добавить в файле highlight.xsl.
Недолго думая, добавил туда следующие строки:
<xsl:template match="csharp-type">
<span class="highlight-csharp-type"><xsl:value-of select="text()" disable-output-escaping="yes" /></span>
</xsl:template>
Уфф, почти все. Осталось последнее - поправить css, добавив туда следующую строку:
.highlight-csharp-type { color: #2B91AF; }
После этого я скомпилировал проект документации, и получил "идеальную" подсветку. Ура! :)
Комментариев нет:
Отправить комментарий
Внимание! Реклама и прочий спам будут беспощадно удаляться.
Примечание. Отправлять комментарии могут только участники этого блога.