Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 12342] ファイルの概要

このコミットは、Go言語の公式ツールである go tool コマンドに対するZshシェル補完機能を追加するものです。これにより、Zshユーザーは go コマンドとそのサブコマンド、および関連するフラグや引数を入力する際に、より効率的に作業を進めることができるようになります。具体的には、go build, go test, go get などの主要なサブコマンドや、go tool の内部ツール(5g, 6g, 8g, 5l, 6l, 8l, dist など)に対する補完が提供されます。

コミット

  • コミットハッシュ: 664481eb6b3e1237a6fcca590326203985a2db5c
  • 作者: Rémy Oudompheng oudomphe@phare.normalesup.org
  • コミット日時: Sat Mar 3 00:12:40 2012 +0100

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/664481eb6b3e1237a6fcca590326203985a2db5c

元コミット内容

misc: add zsh completion for go tool.

R=golang-dev, minux.ma, rsc
CC=golang-dev, remy
https://golang.org/cl/5699079

変更の背景

コマンドラインインターフェース(CLI)での作業効率は、開発者にとって非常に重要です。特に、頻繁に使用するコマンドや複雑なオプションを持つコマンドの場合、手動での入力は時間がかかり、タイプミスを誘発しやすくなります。シェル補完機能は、ユーザーがコマンドの一部を入力した際に、残りの部分や利用可能なオプション、引数などを自動的に提案することで、この問題を解決します。

Go言語の go tool は、ビルド、テスト、パッケージ管理、ドキュメント生成など、Go開発における多岐にわたるタスクを処理する中心的なコマンドです。このコマンドには多くのサブコマンドとフラグが存在するため、それらを手動で覚えるのは困難です。

このコミットは、Zshユーザーが go tool をより快適に、そして効率的に利用できるようにするために、Zshの強力な補完システムを活用して、go tool のサブコマンドやオプション、引数に対する自動補完機能を追加することを目的としています。これにより、開発者はコマンドの入力にかかる時間を削減し、より本質的な開発作業に集中できるようになります。

前提知識の解説

Zsh (Z Shell)

Zshは、Bourne Shell (sh) をベースにしたUnixシェルであり、BashやKshなどの他のシェルと同様に、コマンドラインインターフェースを提供します。Zshは、その強力なカスタマイズ性、豊富な機能、そして特に高度な補完システムで知られています。多くの開発者に愛用されており、macOSのデフォルトシェルとしても採用されています。

シェル補完 (Shell Completion)

シェル補完は、ユーザーがコマンドラインでコマンドやファイル名、オプションなどを入力する際に、Tabキーを押すことで、シェルが残りの部分を自動的に補完したり、利用可能な候補を一覧表示したりする機能です。これにより、入力の手間を省き、タイプミスを防ぎ、コマンドの利用方法を学習するのに役立ちます。

Zshの補完システム

Zshの補完システムは非常に強力で柔軟です。compinit 関数によって初期化され、compctl_arguments_values_alternative などの組み込み関数や、ユーザー定義の補完関数 (_ で始まる関数名が慣例) を使用して、複雑な補完ルールを定義できます。

  • compctl: 特定のコマンドに対して補完関数を割り当てるために使用されます。
  • _arguments: コマンドの引数やオプションに対する補完ルールを定義するための主要な関数です。オプションのショートハンド (-s) や、引数の説明 (:description:)、カスタム補完関数 (:arg:__my_func) などを指定できます。
  • _values: 特定の固定値のリストから補完候補を生成するために使用されます。
  • _alternative: 複数の補完方法を試行し、最初に成功したものを採用するために使用されます。例えば、ファイル名補完とカスタムリスト補完のどちらかを適用する場合などに使われます。
  • _path_files: ファイルパスを補完するための組み込み関数です。

go tool コマンド

go tool は、Go言語のSDKに含まれるコマンドラインユーティリティです。go build, go test, go get など、Go開発者が日常的に使用する主要なコマンドを統合的に提供します。また、コンパイラ (5g, 6g, 8g) やリンカ (5l, 6l, 8l)、プロファイリングツール (vet) など、低レベルのツールを実行するためのインターフェースとしても機能します。

技術的詳細

このコミットで追加されたZsh補完スクリプト misc/zsh/go は、__go_tool_complete という名前の補完関数を定義し、それを go コマンドに compdef で関連付けています。

