Exuberant Ctags#
Wstęp#
Podstawowe informacje o projekcie Exuberant Ctags można znaleźć w poniższych materiałach:
- Strona domowa
- Źródło z SourceForge
- Lista obsługiwanych języków (41)
- Lista krajów korzystających z projektu (50)
- Lista narzędzi korzystających z projektu
- Manual (włącznie z opisem polecenia ctags)
- Format pliku z tagami (czytelniejsza postać)
- Jak dodawać nowe języki
- Zalety Exuberant Ctags
- FAQ
Exuberant Ctags (w skrócie eCtags) to pierwsza wielojęzyczna implementacja Ctags tworzona i rozwijana do 2009 roku przez Darrena Hieberta. Projekt wprowadził indeksację dla 41 języków i względem oryginalnego Ctags rozszerzył nieco format tagu oraz format pliku indeksu, który pozostał wstecznie kompatybilny i można go stosować opcjonalnie.
Na chwile obecną projekt Exuberant Ctags został przejęty przez społeczność i jest rozwijany pod nową nazwą Universal Ctags.
Format tagu i pliku indeksu#
Składnia pojedynczego tagu zgodnego z rozszerzonym formatem Exuberant Ctags # jest następująca:
{tagname}\t{tagfile}\t{tagaddress}[;"\t{tagfield}..]gdzie poszczególne człony oznaczają:
- tagname # - nazwa tagu, którą może być dowolny identyfikator (bez białych znaków) dla danego taga.
\t- dokładnie jeden znak tabulacji (\x0b) stanowiący separator między poszczególnymi polami w tagu. Niektóre edytory i generatory pliku indeksu pozwalają stosować dowolny rodzaj i ilość białych znaków.- tagfile # - ścieżka względna lub bezwzględna do pliku, dla którego utworzono
tagname(szczegóły). - tagaddress # - jedno z poleceń zgodnych z edytorem EX, które pozwoli dowolnemu programowi zlokalizować
tagname. Może to być np. tylko numer linii lub wyrażenie regularne, co ma zabezpieczyć przed wykonaniem dowolnego (potencjalnie niebezpiecznego) polecenia. []# - opcjonalne rozszerzające pola, na które składają się:;"- średnik połączony z podwójnym cudzysłowem umieszczany zaraz zatagaddress, co wygląda jak rozpoczęcie komentarza w edytorze Vi.tagfield # - pojedyncze pole składające się z pary
"nazwy:wartość". Za komentarzem może pojawić się kilka takich pól rozdzielonych od siebie znakiem tabulacji. Całość musi spełniać następujące reguły:- Nazwa może zawierać tylko znaki alfabetyczne. Małe i wielkie litery są dozwolone, ale zaleca się używanie małych liter. Wielkość znaków ma znaczenie, zatem
"kind:"i"Kind:"to dwie różne nazwy. - Wartość może być pusta.
- Kiedy wartość zawiera
"\t"to oznacza znak tabulacji. - Kiedy wartość zawiera
"\r"to oznacza znak CR. - Kiedy wartość zawiera
"\n"to oznacza znak LF. - Kiedy wartość zawiera
"\\"to oznacza pojedynczy znak"\". - Inne sposoby użycia znaku backslasha
"\"są zarezerwowane dla przyszłych rozszerzeń. - Kiedy wartość zawiera ścieżki zgodne z MS-DOS/Windows to znak backslasha
"\"musi zostać zdublowany!
Sugerowane nazwy w dodatkowych polach dla języków C i C++ są następujące (opisy w oryginalnej angielskiej postaci):
arity# - Number of arguments for a function tag.class# - Name of the class for which this tag is a member or method.enum# - Name of the enumeration in which this tag is an enumerator.file# - Static (local) tag, with a scope of the specified file. When the value is empty,tagfileis used.function# - Function in which this tag is defined. Useful for local variables (and functions). When functions nest (e.g., in Pascal), the function names are concatenated, separated with '/', so it looks like a path.kind# - Kind of tag. The value depends on the language. For C and C++ these kinds are recommended:c- class named- macro definitions (from #define XXX)e- enumerators (values inside an enumeration)f- function or method nameF- file nameg- enumeration namel- local variables [off]m- member (of structure or class data)n- namespacesp- function prototype [off]s- structure namet- typedefu- union namev- variablex- external and forward variable declarations [off]
When this field is omitted, the kind of tag is undefined.
struct# - Name of the struct in which this tag is a member.union# - Name of the union in which this tag is a member.
Przy pisaniu programów generujących tagi dla języków innych niż C/C++ powyższa lista powinna zostać rozszerzona o nazwy właściwe dla danego języka. Pomoże to uniezależnić się od używania jednego słusznego programu indeksującego.
- Nazwa może zawierać tylko znaki alfabetyczne. Małe i wielkie litery są dozwolone, ale zaleca się używanie małych liter. Wielkość znaków ma znaczenie, zatem
Prosty przykład:
asdf sub.cc /^asdf()$/;" new_field:some\svalue file: foo_t sub.h /^typedef foo_t$/;" kind:t func3 sub.p /^func3()$/;" function:/func1/func2 file: getflag sub.c /^getflag(arg)$/;" kind:f file: inc sub.cc /^inc()$/;" file: class:PipeBuf
W praktyce nazwę "kind:" w polu tagfield można pominąć (jest to domyślne zachowanie), co pozwoli zmniejszyć plik indeksu o około 15%. Program czytający plik indeksu może rozpoznać nazwę "kind:" w polu tagfield z uwagi na brak ":" przed jego wartością. Prosty przykład:
foo_t sub.h /^typedef foo_t$/;" t getflag sub.c /^getflag(arg)$/;" f file:
Kiedy jakieś pole tagfield występuje wiele razy w linii danego tagu to pod uwagę brane jest ostatnie z nich.
Znaki końca linii#
Oryginalny Ctags początkowo był obsługiwany jedynie przez edytor Vi w systemach Uniksowych, gdzie separatorem między liniami jest pojedynczy znak LF. W systemach MS-DOS/Windows separatorem między liniami jest kombinacja znaków CR LF. Aby zwiększyć przenośność one także są obsługiwane w pliku indeksu pod dowolnym systemem.
W przypadku systemu macOS separatorem między liniami jest pojedynczy znak CR. Jego obsługa w Uniksach powoduje problemy, gdyż większość implementacji funkcji fgets() nie traktuje znaku CR jako separatora między liniami. Z tego też względu obsługa znaku CR jako separatora linii w pliku indeksu została ograniczona tylko do systemu macOS.
Poniższa tabela zawiera podsumowanie dla znaków końca linii stosowanych w poszczególnych systemach operacyjnych i dopuszczalnych w pliku indeksu.
| Znaki końca linii | Używane w systemie | Dopuszczalne w pliku indeksu pod systemem |
|---|---|---|
| LF | Unix | Unix, MS-DOS/Windows, macOS |
| CR | macOS | macOS |
| CR LF | MS-DOS/Windows | Unix, MS-DOS/Windows, macOS |
Z oczywistych względów znaki CR i LF nie mogą występować wewnątrz linii z konkretnym tagiem.
Białe znaki#
W edytorze Vi dozwolone jest używanie dowolnego białego znaku do rozdzielania tagname od tagfile oraz tagaddress od tagfile w linii z tagiem. W praktyce większość programów generujących plik indeksu używa pojedynczego znaku tabulacji do rozdzielnia poszczególnych pól w tagu. Wyjątkiem może być rozwiązanie przyjęte przez wtyczkę SourceCookiefire, gdzie separatorem między polami w tagu jest znak kontrolny SOH (Start of Heading, \x01).
Z uwagi na tę dobrowolność może wystąpić konflikt między wybranym znakiem separacji a wartościami w konkretnych polach, jak w przypadku nazw plików zawierających spację w polu tagfile. Aby to obejść można by użyć jakiegoś znaku ucieczki, dla przykładu "\s". Niestety w systemach MS-DOS/Windows znak backslasha "\" jest używany do oddzielania poszczególnych członów w ścieżkach. Przykładowo w ścieżce "c:\vim\sap" występuje sekwencja "\s", ale w tym kontekście nie oznacza ona spacji. Można by zdublować znaki backslasha "\", ale to spowoduje rozrost pliku indeksu i utrudni/spowolni późniejsze jego parsowanie.
Z uwagi na powyższe problemy oryginalne narzędzie dostarczane przez Exuberant Ctags używa jedynie znaku tabulacji do rozdzielania poszczególnych pól w tagu, i nie zezwala na używanie tego znaku w polu tagname i tagfile. Wciąż istnieje możliwość zastosowania innych znaków, np. poprzez ręczną edycję pliku indeksu lub utworzenie własnego programu generującego/modyfikującego ten plik.
Pseudo tagi#
W pliku indeksu można umieścić dodatkowe linie zawierające pseudo tagi, które przekażą dodatkowe informacje na temat zawartości tego pliku, np. czy tagi zostały posortowane lub czy użyto dodatkowe pola tagfield. Informacje te mogą być wykorzystane zarówno do optymalizacji odczytu pliku indeksu (np. poprzez włączanie/wyłączanie wyszukiwania binarnego), jak i dostarczenia ogólnych informacji na temat tego pliku (np. wersję i rodzaj programu użytego do jego generacji).
Nazwy tych pseudo tagów najlepiej dobierać tak, aby po wykonaniu sortowania zawsze znajdowały się w pobliży pierwszej linii pliku indeksu. W praktyce bardzo dobrze spisuje się początkowa kombinacja "!_TAG_". Należy zauważyć, że po wykonaniu sortowania rzadki tag w postaci "!" może pojawić się przed liniami z pseudo tagami. Programy odczytujące plik indeksu powinny rozpoznawać i w odpowiedni sposób przetwarzać wszystkie pseudo tagi.
Każde narzędzie generujące plik indeksu może wprowadzać własne pseudo tagi w dowolnej ilości (patrz dodatkowe pseudo tagi w Universal Ctags).
Prosty przykład:
!_TAG_FILE_FORMAT {version-number} /optional comment/
!_TAG_FILE_SORTED {0|1} /0=unsorted, 1=sorted/
!_TAG_PROGRAM_AUTHOR {author-name} /{email-address}/
!_TAG_PROGRAM_NAME {program-name} /optional comment/
!_TAG_PROGRAM_URL {URL} /optional comment/
!_TAG_PROGRAM_VERSION {version-id} /optional comment/W pierwszej linii pole {version-number} może informować o użytym formacie tagów, np. wartość 1 oznacza oryginalny format Ctags a wartość 2 oznacza rozszerzony format z Exuberant Ctags. W drugiej linii przekazana zostaje informacja o ewentualnym posortowaniu tagów w pliku indeksu. Pozostałe linie dostarczają szczegółowych informacji o programie użytym do wygenerowania pliku indeksu.