ホーム > タグ > lv3

lv3

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 #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 #224: Vim Hacks に記事を投稿する

平素は vim-users.jp および Vim Hacks をご覧いただき、まことにありがとうございます。ご存知の方もいるかと思いますが、現在 Vim Hacks Project は執筆者陣が自宅サーバのメンテナンスもままならないほど多忙なため、記事の更新が滞っています。

ところで、Vim Hacks の記事を見ていて、次のように思ったことはないでしょうか。

  • 自分の知っているVimの小ネタを披露したい!
  • 自作または愛用のプラグインを布教したい!
  • Vim Hacksに記事を書いてVimユーザの役に立ちたい!
  • 何でもいいから記事を書いて目立ちたい!

Vim Hacks Project では、執筆者を常に募集しています。定期的に書くのは無理な場合でも、ゲスト扱いで1度だけ書くことも可能です。今回は、Vim Hacks に記事を書くための手順を紹介します。

  1. 何について書くか決める

    記事を書くのですから、まずは何について書くのかを決める必要があります。 内容は、Vim に関係のあることで、読んだ人が幸せになるであろうことです。 あまり難しく考えることはありません。

    既出の場合もあるので、過去の記事も参考にしてみてください。 既出かどうか確認が面倒な場合は、執筆者に聞いてください。

    ちなみに、自信があるならば内容は後で考えても構いません。

  2. 執筆者に連絡を取る

    書くことが決まったら、執筆者の誰かに連絡を取って記事を書きたい旨を伝えます。 Twitter やメールで直接連絡を取ってもいいですし、チャットルーム で宣言しても構いません。 執筆者陣は新しい執筆者に飢えているので、拒否されることはまずありません。気軽に声をかけてください。

    公開日をいつにするかなどはこの辺りで決めることになります。希望がある場合は言ってもらえれば大体通ります。

    現時点での執筆者とその主な連絡先は、この記事の最後にまとめてあります。

  3. vim-users.jp にログインする

    vim-users.jp は OpenID を使って誰でもログイン可能です。面倒なアカウントの発行などはありません。 代わりに、OpenID を用意してください。

  4. 執筆する

    執筆ガイドラインに従って記事を書きます。守るべきことは、以下のことです。

    • ポストする記事にはタグ「Vim Hacks」を付けてください。
    • 難易度別に応じてタグ「lv1」「lv2」「lv3」を付けてください。
      • ポストする人の主観で付けてください。
    • カテゴリー「Vim Hacks」にチェックを入れてください。
    • パーマリンクの末尾はhackNにしてください。Nは数字です。例えばHack #1の場合はhack1です。
      • 特にリンクは後からの変更がきかないので注意してください。合っているか不安な場合は他の執筆者に確認してもらってください。
    • ポストする記事は下書き保存しておき、00:00に自動公開するよう設定してください。
  5. 公開される

    おめでとうございます!これであなたも Vim Hacks Author です。 もしよければ、次に投稿する記事の内容を考えましょう。

現在の執筆者一覧

thinca

Vimテクニックバイブル紹介(Shougo版)

この記事について

この記事は、開発環境勉強会における「Vimテクニックバイブル」紹介スライドの完全版です。 「Vimテクニックバイブル」については、taku_oさんやmattnさんなども記事を書いていますが、 私の場合はできるだけ本の内容に触れたものにしています。 これを読んでVimテクニックバイブルを購入していただける人が増えれば、著者達にとって最高の喜びです。

Vimテクニックバイブルができた背景

以前にもブログの記事で触れていますが、Vim界には良質な記事や書籍が不足しています。 特に、まともにVim scriptを書ける人が書いた書籍がほとんどありません。 これはVim界の大きな損失であり、Vim scriptプログラマーが育たない大きな原因となっています。 私がVim勉強会に参加すると、必ずといっていいほど聞かれるのが 「Shougoさんは、Vimの書籍を書かないんですか?」ということです。 この声になかなか答えられないのは悲しいものがありました。

Vimテクニックバイブル

と、いうことで……今年ついにVimの書籍が刊行されます! 書名は「Vimテクニックバイブル」です。 中身は「Emacsテクニックバイブル」のVim版となっており、イメージカラーは当然Vimグリーンです。 そういえば、EmacsテクニックバイブルはEmacsブルー(パープル)でしたね。 キャッチコピーは、「unite.vimで進化する新しいVimの常識を教えます。あなたはVimの本当の姿を知っていますか?」 Emacsテクニックバイブルとは違い、unite.vimを全面に押し出しています。

執筆者紹介

執筆陣はかなり豪華です。おそらく、これだけの人達が一冊の書籍を作るために集まることはもうないでしょう。

taku_o

人気サイト「名無しのVim使い」の管理人です。雑誌にVimの記事を執筆したことがあります。 サイトで紹介しているだけあって、様々なプラグインについて、かなりの知見を持っています。

yukimi

章の基礎知識や用語の統一など、細かいところで手伝ってもらっています。Vim使いです。

