TreeStructInfoDSL - Domain Specific Language dla TSI w object pascalu

5

Link do repozytorium: https://github.com/spartanPAGE/TreeStructInfoDSL

Słowem wstępu: uznałem, nie mając na koncie właściwie żadnego opensourcowego projektu, że warto sięgnąć po coś z forumowej półki - tak oto po drodze powstał TreeStructInfoDSL, jako swoisty dodatek do http://4programmers.net/Forum/Spolecznosc/Projekty/236454-treestructinfo_-_format_tekstowych_i_binarnych_plikow_konfiguracyjnych.

Na wstępie zaznaczając: na tyle DSL, na ile pozwala na to pascal - koniec końców to tylko garstka funkcji
Po co DSL? Dokładnie mówiąc, na wypadek potrzeby tworzenia drzewek konfiguracyjnych z poziomu kodu.


Przykładowe drzewko:

4p-example-tsinfo-tree.png

Vanilla TreeStructInfo:

with TTSInfoTree.Create do
try
  RenameTree('example');
  WriteTreeComment('Example tree', '');

  CreateAttribute('', False, 'attribute');
  WriteString('attribute', 'normal attribute value');
  WriteAttributeComment('attribute', 'normal comment', '', ctDeclaration);

  CreateAttribute('', True, 'referenced attribute');
  WriteString('referenced attribute', 'referenced attribute value');
  WriteAttributeComment('referenced attribute', 'declaration comment', '', ctDeclaration);
  WriteAttributeComment('referenced attribute', 'definition comment', '', ctDefinition);

  CreateChildNode('', False, 'normal-node');
  WriteChildNodeComment('normal-node', 'normal comment', '', ctDeclaration);

  CreateChildNode('', True, 'referenced-node');
  WriteChildNodeComment('referenced-node', 'declaration comment', '', ctDeclaration);
  WriteChildNodeComment('referenced-node', 'definition comment', '', ctDefinition);
finally
  ExportTreeToFile('vanilla-tsi.tsinfo');
  Free;
end;

TreeStructInfoDSL:

with TreeStructInfo(
  Name('example'),
  Comment('Example tree'),

  Nodes([
    Node(
      Name('normal-node'),
      Comment('normal comment')),

    RefNode(
      Name('referenced-node'),
      DeclarationComment('declaration comment'),
      DefinitionComment('definition comment'))
  ]),

  Attributes([
    Attribute(
      Name('attribute'),
      Comment('normal comment'),
      Content('normal attribute value')),

    RefAttribute(
      Name('referenced attribute'),
      DeclarationComment('declaration comment'),
      DefinitionComment('definition comment'),
      Content('referenced attribute value'))
  ])
) do
begin
  ExportTreeToFile('tsidsl.tsinfo');
  Free;
end;

Plany na przyszłość (tj. jutro bądź kiedyśtam):

  • zniesienie musu grupowania atrybutów i węzłów,
  • użycie OpenChildNode/CloseChildNode do uzupełniania drzewa danymi (na ten moment używane są klejone ścieżki),
  • uzupełnienie docósw.

Z pascalem i jego ekosystemem mam małą styczność, dlatego jeżeli widzisz jakiś błąd w ogranizacji plików, formalnych wymagań czy czegokolwiek: powiedz :)
Po więcej zapraszam do samego repo.

0

Bum, nie ma już konieczności grupowania węzłów w Nodes([...]) oraz atrybutów w Attributes([...]) - teraz można walić wszystko jak leci w jednej sekcji Elements([...])

Przykład:

