Home

Vim-users.jp

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 #217: 横幅が長いコードをハイライトする

表示幅に入りきらないコードは読む人に必要以上にストレスを与えます。
プログラミングの70割は読む時間だといいます。
読む人にストレスを与えないよう、
横幅が長いコードを書かないようにするにはどうすればいいでしょうか?

解決

Vim 7.3 *1 では 以下を.vimrcに入れると80文字以上長い列をハイライトしてくれます。

set textwidth=80
if exists('&colorcolumn')
    set colorcolumn=+1
endif

どのファイルに対してもハイライトされてしまうため多少うっとうしいです。
この問題は.vimrcで'textwidth'を0に指定しておき、
コードを書く時のみ:setlocal textwidth=80とすることで回避できます。

set textwidth=0
if exists('&colorcolumn')
    set colorcolumn=+1
    " sh,cpp,perl,vim,...の部分は自分が使う
    " プログラミング言語のfiletypeに合わせてください
    autocmd FileType sh,cpp,perl,vim,ruby,python,haskell,scheme setlocal textwidth=80
endif
tyru

ujihisa.vim が開催されました

去る2011年5月14日に、株式会社タイムインターメディアにて ujihisa.vim が開催されました。

ujihisa.vim の様子

以下、参加者のレポートです。

当日は高まるVim熱で会場も異様な熱気に包まれていました。

主催してくださったujihisaさん、会場を提供してくださった株式会社タイムインターメディアさん、参加者の皆さん、ありがとうございました。

次回予告

ujihisa.vim#2 は全く予定されておりません。どなたでもご自由に主催できますので、この記事を読んでいるあなた、ぜひ開催してみてはいかがでしょうか。

なお、開催するにあたって以下の記事がとても参考になると思います。

http://vim-users.jp/2009/07/hack37/

新しいremoteプロトコルがやってくる

こんにちわ。たまにはこっちで書いてもいいかなと、ブラっと立ち寄ったmattnです。

vimには複数のインスタンス間でコマンドや式がやりとりできるremote機能があります。


# vim --servername GVIM --remote-send file.txt

こんな感じに別のvimにファイルを開かせたり


# vim --remote-expr 1+2
3

難しい計算をやってもらったり出来ます。またvimscriptからも扱えます。Windowsではウィンドウメッセージを使ってプロセス間通信が行われており、gvim.exeでもvim.exeでもやりとりが出来る様になっています。ただしLinuxではX11を使ったイベント通信が使われており、X11を入れていない、またはX11が起動していない場合にはこの素晴らしい機能を使う事が出来ませんでした。

またvimはperlやruby、pythonといったスクリプト言語を補助言語として扱う事が出来ますが、これらの言語で処理をバックグラウンドで起動させておきながら編集中にイベントを発生させるといった事が簡単に出来ませんでした。例えば、vimからLingrのchatが出来るlingr.vimがありますが、あれはバックエンドとしてpythonを使っており、発言があった場合にvimのインタフェースを呼び出しています。しかしながらvimはスレッドセーフではありません。インタフェースを呼び出した瞬間にvimが処理中であった場合には問答無用でクラッシュしてしまいます。これはvimにキーを送りつけるfeedkeys()を使っても同じです。

これまでvimhacker達はこれを回避する為に、カーソルが一定時間停止した際に発生するCursorHoldイベントを駆使してそれっぽいvimscriptを書いて来ましたが、このCursorHoldはキーが連続で押下された場合には発生しませんし、イベントが発生するまでの時間を設定出来るupdatetimeオプションはグローバルオプションである為、異なるvimscript同士が干渉しあう事もありえます。

現在、開発フォーラムであるvim_devではこれの代替とするsetInterval()の提案も行われていますが、かなり望み薄です。

vimにバックグラウンドから通知を行ったり、背後でflymakeを行ったりするvimscriptがこれまで無かったのはこれが原因です。


ところが、この問題を解決してくれるであろうプロジェクトがひっそりと始まりました。古参ユーザなら知ってる人もいるであろうYukihiro Nakadairaさんです。

これまでunixがX11で通信していた部分をunix domainソケットで、windowsがウィンドウメッセージで通信していた部分を名前付きパイプで実装を書き直しています。これにより、Xが起動していなくても、またWindowsではtelnetでログインしていたとしてもバックグラウンドから安全にメッセージを受信出きるようになるのです。

現在まだテスト中ですが、基本的な動作は既に実装済みでVIM1からVIM2へ


:echo remote_expr("VIM2", 'remote_expr("VIM1", "1+2")')

といったオウム返しも実行出来ます。すばらしいですね。

現在、おそらくテスターは僕と中平さんだけだと思いますが、皆さんがテストに参加して頂ければこのあと開発フォーラムへのプッシュも楽になると思っています。

ぜひテストに参加してみて下さい。


clientserver without X

プレゼンの公募 — 東京でVimの勉強会が開催されます

東京都で開催されるujihisa.vimは、ライトニングトークや通常の発表の発表者を募集中です。

この勉強会の一般参加はすでに定員に達してしまったため、新規申し込みを受け付けておりません。しかし、発表者としてならば参加することが可能です。

Twitter上で、代表の @ujm さんに声をかけるか、あるいは lingrのVim部屋で発言していただくなどしてこちらに伝えてください。

