ホーム > タグ > Vim Hacks

Vim Hacks

Hack #241: Haskellで使いたい関数を使ってからそのモジュールをimportする

Haskell Advent Calendar 2011への寄稿記事です。

問題

Haskellを書いていて、長いソースコードの末尾の方にて、急にとあるモジュールのとある関数を使いたい、そんなときはよくあります。ありがちなのがApplicativeの演算子いくつかと、Data.Function.onと、Data.Listのアレとコレと・・・。いくらでもあります。

Haskellでは一般的に、モジュールのimportはソースコードのかなりはじめの方にまとめて記述します。importしたい関数を一度しか使わず、しかもソースコードの末尾の方に位置していようと、おかまい無しです。このとき、ggなどでソースコード上部まで移動し、neco-ghcなどを駆使してmoduleとその関数を的確にimportし、そして<C-o>などでもといた場所に戻ることになると思います。:spなどで画面分割してから行ったり、あるいはmarkをつけるという方法もありますが、いずれにせよこれらの作業のため脳内の作業メモリがスタックオーバーフローするのは明らかでしょう。

解決

unite-haskellimportを用います。

https://github.com/ujihisa/unite-haskellimport

まずは上記プラギンをインストールします。依存プラギンはunite.vim、依存ツールはhoogleです。

$ cabal update && cabal install hoogle && hoogle data

たとえば急に==>という関数を使いたくなったとしましょう。

:Unite haskellimport

としてunite窓を開き、==>と打鍵しましょう。

Test.QuickCheck.Property (==>) :: Testable prop => Bool -> prop -> Property
Test.QuickCheck (==>) :: Testable prop => Bool -> prop -> Property

インストールしているcabalパッケージにもよりますが、たとえば上記のような項目が選択肢にでてくることでしょう。実際にimportしたい側を選択します。

候補を選択すると、unite-haskellimportはdefaultのactionとして:Haskellimportコマンドを発行します。これは、対象を、ソースコードのそれらしい場所に挿入するものです。既に他のimport文がある場合は、最後のimportのあとに挿入します。

Before

import qualified Data.Text as T

f x = ...

After

import qualified Data.Text as T
import Test.QuickCheck ((==>))

f x = ...

今回importしたのは記号からなる関数でした。が、もしも記号ではない関数、つまりVimの<cword>になるようなものならば、:Uniteのかわりに

:UniteWithCursorWord haskellimport

とするとよいでしょう。これを頻繁に行うようならば、適切なキーにマッピングしておくべきです。たとえば以下を ~/.vim/ftplugin/haskell.vimに記述すると、<space>Iと打鍵するだけでカーソル以下にある単語の関数をimportしてくれます。

nnoremap <buffer> <space>I :<C-u>UniteWithCursorWord haskellimport<Cr>
ujihisa

Hack #240: <C-[> での誤爆を防止する

