Home > Vim Hacks

Vim Hacks Archive

Hack #171: 編集している関数名を表示する

数あるエディタの中には現在編集中の関数名をステータスバーに表示させたりできるものがあります。
Vimでもできないでしょうか。

解決

current-func-info.vimを使います。
インストールしたあとstatuslineを次のように設定しましょう。

let &statusline = '[%{cfi#get_func_name()}()]'

こうすると現在編集中の関数名が

[func()]

のように表示されます。

cfi#get_func_name()は関数の外側にいる時は空文字を返すので
外側にいる時は「no function」と表示させたい場合は以下のようにします。

let &statusline = '%{cfi#get_func_name() == "" ? "no function" : "[" . cfi#get_func_name() . "()]"}'

これは少し面倒です。
cfi#format()というヘルパー関数を使うとこのように書けます。

let &statusline = '%{cfi#format("[%s()]", "no function")}'

current-func-infoは現在C, Perl, Ruby, Python, Vimスクリプトに対応しています。

ちなみに

statuslineは

set statusline=...

という風にセットすることもできますが

let &statusline = '...'

としたほうが空白を含む文字列をセットする時にエスケープする必要がなく便利です。

set statusline=this\ is\ my\ statusline

let &statusline = 'this is my statusline'

また

set statusline=%!...

のようにすることもできます。(:help 'statusline' 参照)

tyru

Hack #170: テキストを折り畳む – 基本編

長いテキストやソースコードを閲覧したり編集する場合、全体の俯瞰が知りたかったり、必要のない部分を無視したいことがあります。 こういった場合、Vim の折り畳み機能が便利です。

本 Hack では何回かに分けて折り畳み機能について解説します。

Folding (折り畳み)

Vim にはテキストを折り畳むための Folding と呼ばれる機能があり、これを使うことで複数行を 1 行にまとめて表示することができます。まとめられた行には要約が表示されます。 折り畳み機能を使うことで、テキストをセクション単位や関数単位などで折り畳んで簡潔に表示することができます。

折りたたまれたテキスト

折り畳まれた行は zo で開き、 zc で閉じることができます。詳細は操作編で解説します。

折り畳みレベル

折り畳みは入れ子にすることができます。これにより、構造化された文書やソースコードを階層ごとに折り畳むことができます。 各行には折り畳みレベルというものがあり、入れ子は実際にはこれによって表現されています。折り畳みレベル 0 が、折り畳みがない状態です。

階層化された折り畳みは zR で全て開き、zM で全て閉じることができます。

折り畳みの種類

折り畳みには、以下の 6 つの種類があります。’foldmethod’ オプションに対して以下のいずれかを設定します。

概要
manual 手動で折り畳みを作成します。
marker 特定の印により折り畳みを作成します。
indent インデントによって折り畳みを作成します。
expr 式を使って柔軟に折り畳みを作成します。
syntax syntax での指定により折り畳みを作成します。
diff diff の際に変更されていないテキストを折り畳みます。

‘foldmethod’ オプションはウィンドウローカルなので、同じバッファを別のウィンドウで別の方法で折り畳むこともできます。

折り畳みの作成と削除

基本編ということで、最後に折り畳みの作成/削除の方法を簡単に紹介しておきます。

‘foldmethod’ が “manual” の場合、自分で折り畳みを作成することになります。折り畳みの作成は zf{motion} か Visual-mode で範囲を選択して zf 、もしくは :{range}fold コマンドで行います。繰り返して実行することで、折り畳みレベルを深くすることができます。

zd でカーソル位置にある折り畳みを削除できます。また、zD でカーソル位置にある折り畳みを再帰的に全て削除できます。

thinca

Hack #169: neocomplcacheを拡張する 後編

前編からかなり間が空いてしまいましたが、ここではneocomplcacheのプラグインの記述方法を解説します。neocomplcache Ver.5.1ではプラグインの構成がかなり変化したので、この記事を参考にしてください。

プラグインの仕様

