cafegale(LeafCage備忘録)

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

ユーザに入力をさせるinput()のインターフェイスが不満すぎて仕方がないならctrlp.vim風の入力インターフェイスalti.vimを使おう

Vim Advent Calendar 2012 333日目の記事です。

alti.vimctrlp.vim風の操作感を持つ入力インターフェイスです。

もしもユーザに多少複雑めの入力を要求したい場合は、Vimの組み込み関数input()でそれを実現するのが苦しいことがあります。
なぜならinput()には

という弱点があります。他にも操作性の悪さなどが弱点に挙げられます。

input()やコマンドライン補完で複雑な入力をユーザに負担なくさせるのは難しいと気付き、unite.vimctrlp.viminput()みたいなことをするのも恐らく不可能だ*1と考えた私は、新しい入力インターフェイスを開発しました。ちょうどctrlp.vimを本格的に使い始めて、そのインターフェイスを気に入ったので、その影響をかなり受けています。補完の候補が常時、窓に表示されて、入力と共に絞り込まれていくような感じで。

基本の仕組みはctrlp.vimのものを踏襲しましたので、設定変数もctrlp.vimに似せています。
ヘルプに詳しく記しましたのでご参照ください。
操作法を簡単に説明しますと、<C-j><C-k>で補完候補を選択、<Tab>で補完候補を挿入、<C-f><C-b>でページ送り/戻し(補完候補が多すぎるときのみ)、<C-h><C-l>で左右移動、他はコマンドラインの標準の操作とだいたい同じです。<CR>で入力したコマンドを実行します。*2
alti.vimは拡張あって初めて動作するので、これから拡張の作り方を解説していきます。

*1:もしかするとunite.vimでは私がやり方を知らないだけで不可能ではないのかも知れませんが、向いてはいないでしょう。

*2:標準キーマッピングは予告なく変更する可能性があります。

続きを読む

俺が見たいのはメッセージの最後なんだ!:messagesを便利にするVimプラグイン

Vim Advent Calendar 2012 330日目の記事です。

Vimでエラーが発生した時にメッセージが表示されます。
そのメッセージを後から確認したい場合は:messagesでメッセージ履歴を表示します。
しかし:messagesにはVimが起動してからのメッセージが蓄積されているので、長時間起動しているとメッセージが長くなってしまい、最後の方にある目当てのメッセージを表示するためにスクロールさせるのが苦痛になってきます。
知りたいのはエラーが発生してからのメッセージ数行だけでいいのに、200のメッセージをスクロールしなければいけないかもしれないのです。

見ることのできるメッセージ数は、tiny バージョンでは20に、それ以外のバージョン
では200に固定されている。
:help :messagesより引用)

このメッセージは蓄積される数を変更することも出来ないし、消去することも出来ません*1。非常に不便です。

前々からフラストレーションを感じていたので、こんなのを作りました。

NeoBundle 'LeafCage/lastmess.vim'

これを使えば、

:LastMess

で、デフォルトで最後の10行のメッセージが表示されます。

:LastMess 12

または

:12LastMess

というように数値を与えてやると、最後の与えられた数字行のメッセージ(この場合は最後の12行)が表示されます。

表示される行のデフォルト値は10ですが、g:lastmess_default_countで変更することが可能です。

let g:lastmess_default_count = 15

<Plug>(lastmess)はキーマップ版です。例えばこれを

nmap mz <Plug>(lastmess)

というキーに充てておきますと、mzでデフォルトの10行のメッセージが表示されます。
15mzのようにカウントを与えると、その行数分のメッセージが表示されます。

カウントに999などの大きな数値を与えた場合は、メッセージが最初から表示されます(:messagesと同じですね。)。
これで:messagesを見るストレスが軽減され、特にプラグイン制作者は捗ることでしょう。

*1:for i in range(200)| echom ''| endfor みたいなことをすればメッセージをクリア出来るとおしょーさんに教えていただきました。 http://lingr.com/room/vim/archives/2013/10/26#message-17184693

lightline.vimをカスタマイズする

Vim Advent Calendar 2012 の325日目の記事です。
少し長めになりますので、お時間があるときにお読みください。

Vimのステータスラインを改造するプラグインが、Lokaltog/powerlinebling/vim-airlineに続いてitchyny/lightline.vimが登場しました。
私は今までステータスラインやタブラインは自前で改造していましたが、モダンなラインも経験してみたかったので、導入することにしました。

(当記事の内容)
続きを読む

ctrlp.vimの拡張を作るときにVim7.3.1170以前ではスクリプトローカル関数をFuncrefで渡すことが出来ない

こういうのは無理。
スクリプトローカル関数のFuncrefはそのスクリプト内でのみしか有効でないから。

let s:ctrlp_ext_var
let s:ctrlp_ext_var.accept = function('s:accept')

グローバル関数や、オートロード関数を使おう。

もしかしたらこういうことをしたら可能かもしれない。

function! s:SID()
  return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_SID$')
endfun
let s:FSID = '<SNR>'. s:SID(). '_'

let s:ctrlp_ext_var
let s:ctrlp_ext_var.accept = function(s:FSID. 'accept')

参考:Vim 7.3 の p1170 で追加された便利なアレについて - C++でゲームプログラミング

ctrlp.vimのExtensionを書くときに:NeoBundleLazy autoloadを考慮してg:ctrlp_builtinsをそのまま使わない

ctrlp拡張を作ったら、最後にctrlpからid番号を取得してctrlpに登録することになるが、
このとき、ctrlpがNeoBundleLazy状態であるのなら、

command! CtrlPExtension  call ctrlp#init(ctrlp#{extension-name}#id())
let s:id = g:ctrlp_builtins + len(g:ctrlp_ext_vars)
function! ctrlp#{extension-name}#id()
  return s:id
endfunction

は、g:ctrlp_builtinsが定義されていないためにエラーが出る。
ctrlp.vimctrlp#init()が呼び出されたときに初めてneobundle.vimによって読み込まれるために、それ以前にはg:ctrlp_builtinsが定義されていないからである。

エラーが出ないようにするためには、たとえば以下のように書き改める。

let s:id = ctrlp#getvar('g:ctrlp_builtins') + len(g:ctrlp_ext_vars)
function! ctrlp#{extension-name}#id()
  return s:id
endfunction

ctrlpの変数の値を返すctrlp#getvar()が呼ばれたときに、neobundle.vimがctrlp.vimを読み込むようになるために、エラーが出なくなる。

ctrlpのExtensionの作り方は以下の記事に詳しい。

ctrlp.vim起動時にステータスラインをlightline.vimのものに上書きされるのを防ぐ方法

以下のautocmdをvimrcに定義しておくと、lightline.vimがctrlp.vimのステータスラインを上書きするのを防ぐことが出来ます。

autocmd CursorMoved ControlP  let w:lightline = 0

[注意]

このautocmdはplugin/lightline.vimが読まれるより先に定義されていなければいけません。

[解説]

ctrlpは専用バッファを:noautocmdで開くため、lightline.vim

autocmd WinEnter,BufWinEnter,FileType,ColorScheme * call lightline#update()

が発動せず、ctrlpのステータスラインが描写された後に、

autocmd CursorMoved,BufUnload * call lightline#update_once()

が発動して上書きしてしまうのです。CursorMovedのときにw:lightlineを定義することで上書きを防いでいます。