procedure TreeStructInfoTestCase.TreeWithUnitedContent;
begin
  with TreeStructInfo(
    Name('unioned'),
    Elements([
      Attribute(
        Name('normal-attr'),
        Comment('normal comment'),
        Content('normal value')),

      Node(
        Name('normal-node'),
        Comment('normal comment')),

      RefAttribute(
        Name('ref-attr'),
        DeclarationComment('declaration comment'),
        DefinitionComment('definition comment'),
        Content('ref value')),

      RefNode(
        Name('ref-node'),
        DeclarationComment('declaration comment'),
        DefinitionComment('definition comment'),
        Elements([
          Node(
            Name('nested-node')),

          Attribute(
            Name('nested-attr'))
        ]))
    ])
  ) do try
    // assertions... see: https://github.com/spartanPAGE/TreeStructInfoDSL/blob/718d2d6e4c1d4cf96c3a97ee3b8250df98e507a2/test/source/testtreestructinfo.pas#L239
  finally
    Free;
  end;
end;

Rezultat:
4p-example-tsinfo-unioned-content.png

Zarówno TreeStructInfo jak i Node oraz RefNode akceptują wynik produkowany przez Elements([...]).

0

Proponowałbym, aby możliwość mieszania typów elementów w jednym węźle nie była dostępna; Oczywiście jest to możliwe i zostanie łyknięte prze klasę TSimpleTSInfoTree, jednak według specyfikacji formatu, kolejność elementów powinna być układana według typów elementów - najpierw lista atrybutów, następnie lista węzłów potomnych; I tak właśnie zostanie utworzony plik wyjściowy (z posegregowanymi elementami), co wynika z budowy klasy dla węzłów - posiada ona dwie osobne listy dla elementów, więc już na poziomie dodawania elementów do drzewa, zostaną one posegregowane;

Natomiast kolejność elementów jednego typu według ich stanu referencjonowania jest dowolna;


Pisałem Ci już wcześniej, że nie podoba mi się grupowanie elementów w drzewku za pomocą funkcji Attributes i Nodes (teraz Elements) - nie odzwierciedla to wizualnej budowy plików konfiguracyjnych, według obsługiwanej składni; Elementy w plikach zawsze zapisywane są bezpośrednio w ciele węzłów, bez dedykowanych, nazwanych sekcji;

Według mnie DSL powinien być jak najbardziej zgodny z oryginalną formą źródłowej składni; Tutaj mamy jedynie protezę DSL, co i tak nie zmienia faktu, iż można utrzymać wysoką zgodność, nie tylko jeśli chodzi o brak nazwanych sekcji, ale także kolejności podawanych parametrów dla funkcji Attr, RefAttr, Node, RefNode i TreeStructInfo; To że jest możliwe zachowanie wysokiej zgodności pokazałem Ci w swojej wersji DSL, za pomocą otwartych macierzy;

PS: Proponuję, aby dyskusję z PMek przenieść tutaj - będzie wygodniej.

0

Pisałem Ci już wcześniej, że nie podoba mi się grupowanie elementów w drzewku za pomocą funkcji Attributes i Nodes (teraz Elements) - nie odzwierciedla to wizualnej budowy plików konfiguracyjnych, według obsługiwanej składni; Elementy w plikach zawsze zapisywane są bezpośrednio w ciele węzłów, bez dedykowanych, nazwanych sekcji;

Według mnie DSL powinien być jak najbardziej zgodny z oryginalną formą źródłowej składni; Tutaj mamy jedynie protezę DSL, co i tak nie zmienia faktu, iż można utrzymać wysoką zgodność, nie tylko jeśli chodzi o brak nazwanych sekcji, ale także kolejności podawanych parametrów dla funkcji Attr, RefAttr, Node, RefNode i TreeStructInfo; To że jest możliwe zachowanie wysokiej zgodności pokazałem Ci w swojej wersji DSL, za pomocą otwartych macierzy;

Proponujesz mi po prostu zastąpienie Attributes i Nodes bezpośrednio otwartymi tablicami; Mi jednak zależy na jednorodności - a stosując się do niej pozostają dwie opcje:

  • Pierwsza, która z dsla robi zwykłe przeciętne wywołanie funkcji:
TreeStructInfo('?', '?', [{?}], [{?}]);
  • Druga, która wymaga używania explicit nazw do tego, co robimy.