mattn

webapi-vimやzencoding.vimの作者です。他にも細かなプラグインを作っています。 KaoriYaさんと同じく、昔からVimにパッチを多数送っており、Vimの内部実装や黒魔術に詳しいです。

thinca

quickrunやref.vim, scouter.vimなどの作者です。unite.vimのsourceやvital.vim, eskk.vimなどにも関わっています。 Vim scriptにかなり詳しいです。

fuenor

QFixHowm, QFixGrep, JPFormat.vimの作者で、Vim-UTF8を配布しています。 Windows環境での日本語の扱いについて詳しいです。

Shougo

neocomplcache, vimshell, vimproc, unite.vim, vimfilerなどを作っています。 メンテナンスしているプラグインが多すぎて、なかなか時間が足りないのが悩みです。Vimは環境だと思っています。

内容紹介

もう執筆がほぼ終わっているため、中身は確定しています。プラグインの紹介を中心とした豪華な内容となるようにしました。 紹介しているプラグインは定番からマイナーまで幅広く、作者自ら解説していることもあります。 まだ発売まで期間があるため、詳しい目次を挙げることはできませんが、以下のような内容が含まれています。

vimfiler

vimfilerの使い方や、キーマッピング、マニアックなところではvimfilerを用いた拡張リネーム機能について解説しています。

netrw

netrwによる遠隔地ファイル編集、netrwによるブックマーク、netrwによるファイル操作が解説されています。

quickrun

quickrunの使い方、各種オプション、設定方法について解説されています。

ref.vim

ref.vimの使い方、各種sourceについて解説されています。

vimshell(インタプリタ通信機能を含む)

vimshellの使い方、よく使われる内部コマンド、vimshellのインタプリタ通信機能を解説しています。 ちなみに、Emacsテクニックバイブルでは、eshellについて触れられていません。

Conque

Vimで端末を実現するプラグインであるConqueの使い方、vimshellとの違いについて解説しています。

skk.vim

SKKについて解説しています。eskk.vimも少しだけ書いてあります。それほどページはありません。

neocomplcache

neocomplcacheの使い方、設定方法、neocomplcacheのスニペット機能、スニペットの書き方、 neocomplcacheのsourceの作り方と、大変豪華な内容となっています。 以前Vim Hacksに書いた内容の完全版と言っても良いでしょう。

zencoding.vim

zencoding.vimの基本から、任意の言語への応用例まで挙げられています。

QFixHowm

QFixHowmの使い方、メモの作成方法、メモの検索、ToDo管理について解説されています。

QFixGrep

Windows環境のgrepの注意点についても解説されています。

JPFormat.vim

JPFormatによる日本語の整形方法について解説されています。

autodate.vim

webapi-vim

VimからWebサービスを扱う方法について解説されています。

project.vim

project.vimによるプロジェクト管理やproject.vimの設定方法について解説されています。

Vim scriptが分からない初心者でも安心!

Vimの設定や、Vim pluginについての解説を中心とした書籍となっていますが、 ある程度のVim scriptの知識は必要不可欠です。 そこで、次のような項目について解説しています。 これ以上の詳しい解説については、Vimのヘルプを参照してください。

:helpの読み方

Vimの:helpをすぐに引けるかどうかは、初心者と初級者を分ける大変重要なファクターとなっています。 この本では、Vimの:helpの使い方について解説しています。

Vim script基礎文法最速マスター(改訂版)

これは、thincaさんが以前ブログで書いていた「Vim script基礎文法最速マスター」の改訂版です。 Vim scriptについて一通り学ぶことができます。

マッピングについて

Vimのキーマッピングは大変ややこしいですが、詳しく解説しています。

オプションについて

Vimのオプションには落とし穴が多数あります。Vimのオプションにおける仕様について解説しています。

Vimプラグインの管理

Vimのプラグインをインストールするなら、パッケージマネージャは必要不可欠です。 定番のVundle, pathogenから、vimballに至るまで解説をしています。

テキストオブジェクト

最近のVimを語るにはなくてはならない機能です。 テキストオブジェクトの拡張についても触れられています。

QuickFix

grepやmakeには欠かせない機能です。

:autocmd

外部インタフェース

ちょっとレベルが高いですが、Perl, Pythonの外部インタフェースについて解説されています。

最近のVimといえば、unite.vimは外せないよね!

当然unite.vimについては、1つのChapterを贅沢に使用して解説しています。 Emacsテクニックバイブルはanything.elについて2つのChapterを使っているので、そちらよりは若干量が減ります。 それでも、30ページくらいはあるため、十分満足できると思います。 unite.vimの章では、次の内容を解説しています。 これを読めばあなたもunite.vimマスターに!

vimfiler, vimshellなどといった他のプラグインとの連携

unite.vimの有用なsource

カスタマイズ方法についての解説

unite.vimの各種オプションについて、解説しています。 マニアックなところでは、buffer-name-optionについての解説もあります。

