[インデックス 12236] ファイルの概要
このコミットは、Go言語の公式リポジトリにおける、misc/bash/go
ファイルの追加に関するものです。このファイルは、Bashシェルにおけるgo
コマンドのコマンドライン補完機能を提供します。これにより、ユーザーはgo
コマンドとそのサブコマンド、および関連する引数をより効率的に入力できるようになります。
コミット
commit 1f5fde09159fe2a4a87129af307045ca7b12f727
Author: Yissakhar Z. Beck <yissakhar.beck@gmail.com>
Date: Tue Feb 28 07:41:49 2012 +1100
misc/bash: Completion for go tool.
This covers most of the tool's functionality. At some point,
support should probably be added for testflags and the various go
tools.
R=golang-dev, bradfitz, kyle, minux.ma
CC=golang-dev
https://golang.org/cl/5646066
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1f5fde09159fe2a4a87129af307045ca7b12f727
元コミット内容
このコミットは、go
ツールのBash補完機能を追加します。
これはツールのほとんどの機能をカバーしていますが、将来的にはtestflags
や様々なgo
ツールに対するサポートも追加されるべきです。
変更の背景
コマンドラインインターフェース(CLI)を使用する開発者にとって、コマンドの入力補完は生産性を大幅に向上させる重要な機能です。特に、go
コマンドのように多くのサブコマンドやオプションを持つツールの場合、手動での入力は手間がかかり、タイプミスを誘発しやすくなります。
このコミットの背景には、Go開発者がよりスムーズにgo
コマンドを操作できるようにするという目的があります。Bash補完機能を提供することで、ユーザーはTabキーを押すだけで利用可能なサブコマンドやファイルパス、インポートパスなどを自動的に補完できるようになり、開発ワークフローが効率化されます。これにより、Go言語のツールチェインの使いやすさが向上し、開発者の負担が軽減されます。
前提知識の解説
Bash (Bourne-Again SHell)
Bashは、Unix系オペレーティングシステムで広く利用されているシェル(コマンドラインインタープリタ)です。ユーザーが入力したコマンドを解釈し、実行する役割を担います。多くのLinuxディストリビューションやmacOSでデフォルトのシェルとして採用されています。
コマンドライン補完 (Tab Completion)
コマンドライン補完は、ユーザーがコマンドの一部を入力した際に、Tabキーを押すことで残りの部分を自動的に補完する機能です。これにより、コマンド名、ファイル名、ディレクトリ名、コマンドの引数などを効率的に入力できます。Bashでは、complete
コマンドやcompgen
コマンド、そして特定のシェル関数を組み合わせてこの機能を実現します。
complete
コマンド
Bashの組み込みコマンドで、特定のコマンドに対する補完ルールを定義するために使用されます。
-F function_name
: 指定されたコマンドの補完にfunction_name
というシェル関数を使用することを指定します。この関数が補完候補を生成します。-o filenames
: ファイル名の補完を有効にします。command_name
: 補完ルールを適用するコマンドの名前。
compgen
コマンド
compgen
は、補完候補を生成するためのBashの組み込みコマンドです。
-W wordlist
: スペースで区切られた単語リストから補完候補を生成します。-- string
:string
で始まる単語のみをフィルタリングします。
_get_cword
関数
Bash補完スクリプトでよく使われるヘルパー関数で、現在のカーソル位置にある単語のインデックス(COMP_CWORD
)を取得します。
COMP_WORDS
と COMP_CWORD
Bashの補完機能が呼び出された際に設定される特殊なシェル変数です。
COMP_WORDS
: 現在のコマンドラインを単語の配列として格納します。COMP_CWORD
:COMP_WORDS
配列の中で、現在カーソルがある単語のインデックスを示します。
_filedir
関数
Bash補完スクリプトで一般的に使用されるヘルパー関数で、ファイル名やディレクトリ名を補完候補として提供します。引数に拡張子を指定することで、特定の拡張子のファイルのみを補完対象とすることも可能です。
go list all
go list
コマンドはGoパッケージに関する情報を表示します。go list all
は、Go環境で利用可能なすべてのパッケージのインポートパスをリストアップします。この情報は、補完機能でGoのインポートパスを提案する際に非常に有用です。
技術的詳細
このコミットで追加されたmisc/bash/go
スクリプトは、Bashのプログラマブル補完機能を利用してgo
コマンドの補完を実現しています。スクリプトの主要な部分は以下の通りです。
-
_go_importpath()
関数: この関数は、go list all
コマンドの出力と、all
、std
といった一般的なGoのインポートパスを組み合わせて、補完候補となるGoのインポートパスを生成します。compgen -W
を使用して、現在の入力文字列に一致するパスをフィルタリングします。 -
_go()
関数: このスクリプトの核となる関数で、go
コマンドの補完ロジックを実装しています。_get_cword
を使用して現在のカーソル位置の単語インデックスを取得し、COMP_WORDS
配列から現在のコマンドと直前の単語を特定します。cmds
変数には、go
コマンドの主要なサブコマンド(build
,clean
,doc
,fix
,fmt
,get
,install
,list
,run
,test
,tool
,version
,vet
)が定義されています。addhelp
変数には、help
コマンドの引数として利用できる追加のキーワード(gopath
,importpath
,remote
,testflag
,testfunc
)が定義されています。COMP_CWORD
が1の場合(つまり、go
コマンドの直後にサブコマンドを入力しようとしている場合)、cmds
リストからサブコマンドを補完します。- 各サブコマンド(
build
,clean
,doc
,fix
,fmt
,get
,install
,list
,run
,test
,tool
,vet
,help
)に対して、case
文を用いて個別の補完ロジックが実装されています。- オプションの補完:
-a
,-n
,-o
などのオプションはcompgen -W
を使って補完されます。 - ファイルパスの補完:
_filedir
関数を使って.go
ファイルやその他のファイルパスが補完されます。 - インポートパスの補完:
_go_importpath
関数を使ってGoのインポートパスが補完されます。 go build
の特殊なロジック:.go
ファイルが既に引数として指定されているか、インポートパスが指定されているかによって、ファイルパスとインポートパスのどちらを補完するかを切り替える複雑なロジックが含まれています。go tool
の補完:go tool
コマンドのサブコマンドは、go tool
コマンド自体の出力を解析して動的に取得されます。
- オプションの補完:
TODO
コメントが多数含まれており、testflags
や特定のgo tool
サブコマンド(例:568a
,568c
,api
,cgo
など)に対するより詳細な補完機能が将来的に追加されるべきであることが示されています。
-
complete -F _go go
: この行は、go
コマンドに対して、補完機能が要求された際に_go
関数を呼び出すようにBashに指示します。$filenames
オプションは、デフォルトのファイル名補完も有効にすることを意味します。
このスクリプトは、Bashの強力なプログラマブル補完フレームワークを活用し、Go開発者にとって非常に便利な機能を提供しています。
コアとなるコードの変更箇所
このコミットでは、misc/bash/go
という新しいファイルが追加されています。このファイル全体が、go
コマンドのBash補完機能の実装です。
--- /dev/null
+++ b/misc/bash/go
@@ -0,0 +1,247 @@
+complete -f -X '!*.8' 8l
+complete -f -X '!*.6' 6l
+complete -f -X '!*.5' 5l
+complete -f -X '!*.go' 8g 6g 5g gofmt gccgo
+
+_go_importpath()
+{
+ echo "$(compgen -W "$(go list all) all std" -- "$1")"
+}
+
+_go()
+{
+ # TODO: Only allow flags before other arguments. run already does
+ # this.
+
+ local cur=`_get_cword`
+ local prev="${COMP_WORDS[COMP_CWORD-1]}"
+
+ local cmd="${COMP_WORDS[1]}"
+
+ local cmds="build clean doc fix fmt get
+ install list run test tool version vet"
+ local addhelp="gopath importpath remote
+ testflag testfunc"
+ local other="help"
+
+ if [ "$COMP_CWORD" == 1 ]; then
+ for opt in $cmds; do
+ if [[ "$opt" == "$cmd" ]]; then
+ COMPREPLY=("$opt")
+ return
+ fi
+ done
+ fi
+
+ case "$cmd" in
+ 'build')
+ case "$prev" in
+ '-o')
+ _filedir
+ ;;
+ '-p')
+ ;;
+ *)
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=($(compgen -W "-a -n -o -p -v -x" -- "$cur"))
+ else
+ local found=0
+ for ((i=0; i < ${#COMP_WORDS[@]}; i++)); do
+ case "$i" in
+ 0|1|"$COMP_CWORD")
+ continue
+ ;;
+ esac
+ local opt="${COMP_WORDS[i]}"
+ if [[ "$opt" != -* ]]; then
+ if [[ "$opt" == *.go && -f "$opt" ]]; then
+ found=1
+ break
+ else
+ found=2
+ break
+ fi
+ fi
+ done
+ case "$found" in
+ 0)
+ _filedir go
+ COMPREPLY+=(`_go_importpath "$cur"`)\
+ ;;
+ 1)
+ _filedir go
+ ;;
+ 2)
+ COMPREPLY=(`_go_importpath "$cur"`)\
+ ;;
+ esac
+ fi
+ ;;
+ esac
+ ;;
+ 'clean')
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=($(compgen -W "-i -r -n -x" -- "$cur"))
+ else
+ COMPREPLY=(`_go_importpath "$cur"`)\
+ fi
+ ;;
+ 'doc')
+ COMPREPLY=(`_go_importpath "$cur"`)\
+ ;;
+ 'fix')
+ COMPREPLY=(`_go_importpath "$cur"`)\
+ ;;
+ 'fmt')
+ COMPREPLY=(`_go_importpath "$cur"`)\
+ ;;
+ 'get')
+ case "$prev" in
+ '-p')
+ ;;
+ *)
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=($(compgen -W "-a -d -fix -n -p -u -v -x" -- "$cur"))
+ else
+ COMPREPLY=(`_go_importpath "$cur"`)\
+ fi
+ ;;
+ esac
+ ;;
+ 'install')
+ case "$prev" in
+ '-p')
+ ;;
+ *)
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=($(compgen -W "-a -n -p -v -x" -- "$cur"))
+ else
+ COMPREPLY=(`_go_importpath "$cur"`)\
+ fi
+ ;;
+ esac
+ ;;
+ 'list')
+ case "$prev" in
+ '-f')
+ ;;
+ *)
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=($(compgen -W "-e -f -json" -- "$cur"))
+ else
+ COMPREPLY=(`_go_importpath "$cur"`)\
+ fi
+ ;;
+ esac
+ ;;
+ 'run')
+ if [[ "$cur" == -* && "$prev" != *.go ]]; then
+ COMPREPLY=($(compgen -W "-a -n -x" -- "$cur"))
+ else
+ _filedir
+ fi
+ ;;
+ 'test') # TODO: Support for testflags.
+ case "$prev" in
+ '-file')
+ _filedir go
+ ;;
+ '-p')
+ ;;
+ *)
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=($(compgen -W "-c -file -i -p -x" -- "$cur"))
+ else
+ COMPREPLY=(`_go_importpath "$cur"`)\
+ fi
+ ;;
+ esac
+ ;;
+ 'tool')
+ if [ "$COMP_CWORD" == 2 ]; then
+ COMPREPLY=($(compgen -W "$(go tool)" -- "$cur"))
+ else
+ case "${COMP_WORDS[2]}" in
+ [568]a) # TODO: Implement something.
+ #_go_tool_568a
+ ;;
+ [568]c) # TODO: Implement something.
+ #_go_tool_568c
+ ;;
+ [568]g) # TODO: Implement something.
+ #_go_tool_568g
+ ;;
+ [568]l) # TODO: Implement something.
+ #_go_tool_568l
+ ;;
+ 'api') # TODO: Implement something.
+ #_go_tool_api
+ ;;
+ 'cgo') # TODO: Implement something.
+ #_go_tool_cgo
+ ;;
+ 'cov') # TODO: Implement something.
+ #_go_tool_cov
+ ;;
+ 'dist') # TODO: Implement something.
+ #_go_tool_dist
+ ;;
+ 'ebnflint') # TODO: Implement something.
+ #_go_tool_ebnflint
+ ;;
+ 'fix') # TODO: Implement something.
+ #_go_tool_fix
+ ;;
+ 'gotype') # TODO: Implement something.
+ #_go_tool_gotype
+ ;;
+ 'nm') # TODO: Implement something.
+ #_go_tool_nm
+ ;;
+ 'pack') # TODO: Implement something.
+ #_go_tool_pack
+ ;;
+ 'pprof') # TODO: Implement something.
+ #_go_tool_pprof
+ ;;
+ 'prof') # TODO: Implement something.
+ #_go_tool_prof
+ ;;
+ 'vet') # TODO: Implement something.
+ #_go_tool_vet
+ ;;
+ 'yacc') # TODO: Implement something.
+ #_go_tool_yacc
+ ;;
+ esac
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=($(compgen -W "${COMPREPLY[*]} -h" -- "$cur"))
+ fi
+ fi
+ ;;
+ 'version')
+ ;;
+ 'vet')
+ if [[ "$cur" == -* ]]; then
+ :
+ else
+ COMPREPLY=(`_go_importpath "$cur"`)\
+ fi
+ ;;
+ 'help')
+ if [ "$COMP_CWORD" == 2 ]; then
+ COMPREPLY=($(compgen -W "$cmds $addhelp" -- "$cur"))
+ fi
+ ;;
+ *)
+ if [ "$COMP_CWORD" == 1 ]; then
+ COMPREPLY=($(compgen -W "$cmds $other" -- "$cur"))
+ else
+ _filedir
+ fi
+ ;;
+ esac
+}
+
+complete $filenames -F _go go
+
+# vim:ts=2 sw=2 et syn=sh
コアとなるコードの解説
追加されたmisc/bash/go
ファイルは、Bashのプログラマブル補完機能を利用してgo
コマンドの補完を提供します。
-
初期設定: ファイルの冒頭では、
complete -f -X
コマンドがいくつか記述されています。これらは、特定のコマンド(例:8l
,6l
,5l
,8g
,6g
,5g
,gofmt
,gccgo
)に対して、特定の拡張子(例:.8
,.6
,.5
,.go
)を持つファイルを補完対象から除外する設定を行っています。これは、これらのコマンドが通常、特定の種類のファイル(例: アセンブリファイルやGoソースファイル)を引数として取るため、それ以外のファイルを補完候補として表示しないようにするためのものです。 -
_go_importpath()
関数:_go_importpath() { echo "$(compgen -W "$(go list all) all std" -- "$1")" }
この関数は、Goのパッケージインポートパスを補完するためのものです。
go list all
: 現在のGo環境で利用可能なすべてのパッケージのインポートパスをリストアップします。all std
:all
(すべてのパッケージ)とstd
(標準ライブラリパッケージ)という特別なキーワードも補完候補に含めます。compgen -W "..." -- "$1"
: 上記のリストと、現在の入力文字列($1
)を比較し、一致する補完候補を生成します。これにより、ユーザーがGoのパッケージ名を途中まで入力した際に、関連するインポートパスが提案されます。
-
_go()
関数: この関数がgo
コマンドの実際の補完ロジックを実装しています。local cur=
_get_cword``: 現在カーソルがある単語を取得します。local prev="${COMP_WORDS[COMP_CWORD-1]}"
: 直前の単語を取得します。local cmd="${COMP_WORDS[1]}"
:go
コマンドの直後のサブコマンド(例:build
,test
など)を取得します。cmds
,addhelp
,other
変数には、go
コマンドの主要なサブコマンドやヘルプオプションが定義されています。- サブコマンドの補完:
if [ "$COMP_CWORD" == 1 ]
のブロックでは、ユーザーがgo
と入力した直後にTabキーを押した場合に、利用可能なサブコマンド(build
,clean
など)を補完します。 case "$cmd" in ... esac
ブロック: この大きなcase
文は、go
コマンドの各サブコマンド(build
,clean
,doc
,get
,install
,list
,run
,test
,tool
,vet
,help
)に対して、それぞれ異なる補完ロジックを適用します。- オプションの補完: 多くのサブコマンドでは、
-a
,-n
,-v
などの共通オプションや、サブコマンド固有のオプション(例:go build -o
)がcompgen -W
を使って補完されます。 - ファイルパスの補完:
_filedir
関数が使用され、現在のディレクトリ内のファイルやディレクトリが補完候補として提供されます。_filedir go
のように引数を指定することで、.go
ファイルのみを補完対象とすることも可能です。 - インポートパスの補完:
_go_importpath "$cur"
が呼び出され、Goのインポートパスが補完されます。 go build
の複雑なロジック:build
サブコマンドの補完は特に複雑です。既に.go
ファイルが引数として指定されているか、またはインポートパスが指定されているかによって、ファイルパスとインポートパスのどちらを優先して補完するかを判断します。これは、go build
がファイルパスもインポートパスも引数として受け取るためです。go tool
の動的な補完:tool
サブコマンドの場合、go tool
コマンド自体の出力を実行し、その結果をcompgen -W
に渡すことで、利用可能なツール名を動的に補完します。これにより、Goのバージョンアップによって新しいツールが追加された場合でも、補完スクリプトを更新することなく対応できます。TODO
コメント: スクリプト内には多くのTODO
コメントがあり、testflags
や特定のgo tool
サブコマンド(例:568a
,api
など)に対するより詳細な補完機能が将来的に追加されるべきであることが示されています。これは、このコミットが補完機能の初期実装であり、今後の拡張の余地があることを示唆しています。
- オプションの補完: 多くのサブコマンドでは、
-
complete $filenames -F _go go
:complete $filenames -F _go go
この行は、Bashに対して、
go
コマンドの補完を行う際に_go
関数を呼び出すように登録します。$filenames
は、デフォルトのファイル名補完も有効にすることを意味します。これにより、ユーザーがgo
コマンドを入力し、Tabキーを押すと、_go
関数が実行され、適切な補完候補が提供されるようになります。
このスクリプトは、Bashのプログラマブル補完の強力な機能を活用し、Go開発者にとって非常に便利なコマンドライン体験を提供するための基盤を築いています。
関連リンク
- Go言語公式サイト: https://go.dev/
- Goコマンドドキュメント: https://go.dev/cmd/go/
- Bash Programmable Completion (GNU Bash Manual): https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html
参考にした情報源リンク
- Go言語の公式ドキュメント
- Bashの公式マニュアル
- Go言語のソースコード(特に
cmd/go
ディレクトリ) - 一般的なBash補完スクリプトの慣習と実装例