TreeStructInfo(Name('?'), Comment('?'), Nodes([{?}]), Attributes([{?}]));
  • Druga prim:
TreeStructInfo(Name('?'), Comment('>'), Elements([{?}]));

Przekłada się to także na inne elementy (w szczególności dotyka to elementów referencjowalnych).


Proponowałbym, aby możliwość mieszania typów elementów w jednym węźle nie była dostępna; Oczywiście jest to możliwe i zostanie łyknięte prze klasę TSimpleTSInfoTree, jednak według specyfikacji formatu, kolejność elementów powinna być układana według typów elementów - najpierw lista atrybutów, następnie lista węzłów potomnych; I tak właśnie zostanie utworzony plik wyjściowy (z posegregowanymi elementami), co wynika z budowy klasy dla węzłów - posiada ona dwie osobne listy dla elementów, więc już na poziomie dodawania elementów do drzewa, zostaną one posegregowane;

Zwróć jednak uwagę, że jest to sugestia, nie formalny wymóg; Takie drzewko jest jak najbardziej poprawne:
tsi-ex-pre.png
Jednakże po jego wczytaniu i ponownym zapisaniu zostanie przerobione na:
tsi-ex-post.png
Osobiście nie uważam tego za dobrą opcje, bo w przypadku trzymania takiego pliku w jakimkolwiek systemie kontroli wersji niepotrzebnie narobi to zmian w historii - tutaj moja propozycja do Ciebie, żeby takie zjawisko jednak nie występowało (chociaż sam na ten moment najpierw oddzielam węzły i atrybuty, dopiero później je dodaję);


Pamiętaj, że konwencja != prawo; Jeżeli chcesz, mogę dorobić paczuszkę która będzie traktowała konwencję jako prawo, ale aktualnie zależy mi na tym, żeby główną rolą tego DSLa było umożliwienie tworzenia drzew tsi z poziomu kodu w sposób bardziej intuicyjny, bez wielorazowego powtarzania flag typu, anonimych True/False i im podobnych;


Podsumowując:

  • Na tworzenie elementów w dowolnej kolejności pozwala format treestructinfo, twoje API i jestem za tym, żeby ten DSL też na to pozwalał.
  • Chciałbym, żeby twoje API nie dokonywało samo z siebie tego typu segregacji - powinno zapisywać elementy w takiej kolejności w jakiej były one tworzone; Do pilnowania konwencji proponuję jakieś Sanitize, Normalize lub coś podobnego.
0

Mi jednak zależy na jednorodności - a stosując się do niej pozostają dwie opcje: [...]

Tak, jednak błędne używanie jakiegokolwiek API to nie jest wina API, a programisty; Są dwie opcje:

  • upraczamy wewnętrzną budowę DSL, dając lepszy wygląd docelowych drzew w kodzie (bardziej zbliżony do składni plików), automatycznie pozwalając (ale nie zalecając) na błędne/nieczytelne użycie funkcji,
  • zostaje tak jak jest, funkcje silnie kontrolują programistę, na rzecz gorszego wyglądu docelowych drzewek (bardziej odbiegającego od składni plików);
    Ja osobiście jestem za pierwszą opcją, czyli uproszczeniu konstrukcji docelowych drzew i wykluczeniu konieczności ich grupowania za pomocą nazwanych sekcji; Natomiast jeśli ktoś będzie tych funkcji używać źle/po swojemu to już jego sprawa; Mnóstwo rzeczy można używać źle i to nie jest wina tych rzeczy, tylko użytkowników - gdyby było inaczej, forum było by puste;

Zwróć jednak uwagę, że jest to sugestia, nie formalny wymóg;

Póki co;

Osobiście nie uważam tego za dobrą opcje, bo w przypadku trzymania takiego pliku w jakimkolwiek systemie kontroli wersji niepotrzebnie narobi to zmian w historii - tutaj moja propozycja do Ciebie, żeby takie zjawisko jednak nie występowało (chociaż sam na ten moment najpierw oddzielam węzły i atrybuty, dopiero później je dodaję);