source, kind, action, filterの解説

特にsourceを自作する場合、source, kind, action, filterの概念は重要です。それぞれの仕様について詳しく解説しています。 作者が自ら書いているため、情報が正確で安心です。

sourceの作り方まで載っている!

サンプルsourceの作り方が書いてあります。マニアックなところでは、非同期sourceの仕様についても書いています。

unite.vim Ver.2.2対応(一応)

一応最新版のunite.vimの仕様に準拠しています。残念ながら、あまりにも新しい機能は解説できていません。 不変的な情報に絞って解説しています。 現在開発中のunite.vim Ver.3.0について少しだけ触れています。

もちろん、Emacsの人達にもオススメ

EmacsテクニックバイブルがVimmerにも有用な本であったように、 当然VimテクニックバイブルはEmacsを使っている方々にもオススメです。 Emacsを使っている人も、異文化交流は重要です。 同じ環境で満足していたら、何も生まれません。 auto-complete.elもVimの補完機能を参考にしています。 つまり、Emacs使いであってもVimや他のエディタからも刺激を受けるべきなのです。 この本を読めば、最近のVimの機能やプラグインについて分かります。 例えば、unite.vimとanything.elの違いが分かります。 新人にVimを教えざるをえないときにも使えます。 さらに、一身上の理由により、Vimに乗り換えざるをえないときもVimテクニックバイブルの知識があれば安心ですね。

現在確定している情報

ページ数は384Pで、Emacsテクニックバイブルと同じです。 値段は¥2, 980+税です。基本的に技術書というのは、¥3000を越えると購入までのハードルが上がってしまいます。 ページをできるだけ減らしたこともあり、税抜きでなんとか¥3000は切ったのですが、若干Emacsテクニックバイブルより高くなってしまいました。 発売日は9月中旬予定ですが、詳しい日付けの確定はしていません。 目次については、八月中に確定する予定なので、九月には詳しい情報が出せるでしょう。 まだ発売まで時間があるので、しばらくお待ちください。

残念ながら載せられなかった内容

できるだけ、著名なプラグインは載せるように心がけましたが、 ページ数の都合やその他様々な理由により載せられなかったプラグインがあります。 ここではそれらについて紹介します。

VCSとの連携

VCSと連携できるプラグインとしては、git-vimやfugitive.vimがありますが、決定版と呼べるようなものがなかったためです。 vcs.vimが候補だったんですが、未完成でした。

shadow.vim

時間とページ数の都合が付きませんでした。

snipMate

neocomplcacheのスニペット機能で十分だったためです。解説する人もいませんでした。 ちなみに、snippetsEmuはページ数が足りませんでした。

echodoc

入れてもよかったんですが、他のTipsが削られすぎて入れる暇がありませんでした。

netrwについての詳しい解説

時間とページ数の都合がつきませんでした。 ただし、一部のTipsでnetrwの機能について触れられています。

metarw

使っている人があまりに少ないため削られました。metarwがやっていることは、vimfiler+unite.vimでできるようになる予定です。

vimprocの使い方について

書籍では簡単にvimprocについて触れていますが、さすがに詳しい解説はできませんでした。 想定読者にとっては難しすぎるからです。vimprocをバリバリ使えるような層では、Vim scriptから使い方を解読するのも簡単だと思います。

vital.vim

このプラグインは、プラグイン制作者のためのものです。この本の対象読者にはレベルが高すぎました。 それに、ページ数の余裕もありませんでした。

内容の陳腐化への懸念について

書籍は生物です。コンピュータに関する書籍は出たばかりのときに購入しておかないと、役に立つ前に時代遅れとなってしまうでしょう。 だからこの本が必要なら、発売されたらすぐに購入するのが適切です。特に、一度絶版になってしまったら取り返しがつきません。 本の売り上げが上がると、出版社に対するVimへの印象が良くなり、続編などが企画されるかもしれません:-)

特に私のプラグインは進化が早いため、本が出たらすぐに時代遅れとならないか心配してくれている人がいるようです。 はい。それは正しい指摘です。 ただ、私もそのことは分かっていました。だからわざと新しすぎる機能や、あまり使われない機能については本の中では解説せず、 「適時:helpを参照してください」ということになっています。できるだけ不変であろうことについて解説するようにしています。 それでも現在の実装と齟齬がでてきてしまった場合は、正誤表にて何かしらのフォローをする予定です。

「:helpに全部書いてあるから書籍は必要ない」という意見について

これはある意味事実です。だからこそ、本の対象とする読者は「Vimの基本的な操作や設定はできるが、:helpを自在に引くことはできない層」なのです。 :helpはリファレンスマニュアル的であり、分からない語句を検索するときに使います。 初心者が通読するには向いていません。 本というのは初心者が参照しやすいように、重要な情報をふるい分けることに価値があると考えています。

