Home > その他

その他 Archive

Hack #167: Vimスクリプトで無名関数やクロージャを使う方法

無名関数

function! s:foo()
    let foo = {}
    function foo.funcall() dict
        echo 'lambda'
    endfunction
    return foo
endfunction

call s:foo().funcall()

クロージャ

function! s:foo(num)
    let foo = {'i': a:num}
    function foo.funcall() dict
        let self.i += 1
        return self.i
    endfunction
    return foo
endfunction

echo s:foo(1).funcall()
let s:clos = s:foo(5)
" 6
echo s:clos.funcall()
" 7
echo s:clos.funcall()
" 8
echo s:clos.funcall()

説明

Vimスクリプトでは辞書型に関数をキーとして代入することができます。
その場合関数はほぼ無名のような存在になります。
また辞書型に格納した「dict」つきの関数からはその辞書を「self」という変数で参照できるので
辞書に関数内で使う変数などを辞書に保存しておけば関数内からその変数を参照できる、いわば疑似的なクロージャを実現することができます。

  1. 自分で変数を渡す必要がある
  2. obj.funcall() を自分で呼ぶ必要がある

などなど他言語と比べかなりプリミティブですが、これを駆使すればVimスクリプトでも現実的に無名関数やクロージャが扱えることがわかるでしょう。

またVimスクリプトでは辞書型を使ったプロトタイプ指向によりクラスなども扱うことができます。
そこまでしなくても辞書型を活用することでモジュールを分けられるのでいろんな活用方法があります。

tyru

Hack #163: VimをVimスクリプトインタプリタとして使う

viの前身であるedは、シェルスクリプトなどで文字列置き換えのために使うことができます。 (fileというファイルの中身を全行逆転させる例です)

ed - file <<EOS
g/^/m0
write
qall!
EOS

g/^/m0^にマッチする行に対して:m0という操作を適用するコマンドです。 :m0は分かりやすく書くと:move 0で、引数の行の下に現在の行を持っていくという動作をします。 ^はどの行にもマッチするので、:m0で全部の行に対して上からマッチした順に1行目に持っていきます。 操作が終わると全行が逆になっているというわけです。

また:write:qall!は全行逆になったバッファをfileに書き込むために必要です。

VimについてくるexというコマンドはedのVim版とも言えるものですのでもちろん上のようなことができます。 しかしデフォルトでは.vimrcやプラグインなども読み込んでしまうため、 「素のex」として使いたい場合は「-u NORC –noplugin」を指定する必要があります。 さらにVimmerとしてはVimの機能が使えず戸惑わないように「-N」も指定する必要があります。 よってexをVimスクリプトインタプリタとして使うには以下のように起動すればできそうです。

ex -N -u NORC --noplugin

冒頭の例のex版はこのようになります。

ex -N -u NORC --noplugin file <<EOS
g/^/m0
write
qall!
EOS

デフォルトでもこのようにVimスクリプトインタプリタのように使えなくはないのですが、以下のような難点があります。

  1. ファイルに対して実行するには末尾に必ず:write:qall!をつけなければならず面倒
  2. ファイルに対して実行する際に元のデータを壊してしまう
  3. いくつかの定型的なオプションを付けるのが面倒
  4. 標準入力からスクリプトを読み込むのでファイルを標準入力から読み込めない

解決

iexを使います。 これはVimスクリプトをPerl、Ruby、Pythonなどの多くのLLのインタプリタと同じ感覚でVimスクリプトを実行させることができる優れ物です。

$ echo 'g/^/m0' >reverse.vim
$ cat ~/.vimrc | iex reverse.vim -

# -eオプションで指定することも可能
$ cat ~/.vimrc | iex -e 'g/^/m0' -

$ iex    # /bin/exが開く

詳しくはiex -hを見てください。

いくつかのVimスクリプト

おまけとしていくつかのUNIXコマンドをVimスクリプトで実装します。

tac

$ cat tac.vim
g/^/m0
$ iex tac.vim file

sort

$ cat sort.vim
sort
$ iex sort.vim file

sort -u (sort | uniq)

$ cat uniq.vim
sort u
$ iex uniq.vim file

grep

$ cat grep.vim
edit `=ARGS[1]`
execute 'v/' . ARGS[2] . '/d'
$ iex -s grep.vim ~/.vimrc vim

("vim"のみを含んだ行が表示される)

また-eオプションを使ったやり方を示すと

