Next: , Previous: , Up: 目次   [Index]


プログラムの中を移動する

*usr_29.txt*	For Vim バージョン 8.1.  Last change: 2016 Feb 27

		     VIM USER MANUAL - by Bram Moolenaar

			   プログラムの中を移動する

Vim の作者はプログラマです。當然、プログラムを書くための機能が Vim にはたくさんあります。この章では、識別子が定義された場所、あるいは使はれてゐる場所にジャンプしたり、その定義を別のウィンドウでプレビューしたりする方法を說明します。プログラミング關聯の機能は次章でも說明します。

|29.1|タグを使ふ
|29.2|プレビューウィンドウ
|29.3|プログラムの中を移動する
|29.4|グローバル識別子を檢索する
|29.5|ローカル識別子を檢索する

タグを使ふ

タグとは、識別子が定義された場所のことです。例へば C や C++ の函數定義がさうです。タグの一覽はタグファイルに保存されます。Vim はタグファイルに對應してをり、タグ、つまり識別子の定義場所に直接ジャンプできます。

カレントディレクトリのすべての C ファイルからタグを生成するには、次のコマンドを使ひます:

ctags *.c

"ctags" は Vim に附屬してません。ほとんどの Unix システムには最初からインストールされてゐます。持つてゐない場合は Exuberant ctags を使つてください:

http://ctags.sf.net

Vim のコマンドラインから次のコマンドを實行すると函數定義にジャンプできます:

:tag startlist

"startlist" 函數が檢索されます。他のファイルで定義されてゐても檢索可能です。

CTRL-] コマンドを使ふとカーソルの下の單語をタグとみなしてジャンプできます。これは複雜な C コードの探索を簡單にしてくれます。例へば、"write_block" 函數の中で "write_line" の呼び出しを見つけたとき、その函數の動作を知りたかつたら、"write_line" にカーソルを合はせて CTRL-] を押せば、その函數の定義にジャンプできます。

"write_line" の中で "write_char" が呼ばれてゐたら、その函數の動作も調べる必要があります。"write_char" にカーソルを合はせて CTRL-] を押しませう。これで "write_char" の定義に移動できました。

+-------------------------------------+
|void write_block(char **s; int cnt)  |
|{                                    |
|   int i;                            |
|   for (i = 0; i < cnt; ++i)         |
|      write_line(s[i]);              |
|}          |                         |
+-----------|-------------------------+
            |
     CTRL-] |
            |    +----------------------------+
            +--> |void write_line(char *s)    |
                 |{                           |
                 |   while (*s != 0)          |
                 |      write_char(*s++);     |
                 |}       |                   |
                 +--------|-------------------+
                          |
                   CTRL-] |
                          |    +------------------------------------+
                          +--> |void write_char(char c)             |
                               |{                                   |
                               |    putchar((int)(unsigned char)c); |
                               |}                                   |
                               +------------------------------------+

":tags" コマンドで移動經路を確認できます:

:tags
# TO tag         FROM line  in file/text
1  1 write_line          8  write_block.c
2  1 write_char          7  write_line.c

では元の場所に戾りませう。CTRL-T で直前のタグに戾れます。上の例であれば、"write_line" 函數の中の "write_char" の呼び出しに戾ることになります。

このコマンドはカウント指定を付けてジャンプする回數を指定できます。前方にジャンプして、そして戾つてくることができましたね。もう一度前方に移動してみませう。次のコマンドでタグリストの前方に移動できます:

:tag

コマンドの前にカウント指定を付けてジャンプする回數を指定できます。例へば ":3tag" のやうに使ひます。CTRL-T も同樣に回數指定できます。

このやうに、CTRL-] をで呼び出しをたどり、CTRL-Tでさかのぼることができます。":tags" コマンドで現在地を確認できます。

ウィンドウを分割する

":tag" コマンドはカレントウィンドウを使つてジャンプ先のファイルを表示します。しかし現在の函數とジャンプ先の函數を同時に表示したいこともあると思ひます。":split" コマンドでウィンドウを分割してから ":tag" コマンドを使ふといふ方法もありますが、專用の短縮コマンドが用意されてゐます:

:stag tagname

カーソルの下の單語にタグジャンプするときにウィンドウを分割したい場合は次のコマンドを使ひます:

CTRL-W ]

カウント指定を付けて新しいウィンドウの高さを指定できます。

