cafegale(LeafCage備忘録)

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

nebula.vimを使ってneobundle#tap()をより高速に書く

Shougo/neobundle.vimももんが流NeoBundle管理術 | かなりすごいブログ から機能が取り込まれneobundle#tap() neobundle#untap()が使えるようになりました。

これを利用するとだいたいこんな感じで非常にすっきりとプラグインの設定が書けます。

ここでは LeafCage/nebula.vim を使って、より楽に高速にneobundle#tap()を記述する方法を紹介します。

vimrcのNeoBundle 'repository/pluginname'のある行で:NebulaYankTapを呼び出すと、

if neobundle#tap('pluginname')
endif

レジスタにセットされます。

:NebulaYankTap!のように、!付きで呼ぶと

if neobundle#tap('pluginname') "{{{
endif
"}}}

のようにfoldingmarker付きでヤンクされます。
これを記述したい場所にカーソルを持ってきて貼り付ければよいのです。

中身のneobundle#config()を書くときにはShougo/neosnippet.vimなどで

snippet   neobundleconfig
alias   nbc
  call neobundle#config(${1})

みたいなsnippetを定義しておいて、オプションを:NebulaYankOptionsで生成すると捗ります。

コマンドラインからシームレスにover.vimの全体置き換えを使う

私は今まで:sについて以下の設定をしていた。

cnoreabb <expr>s getcmdtype()==':' && getcmdline()=~'^s' ? '%s/<C-r>=Eat_whitespace(''\s\\|;\\|:'')<CR>' : 's'
function! Eat_whitespace(pat) "{{{
  let c = nr2char(getchar(0))
  if c=~a:pat
    return ''
  elseif c=~'\r'
    return ''
  end
  return c
endfunction
"}}}

こうしておくと、コマンドラインでsと打ってから<Space>キーで %s/ という具合に展開されていたので、全体置き換えをするときに打ちにくい % を打たなくて済むという利点があった。
そんな折、osyo-manga/vim-over がリリースされた。
結果をプレビューする機能はCoolだがぶっちゃけそんなに要らなかったが、置き換え実行後にカーソルが動くことがないというのに魅力を感じたので、上記の操作を over.vim で置き換えることにした。

以下のように設定することでコマンドラインで s を打ってから<Space>を打つと、そのままover.vim%s/ が入力された状態になる。

cnoreabb <silent><expr>s getcmdtype()==':' && getcmdline()=~'^s' ? 'OverCommandLine<CR><C-u>%s/<C-r>=get([], getchar(0), '')<CR>' : 's'

Vimのabbreviate(短縮入力)機能によって、行頭でのsOverCommandLine<CR><C-u>%s/に置き換えているわけである。
その際、トリガーに使った<Space>は不要なのでgetchar(0)に食わせている。
<C-r>=get([], getchar(0), '')<CR>の部分はトリガーに使った文字の処分である。
別にトリガーは短縮入力を展開できる文字なら何でも良いので<Space>でなくて<CR>でも良い。

'nobuflisted' なバッファの作り方

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

バッファをバッファリストに登録したくないときがあります。
バッファリストに登録されていないバッファは:lsでは表示されず、:bnext :bpreviousでのバッファ切換の対象になりません。
また、Vim終了時に次回起動時のための情報を書き込むviminfoファイルのバッファリスト保存・復元から除外されます。つまりVim終了時にはそのバッファは破棄され次回起動に持ち越しません。
このオプションは通常、その場限りの情報を表示するだけで、書き込む予定のない、使い捨てのバッファに設定します。

しかし、せっかく'buflisted'オプションをオフに設定しても、バッファの内容を変更すると勝手に'buflisted'がオンに設定し直されてしまいます。
正しくは「バッファの編集を始めると」オンになるのでした。:edit:split :vsplit :newなどで切り替えるとオンにされるみたいです。つまり:editなどを使わず:bufferでバッファを切り替えるのなら:setlocal nobuflistedを設定するだけでよいです。

ここではいかにして、何らかの形で意図せず'buflisted'オンになっても'buflisted'をオフに保つようにするバッファを作るのかを解説します。また、特別なバッファに設定するべき他のオプションも併せて説明します。

変更の予定のないバッファの場合

バッファを変更する予定がなければ、ただ単に'nobuflisted'オプションを設定すればいいです。
その際、ユーザにバッファの内容を変更を禁止させるように'nomodifiable'オプション、読み込み専用だということをユーザに示すために'readonly'オプションもセットしましょう。

edit examplebuf
setlocal nobuflisted nomodifiable readonly