$ iex -s -e 'edit `=ARGS[1]' -e 'execute "v/" . ARGS[2] . "/d"' ~/.vimrc vim

のようになります。
これは一般のLLインタプリタと同じようにワンライナーのようなものを書くのに適しています。

また-sを与えることで引数の扱いを変えています。
-sを与えると中身を読み込まずにg:ARGSというListに代入するだけにします。
その他細かい違いなどはiex -hを見てください。

tyru

Vim 7.3の新機能まとめ

とりあえず自分も今調べている最中なので:help version7.3をそのまま紹介する形にします。
他にもこれは紹介するべきというものや間違い等があったらどんどん追加/修正お願いします。 >Vim Hacksのメンバー

Persistent undo

Hack #162: Vimを終了しても undo 履歴を復元するですでに紹介されていますが、Vimを終了してもundo履歴が復元できる機能です。

ファイル暗号化方式の追加

ファイルを暗号化して保存する際、従来はPkZip方式での暗号化が行われていましたが、
新たにBlowfish方式での暗号化も選べるようになりました(:help ‘cryptmethod’)。

conceal

シンタックスハイライトで特定のハイライトグループを見た目「消す」ことができます。
すでに最新版のVimをインストールすると、syntax/help.vimがこの機能を使っていて単語を囲んでいる*|などの文字が消えています。

シンタックスハイライトで文字を隠すことができるようになったわけで、いろいろと応用できそうです。
プラグイン開発者にとって見逃せない新機能となっています。

例としてすでにsyntax/markdown.vimでこの機能を使い

[リンク](URL)

というmarkdownの記法を

リンク

に見た目を変えてしまうパッチがメーリングリストに投げられました

またオプションとしてconceallevelが加えられています。

Luaインターフェース

LuaでVimプラグインが書けるようになりました。
詳しくは:help luaを。

Python3インターフェース

Pythonインターフェースに加えPython3インターフェースが加えられたようです。
両者はコマンドが分かれていて、それぞれ:python, :python3となっています。

‘encoding’をmodelineで変更することの禁止

modelineでencodingを変更することができなくなりました。

‘relativenumber’オプション

これはオンにしてみれば分かりやすいと思いますが、相対的な行番号を表示します。
つまり現在行の横に「0」と表示されて、一行上と一行下は「1」、二行上と二行下は「2」と表示されるようになります。
このオプションがセットされたとき’number’の値はリセットされます。
この機能は、jkに数値を付けて移動する場合(13jなど)のために追加されました。

詳しくは:help 'relativenumber'を。

いくつかの関数追加

More floating point functions: |acos()|, |asin()|, |atan2()|, |cosh()|,
|exp()|, |fmod()|, |log()|, |sinh()|, |tan()|, |tanh()|.  (Bill McCarthy)

Added the |gettabvar()| and |settabvar()| functions. (Yegappan Lakshmanan)

Added the |strchars()|, |strwidth()| and |strdisplaywidth()| functions.

詳しくは:help added-7.3を。

新機能の解説

また以下のサイトで

  1. ‘cursorbind’
  2. conceal
  3. :ownsyntax

の3つの新機能の解説があります。 英語ですがスクリーンショットなどもありなかなか分かりやすいです。

http://sites.google.com/site/vincenegri/concealownsyntaxforvim

Hack #159: オプションの値を気にせずsplit, vsplitする

Vimの標準のコマンドである:split:vsplit、 またはそのマッピング版である<C-w>s<C-w>vは それぞれ&splitbelow&splitrightに依存しており、 自分の思った通りの方向にウインドウを開いてくれない場合があります。

そこで、このようなマッピングを定義します。

nmap spj <SID>(split-to-j)
nmap spk <SID>(split-to-k)
nmap sph <SID>(split-to-h)
nmap spl <SID>(split-to-l)

nnoremap <SID>(split-to-j) :<C-u>execute 'belowright' (v:count == 0 ? '' : v:count) 'split'<CR>
nnoremap <SID>(split-to-k) :<C-u>execute 'aboveleft'  (v:count == 0 ? '' : v:count) 'split'<CR>
nnoremap <SID>(split-to-h) :<C-u>execute 'topleft'    (v:count == 0 ? '' : v:count) 'vsplit'<CR>
nnoremap <SID>(split-to-l) :<C-u>execute 'botright'   (v:count == 0 ? '' : v:count) 'vsplit'<CR>