ただし、私が自分の記事の中で気をつけたのは、できるだけ「設計思想や使い方について解説をする」ことです。 これは:helpに書いていないことが多いうえに、内容が陳腐化することも少ないと考えました。 だから、ヘルプを自由自在に参照することができる、レベルが高いVim使いにとっても役立つ書籍だとは思います。 これが本当かどうかは、自分の目で本を見て確認してください。

なぜ「Vimテクニックバイブル」はVim scriptを学ぶ本ではないのか

それは需要がないからです。考えてみてください。あなたの周囲にVim scriptをバリバリ書けるような人はいますか? Vimが好きだから、Vim scriptを本格的に学びたいと考えている人はいますか? まずいないでしょう。「Vimテクニックバイブル」の内容ですら出版社からはリスクがあると考えられている現状において、 日本でVim scriptを学ぶための書籍が出る確率は0です。おそらく売れません。 「Vimテクニックバイブル」にはVim scriptの解説がある程度書いてあります。 しかし、一流のVim scripterになるための道はまだまだ険しいのです。

それでもVim script本の構想を諦めきれない場合は、まず「Vimテクニックバイブル」を購入し、その本に書いてあることを全てマスターしてください。 さらに「Vimテクニックバイブル」を周囲に宣伝し、全国のVimmerのVimレベルを上昇させてください。 その暁には、Vim script本の出版リスクは低減され、いつの日か発売されるかもしれません。

オマケ

今後、vim-users.jpで「Vimテクニックバイブル」に関する何かが掲載されるかもしれません。こうご期待! ちなみに、Vimテクニックバイブルを購入してもらった場合、勉強会でShougoのサインが貰えるかもしれませんよ!

Shougo

Hack #218: Vimでsshに接続する

問題

最も普及しているシェルvimshellを用いて、ローカルなマシン上で快適な環境を堪能していることと思います。neocomplcacheなどのもつファイル名補完などと見事な連携を遂げています。またvimshellの内部コマンドvimは、vimshellを動作させているVim上の別バッファにファイルを開くというつくりになっておりますが、これがまた見事に、昔懐かしのzshやbashなどと似た動作をすることで、非常に容易に移行できたことに感動した方は多いでしょう。

しかしこれらはsshを介することで本来の実力を活かすことができず、残念な思いをすることになります。sshを使うためにはvimshellのiexeコマンドを用いることになります。あとはもうvimshellはiexeでssh経由先のサーバのシェルであるbashやzshを叩くだけの存在になってしまいます。そして接続先で誤ってvimコマンドなどを発行してしまうと・・・あとはもう悲惨の一言です。

解決

vimshell-sshという、今日完成したばかりのプラギンを用います。

このプラギンはコマンドも関数も提供せず、ただ、あなたがvimshellのiexeでsshコマンドを用いたときの、接続先でのvimコマンドの挙動のみを上書きします。

  • 従来のiexe ssh上でのvimコマンド:
    • 接続先サーバでvimのプロセスを立ち上げ、その出力をvimshell上で表示しようとする。
  • 上書きされたiexe ssh上でのvimコマンド:
    • コマンド発行時点では接続先サーバでは何もおこらない
    • こっそりとscpでいま接続しているサーバに新規接続、引数のファイルを取得する
    • ファイルをダウンロードできればそれを、なければ空のものを、別バッファに分割して表示する
    • そのバッファで保存を行うと、こっそりとscpで中身をアップロードする

いかがでしょうか。また、隠れたメリットとして、

  • 接続先サーバにvimがインストールされている必要がない
  • そのため~/.vimrcのコピーは不要。プラギンのインストールも不要。
  • vim自体が手元で動作するため、補完などで画面描画書き換えが頻繁に発生するものの、通信速度に関係なく快適な操作が可能

ということが挙げられます。

今後の課題

vimshellのiexe ssh上で、neocomplcacheのファイル名補完が行われません。これは将来誰かが開発するであろうneocomplcache-vimshell-sshによって解決されるでしょう。また、vimshell-sshへのパッチという形で解決するかもしれません。

vimとsshの連携の今後に期待されます。

ujihisa

Hack #213: ダブルクリックで単語検索するようにする

きっかけ

Vimユーザの方は一日を端末で過ごしている方も多いでしょう。 しかしマウスとはとても便利なものです。 トラックボールを使ったりすると新しい発見があるかもしれません。

キーボードで操作している時はキーボードで操作でき、 マウスで操作している時はマウスで最低限の操作はできるというのが理想です。 最低限の操作とは何でしょうか。 結論から言いますと、あなたがマウスでコードを見ている時必要だと思うことは、大半が単語検索でしょう。

  • 離れた所にある同じ単語に飛ぶ
  • 変数がどこで定義されているか調べる
  • 検索目的ではなくバッファ中にあるテキストをハイライトさせたい

など単語検索でできることは実に様々です。

自分は普通にコードを書いている時でも/コマンドをよく使います。 移動の80%はこれといっていいかもしれません。 言い過ぎだとしても70割かもしれません。 それほど汎用的なコマンドなのです。