スクリプトの主要な部分は、__go_tool_complete 関数内にあります。この関数は、現在のコマンドラインの状態(どの引数を補完しようとしているかなど)に基づいて、適切な補完候補を生成します。

  1. commands 配列の定義: go tool の主要なサブコマンドとその簡単な説明が commands 配列に定義されています。これは、go コマンドの直後にサブコマンドを補完する際に使用されます。

  2. build_flags 配列の定義: go build, go install, go get, go test など、ビルドに関連するサブコマンドで共通して使用されるフラグ(例: -a, -n, -x, -gcflags など)とその説明が build_flags 配列に定義されています。

  3. __go_list 関数: このヘルパー関数は、go list コマンドを使用してGoのインポートパスを動的に取得し、補完候補として提供します。これにより、ユーザーがパッケージ名を正確に覚えていなくても、利用可能なパッケージを補完できるようになります。

  4. case ${words[2]} in ... esac によるサブコマンドごとの補完ロジック: words[2] は、go コマンドの直後に入力されたサブコマンドを指します。この case ステートメントは、選択されたサブコマンドに応じて異なる補完ルールを適用します。

    • clean|doc: _arguments -s -w : '*:importpaths:__go_list' を使用して、インポートパスの補完を提供します。
    • fix|fmt|list|vet: _alternative を使用して、インポートパス (__go_list) または .go ファイル (_path_files -g "*.go") のいずれかを補完候補として提供します。
    • install: build_flags に加えて、-v フラグとインポートパスの補完を提供します。
    • get: build_flags のみを補完します。
    • build: build_flags-v-o (出力ファイル)、そしてインポートパスまたは .go ファイルの補完を提供します。
    • test: build_flags に加えて、テスト固有の多くのフラグ(例: -c, -i, -v, -short, -parallel, -cpu, -run, -bench, -benchtime, -timeout, -cpuprofile, -memprofile, -memprofilerate)と、インポートパスまたは .go ファイルの補完を提供します。
    • help: go help のサブコマンド(例: gopath, importpath, remote など)を補完します。
    • run: build_flags.go ファイルの補完を提供します。
    • tool: go tool の内部ツール(例: 5g, 6g, 8g, 5l, 6l, 8l, dist など)に対する補完ロジックが含まれています。
      • 5g, 6g, 8g (コンパイラ): コンパイラ固有のフラグ(例: -I, -L, -S, -V など)と .go ファイルの補完を提供します。
      • 5l, 6l, 8l (リンカ): リンカ固有のフラグ(例: -o, -L など)とオブジェクトファイル (*.a, *.o) の補完を提供します。
      • dist: dist ツール固有のサブコマンド(例: banner, bootstrap, clean など)を補完します。

このスクリプトは、Zshの強力な補完フレームワークを効果的に利用し、go tool の複雑なコマンド構造に対応した、非常に詳細で使いやすい補完機能を実現しています。

コアとなるコードの変更箇所

diff --git a/misc/zsh/go b/misc/zsh/go
index f17763d93f..23afa96569 100644
--- a/misc/zsh/go
+++ b/misc/zsh/go
@@ -12,3 +12,140 @@ compctl -g "*.go" gofmt
 
 # gccgo
 compctl -g "*.go" gccgo