Zwróć uwagę na to, że format właśnie tak został zaprojektowany; Kolejność elementów od zawsze była narzucona, a możliwość zapisu bez zachowania tej kolejności wynika z procesu tworzenia całego projektu - ostateczną formę składni formatu dobrałem na podstawie istniejącego w tym czasie API, a nie na odwrót;

Moja biblioteka dopuszcza brak zachowanej kolejności tylko dlatego, że jego tesktowy parser jest tak zbudowany a nie inaczej; Bazuje na rekurencji i odpowiednim chodzeniu po węzłach uzupełnianego drzewa, co zapewnia poprawne załadowanie pliku nawet przy pomieszanej kolejności; To jeden z kilku czynników losowych, na które parser jest nieczuły; Oczywiście jest to zaleta, bo nie rzuca wyjątkami w przypadku lekkiego odstępstwa od specyfikacji, które w żaden sposób nie wpływa na finalną budowę drzewa w pamięci;

W przypadku plików binarnych, odpowiednia kolejność elementów jest wymogiem;

  • Na tworzenie elementów w dowolnej kolejności pozwala format treestructinfo, twoje API i jestem za tym, żeby ten DSL też na to pozwalał.

W całej specyfikacji, dokumentacji i tutorialach istnieją liczne przykłady zawartości plików konfiguracyjnych, które zachowują odpowiednią kolejność elementów; Jedyny powód dodania notki o możliwości niezachowania kolejności wynika tylko i wyłącznie z faktu, iż najpierw wykonałem pierwszą wersję biblioteki, a na jej podstawie napisałem specyfikację; W tym właśnie procesie dodałem informację o możliwości niezachowania kolejności, tylko dlatego, że parser był nieczuły (i nadal taki pozostanie);

Biblioteka TreeStructInfo nie wspiera mieszania elementów - o ile parser nie będzie protestował, to wszystkie pliki generowane przez nią zawierają poukładane elementy, bo takie było pierwotne założenie formatu; Umieszczenie notki o możliwości niezachowania odpowiedniej kolejności było moim błędem, który teraz należy naprawić (co właśnie czynię);

Z racji tej, że Twój DSL finalnie i tak korzysta z moich klas, nie masz na to wpływu - albo zachowasz kolejność elementów, albo użytkownik korzystający z niego będzie zawiedziony, bo na wyjściu dostanie nie to co będzie miał w swoim kodzie; Jeszcze wczoraj w swojej paczce tego nie miałeś - dziś jest; I nie rozumiem czy wprowadziłeś to aby celowo ponaginać zalecenia, czy po prostu byłeś ciekaw jak zaimplementować funkcje i cały mechanizm listy elementów, aby złamać wymóg zachowania kolejności; Obstawiam, że górę wzięła ciekawość :]

  • Chciałbym, żeby twoje API nie dokonywało samo z siebie tego typu segregacji - powinno zapisywać elementy w takiej kolejności w jakiej były one tworzone; Do pilnowania konwencji proponuję jakieś Sanitize, Normalize lub coś podobnego.

Nie mogę się na to zgodzić - nie takie było założenie formatu; Zawartość plików miała być tworzona w taki sposób, aby była czytelna; Grupowanie elementów na małych plikach nie wpłynie znacznie na czytelność, ale przy kilkuekranowych i dłuższych drzewach ma to bardzo duże znaczenie;

Załóżmy że w danym węźle jest 50 atrybutów i 20 niepustych węzłów potomnych - plik otwierasz w jakimś edytorze, np. Notepad++; Chcesz znaleźć dany atrybut i sprawdzić jaką ma wartość; Do przeszukania masz jedynie początkowe fragmenty węzłów - od nagłówka węzła do pierwszej deklaracji węzła potomnego, znajdującego się w nim; Nie musisz przeglądać całego ciała węzła, aby znaleźć dany atrybut lub je policzyć, bo wszystkie atrybuty są na początku jego ciała;

