Текстовый редактор с подсветкой синтаксиса


Редактор с подсветкой кода. Проблемы и решения / Хабрахабр

Большая часть аудитории Хабра регулярно пишет код. В текстовом редакторе или IDE. И сколько бы не было в нем окошек и менюшек, а сердце любого редактора — это компонент (widget), который редактирует и подсвечивает код.

Больше года назад на Хабре был цикл статей от namespace про компонент QScintilla (1, 2, 3), и моя статья с его критикой. Получилась некоторая недосказанность. Ясно, что все плохо, но не понятно, что делать.

Сейчас я написал свой велосипед компонент под названием Qutepart, и у цикла есть продолжение. Эта статья расскажет о том, как устроена подсветка синтаксиса в моем проекте: какие проблемы возникали, и как они решались. Она про подходы, а не про специфику конкретного GUI-инструментария. Если интересно заглянуть «под капот» текстового редактора, добро пожаловать под кат. Сразу оговорюсь, что это не фундаментальная научная статья. Это мой опыт. Будет очень интересно почитать в комментариях про альтернативные подходы и решения.

Мотивация
Я делаю текстовый редактор в духе vim и emacs. Универсальный, кроссплатформенный, расширяемый, ориентированный на опытных пользователей. Но с чуть более интуитивным и современным GUI на PyQt. И для этого мне нужен редактор кода. Вариантов нашлось всего 2: QScintilla и katepart. Изначально я использовал QScintilla, но из-за недостатков, перечисленных здесь, решил от него отказаться.katepart очень хорош, но не устраивает тем, что зависит от библиотек KDE. А иметь их в зависимостях не очень удобно, особенно для Windows и MacOS.
Разбор кода
Мой проект не ориентирован на какую-то конкретную технологию, языков хочется подсвечивать много. Больше, чем я в состоянии освоить сам. Поэтому было решено использовать существующую базу описаний синтаксиса от katepart.

Для каждого из поддерживаемых языков программирования в katepart существует Highlight Definition — XML-файл с описанием синтаксиса и подсветки. Highlight Definition описывает нечто вроде конечного автомата. Автомат последовательно разбирает текст, меняя при этом свое состояние — «Context». У каждого из контекстов есть набор правил, по которому нужно перейти в другой контекст. Автомат помнит, из какого состояния он перешел в текущее, и может возвращаться в предыдущие контексты (стек контекстов). Пример: C++. В контексте код есть правило: если встретился символ " — перейти в контекст «строка». Контекст «строка» подсвечивает символы красным цветом и подержит правило: если встретился символ " — вернуться в предыдущий контекст. Для каждого из контекстов задан стиль, которым он подсвечивает текст.

Система очень удобная и универсальная. Формат файлов хорошо документирован. Наверное, именно поэтому katepart подсвечивает около 2 сотен языков и форматов. Минусом, на мой взгляд, является лишь то, что интерпретатор, который разбирает код на основе Syntax Definition, в большинстве случаев будет хуже по производительности, чем парсер для конкретного языка программирования.

Оптимизация
Когда я написал подсветку синтаксиса, радости моей не было предела. Все работает, все красиво. Все 59 файликов на разных языках из коллекции katepart выглядят правильно и открываются шустро. Ура! Кто сказал, что Python медленный язык?! А потом я попробовал открыть большой файлик. Реально большой. И внезапно оказалось, что мой парсер не так уж и быстр. Пришлось браться за оптимизацию.

Пару часов с профайлером ускорили подсветку в несколько раз. Но, все равно получалось слишком медленно. А пространство для оптимизации было практически исчерпано. Можно было ускорить парсер еще на пару десятков процентов за счет жутко запутанного кода, но такая оптимизация погоды не делает.

Я стал осваивать написание модулей на C. Это оказалось совсем не сложно, Python к расширениям очень дружелюбен. В процессе написания парсера возникла проблема: в C нет регулярных выражений. А зависимости подключать очень не хотелось. Проблема решилась за счет того, что взаимодействие C-Python работает в 2-х направлениях. Python вызывает C для парсинга, а из С дергается функция на Python для того, чтобы проверить регулярное выражение.

Когда я начал тестировать парсер с расширением, выяснилось, что производительность не существенно отличается от версии на Python. Снова взял профайлер и пошел искать проблему. Оказалось, что 90% времени мой парсер вызывает Python для проверки регулярных выражений. Что же, хак не удался. Пришлось использовать внешнюю библиотеку. Так у компонента появилась единственная зависимость — библиотека обработки регулярных выражений на C pcre. С ней производительность получилась вполне приемлемой (цифры будут ниже).

