cafegale(LeafCage備忘録)

LeafCage備忘録(はてなダイアリー)と統一しました。

README.mdをVimのヘルプファイルから生成する

この記事はVim Advent Calendar 2012の341日目の記事です。

前回(84日前)Vimプラグインのスクリプトファイルからヘルプファイルを生成するというのをやりました。

しかしGitHubではREADME.mdファイルがリポジトリの窓口であり、説明であり、これによってそのプラグインの概要を知り、それを導入するかどうかが判断されます。
README.mdを置いていないとプラグインの詳細を知るためには、リポジトリトップから2クリックもかかるdoc/xxx.txtを見に行かなければいけませんし、リポジトリトップにはGitHubがREADME.mdを置けと勧めてきます。
つまり我々はREADME.mdを書くことを暗に強いられているのです。

しかし、すでにhelpに概要含め色々書いているのにさらに別の場所で二重に概要を書かなければいけないなんて苦痛です。
なぜhelpに書いたことを改めて書かなければいけないのでしょうか。
そこでLeafCage/vimhelpgenerator:HelpIntoMarkdownというコマンドを追加しました。*1

ヘルプファイル内でREADME.mdに含めたいところをビジュアル行選択(“概要”や“使い方”などを選択すると良いでしょう)して、

f:id:leafcage:20131106074914p:plain

:HelpIntoMarkdownを実行すると、

f:id:leafcage:20131106074933p:plain

そのヘルプファイルの上にあるREADME.mdファイルが開かれます。*2

f:id:leafcage:20131106074944p:plain

ここでPすると、先ほど行選択されたテキストがmarkdown形式に変換されてputされます。(README.mdを初めて作成するときにはプラグイン名と説明もhelpファイルの第1行目から生成されます。)
手直しした後、:writeしましょう。
:HelpIntoMarkdownはアンダースコア(_)のエスケープには対応していません*3。自力で対応させて下さい。

f:id:leafcage:20131106075002p:plain

もしもヘルプファイルを改修したときには改修した箇所をビジュアル行選択して:HelpIntoMarkdownして下さい。その部分の変換結果がレジスタにセットされるのでREADME.mdにて貼り付けて手直しして下さい。

これでヘルプファイルとREADME.mdを管理する手間を大幅に減らすことが出来ました。

:VimHelpGeneratorVirtualでヘルプを仮想バッファに出力する

vimhelpgeneratorには、:VimHelpGeneratorVirtualというコマンドも加わっています。
:VimHelpGeneratorが生成したバッファをそのまま書き込むのに対し、:VimHelpGeneratorVirtualは仮想バッファに出力します。

これにより、vimhelpgeneratorの、ヘルプファイルを初めて作るときにしか使えなかったという欠点を解消しました。
プラグインに機能を追加するなどしてヘルプの改修の必要性が生じたとき、:VimHelpGeneratorVirtualを実行して、出力された仮想バッファ内で必要な部分だけを、既存のヘルプファイルにコピーペースト(Vim風に言うとヤンクプット)すれば良いのです。
単純にVimHelpGeneratorの出力を確認したいときにも利用できます。

:VimHelpGeneratorを実行したときにすでにヘルプファイルが存在している場合もこのモードで出力されます。

*1:これに伴い、:VimHelpGenerator実行時に吐き出していた、クソの役にも立たないREADME.mdを生成させる機能はオミットされました。

*2:ファイルが存在しなくてもOK

*3:技術的に難しかったので

throw "ERROR"のチェックの簡潔な記述

t9md:
if Check1()
throw "ERROR"
endif

こういうチェックをいっぱいやる場合に
Check1() && throw "ERROR"
Check2() && throw "ERROR"
をやりたんですが、出来ない。

http://lingr.com/room/vim/archives/2013/11/03#message-17268337

manga_osyo:
function! Check1()
 return 1
endfunction

function! Check2()
 return 1
endfunction

command! -nargs=* Checker if | throw "ERROR" | endif
Checker Check1()
Checker Check2()

