Exuberant Ctags FAQ


1. なぜExuberant Ctagsと呼ぶんですか?

exuberantという単語の意味の1つが: で、Exuberant Ctagsで生成したtagファイルと 他のctagsで生成したものとを見比べてみれば、この名前がいかにふさわしい かがわかるはずです。

2. 私のエディタでは、tagファイルを動かすには?

3. tagファイルの;"で始まる変なテキストビットは何ですか?

これらは、"拡張フラグ"といいます。 エディタによって、tagをより知的に操作する 場合に利用できるように、tagの追加情報を提供するために出力するようにしていま す。 これらは、既存のViエディタの実装と互換性を保つように、tag行の一部として EXコ マンドに追加されています。 セミコロンは、EXコマンドの区切り文字で、ダブル・ クォートはEXのコメントの開始になります。そのため、Viエディタには、拡張フラグ はEXコメントとして認識され、EXコマンドを処理するエディタには無視されるはずで す。

しかしVi以外のエディタには、検索コマンドやtagファイルの3番目のフィールドの行 番号を処理するために、EXコマンドの必要最小限のみを実装したものがあります。も しこの問題が起こったら、--format=1オプションを使って、この拡張 をtagファイルに出力しないようにしてください(環境変数CTAGSで、デ フォルトの引数を設定することもできます)。ご使用のエディタのEXコマンドの処理 方法については、エディタの提供元にご確認ください。


4. XEmacsのスピードバーモジュールでExuberant Ctagsを動かすには?

etagsとして実行される、XEmacsで使用されるデフォルトのコマンドライン ・スイッチは、Exuberant Ctagsのオプションと互換 性がありません。デフォルトのExuberant Ctagsでは、 "etags"は、ctagsコマンドへのシンボリック・リンクとしてインストールされます。 "etags"としてExuberant Ctagsを実行すれば、Emacs形式 のtagファイルを生成します。

この問題を解決するには、.emacsファイルに以下の行を追加してください。 etagsへのパスは、シンボリック・リンクがインストールされたパス名に置き 換えてください。

