[インデックス 16695] ファイルの概要
このコミットは、Go言語のVim用オートコンプリートプラグイン(misc/vim/autoload/go/complete.vim
)における改善を目的としています。具体的には、複数のGOOS
(オペレーティングシステム)とGOARCH
(アーキテクチャ)の組み合わせに対応できるよう、パッケージ検索ロジックを修正しています。これにより、異なるOSやアーキテクチャ向けにビルドされたGoパッケージの補完が、Vimエディタ内でより正確に行われるようになります。
コミット
commit efced7c6e984f26b4c275b19ba61f2c2629d95ea
Author: Yasuhiro Matsumoto <mattn.jp@gmail.com>
Date: Tue Jul 2 15:24:09 2013 +1000
misc/vim: Allow multiple GOOS/GOARCH.
R=golang-dev, dsymonds, dominik.honnef
CC=golang-dev
https://golang.org/cl/9293043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/efced7c6e984f26b4c275b19ba61f2c2629d95ea
元コミット内容
このコミットの元の内容は、Go言語のVimプラグインであるmisc/vim/autoload/go/complete.vim
ファイルに対する変更です。主な目的は、Goパッケージのオートコンプリート機能が、単一のGOOS
およびGOARCH
の組み合わせに限定されず、複数の環境に対応できるようにすることです。
変更の要点は以下の通りです。
GOPATH
のパス区切り文字の動的な決定: Windows環境ではパス区切り文字が;
であるのに対し、Unix系システムでは:
であるため、これをs:goos
(現在のOS)に基づいて動的に判断するように変更されました。root
変数の展開方法の変更: 以前はexpand(dir . '/pkg/' . s:goos . '_' . s:goarch)
が単一のパスを返すと仮定されていましたが、実際には複数のパスを返す可能性があるため、split
関数を使って複数行に展開される可能性を考慮するように修正されました。これにより、globpath
が複数のpkg
ディレクトリを正しく探索できるようになります。
これらの変更により、VimのGoオートコンプリート機能が、クロスコンパイル環境や異なるOS/アーキテクチャのGoパッケージをより適切に認識し、補完できるようになりました。
変更の背景
Go言語は、その強力なクロスコンパイル機能で知られています。開発者は、自身の開発環境とは異なるオペレーティングシステム(GOOS
)やCPUアーキテクチャ(GOARCH
)向けにバイナリをビルドすることができます。例えば、Linux上でWindows向けの実行ファイルをビルドしたり、ARMプロセッサ向けにビルドしたりすることが可能です。
Goのビルドシステムでは、コンパイルされたパッケージ(.a
ファイル)は通常、$GOROOT/pkg/<GOOS>_<GOARCH>
や$GOPATH/pkg/<GOOS>_<GOARCH>
といったディレクトリに保存されます。VimのGoオートコンプリートプラグインは、これらのディレクトリを探索して利用可能なパッケージを特定し、補完候補として提示します。
しかし、このコミット以前のオートコンプリートロジックは、単一のGOOS
とGOARCH
の組み合わせ(通常は現在のシステムのもの)しか考慮していませんでした。このため、開発者が複数のGOOS
/GOARCH
環境で作業している場合や、クロスコンパイルされたパッケージを参照したい場合に、Vimのオートコンプリートが正しく機能しないという問題がありました。例えば、GOOS=windows GOARCH=amd64
でビルドしたパッケージが、GOOS=linux GOARCH=amd64
で動作しているVimからは補完候補として認識されない、といった状況が発生していました。
このコミットは、この制限を解消し、VimのオートコンプリートがGoのクロスコンパイル機能をより適切にサポートできるようにするために導入されました。
前提知識の解説
Go言語の環境変数 GOOS
と GOARCH
Go言語のビルドシステムは、GOOS
とGOARCH
という2つの環境変数によって、ターゲットとなるオペレーティングシステムとCPUアーキテクチャを決定します。
GOOS
(Go Operating System): ビルドターゲットのオペレーティングシステムを指定します。例えば、linux
,windows
,darwin
(macOS),freebsd
,android
,ios
などがあります。GOARCH
(Go Architecture): ビルドターゲットのCPUアーキテクチャを指定します。例えば、amd64
(x86-64),386
(x86),arm
,arm64
,ppc64
,s390x
などがあります。
これらの変数を設定することで、Goコンパイラは指定されたOSとアーキテクチャに最適化されたバイナリを生成します。例えば、GOOS=windows GOARCH=amd64 go build
と実行すると、Windows 64ビット環境で動作する実行ファイルが生成されます。
Goのパッケージは、これらのGOOS
とGOARCH
の組み合わせごとに、$GOROOT/pkg/<GOOS>_<GOARCH>
や$GOPATH/pkg/<GOOS>_<GOARCH>
といったディレクトリにキャッシュされます。例えば、Linux AMD64環境でビルドされた標準ライブラリのパッケージは$GOROOT/pkg/linux_amd64
に、Windows AMD64環境でビルドされたものは$GOROOT/pkg/windows_amd64
に格納されます。
Vimのオートコンプリート機能
Vimは、プログラミング言語の構文補完をサポートするための強力な機能を備えています。Go言語の場合、go#complete#Package
のような関数がVimスクリプトで実装されており、これがGoのパッケージ名や関数名などを補完候補として提示します。
この機能は通常、以下の手順で動作します。
- ユーザーがVimでGoコードを記述し、補完をトリガーする(例:
.
を入力する、特定のキーバインドを押す)。 - VimのGoプラグインが、現在のコンテキスト(カーソル位置、入力中の文字列など)に基づいて、補完候補を生成するための情報を収集する。
- プラグインは、
GOROOT
やGOPATH
で指定されたディレクトリ内のpkg
サブディレクトリを探索し、コンパイル済みのGoパッケージ(.a
ファイル)やソースコードディレクトリを特定する。 - 特定されたパッケージから、補完候補となる名前(パッケージ名、関数名、変数名など)を抽出し、Vimに返す。
- Vimはこれらの候補をドロップダウンリストなどで表示し、ユーザーが選択できるようにする。
globpath
関数とexpand
関数
Vimスクリプトには、ファイルパスを操作するためのいくつかの組み込み関数があります。
globpath(path, expr)
: 指定されたpath
内で、expr
にマッチするファイルやディレクトリのリストを返します。expr
にはワイルドカード(*
,?
,**
など)を使用できます。例えば、globpath('/usr/local/go/pkg/linux_amd64', '*')
は、そのディレクトリ内のすべてのファイルとディレクトリを返します。expand(expr)
:expr
に含まれる特殊文字(例:~
、環境変数$VAR
)を展開し、完全なパスに変換します。また、expand
は、パスが複数行にわたる場合(例えば、globpath
の結果をexpand
に渡した場合など)に、それらを改行区切りで返すことがあります。
このコミットでは、特にexpand
関数が複数行の結果を返す可能性が考慮されていなかった点が問題となっていました。
技術的詳細
このコミットの技術的な核心は、VimのGoオートコンプリートプラグインがGoのパッケージを探索する際に、GOOS
とGOARCH
の組み合わせによって生成される複数のpkg
ディレクトリを適切に処理できるようにすることです。
以前のコードでは、root
変数を以下のように定義していました。
let root = expand(dir . '/pkg/' . s:goos . '_' . s:goarch)
ここで、s:goos
とs:goarch
は、Vimが起動している現在の環境のOSとアーキテクチャを表す変数です。この行は、例えば/home/user/go/pkg/linux_amd64
のような単一のパスを生成することを意図していました。しかし、Goのビルドシステムや環境設定によっては、go env GOROOT
やgo env GOPATH
が複数のパスを返すことがあり、その結果、expand
関数が複数行の文字列を返す可能性がありました。
例えば、GOROOT
が/usr/local/go:/opt/go
のように設定されている場合、expand(dir . '/pkg/' . s:goos . '_' . s:goarch)
は、/usr/local/go/pkg/linux_amd64\n/opt/go/pkg/linux_amd64
のような複数行の文字列を返す可能性があります。
以前のコードでは、このroot
変数が単一のパスであると仮定し、その後のglobpath
関数がその単一のパス内でパッケージを探索していました。このため、expand
が複数行を返した場合、globpath
は最初の行しか処理せず、残りのパスにあるパッケージを見落としていました。
このコミットでは、この問題を解決するために、root
変数の展開結果をsplit
関数で改行文字("\n"
)によって分割し、それぞれのパスを個別に処理するように変更しました。
let root = split(expand(dir . '/pkg/' . s:goos . '_' . s:goarch), "\n")
for r in root
for i in split(globpath(r, a:ArgLead.'*'), "\n")
" ... (既存のロジック)
endfor
endfor
これにより、root
が複数のパスを含んでいたとしても、それぞれのパス(r
)に対してglobpath
が実行され、すべての関連するpkg
ディレクトリが適切に探索されるようになりました。
また、GOPATH
のパス区切り文字がOSによって異なる(Unix系は:
、Windowsは;
)という点も考慮され、pathsep
変数を導入して動的に区切り文字を決定するように修正されました。これにより、Windows環境でもGOPATH
が正しく解析され、複数のワークスペースがオートコンプリートの対象に含まれるようになりました。
コアとなるコードの変更箇所
変更はmisc/vim/autoload/go/complete.vim
ファイルに集中しています。
--- a/misc/vim/autoload/go/complete.vim
+++ b/misc/vim/autoload/go/complete.vim
@@ -32,39 +32,46 @@ function! go#complete#Package(ArgLead, CmdLine, CursorPos)
let dirs = []
if executable('go')
- let goroot = substitute(system('go env GOROOT'), '\n', '', 'g')
- if v:shell_error
- echo '\'go env GOROOT\' failed'
- endif
+ let goroot = substitute(system('go env GOROOT'), '\n', '', 'g')
+ if v:shell_error
+ echomsg '\'go env GOROOT\' failed'
+ endif
else
- let goroot = $GOROOT
+ let goroot = $GOROOT
endif
if len(goroot) != 0 && isdirectory(goroot)
- let dirs += [ goroot ]
+ let dirs += [goroot]
endif
- let workspaces = split($GOPATH, ':')
+ let pathsep = ':'
+ if s:goos == 'windows'
+ let pathsep = ';'
+ endif
+ let workspaces = split($GOPATH, pathsep)
if workspaces != []
- let dirs += workspaces
+ let dirs += workspaces
endif
if len(dirs) == 0
- " should not happen
- return []
+ " should not happen
+ return []
endif
let ret = {}
for dir in dirs
- let root = expand(dir . '/pkg/' . s:goos . '_' . s:goarch)
- for i in split(globpath(root, a:ArgLead.'*'), "\n")
- if isdirectory(i)
- let i .= '/'
- elseif i !~ '\.a$'
- continue
- endif
- let i = substitute(substitute(i[len(root)+1:], '[\\]', '/', 'g'), '\.a$', '', 'g')
- let ret[i] = i
+ " this may expand to multiple lines
+ let root = split(expand(dir . '/pkg/' . s:goos . '_' . s:goarch), "\n")
+ for r in root
+ for i in split(globpath(r, a:ArgLead.'*'), "\n")
+ if isdirectory(i)
+ let i .= '/'
+ elseif i !~ '\.a$'
+ continue
+ endif
+ let i = substitute(substitute(i[len(r)+1:], '[\\]', '/', 'g'), '\.a$', '', 'g')
+ let ret[i] = i
+ endfor
endfor
endfor
return sort(keys(ret))
コアとなるコードの解説
このコミットにおける主要な変更点は2つあります。
-
GOPATH
のパス区切り文字の動的な設定:let pathsep = ':' if s:goos == 'windows' let pathsep = ';' endif let workspaces = split($GOPATH, pathsep)
この部分では、
GOPATH
環境変数を分割する際の区切り文字を、現在のオペレーティングシステム(s:goos
)に基づいて動的に設定しています。Unix系システムでは:
が、Windowsでは;
がパスの区切り文字として使用されるため、これによりWindows環境でもGOPATH
が正しく解析され、複数のワークスペースディレクトリがworkspaces
リストに追加されるようになります。 -
root
パスの複数行展開への対応:" this may expand to multiple lines let root = split(expand(dir . '/pkg/' . s:goos . '_' . s:goarch), "\n") for r in root for i in split(globpath(r, a:ArgLead.'*'), "\n") " ... (既存のパッケージ探索ロジック) let i = substitute(substitute(i[len(r)+1:], '[\\]', '/', 'g'), '\.a$', '', 'g') let ret[i] = i endfor endfor
この変更が最も重要です。
expand(dir . '/pkg/' . s:goos . '_' . s:goarch)
: この式は、GOROOT
やGOPATH
の各エントリ(dir
)に対して、対応するpkg
ディレクトリのパスを生成します。前述の通り、expand
関数は、入力によっては複数行のパスを返す可能性があります。split(..., "\n")
:expand
の戻り値を改行文字"\n"
で分割することで、root
変数が単一の文字列ではなく、複数のパス文字列のリスト(VimScriptではリスト)として扱われるようになります。for r in root
: 新たに導入されたこのループは、root
リスト内の各パスr
を個別に処理します。これにより、expand
が返したすべてのpkg
ディレクトリが確実に探索対象となります。globpath(r, a:ArgLead.'*')
: 各r
(個々のpkg
ディレクトリパス)に対してglobpath
が実行され、そのディレクトリ内のGoパッケージ(.a
ファイル)やサブディレクトリが検索されます。i[len(r)+1:]
: パッケージ名の抽出部分も、len(root)
からlen(r)
に変更されています。これは、i
からpkg
ディレクトリのパス部分を削除して、純粋なパッケージパス(例:net/http
)を取得するためです。r
は単一のpkg
ディレクトリパスであるため、この変更は正しい動作を保証します。
これらの変更により、VimのGoオートコンプリートは、Goのクロスコンパイル機能によって生成された複数のpkg
ディレクトリを適切にスキャンし、より包括的な補完候補を提供できるようになりました。
関連リンク
- Go言語公式ドキュメント: https://go.dev/doc/
- Vim公式ドキュメント: https://www.vim.org/docs.php
- Go言語のクロスコンパイルに関する情報: https://go.dev/doc/install/source#environment (Goの環境変数に関するセクション)
参考にした情報源リンク
- Go言語の
GOOS
とGOARCH
に関する公式ドキュメントやチュートリアル - VimScriptの
expand
、globpath
、split
関数に関するVimヘルプドキュメント - Go言語のVimプラグインのソースコード(
misc/vim
ディレクトリ) - Go言語の
GOPATH
に関する公式ドキュメント