(脱線) IDEの場合

IDEだとしたら変数にマウスオーバーした時点で定義場所などの情報がポップアップで表示されるかもしれません。 ただこういう機能は「便利すぎて」ユーザが能動的に情報を探そうとした場合邪魔になることが多いと思うのです。 ちなみにマウスオーバーした時点でポップアップするようなIDEを筆者は知りません。勘で言いました。

自動補完にもこういう側面があると私は思います。 キーをタイプしたらポップアップがめまぐるしく表示され、 頭の中にあったコードがデリートされてしまうなどといったことがありました。

Twitterでチカチカしたアイコンをした人をフォローしてる人には身に覚えがあるかもしれませんが、 気分転換にTwitterを見たらTLにアイコンがチカチカした人がいてとても気分を害されたということがあります。 この対処法としてはtermtterなどの画像が表示されないTwitterクライアントを使うことです。 ちなみにTwitter社はサードパーティ製クライアントを推奨していません

ちなみに筆者は以前自動補完はあまり好きではありませんでしたが慣れました。 自動補完プラグインとしてはautocomplpopneocomplcacheというプラグインが代表的です。 自動補完はとても便利な機能なのでぜひ使いましょう。

閑話休題

無理矢理話を戻すと、マウスでの単語検索を快適にするために必要な設定を本稿では解説したいと思います。

ダブルクリックへのマッピング

ダブルクリックへのマッピングは<2-LeftMouse>を使えばできます。

nnoremap <2-LeftMouse> g*

これで終わりでしょうか?いいえ、まだです。

これだとダブルクリックをしようとした時、 シングルクリック(<LeftMouse>)の時点で<LeftMouse>本来の操作、 つまりクリックした場所にカーソルを移動、が実行されてしまうのです。 カーソルが移動するのは多いに結構なのですが、 問題は'scrolloff'の設定も効いてしまうことです。

'scrolloff'は正の値をセットするとカーソルの上部か下部が 常に'scrolloff'分の行だけ表示されるようになるという非常に便利なオプションですが、 これがマウスのシングルクリックの時に効いてしまうと

  1. シングルクリック (クリック1回目)
  2. カーソル移動
  3. ‘scrolloff’分だけ行を開ける
  4. 表示領域がずれて、マウスカーソルが指していた場所にあった単語も移動してしまう
  5. ダブルクリック (クリック2回目)
  6. 検索するつもりじゃなかった単語を検索してしまう
  7. 一瞬何が起こったのか分からない
  8. 理解し悲しみに暮れる

ということになり非常に悲しいです。 そして「9.Vimを説得する」というのがこの記事を書くに至るまでの過程でした。 ちなみに説得というのはEmacsで言うsetqとかけています。

'scrolloff'の再実装 〜yak shaving〜

これを解決するには、つまりシングルクリック(<LeftMouse>)時の移動を抑制すればいい訳です。 しかし'scrolloff'は設定しておきたい。 ならば'scrolloff'がやることをVimスクリプトで再実装して制御可能にし、一時的に無効にすればいい訳です。 以下がその'scrolloff'の実装です。 'scrolloff'オプションの代わりにg:scrolloffグローバル変数で指定するようになっています。

set scrolloff=0
let g:scrolloff = 15

" Hack for <LeftMouse> not to adjust ('scrolloff') when single-clicking.
" Implement 'scrolloff' by auto-command to control the fire.
autocmd vimrc CursorMoved * call s:reinventing_scrolloff()
let s:last_lnum = -1
function! s:reinventing_scrolloff()
    if s:last_lnum > 0 && line('.') ==# s:last_lnum
        return
    endif
    let s:last_lnum = line('.')
    let winline     = winline()
    let winheight   = winheight(0)
    let middle      = winheight / 2
    let upside      = (winheight / winline) >= 2
    " If upside is true, add winlines to above the cursor.
    " If upside is false, add winlines to under the cursor.
    if upside
        let up_num = g:scrolloff - winline + 1
        let up_num = winline + up_num > middle ? middle - winline : up_num
        if up_num > 0
            execute 'normal!' up_num."\<C-y>"
        endif
    else
        let down_num = g:scrolloff - (winheight - winline)
        let down_num = winline - down_num < middle ? winline - middle : down_num
        if down_num > 0
            execute 'normal!' down_num."\<C-e>"
        endif
    endif
endfunction

そしてこれは:autocmdを使って実装されているので 以下のように'eventignore'オプションという Vimスクリプトを書いているような人しか知らないオプションを使って 一時的に無効にできます。

nnoremap <silent> <LeftMouse> <Esc>:set eventignore=all<CR><LeftMouse>:set eventignore=<CR>

visualstar.vim

ここまででも十分ですが、マウスで選択し、クリックで検索できたらどんなに良い事でしょうか。 visualstar.vimを使えばそれができます。

vmap <LeftMouse> <Plug>(visualstar-g*)

まとめ

