Russian version
English version
ОБ АЛЬЯНСЕ | НАШИ УСЛУГИ | КАТАЛОГ РЕШЕНИЙ | ИНФОРМАЦИОННЫЙ ЦЕНТР | СТАНЬТЕ СПОНСОРАМИ SILICON TAIGA | ISDEF | КНИГИ И CD | ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ | УПРАВЛЕНИЕ КАЧЕСТВОМ | РОССИЙСКИЕ ТЕХНОЛОГИИ | НАНОТЕХНОЛОГИИ | ЮРИДИЧЕСКАЯ ПОДДЕРЖКА | АНАЛИТИКА | КАРТА САЙТА | КОНТАКТЫ
 
Программное обеспечение
 
Для зарегистрированных пользователей
 
РАССЫЛКИ НОВОСТЕЙ
IT-Новости
Новости компаний
Российские технологии
Новости ВПК
Нанотехнологии
 
Поиск по статьям
 
RSS-лента
Подписаться
Средства разработки

Компоненты для подсветки синтаксиса. Новый взгляд.

Если Вы читаете этот текст, полагаю, Вам либо интересен сам предмет разговора, либо (как впрочем, и мне) просто любопытно знакомство с новым кодом, представленным на суд Королевства. Что же. Постараюсь заинтересовать и тех и других. Поехали.

Сразу попытаюсь взять быка за рога. Среди читателей найдется немало таких, кто с самого начала попытается заявить о том, что существует, наконец, SynEdit от http://synedit.sourceforge.net/, который поддерживает любой существующий на сегодняшний день синтаксис (кроме *) и позволяет описать любой необходимый, используя некоторые правила и/или шаблоны. Или, например, подобный компонент можно посмотреть на http://www.delphikingdom.ru/asp/viewitem.asp?catalogid=923. Как говорится, зачем изобретать велосипед? Все это так. Кроме нескольких нюансов.

Во-первых, это просто интересно.
Во-вторых, SynEdit позволяет описывать новый синтаксис и применять имеющийся шаблон только в Design Time, т.е. на стадии компиляции. По существу, он генерирует исходный код Вашего парсера, но этот код является статичным, запрограммированным раз и навсегда на этапе разработки.

SynEdit не поддерживает пропорциональные шрифты (я его понимаю :-)). Но поддержка любых шрифтов - это одно из основных начальных требований к моему компоненту. Разработку TMPSyntaxMemo я начинал тогда, когда мне много времени приходилось работать с монитором низкого качества, на котором шрифт Courier в любой его ипостаси выглядел глазопедом-убийцей. На его фоне Arial в MS Visual Studio.Net (Basic.Net - для наладонника, каюсь) был как глоток воздуха. Это был, если хотите, тот вызов, на который я не откликнуться просто не мог;

Секции . Кто работал в MS VS или в Delphi 8, знает, что это такое. Это супер, когда любую семантически законченную часть кода можно просто свернуть и не наматывать километры на мышином колесе, пытаясь перейти от одной процедуры к другой. Вы часто пользуетесь Code Explorer в Delphi? Если бы он не сворачивался постоянно даже при незначительных изменениях кода и переключениями между закладками, может, от него было бы больше пользы. У меня лично нервов не хватает. Да, да, я помню, закладки, конечно. Особенно, когда о них вспоминаешь вовремя.


Рис.1 Общий вид тестовой программы. Нажата клавиша Ctrl.

