SynEdit/pl

From Free Pascal wiki
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ć.

Logiczna/fizyczna pozycja karetki

SynEdit oferuje położenie karetki (migającego kursora tekstowego) w 2 różnych formach:

  • Fizyczne X/Y: Odpowiada pozycji wizualnej (na płótnie) karetki,
  • Logiczne X/Y: Odpowiada przesunięciu bajtów tekstu.

Obydwa są oparte na 1. Obecnie współrzędne Y są zawsze takie same. To może się zmienić w przyszłości.

Współrzędna fizyczna
To pozycja w siatce wyświetlania (pomijająca przewijanie). Przy czym:
litera „a” i „â” zajmują JEDNĄ komórkę w siatce, zwiększając fizyczne x o 1. Mimo że w utf8 kodowanie „a” zajmuje jeden bajt, a „â” zajmuje kilka bajtów.
jednakże znak tabulacji (#9), poza tym, że jest tylko jednym bajtem i jednym znakiem, może zająć kilka komórek w siatce, zwiększając fizyczne x o więcej niż jeden. Istnieją również znaki w językach chińskim i wschodnim, które zajmują 2 pozycje siatki (znak Google o pełnej szerokości i o połowie szerokości)
Współrzędna logiczna
To przesunięcie bajtu w ciągu znaków przechowującym linię.
litera „a” ma 1 bajt i zwiększa się o 1
litera „â” ma 2 (lub 3) bajty i zwiększa się o tą wartość
tab ma 1 bajt i zwiększa się o 1.

Żaden z tych współrzędnych nie podaje pozycji w znakach/punktach kodowych UTF8 (tak jak np. Utf8Copy czy Utf8Length).

Fizyczna współrzędna X jest zawsze liczona od lewej strony tekstu, nawet jeśli jest on przewijany. Aby uzyskać grid-x aktualnie przewijanej kontrolki, wykonaj:

grid-X-in-visible-part-of-synedit := PhysicalX - SynEdit.LeftChar + 1
grid-y-in-visible-part-of-synedit := SynEdit.RowToScreenRow(PhysicalY); // obejmuje zwijanie bloków
użyj ScreenRowToRow do odwrotnych wyliczeń

Zmiana tekstu za pomocą kodu

Warning-icon.png

Ostrzeżenie: Zmiana tekstu za pomocą właściwości SynEdit.Lines nie działa w przypadku funkcji cofnij/ponów.

Dostęp do tekstu można uzyskać za pośrednictwem SynEdit.Lines. Jest to właściwość oparta na TStrings oferująca dostęp do odczytu/zapisu dla każdej linii. Pierwszy wiersz ma index 0.

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

Za pomocą SynEdit.Lines można ustawić początkową wersję tekstu (np. wczytaną z pliku). Należy pamiętać, że SynEdit.Lines.Add/SynEdit.Lines.Append nie obsługuje podziału wierszy wewnątrz dodanych ciągów. Należy dodawać linie jedna po drugiej.

Aby zmodyfikować zawartość SynEdit i pozwolić użytkownikowi na cofnięcie akcji, użyj następujących metod:

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

  // Wstaw tekst w miejscu karetki
  SynEdit.InsertTextAtCaret('Tekst');
  // Zamień tekst od punktu (x=2,y=10) do punktu (x=4,y=20) na Str
  SynEdit.TextBetweenPoints[Point(2,10), Point(4,20)] := Str;
  // Usuń/zamień pojedynczy znak w pozycji karetki
  var
    p1, p2: TPoint;
  begin
    p1 := SynEdit.LogicalCaretXY;
    p2 := p1;
    // Oblicz pozycję bajtu następnego znaku
    p2.x := p2.x + UTF8CharacterLength(@SynEdit.LineText[p2.x]);
    // p1 wskazuje pierwszy bajt znaku, który ma zostać zastąpiony
    // p2 wskazuje pierwszy bajt znaku po ostatnim znaku, który można zastąpić
    // Zamień na „Tekst” (lub użyj pustego ciągu, aby usunąć)
    SynEdit.TextBetweenPoints[p1, p2] := 'Tekst';

Zwiń/rozwiń za pomocą kodu

  • To jest wciąż w budowie.
  • Działa to tylko wtedy, gdy bieżący poddświetlacz składni obsługuje zwijanie kodu (szczegóły w SynEdit_Highlighter).
  • Należy również pamiętać, że niektóre podświetlacze obsługują kilka niezależnych drzew zwijania. Np. w Pascalu możesz zwinąć słowa kluczowe (begin, end, class, procedura itp.), które jest głównym zwijaniem, ale możesz zwinąć też $ifdef lub $region, które jest zwijaniem drugorzędnym.
  • Zwijanie bieżącego zaznaczenia różni się także od zwijania słów kluczowych.

Metody zwijania kodu:

1) TSynEdit.CodeFoldAction

Pasuje na podanej linii. Jeśli jest więcej niż jeden, zwija najbardziej do środka (najbardziej po prawej). Uwaga: to nie działa w przypadku zaznaczania ani w przypadku zakładek, które całkowicie się ukrywają / Wymagają przetestowania dla innych operacji zwijania.

2) TSynEdit.FindNextUnfoldedLine

3) TSynEdit.FoldAll / TSynEdit.UnfoldAll

Zakładki

Więcej informacji

Dyskusje na forum, które zawierają informacje o SynEdit:

Przykładowe aplikacje

Przykładowe zastosowania znajdziesz w folderze "lazarus/examples/synedit".

Dodawanie skrótów klawiszowych do wycinania/kopiowania/wklejania/itp

Skróty klawiszowe można wdrożyć za pomocą poleceń SynEdit.

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;

Dalszy rozwój, dyskusje

  • RTL (od prawej do lewej): rozpoczęty przez Mazena (częściowo zaimplementowany w systemie Windows)
  • SynEdit używa tylko UTF8; wersja ASCII/ANSI już nie istnieje. Czcionka jest wstępnie wybrana w zależności od systemu. Użytkownik może wybrać inną czcionkę, ale musi wtedy wybrać czcionkę o stałej szerokości.
    • automatyczny wybór czcionki o stałej szerokości: W tej chwili SynEdit zaczyna od czcionki „courier”. W tej chwili LCL TFont nie udostępnia właściwości umożliwiającej filtrowanie czcionek o stałej szerokości.
    • automatyczny wybór czcionki UTF-8: To samo co powyżej o stałej szerokości, ale także z czcionką UTF-8, tak aby np. umlauty były wyświetlane poprawnie.
  • Martwe klucze. Większość klawiatur obsługuje wpisywanie dwóch lub więcej klawiszy w celu utworzenia jednego znaku specjalnego (takiego jak znaki akcentowane lub umlaut). (Jest to obsługiwane przez zestaw widżetów LCL)
  • Przeprojektowanie komponentu SynEdit. Podstawowym celem jest bardziej niezawodne wyświetlanie i nawigacja w tekście. Bardziej modułowe podejście pozwala również na lepszą integrację rozszerzeń i wyspecjalizowanych elementów sterujących do użytku poza Lazarusem.
  • Zawijanie słów. Jest to eksperymentalna implementacja oparta na idei klas TextTrimmer/TabExpansion. Powiązany problem z bugtrakerem zawiera klasę i wyjaśnienie zmian wymaganych w innych plikach, aby działał.
  • Haki w przetwarzaniu kluczy/poleceń SynEdit. Na forum: http://forum.lazarus-ide.org/index.php/topic,35592.msg243316.html#msg243316

Zobacz także