こうするのが楽かなぁ。ただし、スクリプトローカル関数は使えないけども

もしくは
function! s:throw(exp)
throw a:exp
endfunction

let _ = Check1() && s:throw("ERROR")
let _ = Check2() && s:throw("ERROR")
とか

書くのは前者の方が楽、使い勝手は後者の方が楽

後者なら
function! s:checker(check, ...)
 let expr = get(a:, 1, "ERROR")
 if a:check
  throw expr
 endif
endfunction

call s:checker(Check1())
call s:checker(Check2(), "ERROR Check2")
の方が楽そげ感

http://lingr.com/room/vim/archives/2013/11/03#message-17268353

Lindan:
スクリプトローカルなコマンドが定義できれば ThrowIf {condition} みたいなコマンドを自作するのも良さそうだけれど…

コマンド名,グローバルにしか定義できないので,他のプラグインとかぶらないようにしないといけないのが辛いですね…

http://lingr.com/room/vim/archives/2013/11/03#message-17268374

t9md:
これまでは、if cond | hoge | endif を一行にして、Alignしてたけど。
後者もいい。 osyo さん、サンクスです。
便利メモにメモっとこう。

http://lingr.com/room/vim/archives/2013/11/03#message-17268401

そもそもの目的は、textmanip でもうやることがない(動かせない)ときのチェックをスッキリ書きたかった。

https://github.com/t9md/vim-textmanip/blob/master/autoload/textmanip.vim#L154-L173

http://lingr.com/room/vim/archives/2013/11/03#message-17268438

コマンドを使った方法を経て

最終的にこうなっていました(結局関数を使った方法に)

isは型の違いも見る

manga_osyo
echo 0 ==# "homu" => 1 になってつらぽよ…

http://lingr.com/room/vim/archives/2013/11/03#message-17267099

Lindan
リストで包んで,[0] ==# ["homu"] ならちゃんと型チェックされます(バッドノウハウ

http://lingr.com/room/vim/archives/2013/11/03#message-17267119

thinca
is 使いましょう

http://lingr.com/room/vim/archives/2013/11/03#message-17267133

Lindan
ですね,is があった

http://lingr.com/room/vim/archives/2013/11/03#message-17267137

thinca
is は型の違いも見る

http://lingr.com/room/vim/archives/2013/11/03#message-17267156

ウィンドウを分割させて新しいバッファを表示させる系のコマンドを含む関数内での:echoは一瞬だけしか表示されない

WarningMsgを表示させてから新窓で開こうとしたら、新窓が開かれた後の画面更新でechoが消されてしまった。関数内で、順番を、[新窓を開く→:echo]の順にしても無駄。

関数が終了して初めて画面更新が行われるので、それで:echoが消されてしまう。
しょうがないので、:echoの代わりに:call input()でメッセージを表示させて、必ず一瞬立ち止まらせるようにした。邪道。
良い回避法があれば知りたい。

ctrlp.vimの拡張作成は想像以上に自由がなかった

実際拡張を作ろうとすると、想像以上に自由がないことが分かる。

  • 複数選択が出来ない(標準プラグインでは出来るのに)
  • 入力パターンマッチの種類が少ない(前方一致が欲しい)
  • ページ送り・戻し機能がないから画面にない候補を選択することが出来ない(何となく一覧するという使い方が不可能)
  • 正規表現モードの切換を拡張作成者側が設定できない(ここは正規表現で入力させたいと思っても、切換はユーザに委ねられている)
  • 初期入力を拡張作成者が設定できない(feedkeys()を使えば出来ないことはないけど標準ではサポートされていない)
  • 候補窓の大きさや表示方法を拡張作成者が設定できない(ユーザの初期グローバル設定に依存する)

候補の表示方法を拡張作成者が選択できないこととか、窓から溢れて見えなくなっている候補にアクセスできないことは、拡張の可能性をとても狭めている。

副作用の少ないYankRing.vimみたいなのができました

