Search
Write a publication
Pull to refresh

Подсветка синтаксиса для собственного языка

Reading time4 min
Views7.4K
imageПодсветка синтаксиса — задача простая и решалась много раз. Но есть у нее одна неприятная особенность — если мы хотим подсветить синтаксис нового языка (например, языка bb-тэгов хабраредактора, или лога какой программы), то большинство решений включает создание грамматики, парсера, и затем встраивание это всего куда-нибудь. А что делать, если получить подсветку для логов желание есть, а тратить на это три часа желания нет?


Подходящий текстовый редактор


Прежде, чем раскрашивать текст нужно выбрать текстовый, редактор в котором мы будем этим заниматься. Текстовых редакторов у нас море, на любой вкус и цвет. Что нам нужно? Что-нибудь простое, конечно бесплатное, чтобы работало под любой операционной системой, чтобы текст было удобно редактировать. Ну и конечно чтобы встраивание своей подсветки синтаксиса не превратилось в перекомпиляцию хардкорного кода на С со встраиванием туда PEG грамматики :). Как не странно, даже с таким требованием текстовых редакторов много. Выбираем scite, потому что его я знаю лучше всего :). Под windows качать отсюда, под MacOS и *NIX можно прямо из репозитория. Устанавливаем, запускаем, получаем примерно следующее:

editor

Подключаемся к внутренностям редактора


Как мы договорились в начале, ничего компилировать не будем — дело это долгое, муторное, и в парадигму «красим за 15 минут» никак не укладывается. Как добраться до внутренностей текстового редактора без его перекомпиляции? Для этого редактор должен либо поддерживать скрипты, либо давать наружу интерфейс межпроцессного взаимодействия вроде COM. У scite — скрипты. Пишутся они на языке lua, который прост как три рубля рублями и осваивается за десять минут. Почти бейсик :). И подключить его к scite тоже нетрудно:
  1. Создаем где нам удобно текстовый файл scite.lua (имя может быть любым).
  2. В scite выбираем options->'open global options file'.
  3. В открывшемся файле настроек прописываем полный путь к script.lua следующим образом:
    editor
    Обратите внимание на отсутствие пробелов перед и после '=', это важно.
  4. Перезагружаем scite.

Hello world


Теперь при каждом запуске scite он будет автоматически загружать и исполнять указанный скрипт. Проверим что все работает классическим способом — добавим в пустой файл scite.lua команду, выводящую в scite всем известный текст:

hello
Если теперь запустить scite, то мы увидим результат:

result
В появившемся специальном окне, которое гордо именуется 'output window', будет отображен текст, который мы в скрипте указали на вывод. С помощью команд print() в скрипте и output window можно достаточно легко отлаживать скрипты и выводить нужную нам информацию — например, количество смайликов в нашей статье :).

Готовимся к покраске


Убедившись в успешном подключении к нутру текстового редактора, можно приступить к покраске. Для успешной покраске нам нужно будет воспользоваться принципом ioc и скрипте указать scite чтобы он консультировался с нами каждый раз, когда обновляется текст. Для этого достаточно задать функцию со специальным именем:

function OnUpdateUI()
print( "text update" )
end


Эта функция будет вызываться каждый раз, когда текст в окне текстового редактора поменяется. Следующая картинка показывает что будет, если запустить тестовый редактор и ввести 'a' — массовые вызовы функции-обработчика нам обеспечены:
update log

Красим


Вообщем-то все готово: есть функция, которую будут автоматически вызывать каждый раз, когда меняется текст. Можно красить. Самое ценное в scite то, что он является редактором, демонстрирующем работу scintilla — библиотеки для создания окошек по редактирования текста. А библиотека, между прочим, популярная — Komodo Edit, wxWidgets, GTK используют для редактирования текста как раз ее. И есть у этой библиотеки штатный механизм взаимодействия со всем и вся, SCT_ сообщения. Подробная документация на сайте доходчиво объяснит, какие именно сообщения и в какой последовательности нужно отправить, чтобы текстовый редактор встал раком раскрасил текст, подчеркнул нужные нам слова и расставил красивых меток на полях. В частности, для того чтобы покрасить текст достаточно отправить два сообщения:
  • SCI_STARTSTYLING с указанием номера символа начиная с какого красить. первый символ имеет номер 1
  • SCI_SETSTYLING с указанием количества символов которые красить и номера цвета

Следующий код раскрасит каждый второй символ в какой-то цвет:

function OnUpdateUI()
text = editor:textrange( 0, editor.Length )
for i = 1, string.len( text ), 2 do
scite.SendEditor( SCI_STARTSTYLING, i, 31 )
scite.SendEditor( SCI_SETSTYLING, 1, 5 )
end
end


Обратите внимание на пару нюансов: '31' — это маска для номера стиля, просто некое волшебное число которое нужно всегда передавать этой функии. '5' — это номер цвета. По умолчанию scite сопоставляет каждому номеру какой-нибудь цвет. Для нашего примера результат будет выглядеть… странно:

update log

Нюансы


Как видите, ничего сложного. Потратив 10 минут на краткое ознакомление с lua и вооружившись списком сообщений для отправки scite можно сделать с текстом очень много ненужных и бесполезных вещей :). На что еще можно обратить внимание:
  • В начале функции OnUpdateUI() имеет смысл отправить сообщение SCI_CLEARDOCUMENTSTYLE чтобы сбросить установленную в прошлый раз раскраску.
  • Для того, чтобы указать редактору какой цвет соответствует какому номеру нужно выбрать options->'open global options file' и добавить строчки вида:
    file.patterns.habr=*.habr
    lexer.$(file.patterns.habr)=habr
    style.habr.5=fore:#FF0000,back:#0000FF

    Где 'habr' — это расширение файла, который мы хотим покрасить.

Всех с праздником!
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
Total votes 22: ↑19 and ↓3+16
Comments9

Articles