SynEdit/pl

From Free Pascal wiki
Revision as of 17:46, 12 March 2024 by Slawek (talk | contribs) (Wstępne tłumaczenie na j. polski, cd.)
Jump to navigationJump to search

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

SynEdit to pakiet edycji/notatek z podświetlaniem składni dostępny na karcie SynEdit obsługujący wiele języków/składni.

SynEdit zawarty w Lazarusie został utworzony na bazie SynEdit 1.0.3, i został w dużym stopniu zaadaptowany i rozszerzony. Zmiany są wymienione poniżej.

Pakiet Lazarus zawiera komponent edytora źródeł o nazwie TSynEdit, kilka podświetlaczy składni i inne komponenty używane do edycji źródeł.

Licencjonowany na tych samych warunkach, co oryginalny SynEdit (MPL lub GPL).

Wersja oryginalna a Lazarus

Wersją dla Lazarus opiekuje się głównie Martin Friebe. Martin napisał na forum, co zostało dodane do wersji Lazarusa od czasu pojawienia się tego forka:

Duże rzeczy dodane do wersji Lazarus:

  • zwijanie bloków kodu
  • konfigurowalny boczny margines i jego części (tzw. rynna)
  • wspólny tekst pomiędzy kilkoma redaktorami
  • obsługa kodowania utf-8
  • wtyczka do edycji synchronizacji
  • podstawowa obsługa RTL/LTR
  • konfiguracja myszy za pomocą MouseActions
  • przepisano różne moduły podświetlania/oznaczeń składni

Bazy kodu wersji Delphi/Lazarus zostały niezależnie przeprojektowane. Pozostało bardzo niewiele do zrobienia.

Port SynEdit 2.0

Istnieje alternatywny port wersji 2.0.x oryginalnego SynEdit. Nie jest aktywnie utrzymywany, ostatnie zatwierdzenie (obecnie jest marzec 2024) miało miejsce w 2023 roku, ale od 2011 roku ma on wersję 2.0.5.

SynEdit w IDE

SynEdit w Lazarusie jest pakietem wbudowanym, ponieważ samo IDE używa go do edycji kodów źródłowych. Z tego powodu nie można usunąć tego pakietu z listy instalacyjnej. Natomiast aby usunąć SynEdit z palety komponentów, można usunąć pakiet SynEditDsgn z instalacji.

Używanie SynEdit

Podświetlacze składni

  • Istnieje kilka standardowych poświetlaczy składni (zobacz karta SynEdit w palecie komponentów)
  • Istnieją podświetlacze skryptowe, które można dostosować do wielu innych formatów plików:
  • Istnieje więcej podświetlaczy innych firm: SynCacheSyn, SynGeneralSyn, SynRCSyn, SynRubySyn, SynSDDSyn, SynSMLSyn, SynSTSyn, SynTclTkSyn, SynUnrealSyn, SynURISyn, SynVBScriptSyn, SynVrml97Syn, Spójrz tutaj.
  • Możesz napisać nowy podświetlacz, zobacz informacje na SynEdit Highlighter.

Znaczniki

Znaczniki umożliwiają dodatkowe kolorowanie SynEdit Markup

Oznaczenia

Znaczniki zapewniają dodatkowe kolorowanie

Edycja istniejącego podświetlacza

Czasami możesz chcieć edytować istniejące podświetlacze składni (tak jak chciałem to zrobić kilka dni temu), które już istnieją. W tym przykładzie będziemy edytować podświetlacz dla kodu pascala (nazwa klasy: TSynPasSyn; pakiet: SynEdit V1.0; moduł: SynHighlighterPas.pas).

Powiedzmy, że chcemy osiągnąć to, aby nasza aplikacja (w tym przypadku Lazarus) rozróżniał trzy typy komentarzy, które istnieją w Pascalu:

  (* standardowe *)
  { klamry }
  // ukośniki

Może to być pomocne, jeśli chcesz rozróżnić różne typy swoich komentarzy (np. „Opis”, „Notatka”, „Referencja” itp.) i chcesz, aby każdy wyróżniony był innym kolorem.

Light bulb  Uwaga: Na wypadek, gdybyś coś zepsuł, sugeruję dodanie kilku komentarzy „NEW” i „/NEW”, ale nie jest to konieczne
  • Najpierw otwórz moduł „SynHighlighterPas”, który powinien znajdować się w katalogu SynEdit.
  • Ponieważ nie chcemy powodować niezgodności, tworzymy nowy rodzaj typu wyliczającego, który pomoże nam później zidentyfikować nasz komentarz:

Np. pod deklaracją „tkTokenKind” napisz:

  {NEW}
  TtckCommentKind = (tckAnsi, tckBor, tckSlash);
  {/NEW}
  • W deklaracji „TSynPasSyn” wyszukaj „FTokenID” i dodaj pomiędzy „FTokenID” a następnym polem
  {NEW}
  FCommentID: TtckCommentKind;
  {/NEW}
  //Tworzy to nowe pole, w którym możemy zapisać informację, jakiego rodzaju mamy komentarz
  • W deklaracji „TSynPasSyn” wyszukaj „fCommentAttri” i dodaj następujący tekst między „fCommentAttri” a następnym polem
  {NEW}
  fCommentAttri_Ansi: TSynHighlighterAttributes;
  fCommentAttri_Bor: TSynHighlighterAttributes;
  fCommentAttri_Slash: TSynHighlighterAttributes;
  {/NEW}
  //Dzięki temu możemy zwrócić różne atrybuty dla każdego typu komentarza
  • Następnie wyszukaj definicję konstruktora „TSynPasSyn”, która powinna brzmieć „constructor TSynPasSyn.Create(AOwner: TComponent);”.
  • Musimy utworzyć nasze nowe atrybuty, dlatego dodajemy nasze atrybuty gdzieś w konstruktorze (sugeruję po domyślnym „fCommentAttri”)
  (...)
  AddAttribute(fCommentAttri);
  {NEW}
  fCommentAttri_Ansi := TSynHighlighterAttributes.Create(SYNS_AttrComment+'_Ansi', SYNS_XML_AttrComment+'_Ansi'); //Ostatnie dwa ciągi to podpis i przechowywana nazwa
  //Jeśli chcesz mieć domyślne ustawienia swojego atrybutu, możesz np. dodać to:
  //fCommentAttri_Ansi.Background := clBlack; //Ustawiłoby to „Tło” na „clBlack” jako domyślne
  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}
  (...)
  • Bardziej skomplikowane działanie polega teraz na wyszukaniu miejsc, w których „FTokenID” jest ustawione na „tkComment” i jednocześnie obsługuje nasz „podtyp” (oczywiście już je dla Ciebie znalazłem :)
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; // długość "(*"
(...)
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
(...)
  • Teraz musimy tylko pobrać informacje po wywołaniu „GetTokenAttribute” i zwrócić właściwy atrybut, dlatego edytujemy „GetTokenAttribute” w następujący sposób:
function TSynPasSyn.GetTokenAttribute: TSynHighlighterAttributes;
begin
  case GetTokenID of
    tkAsm: Result := fAsmAttri;
    {OLD
    tkComment: Result := fCommentAttri; //To jest komentarz i stanowi kopię zapasową, więc zostanie zignorowany
    /OLD}
    {NEW}
    tkComment: begin
      if (FCommentID = tckAnsi) then
        Result := fCommentAttri_Ansi //To jest typ standardowy AnsiComment
      else
        if (FCommentID = tckBor) then
          Result := fCommentAttri_Bor //To jest typ klamrowy BorComment
        else
          if (FCommentID = tckSlash) then 
            Result := fCommentAttri_Slash //To jest typ ukośnikowy SlashComment
          else
            Result := fCommentAttri //Jeśli nasz kod w jakiś sposób zawiódł, przywróć ustawienia domyślne
    end;
    {/NEW}
    tkIDEDirective: begin
(...)

Jeśli używasz lazarusa, po prostu zainstaluj ponownie pakiet SynEdit, jeśli nie, przekompiluj swój projekt/pakiet.

GOTOWE ! Mówię poważnie! Twój kod jest teraz gotowy na rozróżnianie różnych typów komentarzy.

Lazarus-IDE automatycznie wykrywa, jakie atrybuty istnieją i pokazuje je w opcjach, na przykład zapisuje je, jeśli je zmienisz. Jeśli Twoja aplikacja/IDE tego nie robi, będziesz musiał ustawić Kolor/Czcionkę/itp. nowych atrybutów gdzieś ręcznie (np. w konstruktorze TSynPasSyn)

Realizacja wtyczek

Istnieją 3 wtyczki uzupełniające dla SynEdit:

TSynCompletion
  • Oferuje listę słów w rozwijanym menu za pomocą kombinacji klawiszy skrótu (domyślnie: Ctrl-Spacja).
  • Używane w IDE do uzupełniania identyfikatora.
  • Zawarte w przykładach.
  • Dostępne na palecie komponentów (od wersji 0.9.3x).

Przykładowy kod wywołujący programowo wyskakujące okienko uzupełniania (tj. bez naciskania skrótu klawiaturowego):

YourSynEdit.CommandProcessor(YourSynCompletion.ExecCommandID, '', nil)
TSynAutoComplete
  • Zastępuje bieżący token fragmentem tekstu. Nie jest to interaktywne. Nie posiada menu rozwijanego.
  • Zawarte w przykładach.
  • Dostępne na palecie komponentów.
TSynEditAutoComplete
  • Podstawowy moduł szablonów. Nie posiada menu rozwijanego.
  • Używany przez IDE do szablonów kodu. IDE zawiera dodatkowy kod rozszerzający tę funkcję (rozwijane i synchronizujące makra dodawane są przez IDE).
  • Nie ujęte w przykładach.

Todo: Należy udokumentować różnice pomiędzy 2. i 3. poziomem. Może uda się je połączyć.

Logical/Physical caret position

SynEdit offers position of the caret (text blinking cursor) in 2 different forms:

  • Physical X/Y: Corresponds to visual (canvas) position,
  • Logical X/Y: Corresponds to byte offset of the text.

