强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Ctags 完全指南:代码导航与标签索引 / 第 4 章:语言支持与解析器

第 4 章:语言支持与解析器

4.1 概述

Universal Ctags 内置了 60 多种编程语言的解析器(parser),每种语言都有专门的规则来识别符号定义。本章将介绍语言支持的完整体系。


4.2 查看支持的语言

# 列出所有支持的语言
ctags --list-languages

# 输出示例(部分):
# Ant
# Asm
# Asp
# Awk
# Basic
# C
# C#
# C++
# Clojure
# Cobol
# CSS
# D
# Dart
# Elixir
# Erlang
# Fortran
# Go
# HTML
# Java
# JavaScript
# JSON
# Julia
# Kotlin
# Lisp
# Lua
# Make
# Markdown
# ObjectiveC
# Pascal
# Perl
# PHP
# Python
# R
# Ruby
# Rust
# Scala
# Shell
# SQL
# Swift
# Tcl
# TypeScript
# Vera
# Verilog
# VHDL
# Vim
# XML
# YAML
# Yacc

查看语言详情

# 查看某种语言的 kind 列表
ctags --list-kinds=C

# 输出:
# d  macro definitions      [off]
# e  enumerators (values inside an enumeration) [on]
# f  function definitions   [on]
# g  enum names             [on]
# h  included header files  [off]
# l  local variables        [off]
# m  struct/union members   [on]
# n  namespaces             [on]
# p  function prototypes    [on]
# s  structure names        [on]
# t  typedef names          [on]
# u  union names            [on]
# v  variable definitions   [on]
# x  external variable declarations [off]

# 查看某种语言的字段列表
ctags --list-fields=C

# 输出:
# N  name         on   NONE             s--    no    tag name
# F  file         on   NONE             s--    no    file name
# P  pattern      on   NONE             s--    no    pattern
# C  compact      off  NONE             s--    no    compact kind letter
# K  kind         on   NONE             s--    no    kind (long form)
# S  signature    on   NONE             s--    no    signature
# ...

查看文件扩展名映射

# 列出所有语言的扩展名映射
ctags --list-map-extensions

# 查看特定语言的扩展名
ctags --list-map-extensions=Python
# 输出:.py .pyw .pyx

# 查看文件名模式映射
ctags --list-map-patterns

4.3 主要语言详解

C / C++

C 和 C++ 是 Ctags 支持最成熟的语言。

# 查看 C 语言支持的 kinds
ctags --list-kinds=C

