Копия викизеркало

Это зеркало вики с pascal.chat. Правка, история и вход — там.

SynEdit/ru

Deutsch · English · Español · Français · Polski · Русский · 中文(中国) · 日本語

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

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

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

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

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

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

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

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

SynEdit 2.0 port

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

SynEdit в IDE

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

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

Подсветка

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

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

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

pascal
  (* ansi *)
  { bor }
  // Slash

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

Note: На случай, если вы что-то сломаете, я предлагаю сделать несколько "NEW" и "/NEW"-комментариев, но вам не нужно

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

pascal
  {NEW}
  TtckCommentKind = (tckAnsi, tckBor, tckSlash);
  {/NEW}
pascal
  {NEW}
  FCommentID: TtckCommentKind;
  {/NEW}
  //Это создает новое поле, где мы можем хранить информацию, какой у нас комментарий
pascal
  {NEW}
  fCommentAttri_Ansi: TSynHighlighterAttributes;
  fCommentAttri_Bor: TSynHighlighterAttributes;
  fCommentAttri_Slash: TSynHighlighterAttributes;
  {/NEW}
  //Это позволяет нам возвращать различные атрибуты для каждого типа комментариев.
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}
  (...)
pascal
procedure TSynPasSyn.BorProc;
(...)
  fTokenID := tkComment;
  {NEW}
  FCommentID:=tckBor;
  {/NEW}
  if rsIDEDirective in fRange then
(...)
pascal
procedure TSynPasSyn.AnsiProc;
begin
  fTokenID := tkComment;
  {NEW}
  FCommentID:=tckAnsi;
  {/NEW}
(...)
pascal
procedure TSynPasSyn.RoundOpenProc;
(...)
        fTokenID := tkComment;
        {NEW}
        FCommentID:=tckAnsi;
        {/NEW}
        fStringLen := 2; // length of "(*"
(...)
pascal
procedure TSynPasSyn.SlashProc;
begin
  if fLine[Run+1] = '/' then begin
    fTokenID := tkComment;
    {NEW}
    FCommentID:=tckSlash;
    {/NEW}
    if FAtLineStart then begin
(...)
pascal
procedure TSynPasSyn.SlashContinueProc;
(...)
    fTokenID := tkComment;
    {NEW}
    FCommentID:=tckSlash;
    {/NEW}
    while not(fLine[Run] in [#0, #10, #13]) do
(...)
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
(...)

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

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

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

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

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

TSynCompletion

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

pascal
YourSynEdit.CommandProcessor(YourSynCompletion.ExecCommandID, '', nil)

TSynAutoComplete

TSynEditAutoComplete

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

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

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

Оба основаны на 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: Изменение текста с помощью свойства SynEdit.Lines не работает с undo/redo.

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

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

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

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

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 );

Примеры:

pascal
  // Вставляем текст в позицию каретки
  SynEdit.InsertTextAtCaret('Text');
  // Заменяем текст с (x=2,y=10) на (x=4,y=20) с помощью Str
  SynEdit.TextBetweenPoints[Point(2,10), Point(4,20)] := Str;
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';

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

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

1) TSynEdit.CodeFoldAction

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

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

2) TSynEdit.FindNextUnfoldedLine

3) TSynEdit.FoldAll / TSynEdit.UnfoldAll

Закладки

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

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

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

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

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

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

pascal
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;

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

См. также


From the Free Pascal wiki, page "SynEdit/ru", as it stood on 2019-09-04 after a change by Zoltanleo, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Версия 2, последнее изменение: WikiArchive, .