++
+# go tool
+__go_tool_complete() {
+  typeset -a commands build_flags
+  commands+=(\
+    'build[compile packages and dependencies]'\
+    'clean[remove object files]'\
+    'doc[run godoc on package sources]'\
+    'fix[run go tool fix on packages]'\
+    'fmt[run gofmt on package sources]'\
+    'get[download and install packages and dependencies]'\
+    'help[display help]'\
+    'install[compile and install packages and dependencies]'\
+    'list[list packages]'\
+    'run[compile and run Go program]'\
+    'test[test packages]'\
+    'tool[run specified go tool]'\
+    'version[print Go version]'\
+    'vet[run go tool vet on packages]'\
+  )
+  if (( CURRENT == 2 )); then
+    # explain go commands
+    _values 'go tool commands' ${commands[@]}
+    return
+  fi
+  build_flags=(\
+    '-a[force reinstallation of packages that are already up-to-date]'\
+    '-n[print the commands but do not run them]'\
+    "-p[number of parallel builds]:number"\
+    '-x[print the commands]'\
+    "-work[print temporary directory name and keep it]"\
+    "-gcflags[flags for 5g/6g/8g]:flags"\
+    "-ldflags[flags for 5l/6l/8l]:flags"\
+    "-gccgoflags[flags for gccgo]:flags"\
+  )
+  __go_list() {
+      local expl importpaths
+      declare -a importpaths
+      importpaths=($(go list ${words[$CURRENT]}... 2>/dev/null))\
+      _wanted importpaths expl 'import paths' compadd "$@" - "${importpaths[@]}"\
+  }\
+  case ${words[2]} in\
+  clean|doc)\
+      _arguments -s -w : '*:importpaths:__go_list'\
+      ;;\
+  fix|fmt|list|vet)\
+      _alternative ':importpaths:__go_list' ':files:_path_files -g "*.go"'\
+      ;;\
+  install)\
+      _arguments -s -w : ${build_flags[@]} \\\
+        "-v[show package names]" \\\
+	'*:importpaths:__go_list'\
+      ;;\
+  get)\
+      _arguments -s -w : \\\
+        ${build_flags[@]}\
+      ;;\
+  build)\
+      _arguments -s -w : \\\
+        ${build_flags[@]} \\\
+        "-v[show package names]" \\\
+        "-o[output file]:file:_files" \\\
+        "*:args:{ _alternative ':importpaths:__go_list' ':files:_path_files -g \"*.go\"' }"\
+      ;;\
+  test)\
+      _arguments -s -w : \\\
+        ${build_flags[@]} \\\
+        "-c[do not run, compile the test binary]" \\\
+        "-i[do not run, install dependencies]" \\\
+        "-v[print test output]" \\\
+        "-x[print the commands]" \\\
+        "-short[use short mode]" \\\
+        "-parallel[number of parallel tests]:number" \\\
+        "-cpu[values of GOMAXPROCS to use]:number list" \\\
+        "-run[run tests and examples matching regexp]:regexp" \\\
+        "-bench[run benchmarks matching regexp]:regexp" \\\
+        "-benchtime[run each benchmark during n seconds]:duration" \\\
+        "-timeout[kill test after that duration]:duration" \\\
+        "-cpuprofile[write CPU profile to file]:file:_files" \\\
+        "-memprofile[write heap profile to file]:file:_files" \\\
+        "-memprofilerate[set heap profiling rate]:number" \\\
+        "*:args:{ _alternative ':importpaths:__go_list' ':files:_path_files -g \"*.go\"' }"\
+      ;;\
+  help)\
+      _values "${commands[@]}" \\\
+        'gopath[GOPATH environment variable]' \\\
+        'importpath[description of import paths]' \\\
+        'remote[remote import path syntax]'\
+        'testflag[description of testing flags]'\
+        'testfunc[description of testing functions]'\
+      ;;\
+  run)\
+      _arguments -s -w : \\\
+          ${build_flags[@]} \\\
+          '*:file:_path_files -g "*.go"'\
+      ;;\
+  tool)\
+      if (( CURRENT == 3 )); then\
+          _values "go tool" $(go tool)\
+          return\
+      fi\
+      case ${words[3]} in\
+      [568]g)\
+          _arguments -s -w : \\\
+              '-I[search for packages in DIR]:includes:_path_files -/' \\\
+              '-L[show full path in file:line prints]' \\\
+              '-S[print the assembly language]' \\\
+              '-V[print the compiler version]' \\\
+              '-e[no limit on number of errors printed]' \\\
+              '-h[panic on an error]' \\\
+              '-l[disable inlining]' \\\
+              '-m[print optimization decisions]' \\\
+              '-o[file specify output file]:file'\
+              '-p[assumed import path for this code]:importpath'\
+              '-u[disable package unsafe]'\
+              "*:file:_files -g '*.go'"\
+          ;;\
+      [568]l)\
+          local O=${words[3]%l}\
+          _arguments -s -w : \\\
+              '-o[file specify output file]:file'\
+              '-L[search for packages in DIR]:includes:_path_files -/' \\\
+              "*:file:_files -g '*.[ao$O]'"\
+          ;;\
+      dist)\
+          _values "dist tool" banner bootstrap clean env install version\
+          ;;\
+      *)\
+          # use files by default\
+          _files\
+          ;;\
+      esac\
+      ;;\
+  esac\
+}\
+\
+compdef __go_tool_complete go\