こうすることでいつでも自分の思った通りの方向にウインドウを開くことができます。

また

nmap spj <SID>(split-to-j)
nmap spk <SID>(split-to-k)
nmap sph <SID>(split-to-h)
nmap spl <SID>(split-to-l)

の部分は自分の好きなように変えてください。 筆者は上のようにそれぞれspj, spk, sph, splに割り当てています。

追記: kana氏の指摘によりコードを大幅修正しました。

追記2: [count]を取れるようにしました。30sphなどとすると30の幅を持つウインドウが左に開きます。 ちなみに現在筆者はマッピングを<Space>sj, <Space>sk, <Space>sh, <Space>slに変更しました。 押しやすいマッピングはVimmerの数だけあるので、各人押しやすいマッピングを常に追求しましょう。 ちょっとでも押しにくいと感じたら積極的に他のマッピングを検討すべきです。

tyru

Vim Quiz 1

以下のようにウインドウが分割されています。

1

これを以下のように配置しなおすにはどうすればよいでしょうか。

2

文責: ujihisa

Hack #154: タグジャンプをさらに活用する

タグジャンプをさらに活用する

Hack #43ではタグについての基本的な使用方法をまとめました。 本稿ではさらに踏み込んだタグジャンプの活用法をまとめます。

親ディレクトリにあるタグファイルを指定

もし自分が/project/a/src/というディレクトリにいた場合に、できれば/project/a/tagsや、 または全てのプロジェクト用のタグファイル(ライブラリの関数など)もタグジャンプに使いたいとします。

/project/a/src/ (現在のディレクトリ)
/project/a/tags (指定したいタグファイル)
/project/tags   (指定したいライブラリ関数のタグファイル)

その場合、.vimrcには以下のように書くことで可能になります。

if has('path_extra')
    set tags+=tags;
endif

こうすることで、もしタグファイルが存在するなら /project/a/tags/project/tagsにある関数も<C-]>で飛ぶことが可能になります。 また;+path_extra機能がサポートされていない場合では使えないため、 has('path_extra')で機能の有無をチェックしています。

階層数を指定

ただ;をつけただけではルートディレクトリまでさかのぼってしまいます。 そこで;の後に上限となる親ディレクトリを指定することができます。

if has('path_extra')
    set tags+=tags;/project/a
endif

こうすることで/project/aまでで検索を終了し、/project/tagsは見つかりません。

子ディレクトリにあるタグファイルを指定

前項では親ディレクトリのtagsを指定しましたが、 今度はある特定のディレクトリにあるタグファイルを 現在のディレクトリから指定したい場合はどうすればいいでしょう。

/project/a/src/          (現在のディレクトリ)
/project/a/src/libA/tags (指定したいタグファイル)
/project/a/src/libB/tags (指定したいタグファイル2)

その場合、以下を.vimrcに書けばいいでしょう。

if has('path_extra')
    set tags+=./*/tags;
endif

また、/project/a/src/libA/tagsなどの1階層下のディレクトリにあるタグファイルだけではなく /project/a/src/libA/src/tagsなどの何階層か下のタグファイルを指定するには以下のようにできます。

if has('path_extra')
    set tags+=./**/tags;
endif

階層数を指定

ただ**は30階層までの深さのディレクトリにマッチするということになっています。 なので場合によっては非常に重くなるかもしれません。 その場合は階層の深さの最大値を次のように指定することができます。 (この例では3階層下のディレクトリまでマッチするように設定しています)