Итак, компонент TMPSyntaxMemo . Сначала перечислю основные его отличия от известных мне, существующих на сегодняшний день, аналогов. По порядку (не путать со степенью важности - для каждого она своя - есть смысл проглядеть список до конца).

  • Поддерживает 256 типов слов (токенов). Не слов, а именно, типов слов. Для примера - слова Delphi procedure, function, for, end и т.п. являются ключевыми словами языка и принадлежат одному типу. Таких типов можно описать 256 минус несколько зарезервированных, например 0 - пробел, 1 - простой текст, 2 - строковые литералы, 4 - шестнадцатеричная запись числа, 7 - однострочные комментарии и т.д. до 23 (резерв) включительно (см. UnitSyntaxMemo.pas - класс TMPSyntaxAttributes, тип TToken). Остальные 232 - в Вашем распоряжении. Неплохая мысль - начинать отсчет собственных токенов вниз от 255.
  • Для каждого токена задается полный список визуальных атрибутов - цвет шрифта, тип шрифта (TFontStyles), цвет фона. Причем набор может меняться динамически. Вы хотите, чтобы слова с Token = 172, дважды в секунду меняли свой цвет с clBlue на clNavy - нет проблем. Бросаем таймер на форму, устанавливает период в 500ms, в прерывании от таймера пишем что-то типа
     const TmrColors: array [Boolean] of TColor = (clBlue, clNavy);
    MySynMemo.SyntaxAttributes.FontColor[ 172 ] := TmrColors[fTmrTimes];
    MySynMemo.Invalidate;
    fTmrTimes := not fTmrTimes;
  • И атрибуты токенов и сам тип токена для каждого слова задаются динамически. Никаких списков в Design Time. Все делается через прерывание
     type TUserTokenEvent =
     procedure (Sender: TObject; Word: string ; Pos, Line: Integer;
     var Token: TToken) of object ;
     property OnParseWord: TUserTokenEvent;
    
    Где:
    • Sender - понятно, сам TMPSyntaxMemo;
    • Word - слово, тип которого требуется определить;
    • Pos, Line - позиция начала слова в тексте (номер символа в строке и номер строки текста);
    • Token - возвращаемый объект - токен слова.

    Это значительно расширяет возможности компонента. Изначально предполагалось его использовать для редактирования некоторого скрипта с синтаксисом, базирующемся на синтаксисе Forth**. Кто знает - поймет - это незабываемо. Одной из особенностей языка Forth является то, что его словарный запас постоянно растет (в том числе и ключевые слова (!)) при загрузке и компиляции программ. Это, пожалуй, единственный язык программирования на свете, который позволяет описывать новые конструкции языка (например, новый тип цикла) средствами самого языка. Таким образом, необходимо динамически, в RunTime пополнять словарь синтаксического редактора. Понятно, что вышеупомянутый SynEdit здесь уже не поможет. В данном случае, у нас развязаны руки.

    Самый простой способ обслуживания прерывания TUserTokenEvent есть в примере (FormMain.OnCreate) - создаем TStringList, задаем его Sorted := True и заполняем его ключевыми словами. В процедуре определения типа слова выполняем что-либо подобное

     if fDelphiKeyWords.Find(LowerCase(Word), n) then Token := 231 ;
    

    Если Вы не забыли предварительно настроить визуальные атрибуты токена с типом 231, Вы непременно получите требуемый результат. Зуб даю :-).

  • Непосредственный (мгновенный - не требует вычислений) доступ к каждому слову текста. Для того чтобы определить, внутри какого слова находится в данное время курсор, можно, например, воспользоваться функциями
     function GetWordAtPos( const X, Y: Integer; var WordIndex, Row: Integer): Boolean;
     function CharPosToWordIndex( const Col, Row: Integer): Integer;
    

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

  • Равная поддержка как моноширинных (Courier), так и пропорциональных шрифтов. Более того, я намеренно не оптимизировал ни одной функции под возможное использование моноширинных шрифтов, дабы не поддаваться соблазну. Кроме того, в отличие от MSVS, для пропорциональных шрифтов в атрибутах синтаксиса можно задавать жирный шрифт (Bold).
  • Отсутствие какого-либо мелькания при вводе и правке текста. Компонент является наследником TCustomControl, который позволяет установить DubleBuffered := True. Между тем, мелькание отсутствует даже без этой установки благодаря особенностям механизму перерисовки строк.
  • Работает многоуровневый откат (количество уровней регулируется)
  • Есть функция панорамирование при нажатой средней клавиши мыши. Единственный нюанс состоит в том, что для меня не очень удобно то, как эта возможность реализована в MSIE и его наследниках. Мне больше импонирует брутальный ACAD-овский способ - нажимаешь колесо мыши и тащишь вид куда надо. Как раз этот способ панорамирования и реализован в компоненте.
  • Автоматическая поддержка строковых литералов (возможна настройка обрамления - например, в Delphi строка ограничена апострофом, в Basic - кавычками), однострочных комментариев (также возможна настройка - в Delphi //… ), многострочных взаимо-вложенных комментариев (в Delphi, соответственно, {..} и (*..*) ). Причем, обработка таких конструкций ведется целиком от начального символа до последнего. Это позволяет избежать белых пятен между словами внутри комментариев в случаях, когда для комментариев определен цвет фона (см. рисунок - зеленый фон).
  • Поддержка секций. Секции текста могут создаваться, объединяться, разбиваться, разрушаться и т.п. как извне, через использования методов и свойств менеджера секций TMPSynMemoSections, так и вручную, пользователем, используя некоторые сочетания клавиш. Сложно это все кратко сейчас описать - пример с этим справится лучше. Программа - пример использования компонента TMPSyntaxMemo позволяет открыть любой файл с расширением *.pas, производит его синтаксический анализ, создает набор секций (см. рис.1) и активизирует некоторый аналог Code Explorer в левой части. По умолчанию, все процедуры, функции и классы свернуты. При выборе требуемой процедуры из списка, производится скроллинг текста к необходимой строке, и выбранная процедура отображается в развернутом виде. Я думаю, Вы поняли, что сворачивать или разворачивать секции можно, нажимая на квадратики с плюсиками и минусиками. Также необходимо заметить, что копирование в буфер обмена и вставка участков текста производится с сохранением секций, а также возможна запись текста в файл с сохранением границ и состояния секций. Отображение границ и состояния секций возможен двумя способами - как в TTreeInfo (рис.1) или в стиле MS VS (для экономии места - в один столбец).

    Ниже приведен список комбинаций клавиш для работы с секциями текста
    Ctrl + [+] Развернуть секцию
    Ctrl + Shift + [+] Развернуть секцию и все вложенные
    Ctrl + [-] Свернуть секцию
    Ctrl + Shift + [-] Свернуть все вложенные секции
    F5 Создать секцию из выделенных строк
    Ctrl + F5 Создать секцию вокруг выделения и свернуть ее
    Ctrl + Shift + F5 Отображать границы секций
    F6 Разбить секцию
    Ctrl + F6 Разбить секцию и все вложенные
    Ctrl + Shift + F6 Скрывать и заблокировать границы секций

  • Я помню о закладках. Я их описал. Все 10 штук (с 0 по 9). Поклонникам закладок посвящается…
  • Прерывание по нажатию Ctrl + указатель мыши на слово. Отличие от Delphi в том, что у меня слово не подчеркивается как гиперссылка, а обрамляется в красный квадратик. О вкусах не спорят - мне так больше нравится. Вы можете в этом случае обработать прерывание
    TWordInfoEvent = procedure (Sender: TMPCustomSyntaxMemo;
     const X, Y, WordIndex, Row: Integer;
    			 Showing: Boolean) of object ;
    
    и получить сведения о выбранном слове примерно так (см. пример)
     with Memo.Lines.Parser[Row].Tokens[WordIndex] do begin 
     LabelWordInfo.Caption := Copy(Memo.Lines[Row], stStart + 1 , stLength);
     MemoWInfo.Lines.Clear;
     MemoWInfo.Lines.Append( 'Start at ' + IntToStr(stStart));
     MemoWInfo.Lines.Append( 'Length is ' + IntToStr(stLength));
     MemoWInfo.Lines.Append( 'Token is #' + IntToStr(stToken));
     end ;
    
  • Я предлагаю Вам попробовать использовать этот компонент в своих проектах. Вы можете вносить любые изменения в исходный код, использовать его частично или полностью. Единственное требование - ссылка на автора в коммерческих продуктах (). Исходный код был создан в D7. Не думаю, что возникнут какие-либо сложности при открытии его в более ранние версии Delphi. Код снабжен умеренным количеством комментариев, но, по крайней мере, для всех процедур указано их предназначение и некоторые особенности. К сожалению, справку я так и не написал. Оправданием мне может служить то, что изначально компонент должен был интегрироваться в некоторую систему, где справка по внутреннему коду не нужна в принципе. Обсуждение компонента приветствуется. Критика принимается только аргументированная. Вопросы, за исключением "зачем этот надо?" можно задавать напрямую с пометкой "TMPSyntaxMemo". Вот и все. Всех благ.


      Рекомендовать страницу   Обсудить материал  [6] Написать редактору  
      Распечатать страницу
     
      Дата публикации: 25.04.2006  

    ОБ АЛЬЯНСЕ | НАШИ УСЛУГИ | КАТАЛОГ РЕШЕНИЙ | ИНФОРМАЦИОННЫЙ ЦЕНТР | СТАНЬТЕ СПОНСОРАМИ SILICON TAIGA | ISDEF | КНИГИ И CD | ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ | УПРАВЛЕНИЕ КАЧЕСТВОМ | РОССИЙСКИЕ ТЕХНОЛОГИИ | НАНОТЕХНОЛОГИИ | ЮРИДИЧЕСКАЯ ПОДДЕРЖКА | АНАЛИТИКА | КАРТА САЙТА | КОНТАКТЫ

    Дизайн и поддержка: Silicon Taiga   Обратиться по техническим вопросам  
    Rambler's Top100 Rambler's Top100