If you’ve been frustrated because Universal Ctags keeps scanning directories like node_modules, .git, or vendor — even though you explicitly told it to ignore them with --exclude — you’re not alone. And it’s not your fault. The issue isn’t your syntax; it’s the order of your command.
The key detail, buried in the man page, is this:
“<options> must precede the <source_file(s)> following the standard POSIX convention.”
In plain terms, all options — including --exclude, -R, and -f — must come before any file or directory path. If you put options after the path, like
ctags -R . --exclude=node_modules
ctags treats everything after the path as additional input files, not as options. That means your --exclude flags are silently ignored.
The fix is simple: always place all options before the directory. For example, this works:
ctags --exclude=node_modules --exclude=vendor --exclude=.git -R .
But this does not:
ctags -R . --exclude=node_modules
Once you reorder your command correctly, your excludes will finally take effect. You don’t need wildcards or complex patterns—just --exclude=node_modules is enough to skip all directories named node_modules at any depth, as long as the option is parsed correctly.
A practical tip: if you’re still seeing unwanted files scanned, run your command with --verbose=yes to see exactly what ctags is processing. You’ll quickly spot whether a directory is being skipped or not.
In short: options first, paths last. Get that right, and the annoying scanning of undesired directories will stop for good. Happy tagging!
PS. If you’re using ctags through vim-gutentags, you might also run into similar exclude issues — here’s how to fix them. Also, if you are tired of slow IDEs, broken plugins, and messy configs that kill your productivity, you’re not alone — and you’re not stuck. There’s a better way — and it starts with Neovim (take a look at this book).