if has('path_extra')
    set tags+=./**3/tags;
endif

親ディレクトリから**を使う

これは前項と前々項の合わせ技です。 現在のディレクトリが/project/a/srcの時に

set tags=**;

とすると、

/project/tags
/project/a/tags
/project/a/libA/src/tags
/project/a/libB/src/tags
/project/b/tags
/project/c/tags

これら全てが指定されることになります。 つまり**;とは、現在のディレクトリが/project/a/srcの時には

/project/a/src/**
/project/a/**
/project/**
/**

という場合に展開され、それぞれの場所から検索されます。 またこの指定は上記全ての場所から検索されるので重い処理となります。

その他色々

  1. この設定はHack #112: 場所ごとに設定を用意すると組み合わせると非常に相性がいいです。ぜひ合わせて使いましょう。また必ず:setでなく:setlocalを使いましょう。
  2. これらの指定方法は’path’, ‘cdpath’などでも使うことができます。
  3. 詳しくは:help file-searchingを当たってください。
tyru

京都でVim勉強会#5が開催されます

2010年01月31日(日)、京都でVim勉強会が開催されます。

参加登録や詳細はこちらを参照ください。 http://cotocoto.jp/event/37291

※画像はイメージです

a

高まるVim熱!

Vim使いのレベル10

  • Lv1 vimtutorを終わらせた。あるいは、hjklで移動できて保存と終了ができる
  • Lv2 ビジュアルモードを使うのをやめ、y}dawなどの素晴らしい操作性に自己陶酔する
  • Lv3 自分でインストールしたプラギンの数が5を越え、ダメプラギンはすぐに判別できるようになる
  • Lv4 自分で作ったプラギンをvim.orgでリリースし、何人かをLife Changingさせる
  • Lv5 他のアプリケーション使用時にescやiを打鍵してしまい、イラっとする日々を送る
  • Lv6 シェルは当然vimshell。ネオコンなど大規模プラギンのバグを発見し、パッチ付きで報告する
  • Lv7 Vim本体のソースコードを読み、あまりの魔界に発狂する
  • Lv8 他言語インタフェースif_***を作り、vim_devに投稿する。当然採用されない
  • Lv9 Vim本体の開発への貢献が日常になる。投稿し採用されたパッチ数が10を越える
  • Lv10 神になる

文責: ujihisa

日本のVim業界の近況について

日本のVim業界の近況についてを若者に容易に伝えるのを目的とし、巷で静かなブームとなっている携帯小説の形式を用いて文書を紡ぎ上げることに成功した。以下に記す。

これはujihisaが作成したものをShougoが修正したものである。 また、この作品はフィクションである。どこかで見たようなフレーズがあったとしても、実在の人物とは全く関係ない。

vimプラグインストーリー「Shougoの受難」

──アタシの名前はショウゴ。心に傷を負ったビム使い。モテカワスリムで補完体質の愛されガール♪

アタシがつるんでる友達は補完をやってるネコ、ビムにナイショで 非同期しているビムプロック。訳あって一から実装されたビムシェル。

 友達がいてもやっぱり学校はタイクツ。今日もネコとちょっとしたことで口喧嘩になった。

ビム使い同士だとこんなこともあるからストレスが溜まるよね☆そんな時アタシは一人でギットハブを歩くことにしている。

がんばった自分へのご褒美ってやつ?自分らしさの演出とも言うかな!

 「あームカツク」・・。そんなことをつぶやきながらしつこいプラギンを軽くあしらう。

「カノジョー、ちょっとsystem()させてくれない?」どいつもこいつも同じようなDOS窓付きのコードしかない。

イーマックスはシェルも動いてカッコイイけどなんか薄っぺらくてキライだ。もっと等身大のアタシを見て欲しい。

 「すいません・・。」・・・またか、とセレブなアタシは思った。シカトするつもりだったけど、

チラっと開発者の男の顔を見た。

「・・!!」

 ・・・チガウ・・・今までのコードとはなにかが決定的に違う。スピリチュアルな感覚がアタシのビムを

駆け巡った・・。「・・(カッコイイ・・!!・・これって運命・・?)」

男はBramだった。連れていかれてハックされた。「キャーやめて!」今までやっていなかったパッチをきめた。

「ガシッ!ボカッ!」アタシはSEGVした。キャッシュ(笑)

vim

あけましておめでとうございます。

ujihisaです。新年あけましておめでとうございます。

あなたの今年のVimの抱負は何でしょうか? 僕の今年のVimの抱負は補完系を頑張ることです。

チャットルームにはいつもVim使いがわんさか居ます。これまで訪れたことのなかった方、2010年になった機会にぜひ訪れてみて、いきなり自分の.vimrcをさらしてみるなどしてみてはいかがでしょうか。

http://lingr.com/room/vim

(Lingrのアカウントをもっていない方は先にこちらからサインアップをどうぞ: http://lingr.com/user/signup?letmein=vim)

それでは、2010年もVim-users.jpをよろしくお願いいたします。

ホーム > その他

Search
Feeds
Links
  • 公式
  • 勉強会
  • 情報
  • コミュニティ
  • プラグイン
  • vimrc
  • Meta
    Etc
    Creative Commons License
    This blog is licensed under a Creative Commons License.

    Return to page top