[インデックス 15945] ファイルの概要
このコミットは、Go言語のドキュメントツールであるgodocをVimエディタから利用するためのプラグインmisc/vim/plugin/godoc.vimに対する改善です。具体的には、log.Printのような「パッケージ名.関数名」形式の識別子に対して、VimのGodocコマンドが正しく動作するように修正されています。
コミット
commit d7434816c1a2407030f59837496a8dc86e6d1968
Author: Yasuhiro Matsumoto <mattn.jp@gmail.com>
Date: Tue Mar 26 17:39:46 2013 +1100
misc/vim: make Godoc command work with "log.Print".
R=dsymonds
CC=golang-dev
https://golang.org/cl/7757043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d7434816c1a2407030f59837496a8dc86e6d1968
元コミット内容
このコミットは、Vimのgodoc.vimプラグインにおいて、log.Printのようにドットを含む識別子に対してGodocコマンドが期待通りに動作しない問題を修正します。以前は、log.Printのような識別子に対してGodocコマンドを実行すると、logまたはPrintのいずれか一方しか認識されず、logパッケージ全体のドキュメントを表示したり、Printという単独の識別子に対するドキュメントを検索したりすることができませんでした。この変更により、ドットを含む識別子全体を正しく認識し、パッケージのドキュメントを表示した上で、必要に応じてより詳細なフィードバックを提供するようになります。
変更の背景
Go言語では、標準ライブラリやサードパーティライブラリの関数や変数にアクセスする際に、パッケージ名.識別子名という形式(例: fmt.Println, log.Print)が頻繁に用いられます。Vimのgodoc.vimプラグインは、カーソル下の単語(cword)に基づいてgodocコマンドを実行し、Goのドキュメントを表示する機能を提供していました。
しかし、Vimのデフォルトの「単語」の定義(iskeywordオプション)では、ドット(.)が単語の一部として扱われないことが一般的です。このため、log.Printのような識別子にカーソルがある状態でGodocコマンドを実行すると、VimはlogまたはPrintのいずれか一方のみを単語として認識し、godoc log.Printというコマンドを生成できませんでした。結果として、ユーザーはlogパッケージ全体のドキュメントを見たい場合や、Print関数の具体的なドキュメントを見たい場合に、期待する結果を得ることができませんでした。
このコミットは、このVimのiskeyword設定と、godocコマンドへの引数の渡し方を調整することで、ユーザーがGo言語の慣用的なコードスタイルでドキュメントを効率的に参照できるようにすることを目的としています。
前提知識の解説
- Go言語の
godocコマンド:godocはGo言語に標準で付属するツールで、Goのソースコードからドキュメントを生成・表示します。パッケージ名(例:godoc fmt)を指定するとそのパッケージのドキュメントを、パッケージ名.識別子名(例:godoc fmt.Println)を指定すると特定の関数や変数のドキュメントを表示できます。 - Vimの
iskeywordオプション: Vimエディタにはiskeywordというオプションがあり、これは「単語」を構成する文字の集合を定義します。例えば、デフォルトでは英数字やアンダースコアなどが含まれますが、ドット(.)は通常含まれません。この設定は、w(単語移動)や*(カーソル下の単語を検索)などのコマンドの挙動に影響を与えます。 - Vimscriptの
expand('<cword>'): Vimscriptの関数で、カーソル下の「単語」(iskeywordの設定に基づく)を取得します。 - Vimscriptの
setlocal: 現在のバッファ(ファイル)のみにオプションを設定するコマンドです。グローバルな設定を変更せずに一時的に挙動を変える際に使用されます。 - Vimscriptの
split()関数: 文字列を指定した区切り文字で分割し、リスト(配列)として返します。 - Vimscriptの
search()関数: 現在のバッファ内で指定されたパターンを検索します。
技術的詳細
このコミットの技術的な核心は、Vimのiskeywordオプションの一時的な変更と、取得した単語の解析ロジックの改善にあります。
-
iskeywordの一時的な変更:let oldiskeyword = &iskeywordで現在のiskeyword設定を保存します。setlocal iskeyword+=.で、現在のバッファに限り、iskeywordにドット(.)を追加します。これにより、log.Printのような文字列全体がexpand('<cword>')によって一つの単語として認識されるようになります。let word = expand('<cword>')で、ドットを含む単語全体を取得します。let &iskeyword = oldiskeywordで、元のiskeyword設定に戻します。これにより、他のVimの操作に影響を与えません。
-
単語の解析と
godoc呼び出しのロジック:- 取得した
word(例:log.Print)をsplit(word, '\.')でドットを区切り文字として分割し、wordsというリスト(例:["log", "Print"])に格納します。 call s:GodocWord(words[0])を呼び出し、まずリストの最初の要素(例:log)に対してgodocを実行します。これにより、logパッケージ全体のドキュメントが表示されます。これは、log.Printのような呼び出しの場合、通常ユーザーが最初に知りたい情報がパッケージの概要であるためです。if len(words) > 1の条件で、もし単語がドットで区切られた複数の部分からなる場合(例:log.Printのように第二要素が存在する場合)の追加処理を行います。search('^\%(const\|var\|type\|\s\+\) ' . words[1] . '\s\+=\s')とsearch('^func ' . words[1] . '(\')というVimscriptのsearchコマンドを使って、現在のファイル内でwords[1](例:Print)がconst、var、type、またはfuncとして宣言されているかを検索します。- この検索は、ユーザーが入力した
パッケージ名.識別子名の識別子名部分が、実は現在のファイル内で定義されているローカルな定数、変数、型、または関数である可能性を考慮しています。もしローカルで定義されていれば、それ以上の特別なドキュメント検索は不要と判断し、returnします。 - もしローカルな定義が見つからず、かつ
wordsが複数の要素を持つ場合(つまり、godocが直接そのパッケージ名.識別子名の形式でドキュメントを提供しない可能性のある、例えばメソッドや非公開の識別子である場合)、echo 'No documentation found for "' . word . '".'というメッセージを表示します。これは、godocがパッケージレベルのドキュメントは提供したが、指定された完全な識別子に対する直接的なドキュメントは見つからなかったことをユーザーに伝えます。
- 取得した
この一連のロジックにより、log.Printのようなケースでは、まずlogパッケージのドキュメントが表示され、その上でPrintがローカルな定義でなければ「ドキュメントが見つかりません」という補足メッセージが表示されるという挙動になります。
コアとなるコードの変更箇所
変更はmisc/vim/plugin/godoc.vimファイル内のs:Godoc関数に集中しています。
--- a/misc/vim/plugin/godoc.vim
+++ b/misc/vim/plugin/godoc.vim
@@ -70,13 +70,26 @@ endfunction
function! s:Godoc(...)\
let word = join(a:000, ' ')\
if !len(word)\
+ let oldiskeyword = &iskeyword\
+ setlocal iskeyword+=.\
let word = expand('<cword>')\
+ let &iskeyword = oldiskeyword\
endif\
let word = substitute(word, '[^a-zA-Z0-9\\/._~-]', '', 'g')\
- if !len(word)\
+ let words = split(word, '\.')\
+ if !len(words)\
return\
endif\
- call s:GodocWord(word)\
+ 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\
+ endif\
+ echo 'No documentation found for "' . word . '".'\
+ endif\
endfunction\
\
command! -nargs=* -range -complete=customlist,go#complete#Package Godoc :call s:Godoc(<q-args>)
コアとなるコードの解説
if !len(word)ブロック内:let oldiskeyword = &iskeyword: 現在のiskeyword設定をoldiskeyword変数に保存します。setlocal iskeyword+=.: 現在のVimバッファに対して、iskeywordオプションにドット(.)を追加します。これにより、expand('<cword>')がlog.Printのようなドットを含む単語全体を認識できるようになります。let word = expand('<cword>'): カーソル下の単語を取得します。この時点でiskeywordにドットが含まれているため、log.Print全体が取得されます。let &iskeyword = oldiskeyword:iskeyword設定を元の値に戻し、他のVim操作への影響を避けます。
call s:GodocWord(word)の変更:- 元の
call s:GodocWord(word)が削除され、より複雑なロジックに置き換えられています。 let words = split(word, '\.'): 取得した単語(例:log.Print)をドットで分割し、wordsリスト(例:["log", "Print"])に格納します。if !len(words): 分割結果が空の場合(単語が取得できなかった場合)は処理を終了します。call s:GodocWord(words[0]): まず、分割された単語の最初の部分(例:log)に対してs:GodocWordを呼び出します。これにより、logパッケージのドキュメントが表示されます。if len(words) > 1ブロック:search('^\%(const\\|var\\|type\\|\\s\\+\\) ' . words[1] . '\\s\\+=\\s'):wordsの2番目の要素(例:Print)が、現在のファイル内でconst、var、type、または空白で始まる行で定義されているかを検索します。これは、Printがローカルな定数、変数、または型である可能性をチェックします。search('^func ' . words[1] . '(\'):wordsの2番目の要素が、現在のファイル内でfuncとして定義されているかを検索します。これは、Printがローカルな関数である可能性をチェックします。- 上記の
searchのいずれかが成功した場合、returnして処理を終了します。これは、ローカルな定義が見つかった場合、パッケージのドキュメント表示で十分と判断するためです。 - どちらの
searchも成功しなかった場合、echo 'No documentation found for "' . word . '".'というメッセージを表示します。これは、godocが直接その完全な識別子に対するドキュメントを提供できない場合に、ユーザーにその旨を伝えるためのものです。
- 元の
関連リンク
- Go言語の
godocコマンドに関する公式ドキュメント: https://pkg.go.dev/cmd/godoc - Vimの
iskeywordオプションに関するヘルプ::help 'iskeyword'(Vim内で実行) - Vimscriptの
expand()関数に関するヘルプ::help expand()(Vim内で実行) - Vimscriptの
split()関数に関するヘルプ::help split()(Vim内で実行) - Vimscriptの
search()関数に関するヘルプ::help search()(Vim内で実行)
参考にした情報源リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- Go言語のGerritコードレビューシステム (過去の変更履歴): https://go-review.googlesource.com/
- Vimのドキュメント (オンライン): https://vimhelp.org/
logパッケージのドキュメント: https://pkg.go.dev/logfmtパッケージのドキュメント: https://pkg.go.dev/fmt