(autoload 'speedbar "speedbar")
(setq speedbar-fetch-etags-command "/usr/local/bin/etags"
      speedbar-fetch-etags-arguments '("-f" "-"))

5. XEmacsで、正確にtagジャンプするには?

この問題は、version 20.3で発生します。 XEmacsがtagを検索する際に、TAGSファ イル内に出力された検索文字列ではなく、tag名を使っているようです。これは、 XEmacsのバグでGNU版Emacsでは発生しません。この問題は、すでに報告され、Version 21.4.16で改修されるでしょう。

6. NEditで、正確にtagジャンプするには?

5.1以前のNEditは、Exuberant Ctags がデフォルトで生成する拡張tagファイル・フォーマットをサポートし ていません。version 5.1にアップグレードするか、ctagsを実行するときに旧フォー マットのtagファイルを出力するように--format=1オプションを指定し てください。


7. class::memberにtagジャンプできるようにするには?

これは、ctagsがデフォルトではソースファイル中に見つけた区切り文字からのみ tagsを生成するためです。--extra=+qオプションを指定することで、 ctagsは2番目に、C++のclass::member形式のクラスメンバ(データと 関数/メソッド)とEiffel、 Javaのclass.method形式のそれぞれのク ラスレベルのtagを生成するようになります。


8. コマンドに与えなくても、オプションを常に有効にするには?

CTAGS環境変数にカスタム・オプションを設定するか、ホーム・ディレクトリの .ctagsファイルにオプションを記述してください。


9. tagジャンプで不正な行にジャンプしないようにするには?

デフォルトでは、ctagsは、マクロ(#define)tagの場所を検索するのに、ファイル中 の行番号を使用します。これは、ctagsのオリジナルUNIXバージョンと互換性を維持 するために行っています。もしtagファイルの再構築せずにtagを含むソースファイル を変更した場合、tagファイルのtagの定義位置とソースファイルの定義位置がずれて しまいます。

この問題を回避するには、ctagsがマクロtagの生成に検索パターンを使うように、 --excmd=pオプションを指定してください。オリジナルのUNIX版ctags が、マクロtagにだけ行番号を使っている理由を、決して非難するわけではありませ ん。かといって、Exuberant Ctagsのデフォルトの振る 舞いが、オリジナルと異なる振る舞いをするように変更することに反対するわけでも ありません。


10. 同名の別の行ではなく、正しい行にジャンプするには?

tagファイルは、単純な、tag名とtagの発見場所の一覧です。もしエントリに重複が あれば、tagはソートされており、エディタはtagファイルから最初に見つけた場所 へジャンプするため、不正な場所にジャンプしてしまいます。

標準のViは、この振る舞いを変更できるほど器用ではありません。しかしながら、 Vimには、この問題を最小限にする便利な機能が搭載されています。まずマッチした すべてを検査し、その状況で最適と思われるものを選択します。Vimは、要求された tagからマッチしたものを選択するためのコマンドも提供しています。


11. Vimって何ですか?

Vimは、多くのプラットフォームでコンパイル可能な、vi互換の高機能エディタです。 Vimを初めて知った人は、これを避けるリアクションをするように思います。しかし Vimを使うことに後悔することがなく、特性を徐々に学習していくことで、その特徴 にすぐ慣れると思います。Vimは、アルファ・ケンタウリまでの4光年の中で、最高の viクローンです。Vimは、スタンダードのviと同様(近い)動作をします。しかし信じ られないほど、便利な拡張機能(そのうちのいくつかは、私が作者とともに設計に参 加した)が提供されています。ほとんどのLinuxディストリビューションは、スタンダ ードviとして、Vimを採用しています。


12. 指定した関数や変数のすべての参照位置にジャンプするには?

この機能を提供するパッケージがいくつかあります。本稿執筆時点では、

13. tagファイルに新しいtagを追加するには?

たくさんのディレクトリで構成された巨大なソースツリーにある全ソースファイルか ら、tagファイルを構築しようとするとき、各階層にあるそれぞれのディレクトリに たいして、追加モード(-a)でctagsを実行しようとする人がいます。そのたびに、 ctagsは、デフォルトの動作として、tagに追加されたパスをtagファイル内でソート します。累積したtagファイルは大きくなっていくので、ソート時間は急激に増加 していきます。

この問題を回避する最善の(もっとも効果的な)方法は、ディレクトリ階層のrootで、 --recurse(または -R)オプションを使って、ctagsを実行することです(したがって、 ctagsの実行回数は1回だけです):

        ctags -R
各ディレクトリで分割して、ctagsを実行したい場合は、--sort=noオ プションを指定することで、毎回パス名でソートすることを防ぐことができます。い ったんtagファイルが完成すれば、完成したtagファイルをsortコマンドで、マニュア ルでソートするか、ctagsを最後に実行する際に--sortオプションを有効にすればよい はずです。


14. Win32版regexはどこで取得できますか?

以下の場所から、Win32版GNU regexパッケージをダウンロードできます:
http://people.delphiforums.com/gjc/gnu_regex.html
mk_mvc.makとmk_bc5.makにあるREGEX_DIR makefileマクロにアーカイブを解凍して、 生成されたディレクトリへのパスを設定してください。

15. 多重ディレクトリ階層のtagファイルを生成するには?

これには、いくつかの方法があります:

  1. そのディレクトリ内のソースファイル用のtagのみを含むローカルtagファイルを、 各ディレクトリごとに作る。
  2. ディレクトリ・ツリーのrootディレクトリで、ツリー内の全ソースファイル にたいする1つの巨大なtagファイルを作る。
  3. そのディレクトリ内のソースファイル用のtagのみを含むローカルtagファイ ルを、各ディレクトリごとにつくり、さらにディレクトリ・ツリーのrootディレ クトリで、ツリー内の全ソースファイルにたいして、非staticなtagのみの1つ の巨大なtagファイルを作る。
  4. 各階層のそれぞれのディレクトリに、そのディレクトリのソースファイル 中の全てのtagと、そのディレクトリ以下の非staticなtagを含むローカルtagを 作る(ツリーのrootディレクトリには巨大な1つのtagファイルが含まれること に注意)。
これらの個々のアプローチには、どれにも一長一短があり、そのときに応じて、 もっとも適したものを採用するといいでしょう。どのアプローチを選択するか、 判断するには、以下のような要素があります:

  1. エディタに、複数tagファイルを使用する機能があるか

    もし、エディタに複数tagファイルを扱う機能がなければ(オリジナルviにはな い)、他のディレクトリにあるtagにジャンプするのに、巨大な1つのtagファイ ルを使う以外に方法はありません。別のディレクトリのtagにジャンプする必要 がない(i.e. 各ディレクトリのソースファイルが完全に完結している)のであれ ば、必要に応じて、各ディレクトリ内にローカルtagファイルを作ればよいです。

  2. tagファイルからtagを検索するのに、時間がかかる

    この要素の趣旨は、ソース・ツリーのサイズと、ソースファイルがローカルか 遠隔のファイル・システム上にあるか、ということに依存します。 ソースファイルとtagファイルがローカルなファイル・システム上にある場合、 典型的なviは、ソートされたtagファイルを二分探索するように実装されている ため、tagの検索は、最初の想像ほど負荷はかかりません。これはご使用のエデ ィタにも該当するかどうかはわかりません。遠隔のファイル・システムにソー スファイルがある場合、大きなファイルを読むのに大きな負荷がかかります。

  3. ソースコードの更新により、tagファイルの再構築に時間がかかるか どうか。

    Exuberant Ctagsは、ソースコードの走査が特に 速い(およそ1-2 MB/sec)ですが、大規模なプロジェクトでは、頻繁な更新にた いしてtagファイルを維持する場合、または遠隔のファイル・システム上にソ ースファイルがある場合、不快な遅延が発生します。

  4. ソースコードにtagの重複があり、それらの操作を有効にしたい。この 要素の衝動は、以下の3つの課題から影響を受ける:

    1. プロジェクト内にtagの重複がどのくらい共通しているか?

    2. 重複tagを扱う機能が、エディタに搭載されているか?

    スタンダードviには搭載されていません。Vimのような最近の多くのvi実装 には、重複tagのリストから選択する機能があります。もしご使用のエディ タが重複tagをサポートしていない場合、期待したものかどうかに関係なく (他にマッチするものがあるかどうかさえ気づかず)、重複するうちの1つに ジャンプしてしまいます。

    3. 重複tagの重要性は何か?

    例えば、完全に独立したソフトウェア・コンポーネントから同じ名前の tagが2つある場合、最初にマッチしたコンポーネントBのtagにジャンプ してしまい、コンポーネントAのtagは、完全に見落としたり、混乱した りするため、都合が悪くなります(もしご使用のエディタにマッチしたtag の一覧を表示する機能があれば、そこから1つを選択すればよいのですが)。 しかしながら、並列に定義された同名の2つのtagがある場合(ことなるホス トのための、2つの初期化ルーチンなど)、常にどちらかを指定したいかも しれません。

上記アプローチのリストの中では、アプローチ3がもっとも好まれているように 思います。私は、エディタではVimを使っています。複数tagファイルを操作する 強力な機能が実装されているためです。もし、ファイル・システムからソースフ ァイルを削除した場合、アプローチ3かアプローチ4をお勧めします。グローバル tagファイルを読んだとき、ヒットするかどうかに関係するためです。

アプローチ3の利点は、(ご使用のエディタが複数tagファイルと重複tagの両方を サポートしているものと仮定すると)たくさんあります。カレント・ディレクトリ にtagの位置があれば、検索が高速で、ローカルtagファイルの再生成も、1秒以下 で、すばやく簡単にできます(私はこの操作を簡単にするために、ショートカット キーを定義しています)。別のディレクトリで見つかったtag(必要であれば非 static)の検索は、ローカルtagファイルでは失敗します。しかし全ディレクトリ にまたがるグローバルtagファイルでは成功します。グローバルtagファイル(とロ ーカルtagファイルも)は、cronデーモンのジョブで定期的に自動再生成するように しておくとよいでしょう。

今、アプローチ3の実現方法の例を示します。他のアプローチ方法は、似たような 手段で実現することができます。

ディレクトリ構造の例を、ここに示します。

project
  `-----misccomp
  |       `...
  `-----sysint
          `-----client
          |       `-----hdrs
          |       `-----lib
          |       `-----src
          |       `-----test
          `-----common
          |       `-----hdrs
          |       `-----lib
          |       `-----src
          |       `-----test
          `-----server
                  `-----hdrs
                  `-----lib
                  `-----src
                  `-----test
tagファイルを構築するための、お勧めの(概念的な)解決策を示します。

    各葉ノード(i.e. hdrs, lib, src, test)内で、"ctags *.[ch]"を実行して、 tagファイルを生成します。これはシェルスクリプトを作ることで、階層全体 で簡単に実行できるようになります。"dirtags"と呼ぶことにする、スクリプト の内容を以下の行に示します:
            #!/bin/sh
    	cd $1
    	ctags *
    
    そして、以下のコマンドを実行します:
            find * -type d -exec dirtags {} \;
    
    これらのtagファイルの再構築は、ディレクトリ内に変更がある間実行される ので、軽微(かつ究極に高速)です。現ソースファイルのディレクトリ内のtag ファイルを再構築するための、たいへん便利なVimキーマッピングの設定を以 下に示します:
            :nmap ,t :!(cd %:p:h;ctags *.[ch])&
    
  1. グローバルtagファイルを構築します:
            cd ~/project
            ctags --file-scope=no -R
    
    これで、全ディレクトリにわたる全ソースファイルにたいする非staticなtag のみを含むtagファイルを構築できます。
  2. 初めにローカルtagファイルを読み、ローカルtagファイルに見つからなか ったときにグローバルtagファイルを読むように、エディタを設定します。Vim であれば、以下のようにします: :set tags=./tags,tags,~/project/tags
もしアプローチ4を実装したいのであれば、ステップ1に示した"dirtags"スクリプト を以下のように置き換えるとよいでしょう:

        #!/bin/sh
	cd $1
	ctags *
	# カレント・ディレクトリ以下の非static tagを追加する
	find * -type d -prune -print | ctags -aR --file-scope=no -L-
そしてステップ3に示した環境設定を、以下のように置き換えます:

        :set tags=./tags,./../tags,./../../tags,./../../../tags,tags
注意としては、ステップ2で構築したグローバルtagファイルのファイル名フィールド には、グローバルtagファイルのあるディレクトリからの相対パスが出力されていると いう点である。これは、Vimの'tagrelative'オプションの利点を生かすためで、カレ ント・ディレクトリの代わりに、tagファイルの場所からの相対パスを解釈するためで す。スタンダードviでは、カレント・ディレクトリからの相対パスとして、パスを 解 釈するので、グローバルtagファイルを構築する場合、絶対パスを使わなければならな い。これは、ステップ2を以下のように置き換えることで実現できます:

        cd ~/project
	ctags --file-scope=no -R `pwd`