ここまでのまとめとして、以下を.vimrcに貼り付ければ完了です。

set scrolloff=0
let g:scrolloff = 15

" Hack for <LeftMouse> not to adjust ('scrolloff') when single-clicking.
" Implement 'scrolloff' by auto-command to control the fire.
autocmd vimrc CursorMoved * call s:reinventing_scrolloff()
let s:last_lnum = -1
function! s:reinventing_scrolloff()
    if s:last_lnum > 0 && line('.') ==# s:last_lnum
        return
    endif
    let s:last_lnum = line('.')
    let winline     = winline()
    let winheight   = winheight(0)
    let middle      = winheight / 2
    let upside      = (winheight / winline) >= 2
    " If upside is true, add winlines to above the cursor.
    " If upside is false, add winlines to under the cursor.
    if upside
        let up_num = g:scrolloff - winline + 1
        let up_num = winline + up_num > middle ? middle - winline : up_num
        if up_num > 0
            execute 'normal!' up_num."\<C-y>"
        endif
    else
        let down_num = g:scrolloff - (winheight - winline)
        let down_num = winline - down_num < middle ? winline - middle : down_num
        if down_num > 0
            execute 'normal!' down_num."\<C-e>"
        endif
    endif
endfunction

" Do not adjust current scroll position (do not fire 'scrolloff') on single-click.
nnoremap <silent> <LeftMouse>   <Esc>:set eventignore=all<CR><LeftMouse>:set eventignore=<CR>
" Double-click for searching the word under the cursor.
nnoremap          <2-LeftMouse> g*
" Single-click for searching the word selected in visual-mode.
vmap              <LeftMouse> <Plug>(visualstar-g*)
tyru

Hack #192: 任意の言語で任意のマクロを使う

新年あけましておめでとうございます。いまこの記事はVancouver時間で30日木曜の夜5:54pm、もう外は真っ暗でございます。寒波到来で気温は-1Cまで下がり、凍えるような日々を過ごしております。といっても札幌より暖かいです。明日からは-4Cまで下がるそうです。

問題

さて皆様は日頃どのような言語を使っているのでしょうか。みんな大好きVim script、ブラウザ制御やサーバサイドの処理の記述にJavaScript、JVMを操作するためのJava、Hyper Textを記述するためのプリプロセッサであるPHP、そして究極にして高貴な言語Haskell、いろいろな言語を使っているものと思います。

いくつかの言語には、いつくかのレベルでのマクロの概念のサポートがあります。 有名な言語の中でもっとも強力なマクロを持っているものはCommon Lispであるという意見にほとんど異論はなさそうです。マクロの強力さを順に並べる事ができるとすれば、Schemeなど他のLisp系言語が、C言語のマクロとの間に位置するでしょう。一方Rubyをはじめとするスクリプト言語の多くはマクロを持っていません。言語自体が柔軟なのでマクロは不必要にものごとを複雑にするだろうとの判断でしょう。またJavaは柔軟でない言語にかかわらずマクロを持っていません。Annotationや、開発環境からの自動入力で対処せよとのことでしょう。

マクロは劇薬のようなものです。ちょっとした使用で、コードの冗長性を一気に排除でき、シンプルでエレガントで管理しやすいコードを生み出すことができることもあれば、作者でさえ二日後には読解不可能な難解なコードを生み出し、結局それがバグの温床になることもあります。これはとりわけ複数人でのコラボレーションで問題をより深刻にするでしょう。

複数人でソフトウェアを開発する際、あなたはマクロを多用したいものの、他の人にも使うことを強要したくないというシーンは多々あります。この場合、マクロを使用するなんらかのコードを記述した上で、MakefileなりRakefileなりbuild.xmlなりでマクロを適用し、それで生成されたコードをプロジェクトのレポジトリにコミットするという方法が実用的です。つまり、あなたが記述したコードそれ自体はあなた個人が管理し、そのコードを元に自動生成された別のコードのみをプロジェクトで共有するのです。

しかしながら、この方法は大変面倒です。

  • いちいちビルドしなければならない、あるいはビルドツールを記述しなければならない
  • 自分が記述したコードをコミットしないように気をつけなければならない
  • 自分が改変したビルドツールをコミットしないように気をつけなければならない
  • ちょっとしたマクロの適用でもいちいちビルドツールを修正しなければならない
  • コミット前のdiffの確認が行いにくい。コミットするのはいま編集しているバッファではなく、別で管理されているファイルであり、git-vimなどのツールとの連携がうまくいかない

解決

shadow.vimを使用します。

shadow.vimは著者により開発が進められているVimのプラギンです。これは任意の言語で任意の事前処理を行なうための薄いフレームワークです。コマンドや関数は提供されず、以下のようなファイル読み込み時と保存時のフックのみを提供します。

具体例で示しましょう。あなたはいまa.jsというファイル名のJavaScriptのコードを作成し、コミットしなければならない立場にいます。しかしあなたはJavaScriptなどという冗長な言語を記述する気はさらさらなく、CoffeeScriptで記述する気持ちしかありません(*1)。