В итоге в целесообразности использования Python я не разочаровался. Парсер на C — это примерно 1/3 кодовой базы моего компонента. Думаю, по трудозатратам такой гибридный вариант получился легче, чем решение на С++.

Асинхронная подсветка
Большинство текстовых файлов довольно небольшие по размеру. Но иногда мне приходилось редактировать исходники, в которых больше 300К строк. Насколько бы не был крут разработчик парсера, и насколько бы не был быстр его язык, а разбираться до конца такой файл будет дольше, чем пользователь согласен ждать.

katepart подсвечивает код в потоке GUI. И делает это лениво — подсвечивается столько, сколько нужно отрисовать на экране. Такой подход очень хорошо работает, если файл открывается в начале. Однако если перескочить в конец большого файла — GUI просто зависает. Меня такой подход не устроил.

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

Сейчас, когда рост тактовых частот перешел в рост количества ядер, часто актуальна оптимизация вычислений за счет разделения по ядрам. Но и здесь возникают проблемы, и не только потому, что парсить файл нужно последовательно. Пользователь постоянно редактирует код. Поток GUI обрабатывает нажатия клавиш и меняет документ. Если при этом документ парсится и подсвечивается в потоках, изменения нужно синхронизировать. Инструментарий Qt пока не везде ориентирован на многопоточность, я не нашел способа надежно синхронизировать доступ к документу. Пришлось от потоков отказаться.

В итоге экспериментов у меня получилось следующее решение: файл разбирается и подсвечивается в потоке GUI по таймеру. Таймер работает 20 милисекунд, потом возвращает управление в main loop для обработки действий пользователя, потом снова вызывается… Если пользователь открыл огромный файл и сразу перескочил в его конец — файл отображается, но без подсветки. Код можно править, а подсветка появится чуть позже.

Инкрементальная подсветка
Когда пользователь редактирует код — подсвечивать заново текст нужно с того места, которое редактируется, а не с начала. И, как правило, меняется одна или несколько строк. В процессе парсинга к каждой строке файла прикрепляется блок метаданных с состоянием парсера. Если текст был изменен, метаданные используются, чтобы начать разбор с конкретной строки. Продолжается инткрементальный парсинг до тех пор, пока не будет найдена строка, метаданные которой не изменились после нового парсинга.
Сравнение производительности
Сравнение производительности подсветки — дело не благодарное. Уж очень от многих факторов она зависит: железа, версий софта, языка, содержимого конкретного файла, фазы луны,… Однако без него статья была бы не полной. Поэтому придется раздел добавить.Отказ от ответственности

Как уже сказано выше, производительность зависит от многих факторов, и при других обстоятельствах картина может коренным образом отличаться. Этот раздел — не основная цель статьи. Измерения производились только на одном файле на одном языке, очень поверхностно. Цифры, наблюдения и выводы могут содержать грубые ошибки. Поэтому методику измерений не публикую; предлагаю считать, что информацию, приведенную ниже, я выдумал сам исключительно для развлечения аудитории Хабра. Приведенная информация ни в коем случае не может быть использована для выводов о том, что текстовый редактор X лучше, чем текстовый редактор Y.