プラグインはautoload/neocomplcache/sourcesにインストールしたものが自動的に読み込まれます。 プラグインには、neocomplcache#sources#プラグイン名#define()が絶対必要です。 これはneocomplcacheが初期化するときに呼ばれ、neocomplcacheにディクショナリ変数を渡します。 ここではディクショナリ変数に必要な要素について説明をします。

name

プラグインの名前です。

kind

プラグインの種類を現します。plugin, ftplugin, complfuncの三種類があります。これらの違いについては後述します。

filetypes

ftpluginにのみ存在します。どのファイルタイプで補完するかを表すディクショナリ変数です。

initialize()

プラグインの初期化時に呼ばれます。コマンドの登録等を行います。ftpluginの場合、これが呼ばれるのはfiletypesに対応するバッファで補完されたときです。

finalize()

neocomplcacheの無効化されるときに呼ばれます。ここで不要になったコマンドやautocmdを削除します。

get_keyword_list(cur_keyword_str)

pluginにのみ存在します。a:cur_keyword_strにマッチするリストを返すために呼ばれる関数です。get_keyword_list()が返す補完リストは、特定のキーを含むディクショナリのリストとなっています。詳しくは「補完リストの仕様」の項を参照してください。

get_keyword_pos(cur_text)

ftpluginとcomplfuncに存在します。cur_textにマッチする補完位置を返します。

get_complete_words(cur_keyword_pos, cur_keyword_str)

ftpluginとcomplfuncに存在します。a:cur_keyword_strにマッチするリストを返すために呼ばれます。get_complete_words()が返す補完リストは、特定のキーを含むディクショナリのリストとなっています。詳しくは「補完リストの仕様」の項を参照してください。

補完リストの仕様

get_keyword_list()get_complete_words()が返す補完リストは、特定のキーを含むディクショナリのリストとなっています。一部のキー以外はVim標準の補完で使用するものと同じです。

word, abbr, menu, info, icase, dup

