Indeksowanie kodu#
Universal Ctags#
Podstawowe informacje o Universal Ctags można znaleźć w poniższych materiałach:
Exuberant Ctags 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.
Ostatnia wersja Exuberant Ctags oznaczona numerem 5.8 pochodzi z 9 lipca 2009 roku. Na chwile obecną projekt został przejęty przez społeczność i jest rozwijany pod 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 - dowolny identyfikator (bez białych znaków).
\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 do pliku względem bieżącego folderu, w którym zdefiniowano tagname.
- tagaddress - polecenie trybu ex, które pozwoli edytorowi zlokalizować tagname. Może to być np. tylko numer linii lub wyrażenie regularne, co ma zabezpieczyć przed wykonaniem dowolnego polecenia.
[]
- opcjonalne rozszerzające flagi, na które składają się:;"
- średnik połączony z podwójnym cudzysłowem umieszczany zaraz za tagaddress, 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 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
- define (from #define XXX)f
- function or method namec
- class nameF
- file nameg
- enumeration namem
- member (of structure or class data)p
- function prototypes
- structure namet
- typedefu
- union namev
- variable
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ę pola "kind:"
można pominąć, co pozwoli zmniejszyć plik indeksu o około 15%. Program czytający plik indeksu może rozpoznać pole "kind:"
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 dodatkowych pól 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 znacznikó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.
Obsługiwane języki#
Exuberant Ctags bez dodatkowych zabiegów obsługuje 41 różnych języków. Poniższa lista wymienia je wszystkie w kolejności alfabetycznej:
- Ant
- Assembler
- ASP
- Awk
- BASIC
- BETA
- C
- C++
- C#
- COBOL
- DOS Batch
- Eiffel
- Erlang
- Flex
- Fortran
- HTML
- Java
- JavaScript
- Lisp
- Lua
- Make
- MATLAB
- Objective Caml
- Pascal
- Perl
- PHP
- PL/SQL
- Python (Pyrex/Cython)
- REXX
- Ruby
- Scheme
- Shell scripts (Bourne/Korn/Z)
- S-Lang
- SML (Standard ML)
- Tcl
- TeX
- Vera
- Verilog
- VHDL
- Vim
- YACC