まず、a.js.shdというファイル名のファイルを作成します。Vimで開き、一行目に以下のようなおまじないを書きましょう(*2)。

## coffee -csb

あとは以下にCoffeeScriptでコードを記述しましょう。適当に記述したところで、一旦そのファイルを完全に閉じ、a.jsという名前の空ファイルを作成してからそのファイルを開きます。

:!touch a.js
:e a.js

するとそこには先ほど編集したばかりのa.js.shdファイルの中身が展開されています。しかし実際にa.jsファイルには、coffee -csbコマンドの適用結果であるJavaScriptのコードが記述されています。これは以下のコマンドなどで確認できます(*3)。

:!cat %

補足ながら、shadow.vimとは直接関係ないことがらですが、適切なmodelineを与えることによって、.shdという拡張子でも適切なfiletypeを与えることができます。

筆者の使い方

筆者はshadow.vimを至る所で使用しています。先ほど例として挙げたCoffeeScriptを用いてのJavaScriptの記述以外に、Cのマクロを使用したVim scriptやPHPの記述、またRubyで独自に開発した独自言語処理系の適用、などなど。

shadow.vimはまさしくlife changingなフレームワークです。筆者のソフトウェア開発体験はこのプラギンによって大きく変わりました。非常に短い実装ながら、これはソフトウェア開発者個人に大いなる自由を与える哲学的かつ革新的なツールといえます。

参考文献

  • (*1) Hack #164: JavaScript開発環境 その2 CoffeeScriptを使う http://vim-users.jp/2010/07/hack164/
  • (*2) もしお使いのCoffeeScriptのバージョンが古ければ、-csbではなく-cs --nowrapというオプションを指定してください。
  • (*3) 著者はこれをQuickRunで代用しています。詳しくは以下の記事を参照ください。

追記

投稿するのがだいぶ遅くなってしまいました…。

ujihisa

Hack #190: テキストを折り畳む – 表示編

fold 機能によって折り畳まれた部分の表示は、カスタマイズすることができます。

'foldtext' オプション

折り畳まれた行に表示されるテキストを、'foldtext' でカスタマイズすることができます。これは 'foldexpr' のように、表示するテキストを生成する式を指定します。式の中では以下の変数を参照できます。

変数 意味
v:foldstart 折り畳みの先頭の行番号。
v:foldend 折り畳みの末尾の行番号。
v:foldlevel 折り畳みレベル。
v:folddashes repeat(“-”, v:foldlevel) と同じもの。

例えば、Hack #186: テキストを折り畳む – 自作編 で紹介した help の折り畳みは、”=” の連続した行が先頭になるため、折り畳むと “=” だけが表示されてしまい中身がよくわかりません。 以下のように設定することで次の行が表示されるようになり、少しわかりやすくなります。

setlocal foldtext=getline(v:foldstart+1)

もちろん、関数を作成してもっと凝った設定を行うことで折り畳みレベルや折り畳まれた行数などの情報を表示することも可能です。

thinca

Hack #186: テキストを折り畳む – 自作編

'foldmethod' に “expr” を指定すると、折り畳みの定義を式で指定することができます。

設定方法

'foldmethod' が “expr” の場合、'foldexpr' オプションに折り畳みレベルを計算するための式を設定します。 この式の中では v:lnum に行番号が格納されているので、その行の折り畳みレベルを計算します。

大抵の場合、関数を定義してその関数を呼び出すような式を設定します。

setlocal foldexpr=MyFold(v:lnum)

式の返す値

式は、その行の折り畳みレベルを以下のうちの1つの形式で返す必要があります。

意味
数字 0, 1, 2 折り畳みレベルです。0 の場合、その行は折り畳みに含まれません。
-1 -1 直前か直後の行のうち低い方の行のレベルを使います。
“=” “=” 直前の行と同じレベルを使います。
“a” + 数字 / “s” + 数字 “a1″, “a2″, “s1″, “s2″ 直前の行のレベルからの相対値でレベルを指定します。a が + で s が – です。
“<” + 数字 “<1″, “<2″ 指定したレベルの折り畳みを終了します。
“>” + 数字 “>1″, “>2″ 指定したレベルの折り畳みを開始します。

式の評価中に副作用があってはいけません。また、この式の評価はバッファ内の各行に対して行われるので、なるべく軽い処理で済ませる必要があります。

具体例

ここでは簡単な具体例を紹介します。

setlocal foldmethod=expr foldexpr=MyHelpFold(v:lnum)

function! MyHelpFold(lnum)
  let line = getline(a:lnum)
  let next = getline(a:lnum + 1)
  if line =~ '^=\{78}$'
    return 1
  elseif next =~ '^=\{78}$'
    return '<1'
  elseif line =~ '^-\{78}$'
    return 2
  elseif next =~ '^-\{78}$'
    return '<2'
  endif
  return '='
endfunction