※これらはバッファにローカルなオプションなので:setlocalではなく通常の:setで設定しても問題ありません。
:edit+cmdオプションを与えればこのコマンドを1行で表せます。

edit +setlocal\ nobuflisted\ nomodifiable\ readonly   examplebuf

変更の予定があるバッファの場合

編集が禁止されたバッファでスクリプトがバッファの内容を変更するのなら、そのときに一時的にこれらのオプションを変更し、内容変更後に元に戻せばいいでしょう。

setlocal buflisted modifiable noreadonly
call setline(1, '==example==')
setlocal nobuflisted nomodifiable readonly

ユーザが自由に編集できるバッファをバッファリストに登録させたくない場合は、そのバッファを抜けるときに'nobuflisted'に設定しなおすバッファローカルなautocmdをそのバッファに設定しておきます。
また、たまたまそのバッファにいるときにVimを終了させようとしたときにもバッファリストから除外するようにVimLeavePreイベントのautocmdも設定します。

edit examplebuf
augroup example_local
  autocmd!
  autocmd BufLeave <buffer>   setlocal nobuflisted
  autocmd VimLeavePre <buffer>    setlocal nobuflisted
augroup END

需要があるか分かりませんが、現在Vimではbuflistedにしておきたいが、Vim終了時にはnobuflistedにして次回起動時のバッファに残させないときの設定です。この場合そのバッファのバッファ番号を何らかの形で記憶しておき、autocmd VimLeavePre \*でそれらのバッファにnobuflistedオプションを設定します。

augroup example_local
  autocmd!
  autocmd VimLeavePre *    call <SID>make_bufs_nobuflisted()
augroup END

let s:bufnrs = []
function! s:make_bufs_nobuflisted()
  for bufnr in s:bufnrs
    if buflisted(bufnr)
      call setbufvar(bufnr, "&bl", 0)
    endif
  endfor
endfunction
edit examplebuf
call add(s:bufnrs, bufnr('%'))

特別なバッファに設定するべき他のオプション

readonly, nomodifiable

説明済み

buftype

ファイルと関連がなく、書き込まれる予定のない仮想的なバッファにはset buftype=nofileを設定します。
buftype=nofileを設定すると:writeが禁止されます。しかし、バッファの編集変更自体は出来るので、それを禁止させたければ前述のnomodifiableも設定しましょう。

bufhidden

バッファの使い捨てという正確を強めたいときに利用します。 バッファがウィンドウに表示されなくなったときの挙動を指定するオプションですが、そのバッファを閉じたらバッファの内容を破棄させたいのならbufhidden=unload、削除や完全削除をしたいのならbufhidden=delete(←これはまだ:bでバッファ指定するなどしたらアクセスできる)やbufhidden=wipe(←完全に削除される)を使います。
例えばkien/ctrlp.vimbufhidden=unloadが利用されています。ctrlp.vimの作るバッファは刹那的な性格が強いですし、無駄なメモリも解放されるからでしょう。

活用例

例えばShougo/vimfiler.vimの作るバッファをバッファリストに残したくない時には.vimrcに次の設定を書きます。

autocmd FileType vimfiler  setlocal nobuflisted
  \ | autocmd BufLeave    <buffer>  setlocal nobuflisted
  \ | autocmd VimLeavePre <buffer>  setlocal nobuflisted

これでバッファを閉じたときにはバッファリストのサイクルから外れますし、次回起動時にvimfilerのバッファが残っているということもありません。

Vimスクリプトのファイル先頭にscriptencodingを書く意味

LeafCage
ファイルの先頭でscriptencoding を書くのはどういった利点があるのでしょうか?今までscriptencodingを書いてこなかったのですが、書いておくとVimがencodingを調べることがなくなって読み込み速度がアップするのでしょうか?


thinca
マルチバイト文字がスクリプト内に含まれている場合は、scriptencoding がないと、'encoding' とファイルのエンコーディングが異なる場合に文字化けします


LeafCage
echoで日本語を表示させるときならscriptencodingは必須と言うことでしょうか?
コメントに日本語が含まれている場合には大丈夫ですか


thinca
> echoで日本語を表示させるときならscriptencodingは必須と言うことでしょうか?
そうなります
コメントでも、改行文字が食われるような化け方をする可能性を考えると、書いておいた方がいいです

http://lingr.com/room/vim/archives/2013/11/07#message-17304879

set encodingよりも後に書かなければいけないらしい。

thinca

scriptencoding は set encoding よりも後にやる必要があり、しかしマルチバイト文字が出てくる前に scriptencoding は実行しないと意味がない

http://lingr.com/room/vim/archives/2013/11/23#message-17465700

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