Difference between revisions of "SynEdit/ru"

From Free Pascal wiki
Jump to navigationJump to search
 
(27 intermediate revisions by 5 users not shown)
Line 1: Line 1:
 
{{SynEdit}}
 
{{SynEdit}}
  
= Общее =
+
'''SynEdit''' - пакет [[Syntax highlighting|подсветки синтаксиса]] для edit/memo, доступный на [[SynEdit_tab/ru|вкладке SynEdit]] с поддержкой многих языков/синтаксиса.
  
SynEdit находящийся в Lazarus основан на SynEdit 1.0.3 [[http://synedit.sourceforge.net/]], и была адаптированна и расширенна довольно сильно. Например поддержка UTF-8 и свертывание кода были добавлены.
+
SynEdit, содержащийся в Lazarus, был ответветлен от [https://sourceforge.net/projects/synedit/files/ SynEdit 1.0.3], адаптирован и довольно сильно расширен. Изменения перечислены ниже.
  
Пакет содержащий исходник компонента называется TSynEdit, некоторые syntax highlighters и другие компоненты используют исходник редактора.
+
Пакет Lazarus содержит компонент редактора исходного кода под названием [TSynEdit/ru|[TSynEdit]], несколько подсветок синтаксиса и другие компоненты, используемые для редактирования исходного кода.
  
Он лицензирован под теми же условиями что и оригинал SynEdit (MPL или GPL)
+
Лицензирован на тех же условиях, что и исходный SynEdit (MPL или GPL).
  
= Synedit 2.0.5 port  =
+
__TOC__
 +
== Оригинальная версия против версии Lazarus ==
 +
Версия Lazarus поддерживается в основном [[[User:Martin|Martin Friebe]]. Martin написал на форуме, что было добавлено в версию Lazarus с момента появления fork:
  
Альтернативный порт текущей версии оригинального SynEdit :
+
Большие изменения, добавленные в версию Lazarus:
 +
* сворачивание блоков кода
 +
* настраиваемые боковое поле / части бокового поля
 +
* общий текст между несколькими редакторами
 +
* поддержка utf-8
 +
* плагин для синхронизации
 +
* базовая поддержка RTL/LTR
 +
* настройка мыши через MouseActions
 +
* переписаны различные модули подсветки/разметки
  
http://wiki.lazarus.freepascal.org/SynEdit/port
+
Кодовые базы версий Delphi / Lazarus были независимо переработаны. Осталось очень мало совпадений.
  
code:
+
== SynEdit 2.0 port  ==
https://github.com/rnapoles/
+
Существует альтернативный порт оригинальной версии SynEdit версии 2.0.x.
 +
Активность не поддерживается, последний коммит (сейчас июнь 2014) был в 2011 году.
 +
* [[SynEdit/port]]
 +
* [https://github.com/rnapoles/ Github page]
  
= SynEdit в IDE =
+
== SynEdit в IDE ==
 +
SynEdit в Lazarus - это встроенный пакет, потому что среда IDE использует его сама. Поэтому пакет не может быть удален из списка установки. Чтобы удалить записи из палитры компонентов, пакет SynEditDsgn можно удалить из установки.
  
SynEdit в Лазарус является встроенным пакетом, потому что его использует IDE. Вот почему нет файлов .lpk.
+
== Использование SynEdit==
Компонент может быть найден в палитре компонентов на странице 'SynEdit'.
+
=== Подсветка ===
 +
* Есть несколько стандартных маркеров подсветки (см. [[SynEdit_tab/ru|вкладку SynEdit]] в [[Component_Palette/ru|палитре компонентов]])
 +
* Существуют сценарии подсветки, которые можно адаптировать ко многим другим форматам файлов:
 +
** [[TSynAnySyn]] (стандартный, на палитре компонентов по умолчанию)
 +
** [[TSynPositionHighlighter]] (стандартный, '''не''' на палитре компонентов)
 +
** [[TSynUniHighlighter]] (стандартный, '''не''' на палитре компонентов)
 +
** SynFacilSyn ([https://github.com/t-edson/SynFacilSyn Github])
 +
* Существуют и другие сторонние маркеры подсветки: SynCacheSyn, SynGeneralSyn, SynRCSyn, SynRubySyn, SynSDDSyn, SynSMLSyn, SynSTSyn, SynTclTkSyn, SynUnrealSyn, SynURISyn, SynVBScriptSyn, SynVrml97Syn, [http://bugs.freepascal.org/view.php?id=18248 см. здесь].
 +
* Вы можете написать новый маркер подсветки, см. информацию в [[SynEdit Highlighter]].
  
= Использование SynEdit=
+
=== Изменение существующего маркера подсветки ===
 +
Иногда у вас может появиться желание отредактировать существующие маркеры подсветки (как этого хотел я несколько дней назад), которые уже существуют.
 +
В этом примере мы собираемся отредактировать маркер подсветку для паскаль-подобного кода (classname: [[TSynPasSyn]]; package: SynEdit V1.0; unit: SynHighlighterPas.pas).
  
== Подсветка ==
+
Скажем, мы хотим достичь того, чтобы наше приложение (в данном случае Lazarus) различало три типа комментариев, которые существуют в Pascal:
 +
<syntaxhighlight lang=pascal>
 +
  (* ansi *)
 +
  { bor }
 +
  // Slash
 +
</syntaxhighlight>
 +
Это может быть полезно, если вы хотите различать различные типы ваших комментариев (например, "Description", "Note", "Reference" и т.д.) и хотите, чтобы они были, например, окрашивались по-разному.
  
* Используйте существующую подсветку или загрузите другую из [http://bugs.freepascal.org/view.php?id=18248 Подсветка для SynEdit]
+
{{Note|На случай, если вы что-то сломаете, я предлагаю сделать несколько "NEW" и "/NEW"-комментариев, но вам не нужно}}
* Используйте настраеваимую подсветку (SynAnySyn или SynPositionSyn) (Смотрите прмеры того как использовать)
+
*  Сначала откройте модуль "SynHighlighterPas", который должен находиться в вашем SynEdit-каталоге.
* Напишите свою собственную [[SynEdit_Highlighter]]
+
*  Поскольку мы не хотим создавать несовместимости, мы создаем новый перечислимый тип, который поможет нам позже идентифицировать наш комментарий:
 +
Напр., под объявлением "tkTokenKind" напишите это:
 +
<syntaxhighlight lang=pascal>
 +
  {NEW}
 +
  TtckCommentKind = (tckAnsi, tckBor, tckSlash);
 +
  {/NEW}
 +
</syntaxhighlight>
 +
*  В объявлении "TSynPasSyn" найдите "FTokenID" и добавьте следующее между "FTokenID" и следующим полем
 +
<syntaxhighlight lang=pascal>
 +
  {NEW}
 +
  FCommentID: TtckCommentKind;
 +
  {/NEW}
 +
  //Это создает новое поле, где мы можем хранить информацию, какой у нас комментарий
 +
</syntaxhighlight>
 +
*  В объявлении "TSynPasSyn" найдите "fCommentAttri" и добавьте следующее между "fCommentAttri" и следующим полем
 +
<syntaxhighlight lang=pascal>
 +
  {NEW}
 +
  fCommentAttri_Ansi: TSynHighlighterAttributes;
 +
  fCommentAttri_Bor: TSynHighlighterAttributes;
 +
  fCommentAttri_Slash: TSynHighlighterAttributes;
 +
  {/NEW}
 +
  //Это позволяет нам возвращать различные атрибуты для каждого типа комментариев.
 +
</syntaxhighlight>
 +
*  Затем найдите определение конструктора "TSynPasSyn", которое должно быть "constructor TSynPasSyn.Create(AOwner: TComponent);"
 +
*  Нам нужно создать наши новые атрибуты, таким образом, мы добавляем наши атрибуты где-нибудь в конструкторе (я предлагаю после значения по умолчанию "fCommentAttri")
 +
<syntaxhighlight lang=pascal>
 +
  (...)
 +
  AddAttribute(fCommentAttri);
 +
  {NEW}
 +
  fCommentAttri_Ansi := TSynHighlighterAttributes.Create(SYNS_AttrComment+'_Ansi', SYNS_XML_AttrComment+'_Ansi'); // Последние две строки - это заголовок и сохраненное имя
 +
  //Если вы хотите иметь настройки по умолчанию для вашего атрибута, вы можете, например, добавить это:
 +
  //fCommentAttri_Ansi.Background := clBlack; //Установит "Background" в "clBlack" по умолчанию
 +
  AddAttribute(fCommentAttri_Ansi);
 +
  fCommentAttri_Bor := TSynHighlighterAttributes.Create(SYNS_AttrComment+'_Bor', SYNS_XML_AttrComment+'_Bor');
 +
  AddAttribute(fCommentAttri_Bor);
 +
  fCommentAttri_Slash := TSynHighlighterAttributes.Create(SYNS_AttrComment+'_Slash', SYNS_XML_AttrComment+'_Slash');
 +
  AddAttribute(fCommentAttri_Slash);
 +
  {/NEW}
 +
  (...)
 +
</syntaxhighlight>
 +
* «Сложная» часть теперь состоит в том, чтобы найти места в коде, где "FTokenID" установлен в "tkComment", и установить наш «подтип» одинаково (конечно, я их уже поискал :)
 +
<syntaxhighlight lang=pascal>
 +
procedure TSynPasSyn.BorProc;
 +
(...)
 +
  fTokenID := tkComment;
 +
  {NEW}
 +
  FCommentID:=tckBor;
 +
  {/NEW}
 +
  if rsIDEDirective in fRange then
 +
(...)
 +
</syntaxhighlight>
 +
<syntaxhighlight lang=pascal>
 +
procedure TSynPasSyn.AnsiProc;
 +
begin
 +
  fTokenID := tkComment;
 +
  {NEW}
 +
  FCommentID:=tckAnsi;
 +
  {/NEW}
 +
(...)
 +
</syntaxhighlight>
 +
<syntaxhighlight lang=pascal>
 +
procedure TSynPasSyn.RoundOpenProc;
 +
(...)
 +
        fTokenID := tkComment;
 +
        {NEW}
 +
        FCommentID:=tckAnsi;
 +
        {/NEW}
 +
        fStringLen := 2; // length of "(*"
 +
(...)
 +
</syntaxhighlight>
 +
<syntaxhighlight lang=pascal>
 +
procedure TSynPasSyn.SlashProc;
 +
begin
 +
  if fLine[Run+1] = '/' then begin
 +
    fTokenID := tkComment;
 +
    {NEW}
 +
    FCommentID:=tckSlash;
 +
    {/NEW}
 +
    if FAtLineStart then begin
 +
(...)
 +
</syntaxhighlight>
 +
<syntaxhighlight lang=pascal>
 +
procedure TSynPasSyn.SlashContinueProc;
 +
(...)
 +
    fTokenID := tkComment;
 +
    {NEW}
 +
    FCommentID:=tckSlash;
 +
    {/NEW}
 +
    while not(fLine[Run] in [#0, #10, #13]) do
 +
(...)
 +
</syntaxhighlight>
 +
* Теперь нам просто нужно извлечь информацию при вызове "GetTokenAttribute" и вернуть правильный атрибут, поэтому мы отредактируем "GetTokenAttribute" следующим образом:
 +
<syntaxhighlight lang=pascal>
 +
function TSynPasSyn.GetTokenAttribute: TSynHighlighterAttributes;
 +
begin
 +
  case GetTokenID of
 +
    tkAsm: Result := fAsmAttri;
 +
    {OLD
 +
    tkComment: Result := fCommentAttri; //Это закомментировано и на всякий пожарный сохранено, поэтому оно будет игнорироваться
 +
    /OLD}
 +
    {NEW}
 +
    tkComment: begin
 +
      if (FCommentID=tckAnsi) then Result:=fCommentAttri_Ansi //Тип - AnsiComment
 +
      else
 +
      if (FCommentID=tckBor) then Result:=fCommentAttri_Bor //Тип - BorComment
 +
      else
 +
      if (FCommentID=tckSlash) then Result:=fCommentAttri_Slash //Тип - SlashComment
 +
      else
 +
        Result:=fCommentAttri //Если наш код каким-то образом упал, возврат к умолчанию
 +
    end;
 +
    {/NEW}
 +
    tkIDEDirective: begin
 +
(...)
 +
</syntaxhighlight>
 +
 
 +
Если вы используете lazarus, просто переустановите SynEdit-Package, если нет, перекомпилируйте ваш проект/пакет/<аналог>.
 +
 
 +
'''ГОТОВО! Нет, серьезно, теперь вы готовы отличать различные типы комментариев.'''
 +
 
 +
Lazarus-IDE автоматически определяет, какие атрибуты существуют, и показывает их в опциях, например сохраняет их, если вы их изменяете.
 +
Если ваше приложение/IDE не делает этого, вам придется установить Color/Font/ и т.д. новых атрибутов где-то вручную (например, в конструкторе TSynPasSyn)
 +
 
 +
=== Плагины для автозавершения кода ===
 +
Для SynEdit есть 3 подключаемых плагина автозавершения кода:
 +
;[[TSynCompletion]]
 +
* Предлагает список слов в раскрывающемся списке с помощью сочетания клавиш (по умолчанию: {{keypress|Ctrl}}+{{keypress|space}}).
 +
* Используется в IDE для автозавершения идентификатора.
 +
* Включен в примеры.
 +
* Доступен в палитре компонентов (начиная с 0.9.3x).
 +
 
 +
Пример кода, чтобы вызвать автозавершение, всплывает программно (т.е. без нажатия сочетания клавиш):
 +
<syntaxhighlight lang=pascal>
 +
YourSynEdit.CommandProcessor(YourSynCompletion.ExecCommandID, '', nil)
 +
</syntaxhighlight>
 +
 
 +
;[[TSynAutoComplete]]
 +
* Заменяет текущий токен фрагментом текста. '''Не''' интерактивный. '''Не''' выпадающий.
 +
* Включен в примеры.
 +
* Доступен в палитре компонентов.
  
== (Авто-)завершение==
+
;[[TSynEditAutoComplete]]
 +
* Основной шаблон модуля. '''Не''' выпадающий.
 +
* Используется IDE для шаблонов кода. IDE содержит дополнительный код, расширяющий эту функцию (IDE добавляет макросы выпадающего списка и синхронизации).
 +
* '''Не''' включен в примеры.
  
Существует 2 плагина для SynEdit, для завершения:
+
Todo: Различия между 2-м и 3-м должны быть задокументированы. Может быть, они могут быть объединены.
* TSynCompletion (используется в IDE)
 
* TSynAutoComplete
 
  
Примечание: TSynAutoComplete (из палитры компонентов) <b>не</b> имеет выпадающего списка.
+
=== Логическая/Физическая позиция каретки===
  
Смотрите примеры как использовать их обоих.
+
SynEdit предлагает положение каретки (текстовый мигающий курсор) в 2 различных формах:
  
== Изменение текста из кода ==
+
* Физический X/Y: соответствует визуальной (холст) позиции
 +
* Логический X/Y: соответствует байтовому смещению текста
  
Текст может быть получен через SynEdit.Lines. изменение текста через свойство Lines не работает с undo/redo.  
+
Оба основаны на 1 (отсчет начинается с 1). В настоящее время координаты Y всегда одинаковы. Это может измениться в будущем.
  
Используйте TextBetweenPoints и TextBetweenPointsEx для изменения текста, если вы хотите чтобы undo/redo работали.
+
;Физическая координата: это позиция в сетке дисплея (без учета прокрутки). То есть:
 +
: обе буквы "a" и "â" занимают ОДНУ ячейку в сетке, увеличивая физический x на 1. Несмотря на то, что в кодировке utf8 "a" занимает один байт, а "â" занимает несколько байтов.
 +
: однако символ табуляции (#9), помимо одного байта и одного символа, может занимать несколько ячеек в сетке, увеличивая физический x более, чем на одну позицию. Есть также некоторые символы в китайском и восточном языках, которые занимают 2 позиции в сетке (гуглите отличие full-width(полной ширины) от half-width (полуширины) символа).
  
== Закладки ==
+
;Логическая координата: смещение байта в строчке, содержащей строку.
 +
: буква «а» имеет 1 байт и увеличивается на 1
 +
: буква "â" имеет 2 (или 3) байта и увеличивается на эту величину
 +
: символ tab имеет 1 байт и увеличивается на эту величину.
  
Пожалуйста обратитесь к следующей теме на форуме: http://forum.lazarus.freepascal.org/index.php/topic,14948.msg79794.html
+
Ни один из 2 не дает позицию в символах/кодовых точках UTF8 (например, для Utf8Copy или Utf8Length).
  
= Примеры =
+
Физический X всегда отсчитывается слева от текста, даже если он прокручивается.
 +
Чтобы получить grid-x текущего прокручиваемого элемента управления, выполните:
  
<b>Примеры можно найти в каталоге lazarus/examples/synedit</b>
+
: grid-X-in-visible-part-of-synedit := PhysicalX - SynEdit.LeftChar + 1
 +
: grid-y-in-visible-part-of-synedit := SynEdit.RowToScreenRow(PhysicalY); // включает в себя складывание (схлопывание) текста
 +
: используйте ScreenRowToRow для обратного процесса
  
== Как добавить поддержку Copy, Paste, Cut, Undo, Redo и др. ==
+
=== Изменение текста из кода ===
 +
{{Warning | Изменение текста с помощью свойства <i>SynEdit.Lines</i> <u>не работает с undo/redo.</u>}}
  
Эти возможности могут быть реализованны использованием комманд SynEdit.
+
Доступ к тексту можно получить через SynEdit.Lines.
 +
Это свойство на основе TStrings, предлагающее доступ на чтение/запись к каждой строке. Основано на нумерации строк с 0.
 +
<syntaxhighlight lang=pascal>
 +
  SynEdit.Lines[0] := 'Text'; // первая строчка
 +
</syntaxhighlight>
 +
SynEdit.Lines можно использовать для установки начальной версии текста (например, загруженного из файла). Обратите внимание, что методы SynEdit.Lines.Add/SynEdit.Lines.Append не поддерживают разрывы строк внутри добавленных строк. Вы должны добавлять строки одну за другой.
  
<delphi>uses
+
Чтобы изменить содержимое SynEdit и позволить пользователю отменить действие, используйте следующие методы:
   ...
+
<syntaxhighlight lang=pascal>
 +
    procedure InsertTextAtCaret(aText: String; aCaretMode: TSynCaretAdjustMode = scamEnd);
 +
    property TextBetweenPoints[aStartPoint, aEndPoint: TPoint]: String // Логические точки
 +
      read GetTextBetweenPoints write SetTextBetweenPointsSimple;
 +
    property TextBetweenPointsEx[aStartPoint, aEndPoint: TPoint; CaretMode: TSynCaretAdjustMode]: String
 +
      write SetTextBetweenPointsEx;
 +
    procedure SetTextBetweenPoints(aStartPoint, aEndPoint: TPoint;
 +
                                  const AValue: String;
 +
                                  aFlags: TSynEditTextFlags = [];
 +
                                  aCaretMode: TSynCaretAdjustMode = scamIgnore;
 +
                                  aMarksMode: TSynMarksAdjustMode = smaMoveUp;
 +
                                  aSelectionMode: TSynSelectionMode = smNormal );
 +
</syntaxhighlight>
 +
 
 +
Примеры:
 +
<syntaxhighlight lang=pascal>
 +
  // Вставляем текст в позицию каретки
 +
   SynEdit.InsertTextAtCaret('Text');
 +
  // Заменяем текст с (x=2,y=10) на (x=4,y=20) с помощью Str
 +
  SynEdit.TextBetweenPoints[Point(2,10), Point(4,20)] := Str;
 +
</syntaxhighlight>
 +
 
 +
<syntaxhighlight lang=pascal>
 +
  // Удаляем/заменяем один символ в позиции каретки
 +
  var p1, p2: TPoint;
 +
  begin
 +
    p1 := SynEdit.LogicalCaretXY;
 +
    p2 := p1;
 +
    // Высчитываем позицию байта следующего символа
 +
    p2.x := p2.x + UTF8CharacterLength(@SynEdit.LineText[p2.x]);
 +
    // p1 указывает на первый байт заменяемого символа
 +
    // p2 указывает на первый байт символа после последнего заменяемого символа
 +
    // Заменяем на "Text" (или используем пустую строку для удаления)
 +
    SynEdit.TextBetweenPoints[p1, p2] := 'Text';
 +
</syntaxhighlight>
 +
 
 +
=== Сворачивание/Разворачивание текста из кода ===
 +
* Это все еще находится в стадии разработки.
 +
* Это работает, только если текущий маркер подсветки поддерживает сворачивание (подробности в [[SynEdit_Highlighter]]).
 +
* Также обратите внимание, что некоторые маркеры подсветки поддерживают несколько независимых сворачиваемых деревьев. Например, в Паскале у вас есть сворачивание по ключевым словам (<tt>begin</tt>, <tt>end</tt>, <tt>class</tt>, <tt>procedure</tt> и т.д.), которое является основным, и сворачивание по <tt>$ifdef</tt> или <tt>$region</tt>, которое является дополнительным.
 +
* Сворачивание текущего выделения также отличается от сворачивания по ключевым словам.
 +
 
 +
Методы сворачивания:
 +
 
 +
1) TSynEdit.CodeFoldAction
 +
 
 +
Сворачивание в данной строчке. Если их больше одной, сворачивается самая внутренняя (самая правая).
 +
{{Note| Это не работает ни с выделением, ни с блоком сворачиваемого кода, которые уже полностью скрыт/требуется тестирование для дополнительных блоков сворачиваемого кода.}}
 +
 
 +
2) TSynEdit.FindNextUnfoldedLine
 +
 
 +
3) TSynEdit.FoldAll / TSynEdit.UnfoldAll
 +
 
 +
=== Закладки ===
 +
* [http://forum.lazarus.freepascal.org/index.php/topic,14948.msg79794.html Топик форума о добавлении меток и цветных линий]
 +
 
 +
=== Дополнительная информация ===
 +
Обсуждения на форуме, которые содержат информацию о SynEdit:
 +
 
 +
* [http://forum.lazarus.freepascal.org/index.php/topic,19520.msg111158.html#msg111158 Search/replace; caret; position logical/physical]
 +
* [http://forum.lazarus.freepascal.org/index.php/topic,19645.msg111962.html#msg111962 Diff between SynEdit/SynMemo]
 +
* [http://forum.lazarus.freepascal.org/index.php/topic,23997.msg144100.html#msg144100 Mouse actions; disable paste on middle click]
 +
* [http://forum.lazarus.freepascal.org/index.php?topic=24842 TSynEditMarkup info]
 +
 
 +
=== Примеры приложений ===
 +
Примеры приложений можно найти в папке "lazarus/examples/synedit".
 +
 
 +
=== Добавление горячих клавиш для Cut/Copy/Paste/и т.д. ===
 +
Горячие клавиши могут быть реализованы с помощью команд SynEdit.
 +
 
 +
<syntaxhighlight lang=pascal>uses
 
   SynEdit, SynEditKeyCmds;
 
   SynEdit, SynEditKeyCmds;
  
procedure TfrmPrincipal.HandleCodigoKeyDown(Sender: TObject; var Key: Word;
+
procedure TForm1.SynEdit1KeyDown(Sender: TObject; var Key: Word;
 
   Shift: TShiftState);
 
   Shift: TShiftState);
 
begin
 
begin
Line 69: Line 323:
 
   begin
 
   begin
 
     case Key of
 
     case Key of
     VK_C: synCodigo.CommandProcessor(TSynEditorCommand(ecCopy), ' ', nil);
+
     VK_C: SynEdit1.CommandProcessor(TSynEditorCommand(ecCopy), ' ', nil);
     VK_V: synCodigo.CommandProcessor(TSynEditorCommand(ecPaste), ' ', nil);
+
     VK_V: SynEdit1.CommandProcessor(TSynEditorCommand(ecPaste), ' ', nil);
     VK_X: synCodigo.CommandProcessor(TSynEditorCommand(ecCut), ' ', nil);
+
     VK_X: SynEdit1.CommandProcessor(TSynEditorCommand(ecCut), ' ', nil);
 
     end;
 
     end;
 
   end;
 
   end;
end;</delphi>
+
end;
 
+
</syntaxhighlight>
= Дальнейшее развитие, обсуждение =
 
 
 
* RTL (right-to-left): started by Mazen
 
* automatic monospace font selection: At the moment SynEdit starts with a font 'courier'. But it would be better, if SynEdit would start with a monospace font (meaning: every character has the same width). At the moment the LCL TFont does not provide such a property. At the moment the user has to choose the right font.
 
* automatic UTF-8 font selection: Same as above monospace, but also with an UTF-8 font, so that for example umlaute are shown correctly. At the moment the user has to choose the right font.
 
* Dead keys. Most keyboards support typing two or more keys to create one special character (like accented or umlaut characters).
 
* [[Redesign of the SynEdit component]]. The primary goal is more reliable display and navigation in the text. A more modular approach also allows for better integration of extensions, and for specialized controls, for use outside of Lazarus.
 
  
=Смотрите также=
+
== Дальнейшее развитие, обсуждение ==
 +
* RTL (справа налево): начат Mazen'ом (частично реализован в Windows)
 +
* SynEdit использует только UTF8; версия ASCII/ANSI больше не существует. Шрифт предварительно выбирается в зависимости от системы. Пользователь может выбрать другой шрифт, но затем должен позаботиться о выборе моноширинного шрифта.
 +
** автоматический выбор моноширинного шрифта: в данный момент SynEdit запускается со использованием шрифта 'courier'. На данный момент TFont LCL не предоставляет свойство для фильтрации моноширинных шрифтов.
 +
** автоматический выбор шрифта UTF-8: по аналогии с выше описанным моноширинным шрифтом + также со шрифтом UTF-8, так что, например, умлауты отображаются правильно.
 +
* "Немые" клавиши. Большинство клавиатур поддерживают ввод с использованием двух или более клавиш для создания одного специального символа (например, символы ударения или умлаута). (Это обрабатывается LCL widgedset)
 +
* [[Redesign of the SynEdit component|Редизайн компонента SynEdit]]. Основная задача - более надежное отображение и навигация по тексту. Более модульный подход также позволяет лучше интегрировать расширения и специализированные элементы управления для использования вне Lazarus.
 +
* [http://bugs.freepascal.org/view.php?id=30395 Перенос слов]. Это экспериментальная реализация, следующая идее классов TextTrimmer/TabExpansion. У связанной проблемы bugtraker есть класс и объяснение изменений, требуемых в других файлах для его работы.
 +
* Хуки в обработке клавиши/команды SynEdit. На форуме:http://forum.lazarus-ide.org/index.php/topic,35592.msg243316.html#msg243316
  
 +
==См. также==
 
* [[SynEdit Highlighter]]
 
* [[SynEdit Highlighter]]
 +
* [http://www.lazarusforum.de/viewtopic.php?f=5&t=8723 "Изменение существующего маркера подсветки": немецкая ветка форума на "lazarusforum.de"]
 +
* [[ATSynEdit]]
  
[[Category:Components]]
+
{{AutoCategory}}
 +
[[Category:Russian (unfinished translation)]]
 +
[[Category:Lazarus/ru]]
 +
[[Category:Components/ru]]
 +
[[Category:SynEdit/ru]]

Latest revision as of 08:47, 4 September 2019

Deutsch (de) English (en) español (es) français (fr) 日本語 (ja) polski (pl) русский (ru) 中文(中国大陆)‎ (zh_CN)

SynEdit - пакет подсветки синтаксиса для edit/memo, доступный на вкладке SynEdit с поддержкой многих языков/синтаксиса.

SynEdit, содержащийся в Lazarus, был ответветлен от SynEdit 1.0.3, адаптирован и довольно сильно расширен. Изменения перечислены ниже.

Пакет Lazarus содержит компонент редактора исходного кода под названием [TSynEdit/ru|[TSynEdit]], несколько подсветок синтаксиса и другие компоненты, используемые для редактирования исходного кода.

Лицензирован на тех же условиях, что и исходный SynEdit (MPL или GPL).

Оригинальная версия против версии Lazarus

Версия Lazarus поддерживается в основном [[[User:Martin|Martin Friebe]]. Martin написал на форуме, что было добавлено в версию Lazarus с момента появления fork:

Большие изменения, добавленные в версию Lazarus:

  • сворачивание блоков кода
  • настраиваемые боковое поле / части бокового поля
  • общий текст между несколькими редакторами
  • поддержка utf-8
  • плагин для синхронизации
  • базовая поддержка RTL/LTR
  • настройка мыши через MouseActions
  • переписаны различные модули подсветки/разметки

Кодовые базы версий Delphi / Lazarus были независимо переработаны. Осталось очень мало совпадений.

SynEdit 2.0 port

Существует альтернативный порт оригинальной версии SynEdit версии 2.0.x. Активность не поддерживается, последний коммит (сейчас июнь 2014) был в 2011 году.

SynEdit в IDE

SynEdit в Lazarus - это встроенный пакет, потому что среда IDE использует его сама. Поэтому пакет не может быть удален из списка установки. Чтобы удалить записи из палитры компонентов, пакет SynEditDsgn можно удалить из установки.

Использование SynEdit

Подсветка

  • Есть несколько стандартных маркеров подсветки (см. вкладку SynEdit в палитре компонентов)
  • Существуют сценарии подсветки, которые можно адаптировать ко многим другим форматам файлов:
    • TSynAnySyn (стандартный, на палитре компонентов по умолчанию)
    • TSynPositionHighlighter (стандартный, не на палитре компонентов)
    • TSynUniHighlighter (стандартный, не на палитре компонентов)
    • SynFacilSyn (Github)
  • Существуют и другие сторонние маркеры подсветки: SynCacheSyn, SynGeneralSyn, SynRCSyn, SynRubySyn, SynSDDSyn, SynSMLSyn, SynSTSyn, SynTclTkSyn, SynUnrealSyn, SynURISyn, SynVBScriptSyn, SynVrml97Syn, см. здесь.
  • Вы можете написать новый маркер подсветки, см. информацию в SynEdit Highlighter.

Изменение существующего маркера подсветки

Иногда у вас может появиться желание отредактировать существующие маркеры подсветки (как этого хотел я несколько дней назад), которые уже существуют. В этом примере мы собираемся отредактировать маркер подсветку для паскаль-подобного кода (classname: TSynPasSyn; package: SynEdit V1.0; unit: SynHighlighterPas.pas).

Скажем, мы хотим достичь того, чтобы наше приложение (в данном случае Lazarus) различало три типа комментариев, которые существуют в Pascal:

  (* ansi *)
  { bor }
  // Slash

Это может быть полезно, если вы хотите различать различные типы ваших комментариев (например, "Description", "Note", "Reference" и т.д.) и хотите, чтобы они были, например, окрашивались по-разному.

Light bulb  Примечание: На случай, если вы что-то сломаете, я предлагаю сделать несколько "NEW" и "/NEW"-комментариев, но вам не нужно
  • Сначала откройте модуль "SynHighlighterPas", который должен находиться в вашем SynEdit-каталоге.
  • Поскольку мы не хотим создавать несовместимости, мы создаем новый перечислимый тип, который поможет нам позже идентифицировать наш комментарий:

Напр., под объявлением "tkTokenKind" напишите это:

  {NEW}
  TtckCommentKind = (tckAnsi, tckBor, tckSlash);
  {/NEW}
  • В объявлении "TSynPasSyn" найдите "FTokenID" и добавьте следующее между "FTokenID" и следующим полем
  {NEW}
  FCommentID: TtckCommentKind;
  {/NEW}
  //Это создает новое поле, где мы можем хранить информацию, какой у нас комментарий
  • В объявлении "TSynPasSyn" найдите "fCommentAttri" и добавьте следующее между "fCommentAttri" и следующим полем
  {NEW}
  fCommentAttri_Ansi: TSynHighlighterAttributes;
  fCommentAttri_Bor: TSynHighlighterAttributes;
  fCommentAttri_Slash: TSynHighlighterAttributes;
  {/NEW}
  //Это позволяет нам возвращать различные атрибуты для каждого типа комментариев.
  • Затем найдите определение конструктора "TSynPasSyn", которое должно быть "constructor TSynPasSyn.Create(AOwner: TComponent);"
  • Нам нужно создать наши новые атрибуты, таким образом, мы добавляем наши атрибуты где-нибудь в конструкторе (я предлагаю после значения по умолчанию "fCommentAttri")
  (...)
  AddAttribute(fCommentAttri);
  {NEW}
  fCommentAttri_Ansi := TSynHighlighterAttributes.Create(SYNS_AttrComment+'_Ansi', SYNS_XML_AttrComment+'_Ansi'); // Последние две строки - это заголовок и сохраненное имя
  //Если вы хотите иметь настройки по умолчанию для вашего атрибута, вы можете, например, добавить это:
  //fCommentAttri_Ansi.Background := clBlack; //Установит "Background" в "clBlack" по умолчанию
  AddAttribute(fCommentAttri_Ansi);
  fCommentAttri_Bor := TSynHighlighterAttributes.Create(SYNS_AttrComment+'_Bor', SYNS_XML_AttrComment+'_Bor');
  AddAttribute(fCommentAttri_Bor);
  fCommentAttri_Slash := TSynHighlighterAttributes.Create(SYNS_AttrComment+'_Slash', SYNS_XML_AttrComment+'_Slash');
  AddAttribute(fCommentAttri_Slash);
  {/NEW}
  (...)
  • «Сложная» часть теперь состоит в том, чтобы найти места в коде, где "FTokenID" установлен в "tkComment", и установить наш «подтип» одинаково (конечно, я их уже поискал :)
procedure TSynPasSyn.BorProc;
(...)
  fTokenID := tkComment;
  {NEW}
  FCommentID:=tckBor;
  {/NEW}
  if rsIDEDirective in fRange then
(...)
procedure TSynPasSyn.AnsiProc;
begin
  fTokenID := tkComment;
  {NEW}
  FCommentID:=tckAnsi;
  {/NEW}
(...)
procedure TSynPasSyn.RoundOpenProc;
(...)
        fTokenID := tkComment;
        {NEW}
        FCommentID:=tckAnsi;
        {/NEW}
        fStringLen := 2; // length of "(*"
(...)
procedure TSynPasSyn.SlashProc;
begin
  if fLine[Run+1] = '/' then begin
    fTokenID := tkComment;
    {NEW}
    FCommentID:=tckSlash;
    {/NEW}
    if FAtLineStart then begin
(...)
procedure TSynPasSyn.SlashContinueProc;
(...)
    fTokenID := tkComment;
    {NEW}
    FCommentID:=tckSlash;
    {/NEW}
    while not(fLine[Run] in [#0, #10, #13]) do
(...)
  • Теперь нам просто нужно извлечь информацию при вызове "GetTokenAttribute" и вернуть правильный атрибут, поэтому мы отредактируем "GetTokenAttribute" следующим образом:
function TSynPasSyn.GetTokenAttribute: TSynHighlighterAttributes;
begin
  case GetTokenID of
    tkAsm: Result := fAsmAttri;
    {OLD
    tkComment: Result := fCommentAttri; //Это закомментировано и на всякий пожарный сохранено, поэтому оно будет игнорироваться
    /OLD}
    {NEW}
    tkComment: begin
      if (FCommentID=tckAnsi) then Result:=fCommentAttri_Ansi //Тип - AnsiComment
      else
      if (FCommentID=tckBor) then Result:=fCommentAttri_Bor //Тип - BorComment
      else
      if (FCommentID=tckSlash) then Result:=fCommentAttri_Slash //Тип - SlashComment
      else
        Result:=fCommentAttri //Если наш код каким-то образом упал, возврат к умолчанию
    end;
    {/NEW}
    tkIDEDirective: begin
(...)

Если вы используете lazarus, просто переустановите SynEdit-Package, если нет, перекомпилируйте ваш проект/пакет/<аналог>.

ГОТОВО! Нет, серьезно, теперь вы готовы отличать различные типы комментариев.

Lazarus-IDE автоматически определяет, какие атрибуты существуют, и показывает их в опциях, например сохраняет их, если вы их изменяете. Если ваше приложение/IDE не делает этого, вам придется установить Color/Font/ и т.д. новых атрибутов где-то вручную (например, в конструкторе TSynPasSyn)

Плагины для автозавершения кода

Для SynEdit есть 3 подключаемых плагина автозавершения кода:

TSynCompletion
  • Предлагает список слов в раскрывающемся списке с помощью сочетания клавиш (по умолчанию: Ctrl+space).
  • Используется в IDE для автозавершения идентификатора.
  • Включен в примеры.
  • Доступен в палитре компонентов (начиная с 0.9.3x).

Пример кода, чтобы вызвать автозавершение, всплывает программно (т.е. без нажатия сочетания клавиш):

YourSynEdit.CommandProcessor(YourSynCompletion.ExecCommandID, '', nil)
TSynAutoComplete
  • Заменяет текущий токен фрагментом текста. Не интерактивный. Не выпадающий.
  • Включен в примеры.
  • Доступен в палитре компонентов.
TSynEditAutoComplete
  • Основной шаблон модуля. Не выпадающий.
  • Используется IDE для шаблонов кода. IDE содержит дополнительный код, расширяющий эту функцию (IDE добавляет макросы выпадающего списка и синхронизации).
  • Не включен в примеры.

Todo: Различия между 2-м и 3-м должны быть задокументированы. Может быть, они могут быть объединены.

Логическая/Физическая позиция каретки

SynEdit предлагает положение каретки (текстовый мигающий курсор) в 2 различных формах:

  • Физический X/Y: соответствует визуальной (холст) позиции
  • Логический X/Y: соответствует байтовому смещению текста

Оба основаны на 1 (отсчет начинается с 1). В настоящее время координаты Y всегда одинаковы. Это может измениться в будущем.

Физическая координата
это позиция в сетке дисплея (без учета прокрутки). То есть:
обе буквы "a" и "â" занимают ОДНУ ячейку в сетке, увеличивая физический x на 1. Несмотря на то, что в кодировке utf8 "a" занимает один байт, а "â" занимает несколько байтов.
однако символ табуляции (#9), помимо одного байта и одного символа, может занимать несколько ячеек в сетке, увеличивая физический x более, чем на одну позицию. Есть также некоторые символы в китайском и восточном языках, которые занимают 2 позиции в сетке (гуглите отличие full-width(полной ширины) от half-width (полуширины) символа).
Логическая координата
смещение байта в строчке, содержащей строку.
буква «а» имеет 1 байт и увеличивается на 1
буква "â" имеет 2 (или 3) байта и увеличивается на эту величину
символ tab имеет 1 байт и увеличивается на эту величину.

Ни один из 2 не дает позицию в символах/кодовых точках UTF8 (например, для Utf8Copy или Utf8Length).

Физический X всегда отсчитывается слева от текста, даже если он прокручивается. Чтобы получить grid-x текущего прокручиваемого элемента управления, выполните:

grid-X-in-visible-part-of-synedit := PhysicalX - SynEdit.LeftChar + 1
grid-y-in-visible-part-of-synedit := SynEdit.RowToScreenRow(PhysicalY); // включает в себя складывание (схлопывание) текста
используйте ScreenRowToRow для обратного процесса

Изменение текста из кода

Warning-icon.png

Предупреждение: Изменение текста с помощью свойства SynEdit.Lines не работает с undo/redo.

Доступ к тексту можно получить через SynEdit.Lines. Это свойство на основе TStrings, предлагающее доступ на чтение/запись к каждой строке. Основано на нумерации строк с 0.

  SynEdit.Lines[0] := 'Text'; // первая строчка

SynEdit.Lines можно использовать для установки начальной версии текста (например, загруженного из файла). Обратите внимание, что методы SynEdit.Lines.Add/SynEdit.Lines.Append не поддерживают разрывы строк внутри добавленных строк. Вы должны добавлять строки одну за другой.

Чтобы изменить содержимое SynEdit и позволить пользователю отменить действие, используйте следующие методы:

    procedure InsertTextAtCaret(aText: String; aCaretMode: TSynCaretAdjustMode = scamEnd);
    property TextBetweenPoints[aStartPoint, aEndPoint: TPoint]: String // Логические точки
      read GetTextBetweenPoints write SetTextBetweenPointsSimple;
    property TextBetweenPointsEx[aStartPoint, aEndPoint: TPoint; CaretMode: TSynCaretAdjustMode]: String
      write SetTextBetweenPointsEx;
    procedure SetTextBetweenPoints(aStartPoint, aEndPoint: TPoint;
                                   const AValue: String;
                                   aFlags: TSynEditTextFlags = [];
                                   aCaretMode: TSynCaretAdjustMode = scamIgnore;
                                   aMarksMode: TSynMarksAdjustMode = smaMoveUp;
                                   aSelectionMode: TSynSelectionMode = smNormal );

Примеры:

  // Вставляем текст в позицию каретки
  SynEdit.InsertTextAtCaret('Text');
  // Заменяем текст с (x=2,y=10) на (x=4,y=20) с помощью Str
  SynEdit.TextBetweenPoints[Point(2,10), Point(4,20)] := Str;
  // Удаляем/заменяем один символ в позиции каретки
  var p1, p2: TPoint;
  begin
    p1 := SynEdit.LogicalCaretXY;
    p2 := p1;
    // Высчитываем позицию байта следующего символа
    p2.x := p2.x + UTF8CharacterLength(@SynEdit.LineText[p2.x]);
    // p1 указывает на первый байт заменяемого символа
    // p2 указывает на первый байт символа после последнего заменяемого символа
    // Заменяем на "Text" (или используем пустую строку для удаления)
    SynEdit.TextBetweenPoints[p1, p2] := 'Text';

Сворачивание/Разворачивание текста из кода

  • Это все еще находится в стадии разработки.
  • Это работает, только если текущий маркер подсветки поддерживает сворачивание (подробности в SynEdit_Highlighter).
  • Также обратите внимание, что некоторые маркеры подсветки поддерживают несколько независимых сворачиваемых деревьев. Например, в Паскале у вас есть сворачивание по ключевым словам (begin, end, class, procedure и т.д.), которое является основным, и сворачивание по $ifdef или $region, которое является дополнительным.
  • Сворачивание текущего выделения также отличается от сворачивания по ключевым словам.

Методы сворачивания:

1) TSynEdit.CodeFoldAction

Сворачивание в данной строчке. Если их больше одной, сворачивается самая внутренняя (самая правая).

Light bulb  Примечание: Это не работает ни с выделением, ни с блоком сворачиваемого кода, которые уже полностью скрыт/требуется тестирование для дополнительных блоков сворачиваемого кода.

2) TSynEdit.FindNextUnfoldedLine

3) TSynEdit.FoldAll / TSynEdit.UnfoldAll

Закладки

Дополнительная информация

Обсуждения на форуме, которые содержат информацию о SynEdit:

Примеры приложений

Примеры приложений можно найти в папке "lazarus/examples/synedit".

Добавление горячих клавиш для Cut/Copy/Paste/и т.д.

Горячие клавиши могут быть реализованы с помощью команд SynEdit.

uses
  SynEdit, SynEditKeyCmds;

procedure TForm1.SynEdit1KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if (Shift = [ssCtrl]) then
  begin
    case Key of
    VK_C: SynEdit1.CommandProcessor(TSynEditorCommand(ecCopy), ' ', nil);
    VK_V: SynEdit1.CommandProcessor(TSynEditorCommand(ecPaste), ' ', nil);
    VK_X: SynEdit1.CommandProcessor(TSynEditorCommand(ecCut), ' ', nil);
    end;
  end;
end;

Дальнейшее развитие, обсуждение

  • RTL (справа налево): начат Mazen'ом (частично реализован в Windows)
  • SynEdit использует только UTF8; версия ASCII/ANSI больше не существует. Шрифт предварительно выбирается в зависимости от системы. Пользователь может выбрать другой шрифт, но затем должен позаботиться о выборе моноширинного шрифта.
    • автоматический выбор моноширинного шрифта: в данный момент SynEdit запускается со использованием шрифта 'courier'. На данный момент TFont LCL не предоставляет свойство для фильтрации моноширинных шрифтов.
    • автоматический выбор шрифта UTF-8: по аналогии с выше описанным моноширинным шрифтом + также со шрифтом UTF-8, так что, например, умлауты отображаются правильно.
  • "Немые" клавиши. Большинство клавиатур поддерживают ввод с использованием двух или более клавиш для создания одного специального символа (например, символы ударения или умлаута). (Это обрабатывается LCL widgedset)
  • Редизайн компонента SynEdit. Основная задача - более надежное отображение и навигация по тексту. Более модульный подход также позволяет лучше интегрировать расширения и специализированные элементы управления для использования вне Lazarus.
  • Перенос слов. Это экспериментальная реализация, следующая идее классов TextTrimmer/TabExpansion. У связанной проблемы bugtraker есть класс и объяснение изменений, требуемых в других файлах для его работы.
  • Хуки в обработке клавиши/команды SynEdit. На форуме:http://forum.lazarus-ide.org/index.php/topic,35592.msg243316.html#msg243316

См. также