これは Vim の help を折り畳む設定の例です。

“=” が 78 個並ぶ行を目印として、その行以降をレベル 1、同じように “-” が 78 個並ぶ行以降をレベル 2 として定義しています。 また、同じレベルが連続した場合に別々の折り畳みになるように、次の行が新しい目印の行だった場合はその行で折り畳みを終了するようにしています。

thinca

Hack #173: Vimでタイマー機能を模倣する

VimにはEmacsとは違ってタイマー機能がありませんが、タイマーが欲しくなることがままあります。ここではタイマー機能について議論を行い、Vimで模倣する方法について述べます。

タイマー機能が必要なとき

まず、どのようなときにタイマー機能が必要なのでしょうか。例えば処理のポーリングです。Vimと外部プロセスが通信をした場合、Vimがプロセスの終了を待っていると、その間はVimの動作がストップしてしまいます。これでは他の作業ができなくなるので、大きなストレスとなります。そして残念ながらVimでは割り込みが使えません(#1)。そこでタイマーを使って時々通信をしながら処理の完了を待つという方法を通常取ります。

1: vim-serverという機能を使えばできないこともありませんが、ここでは触れません。

Emacsのタイマー機能

VimのライバルであるEmacsにはきちんとタイマー機能があります。これは外部プロセスとの通信のために実装されたと考えられます。巨大なデスクトップ環境であるEmacsでは、インタプリタとの通信を実現するためにタイマー機能が必要不可欠だからです。ただし、タイマーのチェックはアイドル時間にのみ動作します。これは考えてみればあたりまえの話です。もし、処理の途中に割り込まれるとすると、プログラムの状態がどうなるか予想ができないからです。Emacsのタイマーでは一応呼び出す時間を設定できますが、アイドル時にしか呼びだせないので、正確な時間で処理が行われるとは限りません。

タイマー機能の模倣

タイマー機能の模倣はInsert modeとNormal modeで微妙に方法が異なります。 基本的にはCursorHold, CursorHoldIのautocmdに関数の呼び出しのイベントを設定します。 CursorHoldI, CursorHoldは新たにキーが押されない限り連続して発生することがないので、処理の後にダミーのfeedkeys()を呼び出し、関数が連続して呼ばれるようにします。 任意の時間でイベントを発生させるためには、’updatetime’を保存・復元する必要があります。

autocmd CursorHold  call s:foo()

function s:foo()
  "  何か処理をする

  call feedkeys("g\", 'n')
endfunction

autocmd CursorHoldI  call s:bar()

function s:bar()
  "  何か処理をする

  call feedkeys("\\", 'n')
endfunction

疑似タイマーの欠点

残念ながら、疑似タイマーでは次の問題があります。

コマンドごとにタイムアウトを指定できない

‘updatetime’は一つしか設定できないため、複数のプラグインが同時に書きかえようとすると干渉します。 しかもグローバルなオプションなので、値の復元を忘れると影響が大きくなります。 ‘updatetime’は本来スワップファイルの書き出しのための時間です。todoには’holdtime’を新設し、この変数を分離しようという記述がありますが、まだ実現される見こみはありません。Vim 7.3でも実装されていません。

CursorHoldIはポップアップメニューが出てくると動作しない

これは補完処理にタイマーを使おうとした場合に問題となります。CursorMovedIはポップアップメニューの候補がなくなったらイベントが発生するのでまだマシですが、CursorHoldIはそうではありません。よって、neocomplcacheではCursorHoldIとCursorMovedIを無理矢理組み合わせています。

CursorHoldIを連続して発生させるのが難しい

call feedkeys("\<C-r>", 'n')ではキー操作が発生するので副作用があります。feedkeys()を呼んですぐにキーが押される訳ではないので、タイミングによっては動作しないこともあるようです。せめて、call feedkeys("\<NOP>")が使えるといいのですが、これではCursorHoldIは発生しません。

マルチスレッドを使っても問題は解決しない

Vimは外部インタフェースを用いることで、マルチスレッド化したプラグインを書くことができます。Pythonの外部インタフェースの仕様については、Hack #132: Pythonインタフェースを使う(1)を参照してください。しかしマルチスレッドを用いたとしても、マルチスレッド側でVimのバッファをいじると高確率でクラッシュします。Vimはマルチスレッドセーフに作られていないからです。よって通信スレッド側では処理をキューに格納し、メインスレッド側でバッファをいじることになります。よってタイマー機能は必須です。

理想的なタイマー機能

理想的なタイマーとは次のようなものです。

autocmd CursorHoldI <timer=1000> call foo() <timer=xxx>というのがイベントの実行時間を表します。単位はms。CursorHoldI, CursorHoldでのみ有効なオプションです。 省略されると今までと同様にholdtimeかupdatetimeを使います。当然、ポップアップメニューが消えても動作します。 Real Vim Hacksプロジェクトでどうにか実現したいものです。

Shougo

Home > Tags > lv3

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

    Return to page top