Vimmerにハロウィンがアドベントしましたね。
Vim Advent Calendar 2012 335日目の記事です。


Vimレジスタの履歴を取って再利用するプラグインにYankRing.vimというものがあります。間違えてp(テキストを貼り付け)してしまっても<C-p>で即座に履歴を遡(さかのぼ)ってテキストを置き換えられます。とてもお手軽で、優れたインターフェイスです。
しかしながら副作用が多く、他のプラグインや設定と干渉してしまうという問題がありました。(重要なキーマッピングを軒並み置き換えてしまうのは勘弁してほしいです。)
それを見かねたShougoさんはunite-source-history/yankというものを作ってくださいました。レジスタの履歴がunite.vimのインターフェイスで閲覧でき、操作できます。便利でしたが、YankRing.vimと比べると、お手軽さで劣りました。

それでしばらくはレジスタ履歴を使わず、必要になったらその都度ヤンクし直すという原始的なことをしていましたが、ふとYankRing.vimの載っている記事を見て*1あの操作感が羨ましくなったので、それっぽい挙動をするプラグインを作ってみました。

NeoBundle 'LeafCage/yankround.vim'

副作用が(たぶん)起こらないように注意しましたので、ヘビーユーザーにも堪える作りになっています。

YankRing.vimっぽく使う

もしYankRing.vimっぽく利用するのであれば、以下のキーマッピングをして下さい。

nmap p <Plug>(yankround-p)
nmap P <Plug>(yankround-P)
nmap gp <Plug>(yankround-gp)
nmap gP <Plug>(yankround-gP)
nmap <C-p> <Plug>(yankround-prev)
nmap <C-n> <Plug>(yankround-next)

これで貼り付けを行った後に<C-p><C-n>レジスタ履歴を遡ってテキストを置き換えることが出来ます。
カーソルを動かすと候補が確定されます。


デフォルトの履歴取得数は30までです。

let g:yankround_max_history = 35

で変更できます。
Vim終了時に履歴のキャッシュを専用ファイルに保存しますがそのディレクトリはデフォルトで以下の場所になります。

let g:yankround_dir = '~/.cache/yankround'

<C-p><C-n>は貼り付け直後にしか有効ではありません。
もしもそれが勿体ないと感じるのでしたら、<expr>を使ったマッピングでyankround#is_active()を使うことで、普段は別の役割を持たせることが出来ます。
例えば、yankroundが有効でないときの<C-p>で、:CtrlPを呼び出すなど。

nnoremap <silent><SID>(ctrlp) :<C-u>CtrlP<CR>
nmap <expr><C-p> yankround#is_active() ? "\<Plug>(yankround-prev)" : "<SID>(ctrlp)"

CtrlPでレジスタ履歴を利用する

yankround.vimにはkien/ctrlp.vimの拡張も付属しています。
:CtrlPYankRoundコマンドで利用することが出来ます。

nnoremap <silent>g<C-p> :<C-u>CtrlPYankRound<CR>

レジスタ履歴が一覧表示されます。履歴を選択後、

  • <CR>で、その履歴をカーソル位置に挿入します。
  • <C-x>(<C-s>)で、無名レジスタ " にその履歴をセットします。
  • <C-t>で、その履歴を履歴から削除します。

※unite-sourceは時間がなかったのと面倒くさかったので作りませんでした。作りました。

貼り付けた部分をハイライトする機能も付けました。

こうして私に再びレジスタ履歴がアドベントしました。
もしも私と同じく、本当はYankRing使いたかったんだけど、キーマップを乗っ取られるのが嫌で利用を諦めていたのなら、ぜひyankround.vimをお試し下さい。

*1:実はunite-source-history/yankっぽいのをctrlp.vimで実装して、それをVACの記事にしようとしてすでにその記事まで書いていたのだが、YankRing.vimとの比較という項を書くためにYankRing.vimについて調べていたら、やっぱりYankRing.vimのインターフェイスの方が便利じゃねーかと思えてきて急遽YankRingっぽいのを作ったというのが真相。