Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Полигон
  
 

Фильтр по датам

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  15:49[Войти] | [Зарегистрироваться]

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

Максим Парфентьев
дата публикации 23-08-2005 05:04

Доброе время суток, коллеги.

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

Сразу попытаюсь взять быка за рога. Среди читателей найдется немало таких, кто с самого начала попытается заявить о том, что существует, наконец, 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». Вот и все. Всех благ.

    С уважением,
    // Парфентьев Максим aka Max Proof.
    * Имеется в виду динамический синтаксис - с изменяющимся списком ключевых слов - например, Forth
    [Вернуться]
    ** Forth машина мною написана в Delphi в виде DLL (~120кБ) и активно используется уже в двух проектах. Если это кому-нибудь интересно, готов предоставить её описание и исходники в подобном же виде.
    [Вернуться]


    К материалу прилагаются файлы:


    Смотрите также материалы по темам:
    [Подсвеченный синтаксис]

     Обсуждение материала [ 14-10-2018 03:32 ] 42 сообщения
      
    Время на сайте: GMT минус 5 часов

    Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
    Функция может не работать в некоторых версиях броузеров.

    Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
    Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

     
    © При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
    Все используемые на сайте торговые марки являются собственностью их производителей.

    Яндекс цитирования