Takie było pierwotne założenie, które służy do poprawiania czytelności plików - organizacja elementów jest bardzo ważna; A to, że format TreeStructInfo nie kopiuje funkcjonalności z innych formatów, nie oznacza, że jest gorszy - po prostu jest specyficzny, taki jaki chciałem żeby był (lekko pedantyczny) i o to chodziło.

0

Przy okazji, skoro już zabrałem głos w tej sprawie, wspomnę o małej ułomności tej i mojej protezy DSL;

1. Instancje klas drzew

W swoim projekcie, podczas rozruchu aplikacji, ładowana jest zawartość pliku konfiguracyjnego do pamięci; Wygląda to tak, że najpiew tworzę sobie obiekt drzewa (w tym przypadku instancję klasy TSimpleTSInfoTree); Następnie znając jego ścieżkę, wołam metodę LoadFromFile, w celu załadowania kontentu pliku do pamięci; Jeżeli plik nie istnieje, aplikacja nie posiada do niego dostępu lub jego zawartość jest nieprawidłowa (ogólnie w przypadku jakiegokolwiek błędu ładowania drzewa), łapię wyjątek ETSInfoFileException i informuję użytkownika, że nie można załadować pliku; Uzytkownik może przerwać rozruch aplikacji, albo załadować drzewko domyślne i kontynuować rozruch; Załadowanie domyślnego drzewa realizowane jest za pomocą metody LoadFromLazarusResource (ładuję drzewo z zasobów aplikacji);

Do czego zmierzam - bez względu na to czy plik istnieje czy nie, czy da się go załadować czy nie, a także czy ładuję go z pliku czy dowolnego innego obsługiwanego źródła, operuję cały czas na jednej instancji klasy drzewa; Mało tego - obiekt drzewa tworzony jest raz podczas rozruchu programu oraz zwalniany z pamieci także raz, podczas zamykania aplikacji; Twój (mój też) ~DSL wyklucza możliwość operowania na jednej instancji klasy, bo nie pobiera obiektu drzewa w argumencie głównej funkcji - tworzy go wewnętrznie i zwraca w rezultacie;

Już pomijam fakt, iż nie jest zalecane tworzenie obiektów i zwracanie ich przez procedury/funkcje/metody na zewnątrz - aby utrzymać czytelność tej protezy, trzeba z takiego sposobu skorzystać; Mimo wszystko wymusza to tworzenie nowego obiektu drzewa, co w wielu przypadkach niestety ale jest nadmiarowe i nieco komplikuje kod wykonujący; Jeżeli mamy już obiekt drzewa w pamięci to aby skorzystać z tego ~DSL, najpierw trzeba go zwolnić, po to aby znów go utworzyć w głównej funkcji;

To pierwsza ułomność, której nie ma jak sensownie rozwiązać, aby zachować czytelność wywołań funkcji z Twojej biblioteki :]

2. Typ tworzonych drzew

Póki co, Twoja proteza umożliwia tworzenie drzew TTSInfoTree, czyli klasy rozszerzającej funkcjonalność tej podstawowej; Nie istnieje możliwość utworzenia drzewa klasy TSimpleTSInfoTree, jeśli ktoś taką będzie potrzebował;

3. Brak korzystania z możliwości konstruktora klasy bazowej

Jak wiadomo, druga wersja konstruktora klasy bazowej umożliwia określenie nazwy pliku docelowego w parametrze AFileName oraz trybu użytkowania drzewa, za pomocą zbioru enumów w parametrze ATreeModes; Twoje główne funkcje nie umożliwiają określenia tych danych, więc każde tworzone drzewo jest drzewem tekstowym oraz służącym jedynie dla operacji w pamięci;