# 查看 C++ 语言支持的 kinds
ctags --list-kinds=C++
KindCC++含义
d宏定义 (#define)
e枚举值
f函数定义
g枚举类型名
h头文件 (#include)
l局部变量
m结构体/类成员
n命名空间
p函数原型
s结构体名
ttypedef
u联合体名
v变量定义
x外部变量声明

C++ 额外支持:

Kind含义
c类名
A类的别名
N命名空间别名
Z模板参数
Uusing 声明

Python

ctags --list-kinds=Python

# 输出:
# c  classes              [on]
# f  functions            [on]
# m  class members        [on]
# v  variables            [on]
# i  imports              [off]
# l  local variables      [off]
# x  unknown              [on]
# z  parameters           [off]
# I  aliases of names     [off]

Python 特殊标签示例:

class MyClass:                    # → c (class)
    def method(self):             # → m (member)
        local_var = 10            # → v (variable) - 默认关闭

def func():                       # → f (function)
    pass

import os                         # → i (import) - 默认关闭
from sys import argv              # → i (import) - 默认关闭
# 包含 import 信息
ctags -R --kinds-python=+i .

# 查看 Python 类型引用字段
ctags -f - --fields-Python=+{defval}{section}{parameterDecoration} src/*.py

JavaScript / TypeScript

ctags --list-kinds=JavaScript

# 输出:
# C  constants            [on]
# c  classes              [on]
# f  functions            [on]
# g  generators           [on]
# m  methods              [on]
# p  properties           [on]
# v  variables            [on]
# G  global variables     [on]
# E  exported variables   [on]

ctags --list-kinds=TypeScript

# 输出:
# C  constants            [on]
# c  classes              [on]
# e  enumerators          [on]
# f  functions            [on]
# g  enums                [on]
# i  interfaces           [on]
# m  methods              [on]
# n  namespaces           [on]
# p  properties           [on]
# v  variables            [on]
# z  generic types        [on]

JavaScript 模块解析(子语言):

# HTML 内嵌 JavaScript 可以独立解析
ctags --map-HTML=+.vue .

# JSX 支持
ctags --languages=JavaScript --kinds-JavaScript=+g src/*.jsx

Go

ctags --list-kinds=Go

# 输出:
# p  packages             [on]
# f  functions            [on]
# c  constants            [on]
# t  types                [on]
# v  variables            [on]
# m  struct members       [on]
# i  imports              [on]
# n  unknowns             [on]
# s  aliases of types     [on]
# e  unknowns             [on]
# u  unknowns             [on]

Rust

ctags --list-kinds=Rust

# 输出:
# M  modules              [on]
# n  modules              [on]
# c  implementations      [on]
# f  functions            [on]
# g  enum types           [on]
# i  trait items          [on]
# I  implementations      [on]
# m  struct members       [on]
# e  enumerators          [on]
# P  methods              [on]
# s  struct types         [on]
# t  type aliases         [on]
# v  variables            [on]
# G  enum variants        [on]
# T  traits               [on]
# U  unions               [on]
# N  implementations      [on]
# F  method declarations  [on]

4.4 语言映射系统

Ctags 通过三种方式将文件关联到语言:

文件扩展名映射

# 查看当前映射
ctags --list-map-extensions=C
# 输出: .c .h

# 添加新的扩展名映射
ctags --map-C=+.ec --map-C=+.pg .

# 移除扩展名映射
ctags --map-C=--.h .

# 使用配置文件设置(永久)
# ~/.config/ctags/default.ctags
# --map-C=+.ec

文件名模式映射

# 查看文件名模式
ctags --list-map-patterns=Sh

# 输出:
# Sh .sh .bash .bashrc .bash_profile .bash_login .profile

# 添加自定义文件名模式
ctags --map-Sh=+.zshrc --map-Sh=+.zshenv .

特殊文件名映射

# 某些文件名被直接映射到语言
# 例如 Makefile → Make, Rakefile → Ruby

# 添加新的文件名映射
ctags --map-Make=+Makefile.local .

常用语言映射操作

# 将 .inc 文件映射为 PHP
ctags --map-PHP=+.inc .

# 将 .h 文件同时映射到 C 和 C++(联合解析)
ctags --map-C++=+.h .

# Vue 文件映射为 HTML(内嵌 JS/CSS 会被子解析器处理)
ctags --map-HTML=+.vue .

# Dockerfile 作为配置文件解析
ctags --map-Make=+Dockerfile .

💡 提示:修改映射后最好加入 .ctags 配置文件,避免每次命令行输入。


4.5 自定义语言解析器(正则定义)

Universal Ctags 允许用户通过正则表达式定义新的语言解析器。这是 Ctags 最强大也最灵活的功能之一。

基本语法

--regex-<LANG>=<pattern>/<name>/<kind-spec>/[flags]
组件说明
<LANG>语言名称(必须已注册或自定义)
<pattern>匹配符号定义的正则表达式
<name>标签名,可使用 \1 等反向引用
<kind-spec>kind 字母和名称,如 ff:function
flags可选标志:{scope=set}, {exclusive}, {icase}

示例:为自定义配置格式添加解析器

假设项目中有一种 .conf 配置文件:

[server]
host = localhost
port = 8080

[database]
url = postgres://localhost/mydb
max_connections = 100
# 为配置文件格式创建解析器
cat > ~/.config/ctags/myconf.ctags << 'EOF'
# 定义新语言
--langdef=conf

# 映射文件扩展名
--map-conf=+.conf

# section 头: [section_name]
--regex-conf=/^\[([a-zA-Z_][a-zA-Z0-9_]*)\]/\1/s,section/{scope=push}

# key = value
--regex-conf=/^([a-zA-Z_][a-zA-Z0-9_]*)\s*=/\1/k,key/{scope=ref}
EOF

# 测试
ctags -f - --options=myconf test.conf

示例:为 Markdown 标题添加解析器

cat > ~/.config/ctags/markdown-heading.ctags << 'EOF'
# Markdown 标题解析
--regex-Markdown=/^#{1,6}\s+(.+)/\1/c,chapter,heading/{scope=set}
EOF

# 测试
echo '# My Chapter' | ctags -f - --options=markdown-heading.ctags -L -

示例:为 Ansible Playbook 添加解析器

cat > ~/.config/ctags/ansible.ctags << 'EOF'
--langdef=ansible
--map-ansible=+.yml
--map-ansible=+.yaml

# Ansible task name
--regex-ansible=/^  - name:\s*(.+)/\1/t,task/{exclusive}
--regex-ansible=/^- hosts:\s*(.+)/\1/h,host/{exclusive}
EOF

正则定义高级标志

标志说明
{scope=push}推入新的作用域
{scope=pop}弹出当前作用域
{scope=set}设置当前作用域
{scope=ref}引用当前作用域
{scope=clear}清空作用域栈
{exclusive}排除其他正则(互斥)
{icase}不区分大小写
{placeholder}用于占位,不输出
{postrun}在所有输入后运行
{_autoFQTag}自动生成完全限定名

作用域示例

cat > ~/.config/ctags/ini.ctags << 'EOF'
--langdef=ini
--map-ini=+.ini

# [section] 推入作用域
--regex-ini=/^\[([^\]]+)\]/\1/s,section/{scope=push}{_autoFQTag}

# key = value 引用当前作用域
--regex-ini=/^([a-zA-Z0-9_.-]+)\s*=/\1/k,key/{scope=ref}

# 空行或新 section 弹出作用域
--regex-ini=/^\[/\0/s,section/{scope=pop}{exclusive}{placeholder}
EOF

# 生成的标签会包含作用域信息
ctags -f - --fields=+S test.ini

4.6 子语言支持

Universal Ctags 支持在一种语言内嵌入另一种语言的解析。

常见子语言组合

宿主语言子语言场景
HTMLJavaScript<script> 标签
HTMLCSS<style> 标签
HTMLPHP<?php ?> 标签
PythonCctypes/cffi 内联 C
# 查看子语言配置
ctags --list-subparsers=HTML

# 禁用子语言解析
ctags --submap-HTML=-JavaScript .

# 只解析 HTML 中的 JavaScript
ctags --submap-HTML=+JavaScript --submap-HTML=-CSS .

4.7 语言特定选项

特定语言的 kind 控制

# 只包含函数定义(C 语言)
ctags --kinds-C=f .

# 排除局部变量(C 语言)
ctags --kinds-C=-l .

# 包含通常关闭的 kinds
ctags --kinds-C=+d+h+x+l .

# Python:包含 import
ctags --kinds-python=+i .

# 查看默认开启的 kinds
ctags --list-kinds=C

Kind 过滤语法

+k    启用 kind k
-k    禁用 kind k
+k1+k2 同时启用 k1 和 k2
+k1-k2 启用 k1,禁用 k2
*     所有 kinds
# 启用所有 kinds,再禁用局部变量
ctags --kinds-C='*-l' .

# 只启用函数和变量
ctags --kinds-C='f+v' .

4.8 多语言项目的标签策略

按语言分文件输出

# 为每种语言生成单独的标签文件
ctags -R -o tags.c --languages=C .
ctags -R -o tags.python --languages=Python .
ctags -R -o tags.js --languages=JavaScript .

# 或使用一个脚本自动化
cat > gen_tags.sh << 'SCRIPT'
#!/bin/bash
for lang in C C++ Python JavaScript Go; do
    lower=$(echo "$lang" | tr '[:upper:]' '[:lower:]')
    ctags -R -o ".tags.$lower" --languages="$lang" .
    echo "Generated .tags.$lower"
done
SCRIPT
chmod +x gen_tags.sh

混合项目配置示例

# 一个 Web 项目的 .ctags 配置
cat > .ctags.d/web-project.ctags << 'EOF'
# 只索引这些语言
--languages=Python,JavaScript,TypeScript,HTML,CSS,SQL,Sh

# 排除第三方代码
--exclude=node_modules
--exclude=vendor
--exclude=.venv
--exclude=dist
--exclude=build

# JavaScript/TypeScript
--kinds-JavaScript=+v-g

# Python:包含类型注解信息
--fields-Python=+{defval}

# 默认字段
--fields=+S
--sort=yes
EOF

4.9 语言解析器开发

如果内置的正则定义无法满足需求,可以用 C 语言编写自定义解析器并编译进 Ctags。

解析器开发简述

/* 自定义语言解析器的基本结构 (parsers/mylang.c) */
#include "general.h"
#include "entry.h"
#include "parse.h"

static tagRegexTable mylangTagRegexTable[] = {
    {"^(function)\\s+(\\w+)","\\2","f,function,functions",
     "{scope=push}"},
};

/* 注册语言 */
extern parserDefinition* MylangParser(void)
{
    parserDefinition* def = parserNew("MyLang");
    def->extensions = stringNew("ml");
    def->tagRegexTable = mylangTagRegexTable;
    def->tagRegexCount = ARRAY_SIZE(mylangTagRegexTable);
    def->method        = METHOD_NOT_CRAFTED | METHOD_REGEX;
    return def;
}

📖 扩展阅读:自定义解析器开发参见 Universal Ctags 自定义解析器指南


4.10 本章小结

主题要点
语言数量Universal Ctags 支持 60+ 语言
查看语言--list-languages
Kind 控制--kinds-LANG=+kind-kind
扩展名映射--map-LANG=+.ext
自定义解析器--regex-LANG=/pattern/name/kind/
子语言HTML 内嵌 JS/CSS 自动解析

扩展阅读


上一章 ← 第 3 章:基本用法 · 下一章 → 第 5 章:编辑器集成