この記事は Vim Advent Calendar 2020 - Qiita の4日目の記事です。
昨日は id:daisuzu さんの編集を加速するVimのquickfix機能でした。
こんにちは、こんばんは。久しぶりにVimに関連する記事を書いています。
最後に書いたVimの記事は4年前のVim をあまりカスタマイズしなくなった話なのですが、この記事中にも下記のように最後にVimの記事を書いたのが4年前ということが判明しました。
ちなみに、 ctrlp の記事を4年前の Vim Advent Calendar で書いています。
どうやら、次にVimの記事を書くのは2024年になる可能性が高いです。
さて、最近になってやっと社内でVimが盛り上がるようになってきました(ただし、二人のみ)。Vim については上記の記事でも書いた通り、カスタマイズすることがあまり無いのですが、自分で欲しいと思った機能はささっと .vimrc
に検証レベルのPoCコードを書いています。
検証レベルが終わればちゃんとプラグインとして整備するのですが、それの前段階で軽く書いているVim scriptです。
例えば、VimでGoの実装をしているときに https://golang.org/pkg/
のページでドキュメントを見に行くのを省略するのに、 :GoDoc [pkg-path]
で開くようにしています。
今回はこちらの紹介をしようと思います。
:GoDoc
コマンドは fatih/vim-go
に元々入っていたコマンドだった気がします。そこからGoの開発環境をシンプルにしようと思い、 vim-go
を外した際になくなった機能だったのをササっと作った記憶が残っています。
作りは非常にシンプルになっています。そのコードを紹介します。
GoDoc
コマンドの呼び出し先になる関数を定義します。
function s:GoDoc(bang, args)
let arg = join(split(a:args), '.')
silent! let res = system('go doc -cmd -all '.arg.' 2>/dev/null')
if v:shell_error != 0
let err = systemlist('go doc '.arg.' 1>/dev/null')
echohl ErrorMsg | echomsg err | echohl None
return
endif
call s:OpenReadOnlyBuffer('' , arg, res)
endif
setlocal ft=godoc
nnoremap <buffer> <silent> q :q<cr>
endfunction
処理のメインとなるVim scriptは下記のシステムコマンドを叩いているところです。
silent! let res = system('go doc -cmd -all '.arg.' 2>/dev/null')
go doc
コマンドはパッケージ名を指定して、ドキュメントを出力することができます。デフォルトだと関数の詳細な説明などが省かれているので -all
をオプションに指定しています。-cmd
はコマンドのパッケージも出力するという指定です。
パッケージの指定とありますが、指定方法は go doc
通りなので他にも指定方法があります。
Usage of [go] doc:
go doc
go doc <pkg>
go doc <sym>[.<methodOrField>]
go doc [<pkg>.]<sym>[.<methodOrField>]
go doc [<pkg>.][<sym>.]<methodOrField>
go doc <pkg> <sym>[.<methodOrField>]
これは GoDoc
関数で得られた go doc
の結果を展開するバッファを指定しています。
function s:OpenReadOnly(target, name, body)
execute a:target.' '.a:name
setlocal buflisted modifiable modified noreadonly
cal append(line('^'), split(a:body, '\n'))
silent! $d
setlocal noswapfile nobuflisted buftype=nofile bufhidden=unload
setlocal nomodifiable nomodified readonly
setlocal nonumber nobinary nolist
execute 'normal! 1G'
endfunction
function s:OpenReadOnlyTab(type, name, body)
cal s:OpenReadOnly('tabnew!', a:name, a:body)
endfunction
function s:OpenReadOnlyBuffer(type, name, body)
cal s:OpenReadOnly(a:type =~# '^\(v\|vert\)' ? 'vsplit!' : 'split!', a:name, a:body)
endfunction
バッファやタブとかも実は用意していますが、基本的にはバッファに展開をしています。
GoList
で候補となるパッケージを取得するようにします。 go list
ではなくて、単純に $GOROOT
から関連するパッケージ名を取得していて、力技を感じますね。
function s:GoList()
return systemlist('{ cd $(go env GOROOT)/src && find . -type d } | sed -e "s#^\./##" | grep -v "^\(\.\|vendor\)"')
endfunction
command GoList cal s:GoList()
これも目を瞑りましょう。
実行関数として GoDoc
を定義し、補完候補として GoList
を少し改良して定義しています。
function! s:CompleteGoDoc(arg_lead, cmdline, cursor_pos)
return filter(copy(s:GoList()), 'stridx(v:val, a:arg_lead)==0')
endfunction
command -nargs=1 -bang -complete=customlist,s:CompleteGoDoc GoDoc cal s:GoDoc('<bang>', <f-args>)
これで :GoDoc net/http
のように Vim でGoのドキュメントを参照することができます。
CtrlPをずっと使い続けているので、CtrlPでも呼び出せるようにしています。
" CtrlPGoDoc
command! CtrlPGoDoc cal ctrlp#init(ctrlp#godoc#id())
nnoremap <c-e>g :<c-u>CtrlPGoDoc<cr>
ただ、これ今見てみるとかなり適当に作っているのに気づきました。。 fu! ctrlp#godoc#id
が定義されていないので、他のCtrlP extentionが影響受けているはずです。良くない。
しっかりとしたCtrlPのExtentionの作り方は8年前に記事を書いているのでよければ参照してください。
最初は「バイナリエディタとしてVimを使う」という記事を書こうとしたのですが、念のため調べてみたら同じような記事がたくさんあったので、小ネタにしていた GoDoc
コマンドをそのまま本ネタにしました。
プラグインとして作るのも面倒だな、とかあまりプラグインを書くことになれていない人も、躊躇わないで .vimrc
の中に書いてみる良い例ではないでしょうか。
人によっては開発中のものを packpath
で上手く指定したところを読み込んでいる人もいると思いますが、初心者の方へは、まずは自分でVim scriptで遊んでみるのをおすすめします。
とりあえず、本記事で書いたものは自分用に早いうちにプラグイン化と共にリファクタリングをして整理しようと思います。今更ながら恥ずかしい!
それではまた4年後にVimの記事を書きます。
ちなみに、バイナリエディタのTipsを置いておきますね。
コマンド関連:
$ vim -b a.out # open a.out running on Binary Mode
# Make a hexdump
:%!xxd
# The reverse
:%!xxd -r
ちなみに、ターミナルで閲覧したいだけなら xxd
コマンドを使えばOK。
Shintaro Kaneko
December 04, 2020