SynEdit Highlighter/pl

From Free Pascal wiki
Jump to navigationJump to search

English (en) polski (pl) русский (ru)

Aby uzyskać więcej informacji na temat SynEdit, przejdź do: SynEdit
Zobacz także SynEdit Markup


Zrozumienie podświetlacza składni SynEdit

SynEdit - zależność od podświetlacza

SynEdit do podświetlacza ma relację N do 1.

  • Jedna instancja Podświetlacza może obsługiwać N (wiele) instancji SynEdits
  • Każdy SynEdit ma tylko jeden podświetlacz
  • Ale: jeden tekst (bufor tekstu) może mieć wiele podświetlaczy, jeśli jest współdzielony przez kilka SynEdit (każdy SynEdit będzie miał jeden HL, ale wszystkie HL będą działać na tym samym dokumencie)

W wyniku tego:

  • żadna instancja wyróżnienia nie ma (stałego) odniesienia do SynEdit.
(Podświwetlacze przechowują jednak listę buforów SynEditTextBuffer, do których są dołączone)
  • Wszystkie dane dla Podświetlacza są (i muszą być) przechowywane w SynEdit (właściwie w TextBuffer SynEdit (zwanym „Liniami”).

Jednakże przed każdym wywołaniem Podświetlacza SynEdit upewnia się, że Highlighter.CurrentLines jest ustawiony na bieżącym wierszu SynEdit. W ten sposób podświetlacz będzie miał dostęp do danych, kiedy tylko zajdzie taka potrzeba. Format przechowywania danych jest określony przez podświetlacz (TSynCustomHighlighter.AttachToLines).

Scanning and Returning Highlight attributes

Skanowanie i zwracanie atrybutów Podświetlacza

Oczekuje się, że podświetlacz będzie działał w oparciu o linię / wiersz tekstu.

Jeśli jakikolwiek tekst został zmodyfikowany, SynEdit wywoła (TSynCustomHighlighter.ScanFrom / Aktualnie wywołane z TSynEdit.ScanFrom) z zakresem linii. Podświetlacz powinien znać stan poprzedniej linii.

Jeśli wymagane są atrybuty podświetlenia, SynEdit zażąda ich również dla każdej linii. SynEdit będzie przeglądać poszczególne tokeny w linii. Obecnie dzieje się to z zagnieżdżonej procedury PaintLines w SynEdit.PaintTextLines. Wywołuje ona TSynCustomHighlighter.StartAtLineIndex, po którym następuje HL.GetTokenEx/HL.GetTokenAttribute tak długo, jak HL.GetEol ma wartość false.

Również klasa BaseClass dla danych podświetlacza (zobacz AttachToLines) opiera się na przechowywaniu linii tekstu, a bufor tekstowy (linie) SynEdita konserwuje te dane, aby zapewnić ich synchronizację. To znaczy: za każdym razem, gdy wstawiane lub usuwane są wiersze tekstu, wstawiane lub usuwane są także wpisy z danych podświetlaczy (stąd musi istnieć jeden wpis w każdym wierszu).

Zwykle podświetlacze przechowują w tym polu stan końca linii. Jeśli więc podświetlacz będzie działał na linii, będzie kontynuował wprowadzanie stanu z poprzedniej linii.

Zwijanie tekstu

Zwijanie tekstu w SynEdit jest obsługiwane przez moduł SynEditFoldedView i SynGutterCodeFolding. Podświetlacze realizujące zwijanie są oparte na TSynCustomFoldHighlighter.

Podstawowe informacje dotyczące komunikacji pomiędzy SynEditFoldedView i HL wymagają przechowywania 2 wartości dla każdej linii. (Oczywiście sam podświetlacz może przechowywać więcej informacji):

  • FoldLevel na końcu linii
  • Minimalny poziom zwijania napotkany w dowolnym miejscu linii

The Foldlevel indicates how many (nested) folds exist. It goes up whenever a fold begins, and down when a fold ends: Poziom zwinięcia wskazuje, ile istnieje (zagnieżdżonych) zwinięć. Podnosi się, gdy zaczyna się zwijanie, i obniża, gdy zwijanie się kończy:

                           EndLvl   MinLvl
 Procedure a;               1 -      0
 Begin                      2 --     1 -
   b:= 1;                   2 --     2 --
   if c > b then begin      3 ---    2 --
     c:=b;                  3 ---    3 ---
   end else begin           3 ---    2 --
     b:=c;                  3 ---    3 ---
   end;                     2 --     2 --
 end;                       0        0  // "end" zamyka zarówno zwinięcie "begin", jak i zwinięcie "procedure"

W linii

 Procedure a;               1 -      0

MinLvl wynosi 0, ponieważ linia zaczęła się od poziomu 0 (i nigdy nie spadła / nie było żadnych zwinięć). Podobnie we wszystkich wierszach, w których występuje tylko słowo kluczowe otwierające („begin”).

Ale w linii

   end else begin           3 ---    2 --

zaczyna się od poziomu 3 i na nim również się kończy (jeden zamknięty, drugi otwarty). Ponieważ jednak spadł jako pierwszy, minimalny poziom napotkany w dowolnym miejscu na linii wynosi 2.

Bez MinLvl nie byłoby możliwe stwierdzenie, że zwijanie kończy się w tej linii.

Nie ma czegoś takiego jak MaxLvl, ponieważ zwijanie rozpoczynające się i kończące na tej samej linii i tak nie mogą zostać zwinięte. Nie ma potrzeby ich wykrywać.

 if a then begin b:=1; c:=2; end; // nie ma żadnego zwijania na tej linii

Tworzenie podświetlacza SynEdit

Od wersji 0.9.31 35115 podświetlacz składani uległ zmianie. Wdrażanie podstawowego zwijania jest teraz łatwiejsze.

Wszystkie źródła można znaleźć w katalogu instalacyjnym Lazarusa pod adresem:

  • Lazarus 2.3 i nowsze: examples\Components\SynEdit\NewHighlighterTutorial
  • Lazarus przed 2.2.*: examples\SynEdit\NewHighlighterTutorial\

Projekt HighlighterTutorial zawiera 3 przykładowe podświetlenia różnicowe:

  • SimpleHl: użyte w kroku 1 poniżej
  • ContextHl: użyte w kroku 2 poniżej
  • FoldHl: użyte w kroku 3 poniżej

SimpleHl i ContextHl będą działać również z wersją 0.9.30

Składany podświetlacz w akcji:

SynEditFoldingHighlighterDemo.png

Podstawy: zwracanie tokenów i atrybutów

Jak wskazano, moduł SimpleHl demonstruje ten proces.

Co to robi

  • Dzieli każdą linię na słowa i spacje (lub tabulatory)
    • Spacje są częścią tekstu i również muszą być wyróżnione
  • Ten przykład pozwala określić różne kolory dla
- tekst (domyślnie nie jest podświetlony)
- spacje (domyślnie srebrna ramka)
- słowa oddzielone spacjami rozpoczynające się od a,e,i,o,u (domyślnie pogrubione)
- słowo „not” (domyślnie na czerwonym tle)

Jak to działa

  • Create

Podświetlacz tworzy atrybuty, dzięki którym może zwracać słowa i spacje.

  • SetLine

Wywoływane przez SynEdit przed pomalowaniem linii (lub zanim potrzebne będą informacje o podświetleniu)

  • GetTokenEx, GetTokenAttribute, Next, GetEol

Są używane przez SynEdit do iteracji po linii. Zauważ, że pierwszy Token (Słowo lub Spacje) musi być gotowy po SetLine, bez wywołania Next.

Important: Tokeny zwrócone dla każdej linii muszą reprezentować oryginalny tekst linii i zostać zwrócone we właściwej kolejności.

  • GetToken, GetTokenPos, GetTokenKind

SynEdit używa ich np. do wyszukiwania pasujących nawiasów. Jeśli tokenKind zwraca różne wartości dla każdego atrybutu, nawiasy pasują tylko wtedy, gdy są tego samego rodzaju (np. jeśli istnieje atrybut ciągu, nawiasy poza ciągiem nie będą pasować do nawiasów wewnątrz ciągu).

Inne uwagi

Aby zapewnić czytelność, podświetlacz nie ma optymalizacji, więc w przypadku większych tekstów może działać bardzo wolno. Wiele dostarczonych podświetlaczy używa funkcji haszujących, aby znaleźć słowo (lub dowolną grupę znaków).

Krok 2: Używanie zakresów

Jak wskazano, moduł „ContextHl” demonstruje ten proces

Następny przykład pozwala na to, aby zawartość linii wpływała na inne linie następujące po niej. Przykład: znak „(*” w języku Pascal powoduje, że wszystkie kolejne wiersze stają się komentarzem, aż do znalezienia „*)”.

Ten przykład rozszerza powyższy przykład SimpleHl: Tokeny -- i ++ (muszą być otoczone spacją lub początkiem/końcem linii, aby były samodzielnymi tokenami) przełączają słowa zaczynające się od a,e,i,o,u

Można zagnieżdżać wiele ++ i --. Następnie dla każdego -- należy podać ++, zanim słowa zostaną ponownie podświetlone.

Następnie rozkładamy skaner. Skanowanie wstępne w celu zapisania informacji wywołuje te same funkcje, co podświetlacz. Jest on wywoływany automatycznie, jeśli coś się zmieni. (Jest wywoływany dla wszystkich linii poniżej zmienionej linii, dopóki linia nie zwróci tej samej wartości zakresu, jaką już miała)

Zliczana jest aktualna ilość „--”.

  FCurRange: Integer;

Ta ilość jest pomniejszana przez każde „++”

Do przechowywania informacji używamy:

GetRange
Wywoływany jest po całkowitym przeskanowaniu linii, aby uzyskać wartość końca linii. Wartość zostanie zapisana.
SetRange
Wywoływany jest przed przeskanowaniem linii. Ustawia wartość przechowywaną od końca poprzedniego wiersza.
ResetRange
Wywoływany jest przed przeskanowaniem pierwszej linii (ponieważ nie ma poprzedniej linii).

Light bulb  Uwaga: Skanowanie jest wyzwalane przez *każdą* zmianę w linii (każde naciśnięcie klawisza). Skanuje bieżącą linię i wszystkie linie poniżej, aż linia zwróci ten sam zakres, który już miała. Zobacz:

http://forum.lazarus.freepascal.org/index.php/topic,21727.msg139420.html#msg139420

Ważna uwaga dotycząca zakresów

Celem zakresu jest umożliwienie HL rozpoczęcia skanowania w dowolnej linii. HL nigdy nie będzie musiał patrzeć na poprzednią linię. Wszelkie informacje potrzebne do przeskanowania bieżącej linii można uzyskać z wartości zakresów.

Przykład:

  writeln; (*
  readln;
  *)

podczas skanowania „readln” HL wie z zakresu, że znajduje się w komentarzu, nie musi przeglądać poprzednich linii.

Dlatego skanowanie można rozpocząć w dowolnej linii.

Wyjaśnia to również uwaga z poprzedniego rozdziału. „dopóki linia nie zwróci tego samego zakresu, który już miała”. Bo nawet jeśli tekst linii nie został zmieniony, jeśli wartość zakresu na początku linii uległa zmianie, to wynik skanowania również się zmieni.

Step 3: Dodanie zwijania

Jak wskazano, moduł „FoldHl” demonstruje ten proces

Przykładowo, podświetlacz powinien zwijać wszystko pomiędzy wolnostojącymi „-(-”, „-)-”.

Zmień dziedziczenie:

  uses SynEditHighlighterFoldBase;
  ...
  TSynDemoHl = class(TSynCustomFoldHighlighter)

Zmień sposób przechowywania informacji o zakresie, ponieważ klasa bazowa używa ich do informacji o zwijaniu:

procedure TSynDemoHl.SetRange(Value: Pointer);
begin
  inherited;
  FCurRange := PtrInt(CodeFoldRange.RangeType);
end;

procedure TSynDemoHl.ResetRange;
begin
  inherited;
  FCurRange := 0;
end;

function TSynDemoHl.GetRange: Pointer;
begin
  CodeFoldRange.RangeType := Pointer(PtrInt(FCurRange));
  inherited;
end;

Teraz dodaj do skanera kod, który poinformuje podświetlacz o otwieraniu i zamykaniu zwijania:

procedure TSynDemoHl.FindTokenEnd;
begin
   ...

  if (FTokenEnd = FTokenPos+1) and (FLineText[FTokenPos] = '[') then
    StartCodeFoldBlock(nil);
  if (FTokenEnd = FTokenPos+1) and (FLineText[FTokenPos] = ']') then
    EndCodeFoldBlock();
end;
  • Dla 0.9.30

Proszę zapoznać się z historią tej strony, jeśli używasz wersji 0.9.30 [[1]]

Więcej informacji na temat StartCodeFoldBlock / EndCodeFoldBlock

  function StartCodeFoldBlock(ABlockType: Pointer; IncreaseLevel: Boolean = true): TSynCustomCodeFoldBlock; virtual;
ABlockType
Można go użyć do określenia identyfikatora bloku.

Pole to nie jest zwykle używane jako wskaźnik (choć jest to dozwolone). Zwykle identyfikatory są typem wyliczeniowym.
Jeśli masz różne typy bloków (np. w Pascalu: begin/end; repeat/until, ...) i nie chcesz, aby blok został zamknięty niewłaściwym słowem kluczowym („end” nie powinien zamykać „repeat”), to możesz nadać im identyfikatory:
StartCodeFoldBlock(PtrUInt(1)) // lub inne liczby

IncreaseLevel
Jeśli ustawione na False, zostanie wstawiony blok, którego nie można zwinąć.
Takie bloki można wykorzystać do wewnętrznego śledzenia.

UWAGA: Wszystkie zwijania muszą być zagnieżdżone, nie mogą zachodzić na siebie. Oznacza to, że ostatnia otwarta zakładka musi być najpierw zamknięta.
Odnosi się to do „EndLvl”, jak pokazano w „Składaniu” (1.3) powyżej
Nakładanie się (jak "IFDEF" i "begin" w IDE) nie może być wykonane w ten sposób.

  procedure EndCodeFoldBlock(DecreaseLevel: Boolean = True); virtual;
DecreaseLevel
To *musi* odpowiadać IncreaseLevel, tak jak zostało to podane w StartCodeFoldBlock

True oznacza zakończenie zwijania; False oznacza, że ​​blok wewnętrzny został zakończony. W przypadku niedopasowania zawijanie będzie kontynuowane lub zakończy się wcześniej.
TopCodeFoldBlockType może służyć do wskazania identyfikatora najbardziej wewnętrznego otwartego bloku. Można używać różnych identyfikatorów dla bloków wewnętrznych i używać ich do ustawiania wartości.

  function TopCodeFoldBlockType(DownIndex: Integer = 0): Pointer;

Returns the ID of the innermost block.

DownIndex
może zostać użyty do uzyskania identyfikatora drugiego bloku.

DownIndex=1 oznacza blok otaczający najbardziej wewnętrzny.

Konfigurowalne podświetlacze (w tym produkty innych firm)

SynAnySyn

Naprawdę prosty podświetlacz. Bardziej referencyjna implementacja niż prawdziwy, użyteczny zakreślacz.

SynUniHighlighter

Używa plików konfiguracyjnych opartych na formacie XML. stary oficjalny link

SynFacilSyn

Elastyczny, w pełni konfigurowalny podświetlacz. https://github.com/t-edson/SynFacilSyn

Bibliografia

Wątki na forum:

  • Zwijanie
    • „SynEdit - ulepszone podœietlacze do obsługi zwijania kodu?” [temat,7879]
    • „SynEdit - Dodaj zwijanie kodu pomocniczego dla Java” [temat,7338]
    • „CodeFolding” Konfiguracja [temat,11064]
    • Zwijane bloki, ałowo kluczowe „end” (koniec w linii przed następnym słowem kluczowym) [temat,23411.msg139621]
    • Zwijanie wybranego tekstu z kodu (kod użytkownika/aplikacji): [24473.msg147312]
    • Uzyskanie stanu zwiniętego (zapisz stan złożenia w sesji) [temat=26748]