複數のタグファイルを使ふ

ファイルが複數のディレクトリに分れてゐる場合、ディレクトリ每にタグファイルを作ることもできますが、その方法だとタグファイルと同じディレクトリのファイルにしかジャンプできません。

タグファイルが複數ある場合は ’tags’ オプションを設定して、關聯するすべてのタグファイルが檢索されるやうにしてください。例:

:set tags=./tags,./../tags,./*/tags

カレントファイルと同じディレクトリ、その 1 つ上のディレクトリ、すべてのサブディレクトリからタグファイルが檢索されます。

これでかなり多くのタグファイルが使へるやうになりましたが、まだ十分ではないかもしれません。例へば "~/proj/src" を編輯してゐるときに "~/proj/sub/tags" を見つけることができません。そのやうな場合はディレクトリツリー全體を檢索するやうに設定します。例:

:set tags=~/proj/**/tags

タグファイルを 1 つだけ使ふ

たくさんの場所からタグファイルを檢索してゐるときは、ハードディスクがガリガリと音を立てるのが聞こえると思ひます。これは效率が良くありません。そんなときは少し時閒を掛けて 1 つの巨大なタグファイルを生成するのがベストです。寢てゐる閒にでもやつてしまふといいでせう。

それには上述した Exuberant ctags が必要です。このプログラムにはディレクトリツリー全體を檢索するためのオプションがあります:

cd ~/proj
ctags -R .

Exuberant ctags のいいところは、いろんなファイルタイプを認識してくれるところです。C や C++ だけでなく Effiel や Vim script も處理できます。詳しくは ctags のドキュメントを參照してください。

これで、巨大なタグファイルを 1 つだけ指定するだけでよくなりました:

:set tags=~/proj/tags

定義の重複

同じ名前の函數が何度も定義されてゐる場合、あるいは複數のクラスで同名のメソッドが定義されてゐる場合、":tag" コマンドは最初に見つかつたタグにジャンプします。カレントファイル內にタグがある場合はそれが優先されます。

タグが重複してゐる場合は次のコマンドで別のタグにジャンプできます:

:tnext

もう一度實行するとさらに別のタグにジャンプできます。タグがたくさんある場合は次のコマンドでタグを選擇できます:

:tselect tagname

このやうな選擇畫面が表示されます:

  # pri kind tag               file
  1 F   f    mch_init          os_amiga.c
               mch_init()
  2 F   f    mch_init          os_mac.c
              mch_init()
  3 F   f    mch_init          os_msdos.c
              mch_init(void)
  4 F   f    mch_init          os_riscos.c
              mch_init()
Enter nr of choice (<CR> to abort):

(行頭の) 番號を入力してジャンプしたい場所を選擇してください。他の列にはタグの場所を示すヒントが表示されます。

次のコマンドで他の重複タグに移動できます:

:tfirst最初のタグに移動
:[count]tprevious[count] 個前のタグに移動
:[count]tnext[count] 個次のタグに移動
:tlast最後のタグに移動

[count] を省略すると 1 が使はれます。

タグ名の推測

コマンドライン補完を使ふと長いタグ名の入力が簡單になります。最初の數文字を入力してから <Tab> キーを押してください:

:tag write_<Tab>

最初にマッチしたタグが補完されます。それが意圖したタグでない場合は、目的のタグが見つかるまで <Tab> キーを押してください。

函數名の一部しか知らない場合や、同じ文字で始まるタグ (後半だけが違つてゐる) がたくさんある場合は、パターンを使つてタグを檢索できます。

例へば、名前に "block" が含まれてゐるタグにジャンプする場合は、まづ次のやうに入力します:

:tag /block

そして、コマンドライン補完を使ひます。<Tab> キーを押してください。"block" を含むタグが檢索され、最初にマッチしたタグが使はれます。

タグ名の前に "/" を付けると、續くタグ名はそのまま使はれず、パターンとして解釋されます。檢索パターンと同じ機能がすべて使へます。例へば、"write_" で始まるタグを選擇したい場合は次のやうにします:

:tselect /^write_

最初の "^" はタグ名が "write_" で始まることを示してゐます。"^" がない場合はタグ名の途中にもマッチしてしまひます。同樣に、"$" を最後に付けるとタグ名の末尾にマッチするやうになります。

タグブラウザー

