[インデックス 17719] ファイルの概要
このコミットは、Go言語のVimプラグインにおけるgodoc
コマンドの機能を拡張し、パッケージとそのメンバー(関数、型、変数など)を個別にドキュメント検索できるようにするものです。これにより、ユーザーは特定のパッケージ全体、またはそのパッケージ内の特定の要素(例: encoding/json
パッケージのMarshal
関数)のドキュメントをVim内から直接参照できるようになります。
コミット
commit fcee50c46eebf742afd3b90150a4b5e6c730715d
Author: Yasuhiro Matsumoto <mattn.jp@gmail.com>
Date: Wed Oct 2 08:52:51 2013 +1000
misc/vim: Separate package and package members.
This change allow to godoc:
:Godoc github.com/mattn/go-gtk/gtk
:Godoc github.com/mattn/go-gtk/gtk NewWindow
:Godoc encoding/json
:Godoc encoding/json Marshal
R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/14171043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/fcee50c46eebf742afd3b90150a4b5e6c730715d
元コミット内容
misc/vim: Separate package and package members.
This change allow to godoc:
:Godoc github.com/mattn/go-gtk/gtk
:Godoc github.com/mattn/go-gtk/gtk NewWindow
:Godoc encoding/json
:Godoc encoding/json Marshal
R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/14171043
変更の背景
この変更の背景には、Go言語のドキュメンテーションツールであるgodoc
をVimエディタ内でより柔軟に利用したいというニーズがありました。以前のVimプラグインでは、godoc
コマンドがパッケージ全体を対象とするか、あるいは現在のカーソル位置にある単語を対象とするかのいずれかであり、特定のパッケージのメンバー(例えば、fmt
パッケージのPrintln
関数)を直接指定してドキュメントを検索することが困難でした。
開発者は、godoc encoding/json Marshal
のように、パッケージ名とメンバー名をスペースで区切って指定することで、そのメンバーのドキュメントを直接参照できるような機能拡張を求めていました。これにより、VimでのGo開発におけるドキュメント参照の効率が大幅に向上し、開発者はより迅速に必要な情報を得られるようになります。
前提知識の解説
Go言語のドキュメンテーションとgodoc
コマンド
Go言語は、コードに記述されたコメントから自動的にドキュメンテーションを生成する仕組みを持っています。このドキュメンテーションは、godoc
というツールによって提供されます。godoc
は、Goのソースコードを解析し、パッケージ、関数、型、変数などのドキュメントを生成・表示するコマンドラインツールです。
- パッケージドキュメント: パッケージの概要や目的を説明します。
- メンバー(関数、型、変数など)ドキュメント: 各要素の機能、引数、戻り値などを説明します。
godoc
コマンドは通常、以下のように使用されます。
godoc <package_path>
: 指定されたパッケージのドキュメントを表示します。例:godoc fmt
godoc <package_path> <member_name>
: 指定されたパッケージ内の特定のメンバーのドキュメントを表示します。例:godoc fmt Println
Vimエディタとプラグイン
Vimは、高機能なテキストエディタであり、その機能を拡張するためにプラグインシステムを備えています。Go言語開発のためのVimプラグインは、Goのコード補完、シンタックスハイライト、フォーマット、そしてgodoc
との連携など、様々な機能を提供します。
command!
: Vimのユーザー定義コマンドを定義するためのキーワードです。-nargs=*
は任意の数の引数を取ることを意味し、-complete=customlist,go#complete#Package
は引数の補完にgo#complete#Package
関数を使用することを意味します。expand('<cword>')
: カーソル下の単語を取得するVimスクリプトの関数です。system()
: シェルコマンドを実行し、その出力を取得するVimスクリプトの関数です。split()
: 文字列を区切り文字で分割するVimスクリプトの関数です。substitute()
: 文字列内のパターンを置換するVimスクリプトの関数です。
go env GOROOT
go env GOROOT
は、Goのインストールディレクトリ(Goの標準ライブラリなどが配置されている場所)のパスを取得するためのコマンドです。VimプラグインがGoのソースコードを検索する際に利用されます。
技術的詳細
このコミットは、VimのGoプラグインにおけるgodoc
コマンドの引数処理ロジックを改善することで、パッケージ名とメンバー名を区別して解釈できるようにしています。
具体的には、以下の2つのファイルが変更されています。
-
misc/vim/autoload/go/complete.vim
:go#complete#Package
関数が修正されています。この関数は、VimのGodoc
コマンドの引数補完に使用されます。- 変更前は、コマンドラインの引数が2つ以上ある場合に、パッケージメンバーの補完を適切に処理できていませんでした。
- 変更後、
a:CmdLine
(コマンドライン全体)を解析し、引数の数が2つを超える場合は、パッケージメンバーの補完ロジックを将来的に追加するためのTODO
コメントが追加されています。これにより、パッケージとメンバーの分離を意識した補完の基盤が作られています。
-
misc/vim/plugin/godoc.vim
:Godoc
コマンドの定義が変更されています。command! -nargs=* -range -complete=customlist,go#complete#Package Godoc :call s:Godoc(<q-args>)
からcommand! -nargs=* -range -complete=customlist,go#complete#Package Godoc :call s:Godoc(<f-args>)
へ変更されています。<q-args>
は引数をクォートして渡しますが、<f-args>
は引数をそのまま(スペース区切りで)関数に渡します。これにより、s:Godoc
関数が複数の引数(パッケージ名とメンバー名)を個別に受け取れるようになります。
s:GodocWord
関数が変更されています。return
ステートメントがreturn 0
に変更され、関数が成功したか失敗したかを示す戻り値を持つようになりました。これは、s:Godoc
関数でのエラーハンドリングを改善するために使用されます。
s:Godoc
関数が大幅に修正されています。- 引数
a:000
(可変長引数リスト)の長さをチェックし、引数が与えられていない場合は、以前と同様にカーソル下の単語を取得します。 - カーソル下の単語を取得する際に、
substitute(word, '[^a-zA-Z0-9\\/._~-]', '', 'g')
を追加し、単語から不要な文字を除去しています。 split(word, '\\.\\ze[^./]\\+$')
を使用して、単語をパッケージ名とメンバー名に分割します。\\.\\ze[^./]\\+$
という正規表現は、最後のドット(.
)で区切り、その後にスラッシュ(/
)やドット(.
)が続かない文字列(つまりメンバー名)がある場合に分割することを意味します。これにより、encoding/json.Marshal
のような形式をencoding/json
とMarshal
に分割できます。s:GodocWord(words[0])
を呼び出して、まずパッケージのドキュメントを検索します。- もし複数の単語(パッケージ名とメンバー名)がある場合、
search()
関数を使って、ドキュメント内でメンバー名(words[1]
)が定義されている行を検索します。これにより、godoc
の出力から特定のメンバーのドキュメント部分にジャンプできるようになります。 search('^\\%(const\\|var\\|type\\|\\s\\+\\) ' . words[1] . '\\s\\+=\\s')
は、定数、変数、型の定義を検索します。search('^func ' . words[1] . '(')
は、関数の定義を検索します。- 検索に成功した場合、
return
して処理を終了します。 - 検索に失敗した場合、
echo 'No documentation found for "' . words[1] . '".'
と表示し、メンバーのドキュメントが見つからなかったことをユーザーに通知します。
- 引数
これらの変更により、VimのGodoc
コマンドは、godoc encoding/json Marshal
のように、パッケージとメンバーを区別してドキュメントを検索できるようになりました。
コアとなるコードの変更箇所
misc/vim/autoload/go/complete.vim
--- a/misc/vim/autoload/go/complete.vim
+++ b/misc/vim/autoload/go/complete.vim
@@ -31,6 +31,12 @@ endif
function! go#complete#Package(ArgLead, CmdLine, CursorPos)
let dirs = []
+ let words = split(a:CmdLine, '\s\+', 1)
+ if len(words) > 2
+ " TODO Complete package members
+ return []
+ endif
+
if executable('go')
let goroot = substitute(system('go env GOROOT'), '\n', '', 'g')
if v:shell_error
misc/vim/plugin/godoc.vim
--- a/misc/vim/plugin/godoc.vim
+++ b/misc/vim/plugin/godoc.vim
@@ -31,7 +31,7 @@ if !exists('g:go_godoc_commands')
endif
if g:go_godoc_commands
- command! -nargs=* -range -complete=customlist,go#complete#Package Godoc :call s:Godoc(<q-args>)
+ command! -nargs=* -range -complete=customlist,go#complete#Package Godoc :call s:Godoc(<f-args>)
endif
nnoremap <silent> <Plug>(godoc-keyword) :<C-u>call <SID>Godoc('')<CR>
@@ -71,7 +71,7 @@ function! s:GodocWord(word)
echo "godoc command not found."
echo " install with: go get code.google.com/p/go.tools/cmd/godoc"
echohl None
- return
+ return 0
endif
let word = a:word
silent! let content = system('godoc ' . word)
@@ -80,12 +80,12 @@ function! s:GodocWord(word)
silent! let content = system('godoc ' . s:last_word.'/'.word)
if v:shell_error || !len(content)
echo 'No documentation found for "' . word . '".'
- return
+ return 0
endif
let word = s:last_word.'/'.word
else
echo 'No documentation found for "' . word . '".'
- return
+ return 0
endif
endif
let s:last_word = word
@@ -96,30 +96,34 @@ function! s:GodocWord(word)
silent! normal gg
setlocal nomodifiable
setfiletype godoc
+ return 1
endfunction
function! s:Godoc(...)
- let word = join(a:000, ' ')
- if !len(word)
+ if !len(a:000)
let oldiskeyword = &iskeyword
setlocal iskeyword+=.
let word = expand('<cword>')
let &iskeyword = oldiskeyword
+ let word = substitute(word, '[^a-zA-Z0-9\\/._~-]', '', 'g')
+ let words = split(word, '\\.\\ze[^./]\\+$')
+ else
+ let words = a:000
endif
- let word = substitute(word, '[^a-zA-Z0-9\\/._~-]', '', 'g')
- let words = split(word, '\.')
if !len(words)
return
endif
- call s:GodocWord(words[0])
- if len(words) > 1
- if search('^\\%(const\\|var\\|type\\|\\s\\+\\) ' . words[1] . '\\s\\+=\\s')
- return
- endif
- if search('^func ' . words[1] . '(')
- return
+ if s:GodocWord(words[0])
+ if len(words) > 1
+ if search('^\\%(const\\|var\\|type\\|\\s\\+\\) ' . words[1] . '\\s\\+=\\s')
+ return
+ endif
+ if search('^func ' . words[1] . '(')
+ silent! normal zt
+ return
+ endif
+ echo 'No documentation found for "' . words[1] . '".'
endif
- echo 'No documentation found for "' . word . '".'
endif
endfunction
コアとなるコードの解説
misc/vim/autoload/go/complete.vim
の変更
このファイルは、VimのGoプラグインにおけるコマンドライン補完のロジックを扱っています。
let words = split(a:CmdLine, '\s\+', 1)
if len(words) > 2
" TODO Complete package members
return []
endif
a:CmdLine
は、現在のコマンドラインの文字列全体を指します。split(a:CmdLine, '\s\+', 1)
は、コマンドラインを1つ以上の空白文字で分割し、words
リストに格納します。1
は空の要素を生成しないためのフラグです。if len(words) > 2
の条件は、コマンドラインに3つ以上の単語がある場合(例::Godoc encoding/json Marshal
の場合、Godoc
,encoding/json
,Marshal
の3つ)に真となります。- このブロックは、将来的にパッケージメンバーの補完機能を追加するためのプレースホルダーとして機能します。現時点では、3つ以上の単語がある場合は空のリストを返し、補完を行わないようにしています。これは、パッケージとメンバーの分離を考慮した補完ロジックの第一歩です。
misc/vim/plugin/godoc.vim
の変更
このファイルは、VimのGodoc
コマンドの主要なロジックを含んでいます。
Godoc
コマンドの定義変更
- command! -nargs=* -range -complete=customlist,go#complete#Package Godoc :call s:Godoc(<q-args>)
+ command! -nargs=* -range -complete=customlist,go#complete#Package Godoc :call s:Godoc(<f-args>)
command!
はVimのユーザー定義コマンドを宣言します。-nargs=*
は、コマンドが任意の数の引数を取ることができることを示します。-range
は、コマンドが範囲指定(例::'<,'>Godoc
)を受け入れることを示します。-complete=customlist,go#complete#Package
は、このコマンドの引数補完にgo#complete#Package
関数を使用することを指定します。- 最も重要な変更は、
:call s:Godoc(<q-args>)
から:call s:Godoc(<f-args>)
への変更です。<q-args>
は、コマンドラインの引数を単一の文字列として、適切にクォート(エスケープ)して関数に渡します。例えば、Godoc encoding/json Marshal
は"encoding/json Marshal"
として渡されます。<f-args>
は、コマンドラインの引数をスペースで区切られた複数の引数として、そのまま関数に渡します。例えば、Godoc encoding/json Marshal
はs:Godoc("encoding/json", "Marshal")
のように渡されます。
- この変更により、
s:Godoc
関数がパッケージ名とメンバー名を別々の引数として受け取ることが可能になり、それぞれの引数を個別に処理できるようになります。
s:GodocWord
関数の変更
- return
+ return 0
- return
+ return 0
- return
+ return 0
+ return 1
s:GodocWord
関数は、与えられた単語(パッケージ名またはメンバー名)に対してgodoc
コマンドを実行し、その結果をVimのバッファに表示する役割を担っています。- 変更前は、エラー発生時やドキュメントが見つからない場合に単に
return
していました。 - 変更後、
return 0
またはreturn 1
を返すようになりました。0
は失敗、1
は成功を示します。これにより、呼び出し元のs:Godoc
関数がs:GodocWord
の実行結果に基づいて、後続の処理を制御できるようになります。
s:Godoc
関数の変更
この関数は、Godoc
コマンドが呼び出されたときに実際に実行されるメインのロジックです。
function! s:Godoc(...)
- let word = join(a:000, ' ')
if !len(a:000)
let oldiskeyword = &iskeyword
setlocal iskeyword+=.
let word = expand('<cword>')
let &iskeyword = oldiskeyword
+ let word = substitute(word, '[^a-zA-Z0-9\\/._~-]', '', 'g')
+ let words = split(word, '\\.\\ze[^./]\\+$')
+ else
+ let words = a:000
endif
- let word = substitute(word, '[^a-zA-Z0-9\\/._~-]', '', 'g')
- let words = split(word, '\.')
if !len(words)
return
endif
- call s:GodocWord(words[0])
- if len(words) > 1
- if search('^\\%(const\\|var\\|type\\|\\s\\+\\) ' . words[1] . '\\s\\+=\\s')
- return
- endif
- if search('^func ' . words[1] . '(')
- return
+ if s:GodocWord(words[0])
+ if len(words) > 1
+ if search('^\\%(const\\|var\\|type\\|\\s\\+\\) ' . words[1] . '\\s\\+=\\s')
+ return
+ endif
+ if search('^func ' . words[1] . '(')
+ silent! normal zt
+ return
+ endif
+ echo 'No documentation found for "' . words[1] . '".'
endif
- echo 'No documentation found for "' . word . '".'
endif
endfunction
if !len(a:000)
:Godoc
コマンドに引数が与えられなかった場合(例::Godoc
とだけ入力された場合)の処理です。expand('<cword>')
でカーソル下の単語を取得します。substitute(word, '[^a-zA-Z0-9\\/._~-]', '', 'g')
で、取得した単語からGoの識別子として不適切な文字を除去します。split(word, '\\.\\ze[^./]\\+$')
で、単語をパッケージ名とメンバー名に分割します。例えば、json.Marshal
は["json", "Marshal"]
に分割されます。\\.\\ze[^./]\\+$
は、最後のドットで分割し、そのドットの後にスラッシュやドットが続かない文字列(メンバー名)がある場合にマッチします。
else
:Godoc
コマンドに引数が与えられた場合(例::Godoc encoding/json Marshal
)の処理です。words = a:000
により、<f-args>
で渡された引数リストがそのままwords
変数に代入されます。これにより、words[0]
がパッケージ名、words[1]
がメンバー名となります。
if !len(words)
:words
リストが空の場合(有効な引数がなかった場合)は、何もせずにreturn
します。if s:GodocWord(words[0])
: まず、words
リストの最初の要素(パッケージ名)に対してs:GodocWord
を呼び出し、パッケージのドキュメントを表示します。s:GodocWord
が成功した場合(1
を返した場合)にのみ、次のメンバー検索に進みます。if len(words) > 1
:words
リストに2つ以上の要素がある場合(つまり、パッケージ名とメンバー名が指定された場合)の処理です。search('^\\%(const\\|var\\|type\\|\\s\\+\\) ' . words[1] . '\\s\\+=\\s')
:godoc
の出力バッファ内で、const
、var
、type
、または空白で始まる行で、2番目の単語(メンバー名)が定義されている箇所を検索します。これは、定数、変数、型のドキュメントを見つけるためのものです。search('^func ' . words[1] . '(')
:godoc
の出力バッファ内で、func
で始まる行で、2番目の単語(メンバー名)が定義されている箇所を検索します。これは、関数のドキュメントを見つけるためのものです。silent! normal zt
: 検索に成功した場合、カーソルを検索結果の行に移動し、その行が画面の上部に表示されるようにスクロールします。echo 'No documentation found for "' . words[1] . '".'
: メンバーのドキュメントが見つからなかった場合に、その旨をユーザーに通知します。
これらの変更により、s:Godoc
関数は、引数の有無、引数の形式(パッケージのみか、パッケージとメンバーか)に応じて、適切なgodoc
検索とVimバッファ内のナビゲーションを実行できるようになりました。
関連リンク
- Go言語公式ドキュメント: https://go.dev/doc/
godoc
コマンドの公式ドキュメント: https://pkg.go.dev/cmd/godoc- Vim公式ウェブサイト: https://www.vim.org/
- Go言語用Vimプラグイン (golang/goリポジトリ内のmisc/vim): https://github.com/golang/go/tree/master/misc/vim
参考にした情報源リンク
- Go言語の
godoc
コマンドに関する情報 - Vimscriptの関数(
expand
,split
,substitute
,search
,system
など)に関するVimのヘルプドキュメント - Vimのコマンド定義(
command!
,-nargs
,-complete
,<q-args>
,<f-args>
など)に関するVimのヘルプドキュメント - Go言語のパッケージ構造とドキュメンテーションの慣習に関する情報
- GitHubのコミット履歴と差分表示
- Go CL (Change List) 14171043: https://golang.org/cl/14171043 (これはコミットメッセージに記載されているリンクであり、変更の直接的な情報源です。)