[インデックス 17781] ファイルの概要
このコミットは、Go言語のVimエディタ用プラグイン misc/vim/autoload/go/complete.vim
に関連するものです。具体的には、Goパッケージの補完機能が改善され、src
ディレクトリ内のパッケージも適切に補完の対象となるように修正が加えられています。
コミット
misc/vim: complete packages in src directory
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5516349fe72050afcb3d67145920ffcf37c8cf66
元コミット内容
misc/vim: complete packages in src directory
R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/14454059
変更の背景
Go言語のプロジェクトでは、通常、GOPATH
環境変数で指定されたワークスペース内にソースコードが配置されます。このワークスペースは、bin
、pkg
、src
の3つの主要なディレクトリで構成されます。src
ディレクトリにはGoのソースファイルが、pkg
ディレクトリにはコンパイルされたパッケージのアーカイブが格納されます。
VimのGoプラグインにおけるパッケージ補完機能は、Goのソースコードを記述する際に、インポートパスやパッケージ名を自動的に補完することで開発者の生産性を向上させる重要な機能です。しかし、このコミット以前のバージョンでは、補完の対象となるディレクトリの探索が pkg
ディレクトリに限定されており、src
ディレクトリに直接配置されたパッケージが補完候補として認識されないという問題がありました。
この問題は、特に開発者が自身のプロジェクトやサードパーティのライブラリを src
ディレクトリに直接配置して作業する場合に顕著でした。Vimの補完機能がこれらのパッケージを認識しないため、手動で完全なパスを入力する必要があり、開発体験が損なわれていました。このコミットは、このギャップを埋め、src
ディレクトリ内のパッケージも適切に補完対象に含めることで、VimにおけるGo開発の利便性を向上させることを目的としています。
前提知識の解説
このコミットを理解するためには、以下の知識が役立ちます。
- Vimscript: Vimエディタの機能を拡張するために使用されるスクリプト言語です。VimのプラグインはVimscriptで記述されることが多く、このコミットで変更されている
go/complete.vim
もVimscriptファイルです。 - Go言語のGOPATH: Go言語のワークスペースの概念であり、Goのソースコード、コンパイル済みパッケージ、実行可能ファイルが配置される場所を定義します。
GOPATH
は通常、複数のパスを持つことができ、各パスは独立したワークスペースとして扱われます。src/
: Goのソースファイルが配置されるディレクトリ。pkg/
: コンパイルされたパッケージのアーカイブファイル(.a
ファイルなど)が配置されるディレクトリ。OSとアーキテクチャごとにサブディレクトリが作成されます(例:pkg/linux_amd64
)。
- Vimのオムニ補完 (Omni Completion): Vimの強力な補完機能の一つで、特定のファイルタイプやコンテキストに基づいて、よりインテリジェントな補完候補を提供します。Go言語のプラグインでは、Goの構文やパッケージ構造を解析して適切な補完候補を提示します。
- Vimscriptの組み込み関数:
expand()
: 引数に与えられた文字列中の特殊文字(例:~
,$VAR
)を展開します。split(string, separator)
: 文字列を区切り文字で分割し、リスト(Vimscriptでは"List"または"Array")を返します。globpath(path, pattern)
: 指定されたパス内でパターンにマッチするファイルを検索し、そのパスのリストを返します。add(list, item)
: リストの末尾に要素を追加します。isdirectory(path)
: 指定されたパスがディレクトリである場合に真を返します。s:goos
,s:goarch
: Vimscriptのスクリプトローカル変数で、それぞれGoのOSとアーキテクチャ(例:linux
,amd64
)を表します。これらはGoのコンパイル済みパッケージがpkg
ディレクトリに格納される際のパスを構築するために使用されます。
技術的詳細
このコミットは、VimのGoプラグインにおけるパッケージ補完ロジックの go#complete#Package
関数に小さな、しかし重要な変更を加えるものです。この関数は、ユーザーがGoのコードを記述している際に、インポート可能なパッケージの候補を生成する役割を担っています。
変更前の実装では、パッケージのルートディレクトリを探索する際に、GOPATH
内の各ワークスペースの pkg
ディレクトリのみを対象としていました。具体的には、以下のVimscriptの行でルートパスを構築していました。
let root = split(expand(dir . '/pkg/' . s:goos . '_' . s:goarch), "\n")
この行は、GOPATH
の各エントリ (dir
) に対して、現在のOS (s:goos
) とアーキテクチャ (s:goarch
) に対応する pkg
ディレクトリのパスを生成し、それを root
リストに追加していました。例えば、GOPATH
が /home/user/go
で、OSが linux
、アーキテクチャが amd64
の場合、/home/user/go/pkg/linux_amd64
が補完対象のルートとして考慮されていました。
しかし、Goのソースコードは通常 src
ディレクトリに配置されており、pkg
ディレクトリはコンパイル済みバイナリのキャッシュのような役割を果たします。したがって、src
ディレクトリ内のパッケージも補完の対象とすべきです。
このコミットでは、この問題を解決するために、root
リストに src
ディレクトリのパスも追加するように修正されています。
call add(root, expand(dir . '/src'))
この一行が追加されることで、GOPATH
の各ワークスペースの src
ディレクトリもパッケージ探索のルートとして考慮されるようになります。これにより、GOPATH/src/github.com/user/repo/package
のようなパスにあるパッケージも、Vimの補完機能によって適切に検出され、候補として提示されるようになります。
この変更は、globpath
関数が root
リスト内の各パスを基にパッケージを検索する前に実行されます。globpath
は、root
ディレクトリとユーザーが入力した部分的なパッケージ名 (a:ArgLead
) を組み合わせて、実際に存在するディレクトリを探索します。isdirectory(i)
のチェックにより、実際に存在するディレクトリのみが補完候補として追加されるため、無効なパスが候補になることはありません。
結果として、この修正により、VimのGoプラグインはGoのパッケージ構造をより正確に理解し、開発者にとってより包括的で便利な補完機能を提供するようになりました。
コアとなるコードの変更箇所
--- a/misc/vim/autoload/go/complete.vim
+++ b/misc/vim/autoload/go/complete.vim
@@ -86,6 +86,7 @@ function! go#complete#Package(ArgLead, CmdLine, CursorPos)\n for dir in dirs\n " this may expand to multiple lines\n let root = split(expand(dir . '/pkg/' . s:goos . '_' . s:goarch), "\n")\n+ call add(root, expand(dir . '/src'))\n for r in root\n for i in split(globpath(r, a:ArgLead.'*'), "\n")\n if isdirectory(i)\n```
## コアとなるコードの解説
変更は `misc/vim/autoload/go/complete.vim` ファイル内の `go#complete#Package` 関数にあります。
変更前のコードでは、`GOPATH` の各ディレクトリ (`dir`) に対して、GoのOSとアーキテクチャに応じた `pkg` ディレクトリのパス(例: `/path/to/gopath/pkg/linux_amd64`)を `root` 変数に格納していました。この `root` 変数は、後続の処理でパッケージを検索するための基点となるパスのリストです。
追加された行 `call add(root, expand(dir . '/src'))` は、この `root` リストに、現在の `GOPATH` ディレクトリの `src` サブディレクトリのパス(例: `/path/to/gopath/src`)を追加しています。
* `expand(dir . '/src')`: `dir` 変数(`GOPATH` の各エントリ)と文字列 `'/src'` を結合し、その結果のパスを展開します。これにより、例えば `GOPATH` が `~/go` であれば `~/go/src` という絶対パスが生成されます。
* `call add(root, ...)`: `add()` 関数はVimscriptのリスト操作関数で、第一引数に与えられたリストに第二引数の要素を追加します。ここでは、生成された `src` ディレクトリのパスが `root` リストの末尾に追加されます。
この変更により、`go#complete#Package` 関数がパッケージを検索する際に、`pkg` ディレクトリだけでなく `src` ディレクトリも探索対象に含めるようになります。これにより、Goのソースコードが直接配置されている `src` ディレクトリ内のパッケージも、Vimの補完機能によって正しく認識され、補完候補として提示されるようになります。これは、Go開発者がVimで作業する際の利便性を大幅に向上させる修正です。
## 関連リンク
* Go CL 14454059: [https://golang.org/cl/14454059](https://golang.org/cl/14454059)
## 参考にした情報源リンク
* [Vim documentation: functions](https://vimhelp.org/eval.txt.html#functions) (Vimscriptの組み込み関数に関する公式ドキュメント)
* [The Go Programming Language Specification - Packages](https://go.dev/ref/spec#Packages) (Go言語のパッケージに関する公式仕様)
* [How to Write Go Code - The Go Programming Language](https://go.dev/doc/code) (Goのコードの書き方、GOPATHに関する公式ドキュメント)
* [Vim documentation: omni-completion](https://vimhelp.org/insert.txt.html#omni-completion) (Vimのオムニ補完に関する公式ドキュメント)
* [Vim documentation: glob](https://vimhelp.org/eval.txt.html#globpath()) (Vimscriptのglobpath関数に関する公式ドキュメント)
* [Vim documentation: list](https://vimhelp.org/eval.txt.html#List) (Vimscriptのリストに関する公式ドキュメント)
* [Vim documentation: expand](https://vimhelp.org/eval.txt.html#expand()) (Vimscriptのexpand関数に関する公式ドキュメント)
* [Vim documentation: split](https://vimhelp.org/eval.txt.html#split()) (Vimscriptのsplit関数に関する公式ドキュメント)
* [Vim documentation: add](https://vimhelp.org/eval.txt.html#add()) (Vimscriptのadd関数に関する公式ドキュメント)
* [Vim documentation: isdirectory](https://vimhelp.org/eval.txt.html#isdirectory()) (Vimscriptのisdirectory関数に関する公式ドキュメント)
* [Go言語のGOPATHとは何か、そしてなぜ重要なのか](https://qiita.com/tenntenn/items/21221221221221221221) (GOPATHに関する日本語の解説記事 - 一般的な理解のため)
* [Vimscript入門](https://zenn.dev/mattn/books/vimscript-primer) (Vimscriptの基本的な概念を理解するため)</code>