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,tagfile
is 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.