Вадим Балашов дата публикации 21-06-2006 03:29 Доброго дня всем жителям!
Эта статья будет являться продолжением статьи уважаемого Максима Парфентьева "Компоненты для подсветки синтаксиса. Новый взгляд". Прочитав ее, я был очень впечатлен и решил попробовать использовать этот компонент. Никаких текущих проектов, вынуждающих использовать что-то подобное, не было, но было свободное время и интерес, что, подчас, гораздо важнее. В итоге, взявшись "просто попробовать", я начал дописывать компонент, добавлять в него то, чего, на мой взгляд, не хватало. Кое-какое исправление я даже вынес в обсуждение (http://www.delphikingdom.com/asp/articles_forum.asp?ArticleID=1148), но потом исправлений стало очень много, и я решил просто выложить "готовый" компонент. Потом понял, что без какого-то описания разобраться будет сложно – в итоге решил написать маленькую статью.
Еще раз хочу выразить свое уважение к создателю этого компонента – компонент реализован очень изящно, что и позволило мне добавить все то, что я сделал.
Еще одно отдельное спасибо Александру Бакулину за тестирование, критику и новые идеи.
Ну да это все лирика – приступим к главному.
Возможно, кому-то покажется скучным читать об исправлениях – в таком случае просто пропустите следующий раздел.
Естественно, что исправлять я начал то, на что жаловались жители королевства в комментариях.
Ниже приведены пожелания жителей и мои комментарии.
Sega-Zero: А что, отступы строк (Ctrl+Shift+u/i) не реализованы?
Автором была дана рекомендация, добавить реализацию. Но это было, скорее, побуждение к действию, чем решение, поэтому я доделал это до конца, так как мне казалось правильным (я не буду описать, как именно это теперь работает, какие опции нужно включать для других режимов и т.д. – вы можете просто скачать получившийся компонент и все посмотреть).
Sega-Zero: Кстати говоря, неплохо было бы сделать что-то аналогичное KeyStrokes в SynEdit'e. Я например там с легкостью добавил возможность комментирования кода как в D2005 — Ctrl+/
Я не смотрел SynEdit по этому поводу, но реализацию ctrl+/ сделал в стиле Delphi2005 (на сколько я эту реализацию помню – если что-то не так, исправлю). Комментируются строки символом, указанным в качестве строчного комментария – т.е. если вы будете реализовывать редактор для BASIC’а – данное сочетание клавиш добавит REM в начало каждой строки.
Так же добавлена обработка комбинации ctrl+a — как обычно, выделяет весть текст. Добавлено средствами, подготовленными автором, так что это не моя заслуга.
Если нужны еще какие-то сочетания – их легко можно реализовать в OnKeyDown.
MegaVolt: Только вот прокрутка колёсиком у меня работает очень уж медленно :(
Да, это действительно странно, хотя, возможно, связанно с установленными драйверами мыши. Суть проблемы в том, что если крутить колесо медленно, то за каждый "тик" колеса прокручивается указанное число строк, т.е. все в порядке. Если же прокрутить колесо быстро (сверху донизу – на 4-5 тиков), то обработается только один "тик". Решение не очень ясно, т.к. реализовано это через сообщения и почему система посылает каждое сообщение, или почему не все сообщения обрабатываются – не ясно. В итоге я сделал то, что предложил сам Максим Парфентьев – увеличил число прокруток строк за "тик" с одного до трех.
MegaVolt: Я бы сделал масштаб при панарамировании не 1:1 а несколько больший чтобы перемешение на небольшое расстояние вызывало большое перемещение самого текста.
В моем исходнике компонента панорамирование вообще не было реализовано, хотя видно было, что все подготовлено. Я его реализовал сам так, как мне показалось удобным.
Walker: Всё-таки прокрутка средней кнопкой мыши мне крайне не понравилась... нужно предоставить пользователю возможность выбора.
Вынес включение отключение панорамирования в опции компонента. Теперь можно его просто отключить. Так же вынес туда же отключение горизонтального панорамирования, т.к. мне оно кажется не очень нужным, но не реализовать его было бы не правильно. Добавил еще одну возможность: панорамирование теперь можно включить не только нажатием средней кнопки (которая у меня, например, занята под паузу в WinAmp’е, да и двигать мышь, держа колесо нажатым, мне не очень удобно), но и следующей последовательностью: нажать и удерживать правую клавишу мыши, нажать и отпустить левую. Для выхода их режима панорамирования – просто отпустить правую клавишу. Звучит сложно, но на деле достаточно удобно. Еще одна опция, касающаяся панорамирования – реверс вертикального направления, т.е. текст поползет вверх, если потянуть мышь вниз (по аналогии с колесом – чтобы текст пополз вверх, нужно колесо прокрутить вниз).
Walker: У меня при скроллинге — мигающий курсор залезает на Gutter и на Scrollbars.
Исправление указанно самим автором – я только внес его в код.
Walker: Кстати, последние как-то неуклюже смотрятся, всё-таки привычнее когда в углу, на пересечении скроллбаров — пустой квадратик...
Ответ на этот комментарий смотрите в разделе "Что добавлено".
Walker: Заметил багу, привожу конкретный текст … последняя строка не подсвечивается как комментарий.
Исправлено. Причина простая – не будем заострять внимание.
Walker: Для однострочных комментариев сделана подсветка с начала комментария (с символов //) до последнего символа на этой строке, как и в Делфи. А для меня логичнее подсвечивать до конца строки, т.е. не до последнего символа, а до самого края редактора.
Это дело вкуса, конечно, но для этого и нужны опции. Включается там. Правда, есть одна особенность, если в многострочном комментарии есть пустая строка – она не подкрасится.
Walker: Для пропорциональных шрифтов – нужно что-то делать с позицией курсора: если у меня на одной строке много пробелов, а на другой — "широкие символы" (Ш, W), то курсор начинает скакать влево-вправо.
Исправлено. Причем исправлено функциями уже заложенными в компонент автором, что еще раз говорит о высоком качестве компонента. Есть одна особенность: если курсор нельзя поставить ровно в ту же точку по горизонтали, то он ставится перед символом левее. В итоге, если "пробежать" через большой текст, курсор в итоге будет в нулевой позиции. Этот эффект не наблюдается, при использовании шрифта с фиксированной шириной символов.
Как сам же и писал – исправил неверную работу с распознавание HEX чисел.
Методом "убитого вечера" исправлена ошибка, приводящая к "Runtime error 204" после некоторого времени работы программы (использовался несозданный объект).
Думаю, что будет честно сделать этот раздел, т.к. есть действительные недочеты, которые я не исправил.
Алексей Кузнецов: можно ли с помощью вашего компонента отображать текст с несколькими шрифтами одновременно? А еще бы мне бы очень пригодилась поддержка верхних и нижних индексов.
Автором в обсуждении статьи указано, почему это не сделано. Как вариант решения, могу предложить добавить стили subscript и superscript, дать им свои токены, и перерисовывать специальным образом, но при той же толщине символа. Это частично решит проблему, если вам нужно только поставить x2 (т.е. x в квадрате), но длинная подстрочная или надстрочная запись будет выглядеть не очень красиво – между символами будут ощутимые расстояния. В конечном итоге, как и всегда, все зависит от требований.
AK: А нельзя ли в редакторе кода отображать блоки операторов (ограниченные операторными скобками) вертикальными линиями?
Это и другие вопросы выделения блоков я не рассматривал.
Walker: Возвращаясь к гуттеру. Неплохо добавить обработчик OnPaint, чтобы можно было реализовать прорисовку таких вещей: номер строки, иконку брек-поинта и проч. (пока рисуются только закладки).
Walker: Можно добавить пользовательский обработчик OnPaintLine (или OnDrawRow).
Обработчики так и не сделал, но некоторые из поставленных задач решил.
Walker: Секции, а точнее иконки для управления секциями (+ и -) рисуются на гуттере, а если вспомнить любой xml-редактор, то эти кнопки могут находится в любом месте (прям в тексте).
Это действительно скорее применимо в html/xml редакторам. Согласен, что компонент мог бы быть более универсальным, но переделывать не стал – боюсь нарушить стройность компонента (потому как все-таки полной картины компонента еще не имею).
Walker: По поводу схем (или цветовых шаблонов). Есть такой плагин для Far'а, называется Colorer.
Не исправлено, т.к. не очень понятно, что именно исправлять. Нужно более подробное описание работы.
Walker: Когда я жму Ctrl и навожу указатель мыши на слова: {$DEFINE SYNDEBUG}, то в рамку обводятся слова вместе с символами.
Связано с реализацией парсера, считающего пробел символом деления. Можно научить его отбрасывать не alphanumeric символы по краям, но это вызовет другие особенности работы. Переделывать не стал.
Walker: По поводу выделения. В Делфи, если я начинаю выделять с "пустого места", то само выделение рисуется от начала (или с конца) первого символа.
Действительно – вопрос вкуса (как и сказал сам Walker).
Walker: когда курсор находится где-нибудь в тексте, а над курсором есть свёрнутые блоки (секции), то при нажатии Ctrl+вверх, курсор перескакивет в начало файла.
Не проверял.
neutral: Возможна ли переработка этого компонента для поддержки Unicode?
Задача не простая. Не брался.
Расписывать тут особенно нечего, поэтому перейдем к самому интересному.
Вот тут нужно собраться с мыслями и не упустить нечего, а то добавления будут бесполезными, если о них никто не узнает. =)
Во-первых, слегка переделан внешний вид редактора. Между наползающими скроллами вставлен квадратик, так что теперь внешний вид более привычен. Если быть до конца честным, то квадратик сделан из TPanel. Хорошо это или плохо – я не могу сказать, но это дает возможность программисту вставить туда какой-нибудь элемент управления. Имя объекта – fNavButton, т.к. изначально хотелось вставить туда кнопку, но потом отказался от этой идеи. А так, если кому-нибудь для чего-нибудь пригодится – буду рад.
Во-вторых, в компонент добавлено два новых элемента. Это прокрутка страниц (аналог PageUp/PageDown), располагающаяся под вертикальным скроллом, и поле отображения текущей позиции курсора, располагающееся слева от горизонтального скролла. Оба этих элемента могут быть отключены в свойствах, если они кажутся вам излишними (по умолчанию я их включил).
Все изменения во внешнем виде можно посмотреть ниже:
Внешний вид компонента обновлен – добавлены новые элементы управления
От внешнего перейдем к внутреннему.
Сразу хочется рассказать о том, чем я занимался дольше всего: о брейкпоинтах.
Пользователь может ставить брейкпоинты щелчком по левой части гуттера. Перед этим вызывается событие OnBeforeBreakPointChanged (ну да, звучит кривовато), в котором программист может отследить, куда ставится брейкпоинт и разрешить или запретить его установку.
Брейкпоинты могут быть трех типов:
bkPosible — об этом чуть ниже.
bkEnabled — обычный брейкпоинт.
bkDisabled — "нерабочий" (неактивный) брейкпоинт.
Брейкпоинты могут работать в двух режимах:
свободном – когда пользователь, щелкая на гутере, может поставить либо убрать брейкпоинт в любой строке (будет тип bkEnabled);
"не свободном" (я вынес это в опции компонента и назвал NeedPosibility) – в этом режиме, программист должен по определенному (ему одному известному) алгоритму расставить брейкпоинты с типом bkPosible – при этом на гуттере появятся синие точки (опять же как в Delphi). Щелкая на эти точки, пользователь может снять/поставить брейкпоинт (опять же bpEnabled), но в строках, где точек нет (например, между функциями в коде брейкпоинт будет бессмыслен), поставить брейкпоинт не получится.
Тип bkDisabled отдан на откуп программисту. Автоматически он нигде не ставится...
Если пользователь щелкает на место в коде, где брейкпоинт может быть, но прерывания там быть не может (например, он отброшен при оптимизации компилятором), то необходимо поставить брейкпоинт типа bkDisabled. Для этого нужно в событии OnBeforeBreakPointChanged, запретить установку брейкпоинта, и поставить его вручную в соответствующей строке.
Брейкпоинты имеют следующие свойства (которые никак не используются самим компонентом, но введены для использования программистом): Condition, PassCount, Group, Comment. Первый три свойства были взяты из Delphi 7 (не "Advanced" свойства брейкпоинтов).
К редактору теперь можно подключить выпадающее меню (TPopupMenu), для работы с брейкпоинтами, которое будет появляться при щелчке на их область.
Для более тонкой настройки есть событие OnBeforeBreakPointPopup, в котором можно как просто настроить меню под конкретный брейкпоинт, так и вообще запретить его выпадение. Для определения, на какой строке был щелчок, используйте RowOfCurrentBP – в независимости от того, есть в этой строке брейкпоинт или нет – в событии указывает на строку.
Вот и все про брейкпоинты. Подробнее о том, как с ними работать, читайте в комментариях в тексте примера – там все особенности очень подробно описаны, чтобы не загромождать статью излишними подробностями.
Далее тема стоящая рядом – специальная строка, указывающая место выполнение программы. Работа с ней очень проста: номер строки, которую необходимо подсветить запишите в StepDebugLine. Если подсветка в данный момент не нужна: StepDebugLine:=0.
Цвета линий брейкпоинтов, неактивных брейкпоинтов, и строки дебага можно менять в редакторе свойств.
К компоненту добавлена специальная опция smoSolidSpecialLine – если она включена, внешний вид линий, подобен линиям в Delphi; если выключена – соответствующие строки обводятся в цветные рамки, что позволяет просматривать оригинальную подсветку синтаксиса.
Так как, для брейкпоинтов используется красная линия или рамка, то цвет авторской "красной рамки" по умолчанию был заменен на рыжий (да простит меня автор), но его при желании тоже можно поменять в свойствах.
Что получилось – можно посмотреть ниже:
Пример работы брейкпоинтов и эмуляция отладки
Пример прозрачности линий и заливки комментариев до конца строки
В компонент добавлены новые токены.
Авторский зарезервированный токен (22й) оставлен зарезервированным. Далее следуют:
23 – строчная директива компилятора (создана по аналогии со строчным комментарием, поэтому проблем с использованием быть не должно). Сделано для реализации поддержки C, где директивы обозначаются символом # в начале строки.
24, 25 – многострочная директива компилятора (по аналогии с многострочным комментарием) для более полной реализации синтаксиса Pascal’я. Указываем в качестве стартовой комбинации символов "{$" и получаем директиву, которая не будет внешне похожа на комментарий.
26, 27 – символ (по аналогии со строкой). Опять же добавлен для универсальности компонента. Теперь можно более полно описать синтаксис C, где понятия "строка" и "символ" различаются.
Настройки внешнего вида этих токенов аналогичны другим специальным токенам (смотри пример).
Все эти изменения можно посмотреть на следующем скриншоте, где (для наглядности) смешан синтаксис C и Pascal.
Пример подсветки символа, строки, сточной директивы компилятора и многострочной директивы компилятора, которая отличается от операторных скобок в C (соответственно, в Pascal’е будет отличаться от комментария)
Совершенно новыми являются токены 28 и 29 – tokErroneous и tokErroneous2, соответственно. Они наследуют стиль от простого текста (tokText) и эта настройка не может быть изменена внешне (т.к. менять ее смысла не имеет). Задача этих токенов – подсветить неправильно написанный синтаксис (можно вспомнить MS Word, где неправильно написанные слова подчеркиваются волнистой линией), так первый токен подчеркнет красной, а второй – зеленой линиями. Так, например, в том же C синтаксис критичен к регистру символов, и можно сделать подчеркивание неправильно написанных ключевых слов красной линией (код в примере).
Если сделать полный разбор исходного кода, редактируемого в этом компоненте, налету, и выявить описанные символы (процедуры, переменные и т.д.), то, опять же налету, можно подчеркивать неправильные (неопределенные ранее) символы, или символы, не попадающие в данную область видимости красной линией. Зеленой линией можно было бы подчеркнуть те места, которые, обычно, при компиляции попадают в категорию Warning или Hint: переменная не инициализирована, присвоенное значение нигде не используется и т.п.
Пример работы подчеркивания можно посмотреть ниже:
Слово Continue подчеркнуто как неверное (C различает строчные и заглавные буквы), а goto — как нерекомендуемое для использования
Добавлено несколько небольших (по описанию, но не всегда по реализации) возможностей:
- группировка undo (включается в опциях) – даже если пользователь уснул на клавиатуре, при включенной группировке undo – весть текст вернется одним нажатием на Ctrl+Z.
- в дополнение к описаным в разделе "Что исправлено" клавишам управления добавлены следующие: копирование и вставка с помощью Ctrl+Ins Shift+Ins, "прыгающий" Home, табулированный Enter.
P.S. Из не относящихся напрямую к компоненту изменений – добавлена регистрация компонента в палитре, в разделе Samples. Т.к. большое количество настроек компонента удобнее выполнить сразу, на этапе проектирования, то для этого у компонента многие свойства и события вынесены в раздел Published, перечислять не буду – просто посмотрите внимательно в Object Inspector’е.
Вот и все. Всем приятного использования. С удовольствием выслушаю конструктивную критику.
Специально для Королевства Дельфи.
К материалу прилагаются файлы:
[Подсвеченный синтаксис]
Обсуждение материала [ 16-08-2009 09:01 ] 28 сообщений |