Drzewo to nie zostanie automatycznie zapisane do pliku w destruktorze; Jedynym rozwiązaniem, aby drzewo trafiło do pliku, jest skorzystanie z metody eksportującej ExportTreeToFile, w której to plik faktycznie zostanie zapisany na dysku i która umożliwi jego eksport do konkretnej formy - tekstowej (tryb tmTextTree) lub binarnej (tryb tmBinaryTree); Jednak metody eksportujące zawarte są w klasie rozszerzonej, co wyklucza możliwość używania klasy podstawowej (o tym napisałem w punkcie 2.);


Jak widać, brak obsług prawdziwego DSL przez kompilator skutecznie utrudnia przygotowanie czegokolwiek podobnego i niestety, ale mocno to coś ogranicza; Dlatego też pisz śmiało ten projekt jeśli bardzo chcesz, jednak ja nie będę dodawał takiego ~DSL do oficjalnej paczki - zbyt wiele problemów, ograniczeń i ułomności; Wczoraj rozmawialiśmy na ten temat, kiedy to zastanawiałem się nad dodaniem własnej protezy do głównej paczki, jednak takiej kupce funkcji jest daleko do ideału;

Natomiast jeśli chodzi o paczkę TreeStructInfo mojego autorstwa, czekają mnie jeszcze małe kosmetyczne zmiany; Jeden element jej wymaga, a dokładniej właściwość TreeName; W chwili obecnej służy jedynie do odczytu nazwy drzewa - co mam zamiar zrobić to dodać możliwość modyfikacji tej nazwy, bez konieczności używania klasy rozszerzonej;

Po tej zmianie najprawdopodobniej zostanie wydana paczka w wersji RC1.

0

Twój (mój też) ~DSL wyklucza możliwość operowania na jednej instancji klasy, bo nie pobiera obiektu drzewa w argumencie głównej funkcji - tworzy go wewnętrznie i zwraca w rezultacie;

Spoko, da się załatwić. Będę przyjmował instancję.
2.

Już pomijam fakt, iż nie jest zalecane tworzenie obiektów i zwracanie ich przez procedury/funkcje/metody na zewnątrz

W takim razie pascal (a przynajmniej free pascal) chyba nigdy nie wyjdzie ze średniowiecza :P Ale załatwiając punkt 1 to będzie z głowy
3.

Jak wiadomo, druga wersja konstruktora klasy bazowej umożliwia określenie nazwy pliku docelowego w parametrze AFileName oraz trybu użytkowania drzewa, za pomocą zbioru enumów w parametrze ATreeModes; Twoje główne funkcje nie umożliwiają określenia tych danych

Patrz punkt 1.

Póki co, Twoja proteza umożliwia tworzenie drzew TTSInfoTree, czyli klasy rozszerzającej funkcjonalność tej podstawowej; Nie istnieje możliwość utworzenia drzewa klasy TSimpleTSInfoTree, jeśli ktoś taką będzie potrzebował;

Nie widzę powodu do serwowania TSimpleTSInfoTree, skoro TTSInfoTree z niego dziedziczy (a właśnie tego muszę użyć żeby zrobić swoje)
5.

W swoim projekcie, podczas rozruchu aplikacji, ładowana jest zawartość pliku konfiguracyjnego do pamięci; Wygląda to tak, że najpiew tworzę sobie obiekt drzewa (w tym przypadku instancję klasy TSimpleTSInfoTree); Następnie znając jego ścieżkę, wołam metodę LoadFromFile, w celu załadowania kontentu pliku do pamięci; Jeżeli plik nie istnieje, aplikacja nie posiada do niego dostępu lub jego zawartość jest nieprawidłowa (ogólnie w przypadku jakiegokolwiek błędu ładowania drzewa), łapię wyjątek ETSInfoFileException i informuję użytkownika, że nie można załadować pliku; Uzytkownik może przerwać rozruch aplikacji, albo załadować drzewko domyślne i kontynuować rozruch; Załadowanie domyślnego drzewa realizowane jest za pomocą metody LoadFromLazarusResource (ładuję drzewo z zasobów aplikacji);