コアとなるコードの解説

このコミットは、misc/zsh/go という新しいファイルを追加しており、その内容はGoコマンドのZsh補完スクリプトです。

  1. __go_tool_complete() 関数定義: この関数が go コマンドの補完ロジックの本体です。

    • typeset -a commands build_flags: commandsbuild_flags という2つの配列を宣言しています。
    • commands+=(\ ... ): go コマンドの主要なサブコマンド(build, clean, doc, fix, fmt, get, help, install, list, run, test, tool, version, vet)とその簡単な説明を定義しています。これらの説明は、補完候補が表示される際にユーザーに役立ちます。
    • if (( CURRENT == 2 )); then ... fi: これは、ユーザーが go と入力した直後(つまり、2番目の引数を補完しようとしている場合)に、上記の commands 配列の内容を補完候補として表示するためのロジックです。_values 関数が使用され、サブコマンドのリストと説明が表示されます。
    • build_flags=(\ ... ): go buildgo test など、ビルド関連のコマンドで共通して使用されるフラグ(例: -a, -n, -p, -x, -work, -gcflags, -ldflags, -gccgoflags)とその説明を定義しています。
    • __go_list() 関数:
      • このネストされた関数は、go list コマンドを実行してGoのインポートパスを動的に取得し、補完候補として提供します。これにより、ユーザーは既存のGoパッケージを簡単に補完できます。2>/dev/null はエラー出力を抑制しています。
      • _wanted はZshの補完システムの一部で、補完候補を整形して表示するために使われます。
    • case ${words[2]} in ... esac: これは、ユーザーが入力した go コマンドのサブコマンド(words[2] に格納される)に基づいて、異なる補完ロジックを適用するための主要な分岐点です。
      • clean|doc: _arguments を使用して、インポートパス (__go_list) の補完を提供します。
      • fix|fmt|list|vet: _alternative を使用して、インポートパス (__go_list) または .go ファイル (_path_files -g "*.go") のいずれかを補完候補として提供します。
      • install: build_flags に加えて、-v (詳細表示) フラグとインポートパスの補完を提供します。
      • get: build_flags のみを補完します。
      • build: build_flags-v-o (出力ファイル)、そしてインポートパスまたは .go ファイルの補完を提供します。
      • test: build_flags に加えて、テストに特化した多くのフラグ(-c, -i, -v, -x, -short, -parallel, -cpu, -run, -bench, -benchtime, -timeout, -cpuprofile, -memprofile, -memprofilerate)と、インポートパスまたは .go ファイルの補完を提供します。
      • help: go help のサブコマンド(gopath, importpath, remote, testflag, testfunc)を補完します。
      • run: build_flags.go ファイルの補完を提供します。
      • tool: go tool の内部ツールに対する補完ロジックです。
        • if (( CURRENT == 3 )); then ... fi: go tool の直後(3番目の引数)に、利用可能な内部ツール(5g, 6g, 8g, 5l, 6l, 8l, dist など)を go tool コマンドの出力から動的に取得して補完します。
        • case ${words[3]} in ... esac: 選択された内部ツールに応じて、さらに詳細な補完を提供します。
          • [568]g) (コンパイラ): コンパイラ固有のフラグ(-I, -L, -S, -V, -e, -h, -l, -m, -o, -p, -u)と .go ファイルの補完を提供します。
          • [568]l) (リンカ): リンカ固有のフラグ(-o, -L)とオブジェクトファイル (*.a, *.o) の補完を提供します。
          • dist): dist ツール固有のサブコマンド(banner, bootstrap, clean, env, install, version)を補完します。
          • *): 上記のいずれにも該当しない場合は、デフォルトでファイル補完 (_files) を行います。
  2. compdef __go_tool_complete go: この行は、定義した __go_tool_complete 関数を go コマンドの補完関数としてZshに登録します。これにより、Zshは go コマンドが入力された際に、この関数を呼び出して補完候補を生成するようになります。

このスクリプト全体で、Zshの強力な補完機能が活用され、Go開発者が go コマンドをより効率的に使用できるようになっています。

関連リンク

参考にした情報源リンク