高まるVim熱

ujihisa.vimの公式ハッシュタグは #ujihisa です。

Hack #216: 消してしまった直前のメッセージを再表示する

Vim でプラグインなどが下部に表示されたメッセージを読まずに消してしまった場合はどうすればいいでしょう。:echomsg で表示されていた場合は履歴に記録されているので、:messages を実行すれば表示できます。しかし、常に :echomsg が使われているとは限りません。

g<

g< を使うことで、最後に表示されたメッセージを再表示させることができます。ただし、本当に最後に表示されたものしか表示できないので注意してください。例えば、:messages を1度実行してしまうと、g< を押しても :messages の結果が表示されてしまいます。

また、メッセージを途中で q で閉じていた場合は、g< を使っても表示されたところまでしか表示されません。

thinca

プレゼンの公募 — 関西でVim勉強会#6が開催されます

兵庫県尼崎市で開催されるVim勉強会#6は、ライトニングトークの発表者を募集中です。

Twitter上で、代表の @ujm さんに声をかけるか、あるいは lingrのVim部屋で発言していただくなどしてこちらに伝えてください。

高まるVim熱

Vim勉強会#6の公式ハッシュタグは #vim6 です。

Hack #215: Vundle で plugin をモダンに管理する

みなさんは増え続ける plugin のどのように管理されていますか。 pathogen+submodule を使う方法でしょうか。それとも未来に生き VimJolt を使用されているのでしょうか。

今回はモダンな plugin 管理方法として、Vundle を使った方法をご紹介します。

Vundle は Ruby on Rails 3 で採用されている、Gem 管理システム Bundler に影響を受けた、plugin 管理システムです。

開発は Github 上で行われています。 https://github.com/gmarik/vundle

導入

導入は以下の様に ~/.vim にリポジトリを clone すると完了します。

$ git clone http://github.com/gmarik/vundle.git ~/.vim/vundle.git

ドットファイルを git で管理されている方は、submodule として取り込んで下さい。

plugin の管理

plugin の管理は以下の様に .vimrc に Bundle ‘plugin name’ と記述することで行います。

set nocompatible
filetype off                   " (1)

set rtp+=~/.vim/vundle.git/    " (2)
call vundle#rc()               " (3)

" original repos on github
Bundle 'tpope/vim-fugitive'

" vim-scripts repos
Bundle 'rails.vim'

" non github repos
Bundle 'git://git.wincent.com/command-t.git'

filetype plugin indent on     " (5)

特徴的なのは (1) で filetype を一度 off にしている点です。 Vundle の処理が終り次第、(5) で ftplugin と indent を読み込むように指定しています。

(2), (3) で Vundle を初期化し、Bundle ‘plugin name’ を書くことで、plugin を読み込む準備が完了します。

Bundle コマンド

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

Bundle 'user_name/repository_name'

の様に、Github のユーザ名とリポジトリ名を指定します。

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

Bundle 'script_name'

の様に、plugin の名前を指定します。

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

Bundle 'git://repository_url'

の様に、Git リポジトリ のフルパスを指定します。

plugin の管理

plugin のインストールには、:BundleInstall コマンドを使用します。 先程、.vimrc に Bundle ‘name’ と記述した plugin が自動的に取得され、インストールまで行われます。

:BundleInstall

また、既にインストールされている plugin をアップデートするには、 :BundleInstall! コマンドを利用します。

:BundleInstall!

plugin を検索して、インタラクティブに管理することも出来ます。

:Bundles unite

上記のコマンドを実行すると、検索結果が split されて表示されます。リストから目的の plugin を選択し、インストール、削除などが行えます。

ドキュメント

以下のようにしてヘルプを生成します。

:helptags ~/.vim/vundle.git/doc

生成したヘルプは通常通り以下のように引くことが出来ます

:help vundle

参照

Sixeight

Hack #214: 括弧までを消したり置き換えたりする

問題

プログラミングを行うということは、それすなわち括弧と友達になるということです。

現在のカーソル位置から次の閉じ括弧までの文字を一気に置き換えたい、というシーンは多々あると思います。

let l:lines = getbufline(buf■r(a:filename), 1, '$')

例えば上記のようなコードを記述しており、カーソル位置は■とし、この位置からa:filenameの直後の閉じ括弧までをまとめて削除し、bufnrのかわりにbufloadedと記述したいというケースを考えてみましょう。直後に開き括弧が存在し、これらは1トークンではないため、weなどをそのまま使うことはできません。文字数を正確に数えることも困難です。おそらくt)feの二択となるでしょう。ただし後者を使う場合、正確にeがそれまで存在しないことを確認する必要があるため、より非存在性を確認しやすい括弧を用いて指定することになると思います。

こういった操作をする機会は、ほとんど意識されないものの、かなり多いことが最近の研究で明らかになっています。(*1)

解決

文単位モーションの()の使用をやめ、これにt(などを割り当てましょう。~/.vimrcに以下を記述します。

onoremap ) t)
onoremap ( t(
vnoremap ) t)
vnoremap ( t(

あとはt)などを使用するかわりにたんに)などと打鍵します。上記設定のおかげで、あなたのソフトウェア開発にかかる時間の50%が短縮できます。

参考

  • (*1) 要出典
  • :h )
  • :h t
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

Home

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

    Return to page top