CTRL-] を使ふとカーソルの下にある識別子の定義にジャンプできますが、これを利用すると、識別子の一覽を目次として使ふことができます。例を示します。

まず識別子の一覽を作ります (Exuberant ctags が必要です)

ctags --c-types=f -f functions *.c

そして Vim をファイル指定なしで起動し、作成したファイルを縱分割ウィンドウで開きます:

vim
:vsplit functions

ウィンドウにはすべての函數の一覽が表示されてゐます。函數以外の名前も含まれてゐるかもしれませんが、それは無視してください。":setlocal ts=99" を實行して表示を見やすくします。

このウィンドウで、次のマップを定義します:

:nnoremap <buffer> <CR> 0ye<C-W>w:tag <C-R>"<CR>

表示したい函數の行に移動して <Enter> を押すと、カーソルが他のウィンドウに移動して、選擇した函數にジャンプします。

關聯項目

タグ名の大文字と小文字を無視する場合には、’tagcase’ を "ignore" に設定するか、’tagcase’ を "followic" のまま變更せず ’ignorecase’ をオンに設定してください。

tagbsearch’ オプションにはタグファイルがソートされてゐるかどうかを設定します。初期設定ではソート濟みに設定されてゐます。これはタグの檢索を高速に實行できますが、ソートされてゐないタグファイルを扱へなくなります。

taglength’ オプションはタグの識別に使ふ文字數を指定するのに使ひます。

cscope はフリーのプログラムです。識別子の定義場所を探すだけでなく、それが使はれてゐる場所も檢索できます。|cscope| 參照。

プレビューウィンドウ

コードの中で函數を呼び出すときには、その引數を正確に把握する必要があります。引數の意味は函數の定義を見ればわかります。タグの仕組みを使へば簡單に確認できますが、できれば別のウィンドウに定義を表示したいところです。それにはプレビューウィンドウを使ひます。

次のやうにすると "write_char" 函數をプレビューウィンドウで表示できます:

:ptag write_char

ウィンドウが開いて "write_char" タグにジャンプします。カーソルの位置は動かないので CTRL-W コマンドを使つて戾る必要はありません。

テキストの中に函數名がある場合は、次のコマンドでその定義をプレビューウィンドウで表示できます:

CTRL-W }

カーソルの下にある單語の定義場所を自動的に表示してくれるスクリプトもあります。|CursorHold-example|參照。

プレビューウィンドウを閉ぢるには次のコマンドを使ひます:

:pclose

プレビューウィンドウでファイルを開きたい場合は ":pedit" を使ひます。例へばヘッダーファイルを表示するやうな場合に便利です:

:pedit defs.h

最後に ":psearch" コマンドを紹介します。カレントファイルおよびインクルードされてゐるファイルから單語を檢索して、ヒットした場所をプレビューウィンドウで表示できます。これは例へば、ライブラリ函數を使つてゐて、それ用のタグファイルを作つてゐないときに使ひます。例:

:psearch popen

"stdio.h" がプレビューウィンドウで開き、popen() 函數のプロトタイプが表示されます:

FILE    *popen __P((const char *, const char *));

プレビューウィンドウの高さは ’previewheight’ オプションで設定できます (最初に開いたときに使はれる)。

プログラムの中を移動する

プログラムには構造があるので、構文を認識することが可能です。その情報を利用して移動するコマンドが用意されてゐます。

例へば C のプログラムには次のやうな構文がよく現れます:

#ifdef USE_POPEN
    fd = popen("ls", "r")
#else
    fd = fopen("tmp", "w")
#endif

もつと長いかもしれませんし、入れ子になつてゐることもあります。"#ifdef" に移動して % を押すと "#else" にジャンプできます。もう一度 % を押すと "#endif" にジャンプします。さらに % を押すと "#ifdef" に戾ります。

構文が入れ子になつてゐる場合は、正しく對應してゐるものが檢索されます。これは "#endif" の書き忘れがないかどうか確認するのに便利です。

"#if" と "#endif" の閒にカーソルがあるとき、次のコマンドで開始位置にジャンプできます:

[#

"#if" や "#ifdef" の中にゐない場合は警告音が鳴ります。前方の "#else" または "#endif" に移動するには次のコマンドを使ひます:

]#

これらのコマンドは、途中にある "#if" - "#endif" ブロックをスキップします。 例:

#if defined(HAS_INC_H)
    a = a + inc();
# ifdef USE_THEME
    a += 3;
# endif
    set_width(a);

カーソルが最後の行にあるとき、"[#" で最初の行に移動できます。途中の "#ifdef" - "#endif" ブロックはスキップされます。

コードブロック內の移動

C のコードブロックは {} で圍まれてゐます。ブロックはかなり大きい場合もあります。アウターブロック (最も外側のブロック) の開始位置に移動するには "[[" コマンドを使ひます。"][" でブロックの末尾に移動できます。このコマンドは行頭の "{" と"}" をブロックの區切りとして認識します。

"[{" コマンドで現在のブロックの開始位置に移動できます。同じレベルの {} ペアはスキップされます。"]}" で末尾に移動できます。

つまりこのやうな動作です:

                function(int a)
   +->          {
   |                if (a)
   |       +->      {
[[ |       |            for (;;)               --+
   |       |      +->   {                        |
   |    [{ |      |         foo(32);             |     --+
   |       |   [{ |         if (bar(a))  --+     | ]}    |
   +--     |      +--           break;     | ]}  |       |
           |            }                <-+     |       | ][
           +--          foobar(a)                |       |
                     }                         <-+       |
                }                                      <-+

C++ や Java では、最も外側の {} ブロックはクラスです。その次のレベルの {} はメソッドです。クラスの中で "[m" を使ふと、前のメソッドの開始位置に移動できます。"]m" で次のメソッドの開始位置に移動できます。

"[]" で前の函數の末尾に移動、"]]" で次の函數の開始位置に移動できます。行頭が "}" で始まる行が函數の末尾として認識されます。

                        int func1(void)
                        {
                                return 1;
          +---------->  }
          |
      []  |             int func2(void)
          |        +->  {
          |    [[  |            if (flag)
start     +--      +--                  return flag;
          |    ][  |            return 2;
          |        +->  }
      ]]  |
          |             int func3(void)
          +---------->  {
                                 return 3;
                        }

()、{}、[] などの對括弧に移動する場合は "%" も使へることを忘れないでください。括弧の閒に複數の行がはさまつてゐても機能します。

カッコ內の移動

"[(" と "])" は "[{" と "]}" と機能は同じです。ただし、{} のペアではなく () のペアに對して動作します。

                  [(
    <--------------------------------
              <-------
if (a == b && (c == d || (e > f)) && x > y)
                  -------------->
          -------------------------------->
                       ])

コメント內の移動

コメントの開始位置に戾るには "[/" コマンドを使ひます。コメントの終了位置に移動するには "]/" を使ひます。これは /* - */ 形式のコメントのみ對應してゐます。

   +->     +-> /*
   |    [/ |    * A comment about      --+
[/ |       +--  * wonderful life.        | ]/
   |            */                     <-+
   |
   +--          foo = bar * 3;         --+
                                         | ]/
                /* a short comment */  <-+

グローバル識別子を檢索する

C プログラムを編輯してゐて、變數の型が "int" なのか "unsigned" なのか分からなかつたら、"[I" コマンドで簡單に確認できます。

例へば、"column" といふ單語の上でコマンドを實行すると:

[I

マッチした行が一覽表示されます。カレントファイルとインクルードファイル (さらにその中でインクルードされてゐるファイル) が檢索されます。檢索結果は次のやうに表示されます:

structs.h
 1:   29     unsigned     column;    /* column number */

インクルードファイルも檢索されるといふ點が、タグやプレビューウィンドウを使つた檢索よりも便利です。たいていは正しい定義場所が見つかります。タグファイルが更新されてゐなくても、インクルードファイル用のタグファイルがなくても機能します。

ただし、"[I" が動作するためには少し條件があります。ファイルのインクルードを認識するために、’include’ オプションが正しく設定されてゐなければなりません。初期設定は C と C++ 用に設定されてゐるので、他の言語では設定を變更する必要があります。

インクルードファイルの場所

インクルードファイルは ’path’ オプションに設定された場所から檢索されます。設定に含まれてゐないディレクトリがあると、いくつかのインクルードファイルは檢出できないかもしれません。次のコマンドで檢出できないファイルを確認できます:

:checkpath

檢出できなかつたインクルードファイルの一覽が表示されます。インクルードファイルの中のインクルードも檢査されます。次のやうな結果が表示されます:

--- Included files not found in path ---
<io.h>
vim.h -->
  <functions.h>
  <clib/exec_protos.h>

カレントファイルでインクルードしてゐる "io.h" が見つかってゐません。"vim.h" は見つかつたので ":checkpath" はさらにそのファイルのインクルードも檢査しました。そして、"functions.h" と "clib/exec_protos.h" が見つかりませんでした。

Note:
Vim はコンパイラではないので、"#ifdef" ステートメントを認識しません。つまり、"#if NEVER" で圍まれてゐる "#include" ステートメントもすべて檢査されます。

この問題を修正するには ’path’ オプションにディレクトリを追加します。Makefile を見れば必要なディレクトリがわかると思ひます。"-I/usr/local/X11" のやうに、"-I" が使はれてゐる行を調べてください。次のコマンドでディレクトリを追加できます:

:set path+=/usr/local/X11

サブディレクトリがたくさんある場合はワイルドカード "*" が使へます。例:

:set path+=/usr/*/include

これで "/usr/local/include/" や "/usr/X11/include/" などが檢索對象になります。

ディレクトリツリーのあちこちにインクルードファイルがあるやうなプロジェクトでは "**" が便利です。すべてのサブディレクトリを檢索できます。例:

:set path+=/projects/invent/**/include

例へば次のやうなディレクトリからファイルが檢索されます:

/projects/invent/include
/projects/invent/main/include
/projects/invent/main/os/include
etc.

設定方法は他にもあります。’path’ オプションの說明を確認してください。

實際に檢出されたインクルードファイルを確認したい場合は次のコマンドを使ひます:

:checkpath!

インクルードされてゐるファイルの (長大な) 一覽が表示されます。出力を短くするため、同じファイルを見つけた場合は "(Already listed)" とだけ表示し、その中のインクルードファイルは表示しません。

定義場所にジャンプする

"[I" はマッチした行だけを一覽表示します。その周邊を見たい場合は、次のコマンドで最初のマッチにジャンプします:

[<Tab>

<Tab>CTRL-I は同じなので "[ CTRL-I" でも構ひません。

"[I" で表示される一覽には番號が付いてゐます。最初の項目以外の場所にジャンプしてい場合は番號を指定してください:

3[<Tab>

3 番目のマッチにジャンプします。CTRL-O で元の場所に戾れることをお忘れなく。

關聯コマンド

[i最初のマッチだけ表示
]Iカーソルより後ろのマッチを一覽表示
]iカーソルより後ろの最初のマッチだけ表示

定義濟識別子の檢索

"[I" コマンドはすべての識別子を檢索します。"#define" で定義されたマクロだけを檢索するには次のコマンドを使ひます:

[D

このコマンドもインクルードファイルが檢索對象になります。檢索される行の書式は ’define’ オプションで指定します。C と C++ 以外の言語では設定を變更する必要があります。

次のやうな "[D" に關聯したコマンドがあります:

[d最初にマッチしたリストのみ
]Dカーソル下の項目のリストのみ
]dカーソル下の最初にマッチしたリストのみ

ローカル識別子を檢索する

"[I" コマンドはインクルードファイルの中も檢索します。カーソルの下の單語が最初に現れる場所を、カレントファイルの中だけ檢索し、その場所にジャンプするには、次のコマンドを使ひます:

gD

Hint: Goto Definition (定義に移動)。このコマンドはローカル (C 用語で "static") に定義された變數や函數を檢索するのに便利です。例 (カーソルは "counter" の上):

   +->   static int counter = 0;
   |
   |     int get_counter(void)
gD |     {
   |         ++counter;
   +--       return counter;
         }

さらに檢索範圍を狹めて、現在の函數の中だけ檢索したい場合は次のコマンドを使ひます:

gd

現在の函數の開始位置から最初に單語が使はれてゐる場所が檢索されます。實際に、行頭が "{" で始まる行を後方檢索して、その上の空行まで戾り、そこから識別子を前方檢索してゐます。例 (カーソルは "idx" の上):

        int find_entry(char *name)
        {
   +->      int idx;
   |
gd |        for (idx = 0; idx < table_len; ++idx)
   |            if (strcmp(table[idx].name, name) == 0)
   +--              return idx;
        }

Next: , Previous: , Up: 目次   [Index]