挿入モードから抜ける方法は多数ありますが、その中に <C-[> キーがあります。

しかしこの [ キーは、日本語キーボードではすぐ左に @ キーがあり、誤って <C-@> を押してしまうと直前に挿入したテキストがさらに挿入されてから挿入モードを抜けるため、誤爆すると大惨事となります。

誤爆を防止する

誤爆をすると言うことは、本来行いたい操作があるということです。 このような誤爆を簡単かつ確実に防止するには、対象の機能を本来押したいキーに置き換えてしまうのが効果的です。

考えてみましょう。この <C-@> の機能「直前に挿入されたテキストをもう一度挿入し、挿入を終了する。」は、あなたにとって必要でしょうか? もし必要ないと判断したら、本来押したいキー、この場合は <C-[> に置き換えましょう。以下のようにします。

imap <C-@> <C-[>

こうすることで誤爆を防ぐことができます。この @ は [ よりも近いため押し易く、しかもこの位置は英字キーボードと同じ位置であるため、英字キーボードに乗り換える練習にもなります。

もし万が一 <C-@> の機能が使いたい場合、しかし誤爆は避けたい場合は、別の位置に <C-@> をマッピングするとよいでしょう。

thinca

Hack #239: グローバル変数を安全に参照する

こんにちは、ujihisaです。以前からこのvim-users.jp上で告知していた、東京でのVimのカンファレンスであるujihisa.vim#2が、ついに一般参加者の募集をはじめたようです。

このページの末尾から参加申し込みページにアクセスできるようです。なお、HootSuiteをお使いの方は、以下のリンクから簡単にujihisa.vim#2の宣伝ができるようで、とても便利です。

さて、本題です。

問題

Vim scriptを読んでいると以下のようなコードを比較的頻繁に見かけるのではないでしょうか。

if !exists('g:aaa_bbb')
  let g:aaa_bbb = 'something default'
endif

特定のグローバル変数が存在しないときのみデフォルトの値を入れるときのパターンです。これは一行で宣言的に書きたいですよね。かといって、これのためだけに別の関数を用意するのも、健康的でなさそうです。

解決

実はグローバル変数の一覧をg:で取得することができます。同様にバッファローカル変数一覧はb:、スクリプトローカル変数一覧はs:など。これは変数名がキーで変数の値が値の辞書となっています。

echo g:hello

は、実は以下のように書くことができます。

echo (g:).hello
echo (g:)['hello']

いずれもそのキーがなければ実行時エラーをだしてしまいます。しかし、get()関数を用いれば、デフォルト値を使いつつその値に参照することができます。

echo get(g:, 'hello', 'the default value')

これはg:helloが定義されていればその値を表示、定義されていなければthe default valueを表示します。

したがって、元々の問題は、

if !exists('g:aaa_bbb')
  let g:aaa_bbb = 'something default'
endif

以下のように一行で記述することができます。

let g:aaa_bbb = get(g:, 'aaa_bbb', 'something default')

美しいですね。これがVim script使いの生きる道といえるのではないでしょうか。

ujihisa

Hack #238: neobundle.vim で plugin をモダンに管理する

こんにちは。Shougoです。Vimテクニックバイブルの執筆で長らくお休みを頂いていました。 執筆が終わってからも、プラグインの更新で忙しいのが難点です。 とはいえ、あまりに間隔が空いているのは執筆者としてまずいと感じたため、久しぶりに復帰します。

みなさん、Vimプラグイン管理には何をお使いでしょうか。残念ながら、Vimのプラグイン管理には良い方法がなく、 各々が「自分の考える最強のプラグイン管理プラグイン」を作ってきました。 つまり、長らくプラグイン管理プラグインは戦国時代の様相を呈していたわけです。 実装が存在しないVimJoltsもその一つです:-)。 しかし最近、Vimのプラグイン管理はVundleに一元化されることが多くなってきました。 「Hack #215: Vundleでプラグインをモダンに管理する」やVimテクニックバイブルでも紹介されています。 私も最初はpathogenを使っていましたが、アップデートが簡単なVundleに乗り換えた経緯があります。 ただしVundleにもいくつか不満があったため、自分でforkしたVundleをしばらく使用していました。 しかし、最終的にVundleの仕様変更に追随できなくなったため、forkしたVundleを元にneobundle.vimを開発することにしました。 変更点をVundleにマージするには、変更点が多すぎたということもあります。 今回はneobundle.vimについて紹介します。

neobundle.vimとVundleの違い

Vundleをベースにしたため、基本的な機能は同じです。 Vundleを知っているなら、すぐに乗り換えることも可能でしょう。 変更点は幾つかありますが、主なものは以下の通りです。

コマンド名が改善されている

VundleはBundleを意識して付けられたコマンド名になっているため、コマンド名とプラグイン名が対応していません。 例えば、プラグインをインストールするコマンド名は:VundleInstallではなく、:BundleInstallです。 この辺りは、議論がされているようですが、まだ結論は出ていません。 neobundle.vimはプラグイン名をプレフィクスとした、:NeoBundleInstallとなっています。 BundleをNeoBundleに改名するだけで、基本的に使用することができます。

vital化されている

ルーチンを他のプラグインと共通化するために、vital.vimを使用しています。 プラグインの開発が楽になります。

shellslashがオンでも動作する

shellescape()を使用していますが、shellslashオプションがオンの場合、クオートが”になってしまうという問題があります。 つまりWindows環境で、cmd.exeをshellとして使用している場合、shellslashを有効にしていると動きません。 neobundle.vimではshellescape()を使用していません。

vimprocに対応している

vimprocが利用できる環境では、アップデートにvimprocを使用します。 Windows環境では、DOS窓が出ないという利点につながります。

unite.vimインタフェースの実装

実は、これがneobundle.vimで一番やりたかった機能です。 neobundle.vimはunite.vimのインタフェースを実装しているため、unite.vimからVimプラグインをアップデートすることができます。 非同期でアップデートを行うため、使用にはvimprocが必要です。 neobundle.vimはunite.vimやvimprocがない環境でも動作しなくてはならないため、従来のインタフェースも残っています。 ただし、従来のインタフェースは緊急用のため重視していません。

Subversion, Mercurialへの対応

tsukkeeさんがVundleに追加していた機能をマージし、Subversion, Mercurialに試験的な対応をしました。 まだ実験段階ですが、試していただければと思います。

リビジョン指定

:NeoBundle {repository} {revision}という構文でリビジョン指定ができます。まだ実験段階の機能です。 不具合のあるプラグインのバージョンを固定する時に便利だと思います。

neobundle.vimの導入

neobundle.vimの開発は、github上で行われています。https://github.com/Shougo/neobundle.vim 導入する場合、~/.vimにリポジトリをcloneすると楽でしょう。

$ git clone https://github.com/Shougo/neobundle.vim ~/.vim/neobundle.vim.git

plugin の管理

plugin の管理は以下の様に .vimrc に NeoBundle 'plugin name' と記述することで行います。 極端な話、BundleNeoBundleに変更するだけです。

set nocompatible
filetype off

if has('vim_starting')
  set runtimepath+='path to neobundle directory'

  call neobundle#rc(expand('~/.bundle'))
endif

NeoBundle 'git://github.com/Shougo/clang_complete.git'
NeoBundle 'git://github.com/Shougo/echodoc.git'
NeoBundle 'git://github.com/Shougo/neocomplcache.git'
NeoBundle 'git://github.com/Shougo/neobundle.vim.git'
NeoBundle 'git://github.com/Shougo/unite.vim.git'
NeoBundle 'git://github.com/Shougo/vim-vcs.git'
NeoBundle 'git://github.com/Shougo/vimfiler.git'
NeoBundle 'git://github.com/Shougo/vimshell.git'
NeoBundle 'git://github.com/Shougo/vinarise.git'

filetype plugin on
filetype indent on

Vundleと同様、先にfiletypeをoffにしてから、neobundle.vimの設定を行います。 最初、neobudle.vimの関数を呼べるよう、neobundle.vimのパスを&runtimepathに追加します。 そしてneobundle#rc(expand('~/.bundle'))で初期化します。 neobudle#rc()の引数はプラグインをインストールする基準となるパスです。 NeoBundleコマンドで設定をしたあと、最後にfiletype pluginをオンにします。

NeoBundle コマンドの使い方

Vundleと全く同じです。基本的にBundleコマンドがNeoBundleコマンドに変わっただけです。

Github 上のリポジトリから取得する

NeoBundle 'user_name/repository_name'

vim-scripts 上のリポジトリから取得する

NeoBundle 'script_name'

それ以外のリポジトリから取得する

NeoBundle 'git://repository_url'
NeoBundle 'http://svn.macports.org/repository/macports/contrib/mpvim/'
NeoBundle 'https://bitbucket.org/ns9tks/vim-fuzzyfinder'

Gitリポジトリのフルパスを指定します。 MercurialやSubversionにも試験的に対応しています。

pluginの管理

plugin のインストールには、:NeoBundleInstall コマンドを使用します。 .vimrcで設定したplugin が自動的に取得され、インストールまで行われます。 ただし、アップデートしたプラグインを使用するには、Vimを再起動したほうが良いです。

:NeoBundleInstall

既にインストールされている plugin をアップデートするには、 :NeoBundleInstall! コマンドを利用します。 通常のNeoBundleInstallでは、アップデートが行われないからです。

:NeoBundleInstall!

不要になったプラグインを削除するには、:NeoBundleの設定を.vimrcから削除した後、 :NeoBundleCleanコマンドを実行します。

neobundle.vimはplugin検索には対応していません。

neobudle.vimはneobundle/install sourceを使用することで、 unite.vimインタフェースによりアップデートを行うことができます。 アップデートは非同期に行われます。 neobundle/install sourceの引数に!を与えると、:NeoBundleInstall!相当になります。 プラグイン名を引数に与えることで、個別にアップデートをすることも可能です。

:Unite neobundle/install:!
:Unite neobundle/install:neocomplcache
:Unite neobundle/install:neocomplcache:unite.vim

ドキュメントの更新

プラグインのインストール時に、自動的に:helptagsコマンドが実行されるため、 ユーザーは何もする必要がありません。

参考

  • :help neobundle
Shougo

Hack #237: 古い quickfix を参照する

quickfix は :grep:make の結果として使われたり、時にはプラグインが何かの結果を表示するのに使ったりと、様々な状況で使用されます。

それだけ便利なものですが、時には、以前の内容をまだ見たいのにうっかり新しい内容を quickfix へ出力してしまうことがあります。

:colder :cnewer コマンド

実は、quickfix は過去のリストを10個まで保持しています。これは :colder :cnewer コマンドで切り替えられます。

:colder [count]
:cnewer [count]

:colder[count] 個分古い quickfix へ移動します。:cnewer[count] 個分新しい quickfix へ移動します。

もし誤って quickfix を上書きしてしまった場合でも、慌てず騒がず :colder すれば安心です。

thinca

Hack #236: 短期間でVim script力を向上する

こんにちは、ujihisaです。この記事はGentoo上で執筆しています。ちなみに来日することが決まりました。こんなカンファレンスや、勉強会を企画してみました。いずれも来月です。

問題

Vim scriptというプログラミング言語の能力を高める効率的な方法はあるのでしょうか?

Vimテクニックバイブルも既に完読してしまい、次にやるべきことを見失っている読者も多いかもしれません。

解決

Project Eulerというオンラインの問題集があります。オンラインで答え合わせもできます。

ぜひこれにVim scriptで挑戦してみましょう。以下に問1の回答例を示します。

(もっと良い回答を思いついた、という方はぜひコメントやトラックバックで示してください。楽しみにしています!)

問1

http://projecteuler.net/problem=1

If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.

Find the sum of all the multiples of 3 or 5 below 1000.

回答例1

range()で1から1000までの数値の配列を作り、それをもとにforで加算していきます。

function! s:p1()
  let memo = 0
  for i in range(1, 999)
    if i % 3 == 0 || i % 5 == 0
      let memo += i
    endif
  endfor
  return memo
endfunction

echo s:p1()

これをfiletype=vimなバッファで作成し、最後にquickrunしましょう。そこには答えがでているはずです。

回答例1

filter()を使います。

function! s:p1()
  let memo = 0
  let list = filter(range(1, 999), 'v:val % 3 == 0 || v:val % 5 == 0')
  for i in list
    let memo += i
  endfor
  return memo
endfunction

echo s:p1()

さっきよりも見通しのよいコードになったでしょう。lambdaがなくて文字列になってしまうのがちょっと困りものですが…。

さて、この調子でforを消せると考えた人も多くいるかもしれません。そうです、つい5日前に読んだHack #235を思い出しましょう。vitalを使います。

function! s:p1()
  let L = vital#of('vital').import('Data.List')
  let list = filter(range(1, 999), 'v:val % 3 == 0 || v:val % 5 == 0')
  return L.foldl1('v:memo + v:val', list)
endfunction

echo s:p1()

もうここまできたら一行にしたくなるのが人情ですよね。

echo vital#of('vital').import('Data.List').foldl1('v:memo + v:val', filter(range(1, 999), 'v:val % 3 == 0 || v:val % 5 == 0'))

ほかにも多くの解法があると思います。読みやすくかつ短いコードを高速で書くことができるよう、日々鍛錬あるのみです。

ujihisa

Hack #235: リストの要素の総和や総乗を簡単に求める

こんにちは、ujihisaです。無事引っ越しが完了しましたが、インターネット回線の確保にはもうしばらく時間がかかるらしく、しばらく家でオフラインな状態です。ちなみに来日することが決まりました。こんなカンファレンスや、勉強会を企画してみました。いずれも来月です。ふるってプレゼンに応募ください。

問題

リストlistの和は、他の言語ならば以下のようにして求めることが一般的です。

  • Ruby
    • list.inject(:+)
    • これは list.inject {|i, j| i + j }の意。Enumerable#injectの特殊用法
    • list.inject(0, :+)
    • 同じくこれは list.inject(0) {|i, j| i + j }の意。
  • Haskell
    • foldl1 (+) list
    • これはfoldl1 (\i j -> i + j) listの意。言語仕様として演算子は括弧でくくるだけで2引数関数のような振る舞いになる(*1)。
    • sum list
  • Python
    • reduce(lambda a, b: a + b, list)
    • sum(list) と、sumといった直接的な方法がサポートされているかは別にして、なんらかの「リストと関数を受け取り、(リストに限らない)なんらかの値を返す」関数が提供されるのが一般的であることがわかります。

Vim scriptには残念ながらそういった関数が標準では提供されていないようです。どうすればよいでしょうか。

解決

vital.vimという、プラギン埋め込み型の汎用ライブラリを使います。

前述の問題を解決するには、vitalのData.Listモジュールを用いるのがよいでしょう。Data.Listの上記要件を満たす関数は以下の4つで、意味はそれぞれHaskellのPreludeに定義されているものと同じです。

  • foldl
  • foldl1
  • foldr
  • foldr1

たとえばfoldl1を用いると、以下のようにしてリストlistの要素の値の総和を求めることができます。

let L = vital#of('プラギン名').import('Data.List')
echo L.foldl1('v:memo + v:val', list)

非常に書きやすく読みやすく、理想的です。なお、実験的にvitalを用いる場合は、vitalを&rtpに入れた上でvital#of('vital')とするとそのvitalを読み込むことができます。

ujihisa

Hack #234: Vim外にいるときはVimを透けさせる

こんにちは、ujihisaです。無事引っ越しが完了しましたが、インターネット回線の確保にはもうしばらく時間がかかるらしく、しばらく家でオフラインな状態です。ちなみに来日することが決まりました。こんなカンファレンスや、勉強会を企画してみました。いずれも来月です。

問題

Vim使いの大半は、Vimをフルスクリーンで使っていることと思います。このとき、WindowのfocusがVimの窓にあるときは別に構わないのですが、focusがVimの外にあるときに、つねに背景がVimになってしまいます。せっかくファンシーなデスクトップの壁紙を設定しているのに、システム起動直後と終了直前にしか見れないのは、いささか寂しいものです。

解決

VimにはFocusGainedFocusLostというイベントがあります。これを用いてVimの透明度を変更すると、とてもいい感じになります。 参考までに、著者のMacVimのための設定を以下に掲載します。~/.vimrcではなく~/.gvimrcに記述してください。

augroup hack234
  autocmd!
  if has('mac')
    autocmd FocusGained * set transparency=10
    autocmd FocusLost * set transparency=50
  endif
augroup END

普段は透明度10 (ほとんど不透明)にしており、focusがないときは透明度50 (かなり透明) にしています。筆者のGentoo上のGVimは'transparency'をサポートしていないため、if has('mac')で条件分岐しています。

ふだん

ノーフォーカス

追記

mattnさんがWindows版GVimでも動作するためのプロジェクトを発動させたようです。

ujihisa

Hack #233: 現在使用中のColorSchemeの名前を取得する

こんにちは、ujihisaです。今月末に引っ越しをする予定があり、毎日忙しく、Vimを使う暇すらない日々が続いています。

問題

美しいColorSchemeを使用しており、それを友達に自慢したいVim使いは多数いると思います。しかしながら、現在使用中のColorSchemeの名前を取得する方法が分からず、はがゆい思いをしたことがある人も少なくないのではないでしょうか。

解決

二つの方法があります。

:colorscheme

あるいは

:echo g:colors_name

前者は画面にColorScheme名を表示する専用のコマンド、後者はColorScheme名が入っている変数を得てそれを表示しているわけですので、対話的に使用するときは前者を、Vim scriptから値を使用するときは後者を使うと自然になります。

注意

必ずしも上記方法で現在使用中のColorSchemeが得られるとは限りません。ColorScheme開発者には、そのColorScheme名をg:colors_nameというグローバル変数に格納することが推奨されていますが、自動的に行われるわけではありません。

上記のような場合、:colorschemeは無条件にdefaultと出力します。また、g:colors_nameにアクセスするとエラーを吐き出します。

悪評高いColorSchemeの例としてleoがあります。:colorscheme leoして:echo g:colors_nameないし:colorschemeして、絶望しましょう。

そして、パッチを投げましょう。

ujihisa

Hack #232: Clojure開発環境を整える

こんにちは、ujihisaです。Vancouverでは空前のClojureブームが発生しており、毎週のようにClojure勉強会があり、Vim使いやEmacs使いが集結しています。

問題1

Clojureでシンタックスハイライトや自動インデントを行いたい。

解決1

VimClojureというプラギンをインストールします。

https://github.com/jondistad/vimclojure

なお、g:clj_highlight_builtinsを設定すると標準関数のハイライトが、またg:clj_paren_rainbowを設定すると括弧の対応が虹色に美しく輝き、便利です。筆者は以下のようにして両方とも利用可能にしています。

~/.vimrc:

let g:clj_highlight_builtins = 1
let g:clj_paren_rainbow = 1

vimclojure highlighting

問題2

改行するたびにyankしている文字列が消える?

解決2

VimClojureをアンインストールするか、頑張ってパッチを書きます。

VimClojureには深刻なバグがあり、インデントを調整するたびにyankしている文字列が完全に破壊されます。

問題3

Clojureのコードを即座に実行し、結果を別バッファに表示しつづけたい。

解決3

quickrunを用います。もしcljというコマンドを持っていれば、ファイルタイプがclojureであるバッファに対してquickrunは無設定で動作します。

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

quickrun clj

問題4

quickrunに時間がかかりすぎる。

解決4

JVMの立ち上げ時間とライブラリの読み込み時間がとても長いのが原因です。Clojure処理系を立ち上げたままにしておくことで、劇的な高速化が実現できます。そのための道具としてNailGunとJarkがあります。筆者のオススメはJarkです。

https://github.com/icylisper/jark

Jarkをインストールしたら、まずはJarkサーバを立ち上げましょう。vimshellなどで

$ jark vm start

として事前にJarkサーバを立ち上げ、そしてquickrunします。OSを終了するときまで、すなわちVimを終了するときまで、Jarkサーバを終了する必要はありません。

quickrunは既にjarkに対応していますので、無設定でいきなり使えるはずです。

以下に感動的なベンチマーク結果を載せます。

(println "hello")

とだけ書いたコードをcljjarkのそれぞれでtimeコマンドで比較してみたところ、それぞれ

  • clj

      1.40 real         2.09 user         0.15 sys
    
  • jark

      0.19 real         0.11 user         0.06 sys
    

となりました。実験はかなりパワフルなiMacを用いて行ったので、一般的な初代MacBook Airなどのマシンで行うと、さらに顕著な差が出るでしょう。

ujihisa

Home > Tags > Vim Hacks

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

    Return to page top