Я открыл большой C++ файл (364121 строка) в нескольких редакторах, которые мне интересны, и собрал свои наблюдения в эту таблицу.Компонент или редактор Время на подсветку всего файла Блокирует GUI Проблемы подсветки
Qutepart 44 секунды Никогда Открывает файл 3 секунды
katepart Был убит через 6 минут Пока не подсветит столько, сколько нужно отобразить
QScintilla 3 секунды Никогда Тормозит при редактировании
Scintilla 3 секунды Никогда Тормозит при редактировании
Sublime Text 23 секунды При редактировании, пока не обновит всю изменившуюся подсветку
gedit 8 секунд Никогда Тормозит при редактировании
Qt Creator 20 секунд При редактировании висит, пока не обновит всю изменившуюся подсветку
Ninja IDE 14 секунд При открытии Подсветил только первые 51 строки. Жутко тормозит при редактировании.
vim Мгновенно Никогда Парсит файл с середины, в некоторых случаях показывает неправильный результат.
emacs Мгновенно Никогда Парсит файл с середины, в некоторых случаях показывает неправильный результат. Вешается на время около минуты при перемотке вверх.
Подробная версия таблицы Как видим, Qutepart медленнее всех подсвечивает текст. Это закономерно, поскольку используется интерпретируемый язык, связка Python-Qt и интерпретируемые определения синтаксиса в виде XML. С другой стороны, высокоуровневый язык и технологии позволяют подсвечивать много языков, не блокировать GUI и не показывать артефактов. При работе с реальными файлами в абсолютном большинстве случаев файл открывается уже подсвеченным, и при редактировании пользователь не видит, как обновляется подсветка. Поэтому текущее положение дел меня устраивает и от дальнейшей оптимизации я отказался.
И что же получилось
У меня получился компонент для редактирования кода Qutepart и текстовый редактор на его основе Enki. Зависит компонент от PyQt и от pcre. Требует сборки модуля расширения на C. Для небольших файлов можно обойтись без расширения и без pcre. Из katepart позаимствованы файлы Syntax Definition и алгоритмы выравнивания кода. Как и katepart, проект доступен под LGPL.

Сегодня я выпустил первую версию Qutepart и Enki на его основе, потому что решил, что текущая версия уже лучше, чем вариант на QScintilla. Функциональности пока не много. TODO-list большой. Периодически пополняется за счет пожеланий от пользователей и становится меньше за счет сделанных фич.

Буду рад отзывам от Хабра-сообщества!

habrahabr.ru

Редакторы кода (текста) с подсветкой синтаксиса.

Веб разработка / Редакторы кода

Blueprint XML Editor

XML Editor - это мощный XML-редактор для Windows. Он очень легкий в освоении, быстрый в работе, и обладает множеством высококачественных функций для XML-разработчиков, присущих только дорогостоящим профессиональным приложениям. С помощью редактора вы можете легко создавать валидные документы XML, DTD, схемы Relax NG, схемы XML и стили XSLT. Работает под Windows всех версий, поддерживает юникод на всех платформах.

ПОДРОБНЕЕ

Веб разработка / Редакторы кода

MultiEdit 2008

MultiEdit - это текстовый редактор, который предназначенный специально для разработчиков. Мощные возможности программы значительно упрощают процесс написания кода и позволяют разрабатывать любые проекты в кратчайшие сроки. Редактор предлагает удобные инструменты для манипулирования текстом и позволяет выполнять компиляцию непосредственно из редактора.

ПОДРОБНЕЕ

Веб разработка / Редакторы кода

Sublime Text 3

Sublime Text — мощный кроссплатформенный текстовый редактор, поддерживающий подсветку синтаксиса C, C++, C#, CSS, D, Erlang, HTML, Groovy, Haskell, HTML, Java, javascript, LaTeX, Lisp, Lua, Markdown, Matlab, OCaml, Perl, PHP, Python, R, Ruby, SQL, TCL, Textile и XML.

ПОДРОБНЕЕ

Веб разработка / Редакторы кода

IDE Source Insight

IDE Source Insight - это мощный и функциональный редактор исходного текста, программа поддерживает подсветку синтаксиса для огромного количества языков программирования, включая такие языки как: VBScript, JavaScript, Perl, PHP, CSS, C, C++, C#, Java. Поддерживается командная разработка приложений, а также присутствуют все необходимые средства для написания, компилирования, проверки и выполнения программ.

ПОДРОБНЕЕ

Веб разработка / Редакторы кода

JetBrains PhpStorm 6.0

PhpStorm - это легкий и удобный редактор PHP, который значительно способен повысить вашу производительность. Программа отлично понимает код, дает удобные подсказки, быструю навигацию и отслеживает ошибки "на лету".IDE всегда готова помочь Вам собрать ваш код, запустить юнит-тесты и обеспечить визуальную отладку. PhpStorm поддерживает PHP, CSS, HTML, XML, YAML, javascript - все, что нужно для того, что бы разработать свой web-сайт.

ПОДРОБНЕЕ

Новый софт на последних по нумерации страницах
Новые программы
Скачиваемый софт

soft-landia.ru

Создаём подсветку синтаксиса в Notepad++ / Хабрахабр

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

В последнее время подсел на notepad++, он умеет печатать с выбранной подсветкой синтаксиса, но проблема в том, что нет подсветки именно для языка, используемого в 1С. Долго и безрезультатно искал нужную подсветку синтаксиса. Нашел на одном сайте, но они просят денег и нет гарантии, что это то самое искомое.