Both are 1-based. Currently Y coordinates are always the same. This may change in future.

The Physical coordinate
is the position in the display grid (ignoring any scrolling). That is:
the letter "a" and "â" take both ONE cell on the grid, increasing physical x by 1. Even though in utf8 encoding "a" takes one byte, and "â" takes several bytes.
however the tab char (#9), besides being just one byte and one char, can take several cells in the grid, increasing the physical x by more than one. There are also some chars in Chinese and eastern languages, that take 2 grid positions (google full-width vs half-width char)
The Logical coordinate
is the byte offset in the string holding the line.
the letter "a" has 1 byte and increases by 1
the letter "â" has 2 (or 3) bytes, and increases by that
tab has 1 byte and increases by that.

Neither of the 2 give the position in UTF8 chars/code-points (e.g. for Utf8Copy or Utf8Length).

The physical X is always counted from the left of the text, even if this is scrolled out. To get the grid-x of the currently scrolled control do:

grid-X-in-visible-part-of-synedit := PhysicalX - SynEdit.LeftChar + 1
grid-y-in-visible-part-of-synedit := SynEdit.RowToScreenRow(PhysicalY); // includes folding
use ScreenRowToRow for reverse

Change text from code

Warning-icon.png

Ostrzeżenie: Changing text via SynEdit.Lines property does not work with undo/redo.

Text can be accessed via SynEdit.Lines. This is a TStrings based property offering read/write access to each line. It is 0 based.

  SynEdit.Lines[0] := 'Text'; // first line

SynEdit.Lines can be used to set the initial version of the text (e.g. loaded from file). Note that SynEdit.Lines.Add/SynEdit.Lines.Append does not support line breaks inside the added strings. You should add lines one by one.

To modify the content of a SynEdit, and allow the user to undo the action use the following methods:

    procedure InsertTextAtCaret(aText: String; aCaretMode: TSynCaretAdjustMode = scamEnd);
    property TextBetweenPoints[aStartPoint, aEndPoint: TPoint]: String // Logical Points
      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 );

Examples:

  // Insert text at caret
  SynEdit.InsertTextAtCaret('Text');
  // Replace text from (x=2,y=10) to (x=4,y=20) with Str
  SynEdit.TextBetweenPoints[Point(2,10), Point(4,20)] := Str;
  // Delete/replace single char at caret pos
  var
    p1, p2: TPoint;
  begin
    p1 := SynEdit.LogicalCaretXY;
    p2 := p1;
    // Calculate the byte pos of the next char 
    p2.x := p2.x + UTF8CharacterLength(@SynEdit.LineText[p2.x]);
    // p1 points to the first byte of char to be replaced
    // p2 points to the first byte of the char after the last replaceable char
    // Replace with "Text" (or use empty string to delete)
    SynEdit.TextBetweenPoints[p1, p2] := 'Text';

Fold/Unfold from code

  • This is still under construction.
  • This only works if current highlighter supports folding (details at SynEdit_Highlighter).
  • Also note that some highlighters support several independent fold-trees. E.g. in Pascal you have folding on keywords (begin, end, class, procedure, etc) which is the primary fold, and folding on $ifdef or $region which is secondary.
  • Folding of current selection is also different from folding on keywords.

Methods for folding:

1) TSynEdit.CodeFoldAction

Folds at the given Line. If there are more than one, folds the inner most (right most). Note: This does not work with selection, nor with Folds that hide entirely / Need testing for 2ndary folds.

2) TSynEdit.FindNextUnfoldedLine

3) TSynEdit.FoldAll / TSynEdit.UnfoldAll

Bookmarks

More info

Discussions on the forum, which contain info about SynEdit:

Example apps

Example applications can be found in the folder "lazarus/examples/synedit".

Adding hotkeys for Cut/Copy/Paste/etc

Hotkeys can be implemented by using SynEdit commands.

uses
  SynEdit, SynEditKeyCmds;

procedure TForm1.SynEdit1KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if (Shift = [ssCtrl]) then
    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;

Further development, discussions

  • RTL (right-to-left): started by Mazen (partly implemented on Windows)
  • SynEdit only uses UTF8; an ASCII/ANSI version no longer exists. A font is pre-selected depending on the system. The user can choose another font, but must then take care to choose a monospaced font.
    • automatic monospace font selection: At the moment SynEdit starts with a font 'courier'. At the moment the LCL TFont does not provide a property to filter monospaced fonts.
    • automatic UTF-8 font selection: Same as above monospace, but also with an UTF-8 font, so that for example umlauts are shown correctly.
  • Dead keys. Most keyboards support typing two or more keys to create one special character (like accented or umlaut characters). (This is handled by LCL widgedset)
  • 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.
  • Word Wrapping. This is an experimental implementation following the idea of the TextTrimmer/TabExpansion classes. The linked bugtraker issue has the class and the explanation of changes required in other files for it to work.
  • Hooks in SynEdit key/command processing. On the forum: http://forum.lazarus-ide.org/index.php/topic,35592.msg243316.html#msg243316

See also