cafegale(LeafCage備忘録)

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

Vimにパッチが当てられるまでの間、Windowsでとりあえず外部grepを使えるようにする

mattn

jvgrepの問題、vimを直すしか方法ない
vimがCreateProcessWを使えば解決
たしかパッチは出してあってtodoに入ってたと思う

http://lingr.com/room/vim/archives/2013/12/05#message-17742024

とのことなので、それまでは我慢と工夫で乗り切るしかないのだ。

仕方ないので、Grepというコマンドを作った。ついでにターゲット無指定の時には現在バッファ周辺を検索対象にすることにして利便性を高めた。

function! s:grep_for_cmdprompt(cmd, argstr) "{{{
  let args = split(a:argstr, '\%(\\\@<!\s\)\+')
  let i = 0
  while match(args, '^-', i)!=-1
    let i+=1
  endwhile
  let dflbase = expand('%:p:h')
  let dflbase = dflbase==?expand('$HOME') ? expand('%') : dflbase.'/**/*'
  let opts = i==0 ? '' : join(args[:i-1])
  let pat = has('win32') ? iconv(escape(args[i], '#%'), 'utf-8', 'cp932') : escape(args[i], '#%')
  let target = i+1>=len(args) ? dflbase : join(args[(i+1):])
  let g:greped = a:cmd. ' '. opts. ' -8 '. pat. ' '. target
  silent exe g:greped
endfunction
"}}}
command! -nargs=+ -complete=file   Grep    call s:grep_for_cmdprompt('grep', '<args>')

しかし外部grepだと、% # といった特殊文字が展開されてしまうのでエスケープしないといけないのが辛いところ。
vimgrepだとエスケープの必要がない。差別である。
どうにか利用時にエスケープしなくても済むようにしようとしたけれど無駄だった。


関連エントリ:最新版のjvgrepをコンパイルするにはgo言語とMercurialが必要だった - LeafCage備忘録

最新版のjvgrepをコンパイルするにはgo言語とMercurialが必要だった

# go get github.com/mattn/jvgrep

というコマンドを実行するにはThe Go Programming Languageが必要だった。
しかも$GOROOT $GOPATHという意味不明な環境変数を設定しなければいけなかった。

さらに、それに使われているmahonia - Mahonia—a character-set conversion library for Go - Google Project Hostingを得るためには水銀の元素記号hgでおなじみ、もう一つのバージョン管理システムであるJapaneseMercurial - Mercurialが必要だった。

インストーラでインストールするのが嫌だったのでMercurial Cmd Portable 1.5.1 Development test 1 | PortableApps.com - Portable software for USB, portable and cloud drivesを適当なディレクトリに展開した上で、Mercurialにパスを通すことで対応した。

悲しいお知らせ

ここまで苦労して導入しても、Vimの'encoding'オプションを'utf-8'にしている場合、Windowsではなすすべもなく使えないらしい。

set grepprg=jvgrepにして:grepしたいのですが、日本語が通りません。

Vim側がutf-8で吐いた文字をコマンドプロンプトがcp932として呼んでしまうからですが、解消する方法を知りませんか?

http://lingr.com/room/vim/archives/2013/12/05#message-17739621

絶望

つまり現状ではコマンドプロンプトを使うのをやめるか、qfixgrepを使うしか解決策がないわけですね。
qfixgrepは余計なことを色々しているから好きにはなれないのですが。

http://lingr.com/room/vim/archives/2013/12/05#message-17740433

光明

qfixgrepの中身を見てみたが、つまり最悪、外部コマンドに渡すときにiconv()でエンコードを変換してやれば何とか出来るっぽい。

http://lingr.com/room/vim/archives/2013/12/05#message-17740591

おし。
exe 'grep '. iconv('ファイル', 'utf-8', 'cp932'). ' %'だと正常機能した。
つまり渡すときエンコードしてくれるラッパーコマンドを定義してそれを使えば当面は解決できる問題になった。

http://lingr.com/room/vim/archives/2013/12/05#message-17740802

コマンドラインで :b を実行するときにバッファリストを表示する

Vim - hjklマスターに薦める意外と便利な機能 - Qiita [キータ]
バッファを選択する際に、同時にリストを表示する

nnoremap B :ls<CR>:b 

という設定を見て閃いた。

cnoreabb <expr>b getcmdtype()==':' && getcmdline()=='b' ? 'ls<CR>:b' : 'b'

こうすればコマンドラインで:bを打ってから<Space>キーを打つと勝手に:lsが表示されるようになる。

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