Domyślam się że nie jest to typowy use-case, ale w momencie gdyby te drzewka miały używać danych dostarczonych skądinąd, to opcja z lazarusowym zasobem odpada, więc całość diabli biorą.

0

W takim razie pascal (a przynajmniej free pascal) chyba nigdy nie wyjdzie ze średniowiecza :P

Zauważ, że napisałem nie jest zalecane, a nie że nie wolno; Zresztą, niby dlaczego? Zwracanie w kółko self-instancji to nie jest żaden obowiązek współczesnych języków i nie widzę powodu, aby miało to tak wyglądać we Free Pascalu;

Domyślam się że nie jest to typowy use-case [...]

Nie, to jest brak prawidziwego DSLa :]

Przy okazji wybrałem taki sposób, dlatego że ręczne wpisywanie danych do drzewa jest nieco z tyłka, a jeśli domyślne drzewo będzie długie to mi metody wyjdą bardzo długie, czego nie lubię; A tak to mogę jedną instrukcją pobrać drzewko domyślne, bez względu na jego zawartość; Czy będzie miało kilka elementów, czy kilka tysięcy - nie ma to znaczenia, i tak wywołuję jedną metodę i mam całość z głowy;
____Edit: Jeszcze jedna rzecz, jeśli chodzi o poprzedni post;

Spoko, da się załatwić. Będę przyjmował instancję.

Nie możesz przyjmować instancji, bo jeśli ktoś poda instancję klasy podstawowej to nie będziesz na niej mógł wywołać metod z klasy rozszerzonej;

Pamietaj, że klasa TSimpleTSInfoTree nie jest typową klasą bazową do rozszerzania (gdyby tak było, nazwałbym ją TCustomTSInfoTree); Zawiera ona najpotrzebniejsze metody, głównie do ładowania drzew, tworzenia elementów oraz zapisu i odczytu danych z atrybutów; Jest to takie minimum potrzebne do obsługi plików; Natomiast klasa TTSInfoTree zawiera to co podstawowa (co zapewnia dziedziczenie) oraz dodatkowe metody dla rzadziej stosowanych operacji.

0

@spartanPAGE - dostępna jest nowa wersja biblioteki (RC1); Pobierz sobie ją i przekompiluj swoją paczkę;

Jedyną konieczną zmianą jaką musisz wykonać, to zamienić wywołanie metody RenameTree na bezpośrednie użycie właściwości TreeName, które od teraz aż po wieki umożliwiać będzie również modyfikację identyfikatora drzewa; Wiem - nieco z liter czterech ta zmiana, ale dzięki temu możliwa będzie pełna kontrola nazwy drzewa z obiektu klasy podstawowej (TSimpleTSInfoTree), więc zmiana na plus;

Do stabilnej wersji libki coraz bliżej.

0

@spartanPAGE - jedna mała wskazówka, jeśli o highlighter do Notepad++ chodzi;

Albo obsługa własnego kolorowania składni jest upośledzona, albo ja nie umiem tego ustawić (choć próbowałem wielokrotnie i na wszelkie sposoby); Nieważne - jeśli drzewo kończy się linią z frazą kluczową end tree lub end ref node, ostatnią linią w pliku musi być pusta linia; W przeciwnym razie fraza nie zostanie pomalowana, co widać na zrzucie z edytora w pierwszym poście tego wątku, a także na GitHub; Ogólnie rzecz biorąc, Notepad++ nie pomaluje takiej frazy, jeżeli po niej nie będzie już żadnego znaku;

Spacje z prawej strony i tak są trimowane przez moją bibliotekę, więc nie ma sensu ich tam dodawać; Co więcej, każdy tekstowy plik wygenerowany przez moją libkę, na końcu zawierać będzie CRLF (lub samo LF/CR, jeśli to nie Windows), więc edytor ten będzie prawidłowo kolorował składnię;

To tak na marginesie, bo pewnie nie zauważyłeś tego szczegółu na zrzutach :]

1 użytkowników online, w tym zalogowanych: 0, gości: 1