Skip to content

MiguelBarro/cpptags

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 

Repository files navigation

cpptags

Vim plugin that customizes 'tagfunc' for C++ files.

Index

Motivation

The default builtin 'tagfunc' is not suitable for modern C++, lacks support for: namespaces and classes, function and methods signatures, template parameters and specialization.

The new ctags releases provide support for the above features and more.

This filetype plugin will automatically set up a new 'tagfunc' that will profit from the new ctags fields.

Installation

An obvious precondition is having ctags installed. Is available on the most popular package managers:

  • Ubuntu:
    $ sudo apt install universal-ctags
  • Windows:
    > winget install UniversalCtags.Ctags 
  • MacOs:
    $ brew install universal-ctags

This plugin can be installed using any popular plugin manager (vim-plug, Vundle, etc...) but vim plugin integration is extremely easy in later releases (version8.0 introduced package support):

  • A vimball is distributed by www.vim.org. Installation is as easy as sourcing the vimball file:

    :source cpptags.vba

    so is uninstall:

    :RmVimball cpptags.vba
  • The github repo can be cloned direcly into the $VIMRUNTIME/pack directory as explained in matchit-install. Though using this approach many useless files in this repo will be installed too.

  • Use getscript plugin to automatically download an update it. Update the local $VIMRUNTIME/GetLatest/GetLatestVimScripts.dat adding a line associated with this plugin.

Once installed the vim's filetype plugins must be enabled:

:filetype plugin on

is adviceable to introduce this into the .vimrc if not there already.

Usage

For proper operation the plugin requires:

  • set up a tag file suitable for modern C/C++ navigation.
  • learn how to prompt the :tag and :tselect commands.

Required tag generation options

In order to profit from the new ctags output several non-default options must be used:

$ ctags --extras=+f -R --languages=c++ --langmap=c++:+.c. --c++-kinds=+pl -n --fields=+iaeSK --fields-c++=*
        --options=<option-file-path> -f <output-tags-file> <source-dirs>

The options file allows simplifying the command line by including there lenghtly parameters. It is usual to include there options devoted to resolve preprocessor macros that ctags cannot understand by itself.

Old C/C++ libraries relied heavily on preprocessor magic and require tedious composition of option files to generate a useful ctags output. For example, the microsoft version of the STL requires the following options:

    -D _STD_BEGIN= namespace std {
    -D _STD_END=}
    -D _STD= ::std::
    -D _CHRONO= ::std::chrono::
    -D _RANGES= ::std::ranges::
    -D _STDEXT_BEGIN= namespace stdext {
    -D _STDEXT_END=}
    -D _STDEXT= ::stdext::
    -D _CSTD=::
    -I _NODISCARD
    -I _EXPORT_STD
    -I _CRT_GUARDOVERFLOW
    -I _Out_writes_all_+
    -I _In_reads_+
    -I _CONSTEXPR20 
    -I _CONSTEXPR17
    -I __PURE_APPDOMAIN_GLOBAL
    -I _CRTDATA2_IMPORT
    -I _CRTIMP2_PURE_IMPORT
    -I __thiscall
    -I _STL_RESTORE_CLANG_WARNINGS
    -I _STL_DISABLE_CLANG_WARNINGS

fortunately new C/C++ libraries favor the new template capabilities and discourage preprocessor usage. For example, C++/WinRT projection sources can be parsed completely with only two options:

    -D WINRT_IMPL_AUTO(B)=B
    -I WINRT_EXPORT

Once the tag file is generated it must be appended to the 'tags' option which contains patterns to locate tag files. For example if the output file is in ~/mytagfiles/stl.tags we must do:

:set tags+=~/mytagfiles/stl.tags

For convenience, this can be included in the source files using a modeline. For example ending the sources with:

/* vim: set tags+=~/mytagfiles/stl.tags: */

New tag syntax

The syntax follows the natural semantics of C++. The basics are simple:

  • namespaces/classes. The use of qualified identifiers restricts the tag choices. For example:

    void f(); // case a
    namespace A {
        void f(); // case b
    namespace B {
        void f(); // case c
    }}

    Doing:

    • :tsel f will show all 3 cases (a,b & c).
    • :tsel A::f will show 2 cases (b & c) and prioritize b.
    • :tsel A::B will show only case c.
  • template syntax:

    template <typename A, typename B> class A; // forward declaration, case a
    template <typename A, typename B> class A {}; // declaration, case b
    template <> class A<int, double> {}; // specialization, case c

    Case a is ignore by ctags. Doing:

    • :tsel A or :tsel A<> will show case b only (specializations are ignored).
    • :tsel A<int> or :tsel A<int,double> will show case c only.
  • signature syntax:

    class a {}; // case a
    void a(); // declaration, case b
    void a() {} // definition, case c
    void a(int e, int f); // declaration, case d 
    void a(int e, int f) {} // definition, case e 

    Doing:

    • :tsel a will show all cases.
    • :tsel a() will show c, b, e & d. It:
      • ignores non-functions.
      • prioritizes definitions over declarations.
      • prioritizes functions without arguments.
    • :tsel a(int) or :tsel a(int, int) will show e, d because matches the signature.

All the above rules can be combined together, for example in:

namespace A {
    class B {
        template<bool> void m(int) {} // case a
        template<> void m<true>(int) {} // case b
};}

the command:

:tag A::B::m<true>(int)

will match case b which is the result of combining the rules above.