Vim標準の補完で使用するものと同じです。word以外は省略することができます。詳しい解説は、[Hack #14: ]Insert mode補完 自作編を参照してください。 menuはどのプラグインの候補で補完しているかを[B]のような記号で表す習慣になっています。ftpluginは[vim]や[ghc]のように、長めの文字列を使います。

プラグイン用ヘルパ関数

autoload/neocomplcache.vimには、プラグインから呼び出せるようにヘルパ関数が実装されています。ここでは、よく使われる関数について解説します。

neocomplcache#keyword_filter(list, cur_keyword_str)

get_keyword_list()で使用できる、単純なフィルターです。a:listの中から、a:cur_keyword_strにマッチするリストを返します。filter()とは違って、listは変更されるとは限りません。

neocomplcache#unpack_dictionary(dict)

リストの辞書をリスト化して返します。

neocomplcache#unpack_dictionary_dictionary(dict)

辞書の辞書をリスト化して返します。

neocomplcache#keyword_escape(cur_keyword_str)

a:cur_keyword_strをマッチングに使えるようにエスケープします。

neocomplcache#get_cur_text()

現在のカーソル文字列を取得します。

neocomplcache#get_completion_length(plugin_name)

plugin_nameの自動補完する文字列長を返します。

neocomplcache#set_completion_length(plugin_name)

plugin_nameの自動補完する文字列長を設定します。ただし、これはプラグイン側で初期値を設定するためのもので、ユーザーが補完文字列長を設定している場合、そちらが優先されます。

neocomplcache#get_keyword_pattern_end(filetype)

文字列の最後にマッチするfiletypeのキーワードパターンを返します。filetypeは省略可能で、省略すると現在のバッファのfiletypeを参照します。

neocomplcache#get_keyword_pattern(filetype)

文字列にマッチするfiletypeのキーワードパターンを返します。filetypeは省略可能で、省略すると現在のバッファのfiletypeを参照します。

neocomplcache#is_auto_complete()

自動補完の時に1を返します。

neocomplcache#print_caching(string)

キャッシュ時のメッセージを表示します。

neocomplcache#print_error(string)

エラーメッセージを表示します。

キャッシュ用ヘルパ関数

autoload/neocomplcache/cache.vimには、プラグインから呼び出せるキャッシュのヘルパ関数が含まれます。 量が多いので、ここでは詳しく説明することは避けますが、うまく利用すると簡単にキャッシュを使ったプラグインを記述できます。 ちなみに、neocomplcache Ver.4以降では、ほとんどのプラグインがキャッシュ用ヘルパ関数を使うように書き直されています。

プラグインのサンプル

neocomplcacheに標準添付のプラグインを参考にしても良いのですが、機能が複雑化しているため、プラグイン作りの勉強のために見るのは大変です。 eagletmtさんが作成したghc_completeはfiletype pluginでほどよい長さなので、勉強に最適でしょう。

plugin, ftplugin, complfuncの違い

neocomplcache Ver.3.00より、complfuncが実装され、より自由度の高い補完が実装できるようになりました。 pluginとcomplfuncの違いは、pluginはカーソル前のキーワードから補完しますが、complfuncは独自に補完位置を決定できるというところです。 ftpluginとはneocomplcacheのオムニ補完です。 基本的な仕様はcomplfuncと同じです。ただし、必要な時にのみ初期化や呼び出しされるというところが違います。

Shougo

Hack #168: シェル以外から立ち上げたVimでもシェル側の環境変数PATHを考慮したコマンドの呼び出しを行う

問題

:!ruby %

このコマンドが呼び出すrubyとして期待されるものは、/usr/bin/rubyであったり、/usr/local/bin/rubyであったり、はたまた~/git/ruby/local/bin/rubyのように独自で用意したものであったりします。

zshなどのシェルでは、~/.zshrcなどの設定ファイルに

export PATH=~/git/ruby/local/bin/ruby:$PATH

などと記述することによって、単にrubyなどとしてコマンドを呼び出したときに実際にどの場所のコマンドが呼び出されるか制御でき、またそのシェルから起動したVimもそれに準拠します。これはCUIでもGUIでも同様です。

しかしながら、シェルを経由せずVimを起動した場合。~/.zshrcなどに記述したロードパスに関する情報は得られません。MacのDockなどから起動しつつ、そのVimであなたのホームディレクトリ以下にあるrubyなどを呼び出すにはどうすればいいのでしょうか。

解決法

vimprocを用います。vimprocはVimの変数$PATHを参照してコマンドを実行します。

vimprocを用いてのコマンドの実行とVimのデフォルトの機能との対応は以下のようになります。

  • コマンドを単に実行する

    • Vim: :!command
    • vimproc: :call vimproc#system('command')
  • コマンドを実行した結果をバッファに書き足す

    • Vim: :r!command
    • vimproc: :call append(line('.'), vimproc#system('command'))
  • コマンドを実行した結果を変数に格納する

    • Vim: :let x = system('command')
    • vimproc: :let x = vimproc#system('command')
  • コマンドをバックグラウンドで実行する

    • Vim: :!command &
    • vimproc: :call vimproc#system_bg('command')

冗長で不便なので、キーマッピングを追加するか、あるいは:!の使用をさけてかわりにvimshellを用いるとよいでしょう。

nnoremap :! :call vimproc#system('

さて、:!system()を間接的にもっとも頻繁に使うのはなんといってもquickrunでしょう。quickrunでvimprocを用いるためには、以下の設定を~/.vimrcに記述します。

let g:quickrun_config['*'].runmode = "async:remote:vimproc"}

余談ですが、著者はこれにquickrun分割場所指定もあわせ、以下のように指定しています。

let g:quickrun_config['*'] = {'runmode': "async:remote:vimproc", 'split': 'below'}

これで、quickrunで好きなrubyを使いたい放題です。

補足

本Hackは一般的な外部コマンドの実行について述べましたが、この方法は特に、Rubyのような複数の同名のコマンドをインストールする必要があるようなコマンドで役に立ちます。著者はRuby 1.8.6, 1.8.7, 1.9.2, 1.9.3をすべて使用しているため、本設定が大変効いてきています。

なおVimによるRubyプログラミングをより効率的かつ快適に行うための方法については、"Vim" at RubyKaigiに参加し、基調講演を聞く、ライトニングトークの発表を聞く、ライトニングトークで発表する、そしてほかの参加者とその場で情報交換することによって、活き活きとした生の情報を大量に得ることができることは疑いの余地がないでしょう。

参考文献

ujihisa

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 #166: ローマ字の大文字/小文字を変換する

ローマ字の大文字と小文字を変換する機会は意外に多いです。なので、Vim にはこれを行うための便利な機能があります。

~

~ を押すと、カーソル下のローマ字の大文字/小文字を相互に変換します。大文字は小文字に、小文字は大文字になります。この際、カーソルを右に進めます。 [count] を指定することで複数の文字を同時に変換することができます。変換は一括で大文字/小文字にするのではなく、文字単位で作用します。つまり VimHack は vIMhACK になります。

gu gU g~

これらはオペレータで、対象に対してそれぞれ、gu は小文字への変換を、gU は大文字への変換を、g~ は大文字/小文字の相互変換を実行します。 例えば、現在のカーソルのある単語を全て大文字にしたい場合は gUiw です。 また、yy などと同様に行に対して動作する gugu guu gUgU gUU g~g~ g~~ があります。

Visual mode での u U ~

これらは Visual mode では g なしで動作します。

'tildeop'

'tildeop' オプションをオンにすると、~ はオペレータに変化します。g~ を頻繁に使う場合には便利でしょう。

正規表現

もっと複雑な変換を行いたい場合もあるでしょう。正規表現で置換する際、結果の大文字/小文字を指定することができます。 置換後のパターンに u があると、次の文字は大文字になります。l があると小文字になります。

:s/\w\+/\u\0/g
"bram moolenaar" → "Bram Moolenaar"

また、U や L があると、E が出現するまでそれぞれ大文字/小文字に変換します。

thinca

Hack #165: neocomplcache Hacks(6) インクルード補完

今回はインクルード補完です。neocomplcacheに実装されたインクルード補完はあらかじめ設定しておく必要がありますが、使いこなせばタグ補完を越えた使い勝手となります。

インクルード補完とは

インクルード補完とは、バッファ中のインクルード文を解析して、補完候補に加える補完のことです。Vim自体にもインクルード補完が備わっていますが、Perlなど'path'が多数のディレクトリに設定されている言語では重すぎて使いものになりません。neocomplcacheのインクルード補完は結果をキャッシュすることで、Vim標準のインクルード補完に存在する欠点を補うために開発されました。タグ補完と比較すると、インクルードしているファイルだけ解析するので補完候補が多すぎる問題が発生しにくい、あらかじめタグファイルを生成する必要がない、といった利点があります。

インクルード補完の制限

インクルード補完は強力ですが、万能ではありません。'path''includeexpr', 'include'といったオプションが適切に設定されている必要があります。特にWindows環境では、ftpluginなどで設定する必要があるかもしれません。インクルード補完ではctagsを利用するので、システムにインストールされている必要があります。ctagsが存在しなかったり、ctagsに対応していない言語の場合(D言語など)はバッファをそのまま解析するので、buffer_completeと動作が同じになります。

今後のインクルード補完

現在実装されているインクルード補完はまだ実験的な意味合いが強いです。 今後は、gtagsへの対応やechodocへの対応、遅延キャッシュなどを考えています。 ただしgtagsはカレントディレクトリにタグファイルを作るらしく、ctagsのように簡単に解析するのは難しそうです。

参考文献

neocomplcache.vim のインクルード補完の使い方 thincaさんのインクルード補完解説記事。neocomplcache Ver.3.06を対象としていて、neocomplcache Ver.5.0がリリースされた現在では変数名等が古くなっていますが、参考になると思います。

Shougo

Hack #164: JavaScript開発環境 その2 CoffeeScriptを使う

問題

JavScriptはその動的性と柔軟な構文により、ブラウザ上で動作するアプリケーションに限らず多くの場で活躍する便利なプログラミング言語です。しかしそれでもいくつか問題が残されています。

  1. セミコロン

    多くのVimユーザはコロンとセミコロンの配置を入れ替え、コロンの入力を容易にするよう調整しているといわれています。しかしながらJavaScriptでは文末にセミコロンの入力を必要とします。

    var a = 1;
    

    上記のような簡素な例だと単純にFPの消費量のみの問題なのですが、しかし以下のようにセミコロンが必要な場合と必要でない場合がすぐに判別しないときもあり、このとき若干の面倒さが残ります。

    function f1() {
      return 1;
    }
    
    var f2 = function() {
      return 1;
    };
    
  2. 括弧

    JavaScriptは関数と変数の名前空間が一致しているためか、関数適用の括弧を省略できない言語仕様となっています。

    f();
    
    f(x);
    

    引数がない場合は()の有無により関数自体か関数実行結果の値かを区別できるというので納得できるのですが、しかし引数がある場合には納得できません。

    f x;
    

    もしこのように書けば、これはf(x)以外に考えられないはずです。それなのに毎回f(x);と記述しなければならないのは冗長で、開発効率の大幅な低下を招きます。

解決法

~/.vim/ftplugin/javascript.vimなどで努力することで、容易に間違いなくセミコロンを入力するためのサポートを行なう、あるいは括弧を自動的に入力するなどやりかたはありますが、いずれもまじめに対処するとなかなか難しい問題です。

もっとも容易な解決策は、例えばJavaScriptを避けることです。

CoffeeScriptでプログラムを書き、JavaScriptにコンパイルする手法を紹介します。CoffeeScriptでは前述の2つの問題を解決し、ほかにもHack #160: JavaScript開発環境で挙げた長い予約語functionをいかにして入力するかという問題も解決します。

例を挙げます。0から2までの数字を出力するコードをCoffeeScriptで記述してみましょう。

f: ->
  memo: 0
  ->
    memo++

Number.prototype.times: (f) ->
  f i for i in [1..this]

g: f()

3.times (i) ->
  p g()

coffee -cpコマンドで、上記コードが以下のようなJavaScriptにコンパイルされます。

(function(){
  var f, g;
  f = function() {
    var memo;
    memo = 0;
    return function() {
      return memo++;
    };
  };
  Number.prototype.times = function(f) {
    var _a, _b, i;
    _a = []; (_b = (1));

    for (i = _b; i <= this; i += 1) {
      _a.push(f(i));
    }
    return _a;
  };
  g = f();
  (3).times(function(i) {
    return p(g());
  });
})();

前述の問題が全て解決したことが分かります。

CoffeeScriptを使うためにはNodeJsCoffeeScript処理系の二つが必要です。2010年7月現在、MacPortsでインストールできるNodeJsはバージョン0.1.92ですが、0.1.98以降で導入されたreadlineや、一部の正規表現エンジンの動作の違いから、0.1.92ではCoffeeScriptの一部の機能しか利用できません。折角ですのでNodeJsは最新版を導入しましょう。

また、CoffeeScriptをVimから快適に使うためのツールがいくつか存在します。以下で紹介する全てを導入することを強くお勧めします。

  1. vim-coffee-script

    http://github.com/kchmck/vim-coffee-script

    ftdetect, ftplugin, indent, syntax全てがそろっています。

    a

  2. quickrun

    http://github.com/thinca/vim-quickrun

    <Space>rなどで編集中ファイルを実行します。CoffeeScriptに対応しています。

    b

    また、明示的に:QuickRun 'coffee -cp'などとすることで、QuickRun出力バッファに, CoffeeScriptからコンパイルした結果のJavaScriptを表示し続けることができ、大変便利です。

新たな問題

JavaScriptでは変数名や関数名をcamelCaseでつけます。つまり、this_is_a_penよりもthisIsAPenが好まれます。JavaScriptを書き慣れている人ならば何の問題もなくcamelCaseで命名していくでしょうが、しかしCoffeeScriptはむしろPythonやRubyのように見えるため、混乱してアンダースコアで小文字の単語を繋げてしまう傾向にあります。

すぐに思い付く解決方法はCoffeeScriptのコンパイラ部分を書き換えて識別子名を自動で置換することでしょうが、しかしこの方法ですともとのCoffeeScriptのコードはcamelCaseでないままです。

解決法

挿入モードの_をシフトキーとして扱うようにし、this_is_a_penと打鍵するだけでthisIsAPenと打鍵したことになるようVim側の挙動を変更します。そのためにstickykey.vimを用います。

  1. http://www.vim.org/scripts/script.php?script_id=2990 からstickykeyをインストールする
  2. ~/.vim/ftplugin/coffee.vimに以下を記述する

    function! JavaScriptUnderScoreBecomesCamelCase()
      if matchstr(getline('.'), '.', col('.')-2) =~ '\w'
        return "\<Plug>(stickykey-shift)"
      else
        return '_'
      endif
    endfunction!
    
    imap <buffer><expr> _ JavaScriptUnderScoreBecomesCamelCase()
    

カーソル位置が[a-zA-Z0-9_]のときのみ_がシフトキーとして動作します。空白文字の上で_を打鍵したときなどは_のままになりますので、例えば_ではじまる関数名の関数を定義するときなどに気兼ねなく_を打鍵できます。

補足

本HackでCoffeeScriptを用いた効率的なJavaScriptの開発方法を紹介しましたが、しかしながら、CoffeeScriptも万能ではありません。無引数関数の値を取り出すための(), 関数リテラルの仮引数指定の(x), 条件演算子a ? b : cの違いなど。これらの問題を解決するためのパッチが勇士によって開発中とのことです。

また、coffee -iで起動するインタラクティブなcoffeeインタプリタをvimshellを用いたVim内で操作できるはずなのですが、現時点ではなぜかうまく動作しないという問題があります。

ujihisa

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

Hack #162: Vimを終了しても undo 履歴を復元する

Hack #8: 作業の履歴を辿るでも紹介したように Vim には編集履歴を辿る機能が充実していますが、Vim を終了したりファイルを開きなおしたりするとこの undo 履歴は消えてしまいます。

undo-persistence

現在開発中の Vim 7.3 から、新しく undo-persistence (:help undo-persistence) と言う機能が追加されました。これを使うと undo の情報をファイルに保存し、Vim を終了しても次に起動したときに undo の履歴を復元してくれるようになります。

設定

'undodir'

まず、undo 履歴を保存するファイルが作成されるディレクトリを 'undodir' オプションに設定します。これにはディレクトリをカンマ区切りのリストで指定します。 ディレクトリの指定に "." を使うと、対象ファイルのあるディレクトリを指します。デフォルトはこれです。

指定したディレクトリの中で、最初に存在したディレクトリに undo 履歴のファイルが保存されます。また、復元する際は最初に見付かったファイルから履歴を復元します。

例えば、ファイルと同じディレクトリに .vimundo と言うディレクトリがあったらそのディレクトリへ、なければ ~/.vimundo へ保存するには、以下のように設定します。

set undodir=./.vimundo,~/.vimundo

'undofile'

これはバッファローカルなオプションで、これが on のバッファはバッファの保存時に undo 履歴をファイルに保存します。また、読み込み時に undo 履歴を復元します。

全てのファイルで有効にしたい場合は単に :set undofile とすれば良いでしょう。特定のファイル、例えばホームディレクトリ以下のファイルのみで設定したい場合は以下のようにします。

augroup vimrc-undofile
  autocmd!
  autocmd BufReadPre ~/* setlocal undofile
augroup END

+persistent_undo

これらの機能は +persistent_undo がないと利用できないので、if で囲っておくと良いでしょう。

if has('persistent_undo')
  set undodir=./.vimundo,~/.vimundo
  augroup vimrc-undofile
    autocmd!
    autocmd BufReadPre ~/* setlocal undofile
  augroup END
endif

注意点

undo 履歴ファイルは 'undodir' で指定したディレクトリが "." 以外だった場合は対象のディレクトリに、フルパスのパスの区切りを "%" で置き換えたファイル名で保存されます。つまり、ファイルを移動すると履歴ファイルの名前を手動で変更しない限り undo 履歴は失われます。

また、この機能は現在開発中の Vim に含まれているものです。利用する場合は自己責任で利用してください。

thinca

ホーム > Vim Hacks

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

    Return to page top