Попытался сам вручную создать подсветку, но все шаманства с xml файлами не приводили к успеху. Наткнулся на статью «Включаем подсветку синтаксиса для less файлов в Notepad++», ожидал увидеть там рецепт создания этой самой подсветки, но как и многие комментирующие был разочарован содержанием поста. В результате копания мануалов у меня получилось таки создать нужную мне подсветку. Оказывается это совсем не сложно! Итак приступим:

  1. Открываем Notepad++.
  2. Для удобства открываем текстовый файл с кодом, который будем раскрашивать.
  3. В меню «Синтаксис» выбираем параметр «Свои настройки»
  4. В меню «Вид» выбираем пункт «Пользовательское определение языка» или нажимаем на панели инструментов кнопку: Откроется окно настроек синтаксиса. Можно нажать кнопку «Стыковать» в правом верхнем углу (которая может называться «Dock», в зависимости от полноты перевода интерфейса), чтоб прилепить к правому краю окна. Плюсом такого решения будет полоса прокрутки, дающая возможность добраться до нижних параметров, которые могут просто не влезть по вертикали в монитор
  5. На вкладке «Стандартный» настраиваем базовый стиль отображения в разделе «Настройка стандартного стиля»: основной шрифт, размер, цвет фона и текста, который не будет попадать под остальную раскраску. Все параметры применяются сразу и любые изменения можно наблюдать воочию. На этой же вкладке в разделе «Настройка открытия ключевых слов» указываем теги группы, по которым определяются блоки текста. Например, у нас блоком является все, что между операторами «Процедура» и «КонецПроцедуры», следовательно «Процедура» пишем в первое поле, а «КонецПроцедуры» в поле ниже. Для каждой группы можно указать отдельные настройки шрифта и цвета. Ключевые слова разделяются пробелом. Добавим туда процедуры циклов и условий, чтоб можно было сворачивать их в группы.
  6. С блоками разобрались, теперь подкрасим ключевые слова. Для этого нам понадобится вкладка «Списки ключевых слов». Здесь нам доступны 4 группы, которые мы можем использовать как нам заблагорассудится. Добавляем в первую все операторы, которые нужно выделить цветом, это будут всякие «Перем», «Выбрать» и т.п. Во вторую группу пропишем инструкции препроцессора "#" и поставим галочку «Префикс» для того, чтоб цветовая схема распространилась и на слово после этого символа.
  7. Плавно переходим на вкладку раскраски комментариев. Заполнение этой вкладки не должно вызвать каких-либо затруднений.
  8. Вкладка «Операторы» немного отличается от описанных выше. Здесь нам предоставляют список одиночных операторов, которые мы можем раскрасить. При помощи кнопки со стрелкой переносим нужные нам операторы в поле «Активные операторы» и ниже выставляем параметры раскраски. Чекбокс «Включить знак перехода» мне ничем не помог, так как при включении появляются в поле китайские символы и никак не хотят меняться. Далее следует группа разделителей: тут указываем цветовые и шрифтовые параметры для окавыченного текста. Думаю затруднений не составит сделать настройки на свой вкус.
  9. Теперь осталось сохранить наше творение под каким либо именем. Для этого в верхней части окна нажимаем кнопку «Сохранить как...» и вводим имя нашей синтаксической подсветки. Раскраска сохраняется в специальном файле userDefineLang.xml. Настройки можно экспортировать в отдельный файл, который в последствии можно распространять. Кстати чекбокс «Любой регистр» почему-то игнорируется и подсветка остаётся регистрозависимой.
Всё! Наконец-то теперь можно распечатать код в цвете:

Единственное неудобство — это выбирать подсветку синтаксиса вручную при каждом открытии файла.

Ссылка на готовый xml для подсветки языка 1С на Github. Для использования у себя нужно сделать импорт через «Пользовательское определение языка» и перезапустить Notepad++.

habrahabr.ru

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

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

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

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

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

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

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

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

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

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 сопоставляет каждому номеру какой-нибудь цвет. Для нашего примера результат будет выглядеть… странно:

Нюансы
Как видите, ничего сложного. Потратив 10 минут на краткое ознакомление с lua и вооружившись списком сообщений для отправки scite можно сделать с текстом очень много ненужных и бесполезных вещей :). На что еще можно обратить внимание: Всех с праздником!

habrahabr.ru


Смотрите также