Home > Vim Hacks

Vim Hacks Archive

Hack #149: コーディングスタイルを切り替える

プログラミングでは様々なコーディングスタイルがあり、Vimにはそれに沿った編集をするための様々なオプションがあります。
他人のソースコードを編集する時はそれらのオプションを切り替えられると便利です。

解決

このようなコマンド:CodingStyleを定義します。

let s:coding_styles = {}
let s:coding_styles['My style']      = 'set expandtab   tabstop=4 shiftwidth=4 softtabstop&'
let s:coding_styles['Short indent']  = 'set expandtab   tabstop=2 shiftwidth=2 softtabstop&'
let s:coding_styles['GNU']           = 'set expandtab   tabstop=8 shiftwidth=2 softtabstop=2'
let s:coding_styles['BSD']           = 'set noexpandtab tabstop=8 shiftwidth=4 softtabstop&'    " XXX
let s:coding_styles['Linux']         = 'set noexpandtab tabstop=8 shiftwidth=8 softtabstop&'

command!
\   -bar -nargs=1 -complete=customlist,s:coding_style_complete
\   CodingStyle
\   execute get(s:coding_styles, <f-args>, '')

function! s:coding_style_complete(...) "{{{
    return keys(s:coding_styles)
endfunction "}}}

するとこのようにコーディングスタイルを切り替えられます。(もちろん引数は補完が効きます)

CodingStyle Short indent
# または
CodingStyle My style

宣言的に記述できるので.vimrcが見やすくなります。
Hack #112: 場所ごとに設定を用意すると組み合わせて場所ごとのファイルに書くといいでしょう。

関連リンク

coding_style.vim yaifa.vim

tyru

Hack #148: Key mappingの設定を確認する

Vim を操作していると、たまにキー操作に対して意図しない挙動が発生することがあります。これは多くの場合、把握していない Key mapping が設定されているからです。 例えば、プラグインによって知らないうちに設定されていたり、自分で設定していて忘れてしまっていたり、自分で間違えて設定していた場合などです。 こういう時は Key mapping の設定を確認すると解決することが多いです。

:map コマンド

:map 系のコマンドは Key mapping を定義するコマンドですが、設定の確認にも使えます。引数なしで実行すると、コマンドに対応するモードで定義されている Key mapping の一覧が表示されます。例えば、

:imap

を実行すると Insert mode で定義されている Key mapping の一覧が表示されます。

一覧表示の見方

一覧表示は 1 行に 1 つの設定が書かれています。 最初の 3 文字は設定されているモードです。以下の文字のうち最大で 3 文字が表示されます。

文字モード
(空白)ノーマル、ビジュアル、セレクト、演算待ち状態モード
nノーマルモード
vビジュアルモード、セレクトモード
sセレクトモード
xビジュアルモード
o演算待ち状態モード
!挿入モード、コマンドラインモード
i挿入モード
lLang-Argモード
cコマンドラインモード

続いて、展開前の文字列と展開後の文字列が書かれています。展開後の文字列の前には記号が表示されている場合があり、それらは以下の意味になります。

文字意味
*再マップされません。つまり、noremap 系で定義された Key mapping です。
@バッファローカルなマップです。<buffer> 付きで定義された Key mapping です。
&スクリプトローカルなマップだけが再マップされます。<script> 付きで定義された Key mapping です。

特定のキーの設定を調べる

:map 系のコマンドに 1 つだけ引数を与えると、そのキーシーケンスで始まる Key mapping のみを表示します。

定義された場所を調べる

:verbose 付きで :map コマンドを実行すると、その設定を定義したファイルが確認できます。例えば以下のように表示されます。

:verbose map gj
   gj          * j
        Last set from ~/.vimrc

これにより、意図しない設定を行った元凶を調べることができます。

thinca

Hack #147: neocomplcache Hacks(4) シンタックス補完

四回目の今回はシンタックス補完を解説します。

シンタックス補完とは

シンタックス補完とは、標準添付プラグインのautoload/syntaxComplete.vimをさらに汎用的にし、neocomplcacheのプラグインとして独自実装したものです。 内部動作としては:syn listの結果を解析し、キーワードを補完候補に加えます。 1度目は候補をキャッシュするので、若干時間がかかりますが、その後は高速に動作します。 Vimの豊富なシンタックスキーワードを有効活用するので、辞書を別途用意する必要はなく、非常に便利です。

autoload/syntaxComplete.vimとの比較

autoload/syntaxComplete.vimはキーワード文字列しか解析できませんが、neocomplcacheのシンタックス補完は正規表現のパターンも解析するので、HaskellやTeXといったファイルタイプでもシンタックス補完が使えます。 ただし()や[]を使った複雑な正規表現では、上手くパースできないことがあります。 ご了承下さい。

Same FileType Completion

シンタックス補完はneocomplcache独自の補完機能である、Same FileType Completionに対応しています。 Same FileType CompletionとはCとC++のように、似たファイルタイプを関連付けて相互参照できるようにす機能です。 これにより、vimshellのiexeから起動したirbのバッファ上でRubyキーワードを補完できます。

使用上の注意

Action scriptやVim Scriptのautocmdを補完するとき、シンタックスの補完候補が小文字になってしまうことがあります。 これはシンタックスファイルの syn case ignore が悪さをしているせいです。プラグイン側では対処しようがないので、気になるようだったら直接シンタックスファイルを修正してください。

Shougo

Hack #146: Vimで仕事を探す、あるいはVim使いを雇う

問題

Vim使いのあなたは、これまでに鍛えてきたあなたのVimのスキルを最大限有効活用できる仕事をしたいと考えることでしょう。 世の中には二種類の仕事があります。Vimを使う仕事と、そうでない仕事です。 Vimを使わない仕事をすることは、あなたの能力を発揮できないことを意味し、このことはあなたとその会社だけでなく、社会全体の大損害といえるでしょう。

また、あなたの会社がVim使いを欲しているとして、どうやって卓越したVim使いを探せばよいのでしょうか。 www.vim.orgで公開されているpluginから、その作者に直接連絡をとるというのが一つの方法です。 しかし、その作者が現在求職中かどうかが分からないため、この方法は非効率的といえるでしょう。

解決法

上記2つの問題を同時に解決する方法があります。

http://vimjobs.heroku.com/

VimJobs というVimに特化した求人サイトを用います。ここでは誰でも無料でVim使いの求人を行なうことができ、誰でも登録されている会社に応募することができます。VimJobsはVim使いに特化した作りとなっているため、非常に効率的に仕事を探すことができるでしょう。

また、ドキュメントはないものの、VimJobs WEB APIがあるため、Vimから直接VimJobsの求人情報を閲覧できる可能性があります。

VimJobsは現在

  • 英語
  • スペイン語
  • 中国語

の3つの言語のインタフェースが存在します。このことが、さまざまな国で働く機会を得ることに繋がります。

補足

2010年5月12日現在、求人件数は1件です (うち1件はダミーデータ)。

ujihisa

Hack #144: 分かりやすく副作用のないKey-mappingsを定義する

Hack #59: 分かりやすいKey-mappingsを定義するの方法だとグローバルなマッピング空間を使ってしまうのが玉に瑕です。
そのせいで、例えば

nnoremap [tag] <Nop>
nmap     t     [tag]

" ...

" tag jump
nnoremap [t    <C-]>

としている場合に[tと押しても[tag]がまだ候補にあるために
&timeout&timeoutlenなどの値によりますが)すぐには実行されずに
キー入力を一瞬待たされるようになります。

この場合は[tを[Tに変えるなどすれば問題は解決しますが、
こうした副作用を気にかけながらマッピングを定義するのは少々面倒です。
なんとかして副作用のないマッピングを定義できないでしょうか。

解決

マッピングを<SID>で定義します。

nnoremap <SID>[tag] <Nop>
nmap     t          <SID>[tag]

” …

” 重複する候補がないためすぐに実行される
nnoremap [t    <C-]>

<SID>を先頭につけることでスクリプトローカルなマッピングを定義することができます。

発展

これを使ってさらに.vimrcを見通しをよくすることもできます。

nnoremap <SID>[yank-$] y$
nnoremap <SID>[register-+] "+
nnoremap <SID>[register-*] "*

nmap Y  <SID>[yank-$]
nmap ;Y <SID>[register-+]<SID>[yank-$]
nmap ,Y <SID>[register-*]<SID>[yank-$]

複雑なマッピングを定義する時は<SID>マッピングを使うことで
見通しがよく副作用のないマッピングを定義することができます。

tyru

Hack #143: 見た目を変えずにタブ文字とスペースを相互に変換する

プログラムのインデントには昔からスペース派とタブ派があり、どちらを使うかは個々人の好みによりますが、自分の流派と違うファイルを編集することになったとき、場合によっては自分の好みのインデント方式に変えたいこともあるでしょう。

行頭のインデントは置換で済んでしまうことも多いですが、それ以外の場所はタブの幅がまちまちで単純な置換は難しいこともあります。

:retab

:retab コマンドを使うことで、見た目を変えずにタブ文字の幅を変えたりタブ文字とスペースを相互に変換することができます。

タブ幅を変える

:retab コマンドを、新しい ‘tabstop’ の値を引数に指定して実行することで、タブ文字を含む連続した空白を新しいタブ幅で置き換えます。この時、’tabstop’ の値は指定した値に設定されます。新しいタブ幅でタブ文字で表現しきれない部分はスペースが使われます。

例: tabstop=8 (タブ文字を >——- で、インデント中のスペースを _ で表示しています)

int main(int argc, char const* argv[]) {
>--------int>---count>--= 0;
>--------char>--ch>-----= '0';
}

この状態で

:retab 3

を実行すると、

int main(int argc, char const* argv[]) {
>-->--__int>>--_count>--= 0;
>-->--__char>--_ch>-->--= '0';
}

となります。

このコマンドには範囲を与えることができますが、その場合でも 'tabstop' の値は変更されるので範囲外の見た目が変わることになります。

タブ文字をスペースに変換する

'expandtab' がオンの時にこのコマンドを使うと、結果はスペースで置き換えられます。この場合引数を与えることもできますが、'tabstop' の値を変更する以上の意味はありません。

スペースをタブ文字に変換する

:retab コマンドは「タブ文字を含む」連続した空白を置き換えるので通常スペースのみの部分は置き換えられません。しかし、! を付けた :retab! を使えばスペースのみの部分もタブ文字に置き換えてくれます。

ただし、この場合意図しない箇所、例えば文字列リテラル中の空白も置き換えられてしまうので注意が必要です。その場合は :substitute による行頭部分の置換の方が良いかもしれません。

%s@^\v(%( {4})+)@\=repeat("\t", len(submatch(1))/4)@
thinca

Hack #142: Vimでシェルを起動する

VimがEmacsと比較して劣っているのは、コマンドの非同期実行だけではなく、シェルとの連携も挙げられます。ここでは現状のシェル呼び出しの問題点とEmacsとの比較、その解決方法について議論を行います。

:shellの欠点

Vimに搭載されている:shellコマンドは、一時的にシェルを実行できます。しかし当然使用するシェルの設定に左右される上、実行している間はVimが止まってしまいます。 この状態ではコマンドの出力もバッファにとれないですし、VimScriptとの連携もできません。 さらにLinux上のGVimではエスケープシーケンスを解釈しない上、WindowsのGVimでは邪魔なDOS窓が一瞬開くという問題もあります。 根本的な問題として、WindowsのシェルであるCMD.exeは貧弱なので、使う気になれません。 かといって、Windowsの場合は代わりとなるシェルも選択肢が少ないです。

screenの欠点

:shellの代わりに、GNU screenやそれをforkしたtscreen、GNU screenの後継であるtmuxを用いるという解決策もあります。 しかしこれらのソフトウェアは端末を要求するため、当然Windows環境やGVimでは動作しません。 さらにscreen上で起動しているプログラムではVimのキーバインドを使えない、Vimとのデータのやりとりが大変、などの問題点があります。

Vim-Shellパッチについて

Vim-Shellパッチとは、Vimに外部プロセスを実行させる機能を付け加える巨大パッチです。 エスケープシーケンスもきちんと解釈するので、優れたシェル環境なのですが、 パッチは本家に取り込まれることがなく、しかもVim 7.2には対応していません。 Windows上では使えず、GVimでも使えないなど、欠点も多いです。

そのほかのプラグインについて

そのほかにもシェルを模倣するプラグインはありますが、どれも提供される機能に難があったり、 Windows環境やGVimでは使えないものばかりです。

Emacsとの比較

対するEmacsはどうでしょう。Emacsでは、標準的に搭載されているshell-modeを用いてシェルを実行できます。 起動が遅いですが、さらに高機能なeshell-modeを用いて100% Emacs Lispなシェルを使うこともできます。 eshell-modeは端末機能としては不完全なので、端末がほしいだけなら、ansi-termを用いると良いでしょう。 この優れたシェル環境を得るためだけにEmacsへと移行した人々も多いのではないかと思います。 最近では、ansi-termを改良したmulti-termというものが人気らしいです。

vimshellについて

Emacsへの対抗馬になりうるのが、私が開発しているvimshellです。 完全な端末機能を目指すのではなく、純粋な対話シェルとしてeshellを目標にしています。 自動補完プラグインであるneocomplcacheと連携させることで、自動補完のできるシェル環境になります。 vimprocと連携させれば非同期実行も可能、対話プログラムを起動できる、と開発途中の現在でもかなりの機能を誇ります。 vimshellはzshの機能を一部取り込んでいて、コマンドラインスタックや優れた履歴検索機能もあります。

Conqueとの比較

その他のライブラリとして、vimprocやvimshellをもとにNico Raffatoさんが製作しているConqueというプラグインがあります。まだVer.1.0がリリースされたばかりですが、現在も精力的に開発されています。 これは内部でPythonインタフェースを用いて外部プロセスとの通信を行っているので、安定性に難があるのと、Pythonインタフェースが必須になっています。 Emacsのansi-termを目標にしているようで、端末としてはかなりの完成度を誇ります。 ただし日本語が使えなかったり、Windows環境で動作しないという問題があります。 キー入力をすべて奪いとってしまうので、他のプラグインとの連携もしづらいです。 Emacsも内部で動かせるなど、ネタとしてはかなり面白いのですが……。

Shougo

Hack #141: 引数の順序を入れ替える

問題

いくつかのプログラミング言語における関数呼び出しないしメソッド呼び出しは、以下のような書き方をすることが多いです。

aaa(bbb, ccc)

引数部分に大抵の式を書くことができ、

aaa(bbb(ccc, ddd), eee())

のように書くことができる場合も多いです。

さて、上記関数aaaに与える引数の順序が実は間違っていたことに気づいたとします。どのように引数の順序を入れ替えるのがラクでなおかつ確実でしょうか。

aaa(bbb(ccc, ddd), eee())

まず0f(などで引数のリストの手前の括弧に飛び、df,で削除…、おっとコンマは既に途中で使われていました。df)ならば今回はうまくいきますが、しかしこれも汎用的ではありません。%を使うにも、うまく個別の引数を指定できません。

解決法

flipperというスクリプトを使います。

まず、以下のスクリプトを環境変数$PATHの通った位置に配置し、実行権限を与えます。

#!/usr/bin/env ruby
# flip('(aaa, bbb)') #=> '(bbb, aaa)'
def flip(str)
  stack = []
  tmp = ''
  tokens = []
  str.each_char.to_a[1..-2].each do |c|
    case c
    when '('
      stack << ')'
    when ')'
      stack.pop == ')' or abort 'Syntax Error'
    when '"'
      if stack.last == '"'
        stack.pop
      else
        stack << '"'
      end
    when "'"
      if stack.last == "'"
        stack.pop
      else
        stack << "'"
      end
    when ','
      if stack.empty?
        tokens << tmp
        tmp = ''
        next
      end
    when ' '
      if tmp.empty?
        next
      end
    end
    tmp << c
  end
  tokens << tmp
  '(' + tokens.reverse.join(', ') + ')'
end

if __FILE__ == $0
  puts flip(ARGV.shift)
end

つづいて、~/.vimrcに以下を記述します。

" Flip Arguments {{{
"   f(a, b) to f(b, a) when your cursol is on '('.
function! FlipArguments()
  normal! y%
  let @" = split(system('flipper "' . @" . '"'), "\n")[0]
  execute "normal! %p\<C-o>d%"
endfunction
" }}}

これで、:call FlipArguments()とすることでカーソル位置の引数リストの順番を逆順にすることができます。

入力を楽にするために、例えば以下のような設定をします。

nnoremap <space>flip :<C-u>call FlipArguments()<Cr>

<space>flipで同様の処理ができます。この作業はそれほど頻繁に行なうわけではないので、多少長いキーストロークでも問題ないのではないかと思います。

ujihisa

Hack #140: Vimでバッチ処理を行なう

問題

Vimはテキストエディタですから、通常は対話的に使います。 しかしVimの豊富な機能はテキスト処理用のスクリプト言語とみなすこともでき、 そう考えるとシェルなどから非対話的に利用したくなります。

例えばNormal modeでgg=Gを実行すると バッファ内容全体をファイルの種類に応じて適切にインデントします。 手元にインデントを修正したいファイルの数が多数あったとして、 この作業を手動で行なうのは面倒です。 非対話的に処理を行なうにはどうすればよいでしょうか。

解決方法

-c {command}オプションを指定してVimを起動します。 {command}はVimの起動後に実行されます。

例えばファイルのインデントを修正する場合、 以下のようにして非対話的に行うことができます。

$ cat hidoi.html
<title>This is hidoi</title>
<dl>
<dt>UWAAA</dt>
<dd>uwaaa</dd>
<dt>NANDATTE</dt>
<dd>nandatte</dd>
</dl>

$ vim -c 'normal! gg=G' -c 'write' -c 'qall!' hidoi.html

$ cat hidoi.html
<title>This is hidoi</title>
<dl>
    <dt>UWAAA</dt>
    <dd>uwaaa</dd>
    <dt>NANDATTE</dt>
    <dd>nandatte</dd>
</dl>

指定するコマンド数が多くなる場合は別のファイルにまとめて記述しておき、 -c 'source {file}'あるいは -S {file}で実行することもできます。

$ cat reindent.vim
normal! gg=G
write
qall!

$ vim -S reindent.vim hidoi.html

なお、上記のように実行した場合はvimrcなどが読み込まれ viminfoの内容も更新されます。 実行時の環境に依存せず実行するためには 以下のように実行します。

$ vim -u NONE -i NONE -N --cmd 'filetype indent on' -S reindent.vim hidoi.html
  • -u-NについてはHack #134: Vimをデフォルトの状態で起動するを参照してください。
  • -i {file}はviminfoファイルのパスを指定します。NONEはviminfoファイルを利用しないという意味です。viminfoはレジスタの内容やコマンドラインの履歴といった情報を保存しておくファイルなのですが、これを無効にしておかないとレジスタの内容を変更するような処理を実行した場合に後で困る可能性があります。
  • --cmd {command}-cと似ていますが、実行されるタイミングが違います。--cmdはファイルの読み込みなどに先立って実行され、-cはファイルの読み込みが完了した後に実行されます。ファイルの種類に応じたインデントを行なうにはVimがファイルの種類を自動認識するよう設定する必要があるのですが、それはファイルの読み込み前に行なわなければなりません。

応用例

筆者の場合、 拙作VimプラグインのドキュメントのHTML版 を公開していますが、これは :help形式のドキュメントからVimを利用してHTML版を自動生成 させています。

HTML化は

runtime! syntax/2html.vim

あるいは

TOhtml

で行なうことができますが、 ここではそれに加えて :help間のリンク(|:TOhtml|*:TOhtml*)を 適切なハイパーリンク(<a href="...">:TOhtml</a>)に 変換するなどの処理を行なっています。

参考資料

kana

Hack #139: screenのカレントディレクトリにcdする

問題

screen+zshで別スクリーンのカレントディレクトリに簡単にcdできるcddというzshスクリプトがあります。

cdd依存者はこれをvimでも使いたいと思った人は何人もいるかと思います。

解決

cdd.vimを使います。

まずcdd.vimをインストールし、:Cdd {スクリーン番号}を実行することで、 そのスクリーンのカレントディレクトリにcdできます。

使いやすくする

通常では:CddはCをShift-Cで入力しなければならず、非常に小指破壊的です。

そのため、cabbrevで略称を登録すると非常に快適になります。

以下を.vimrcに追記することにより、:cddと入力しスペースを入れると自動的に:Cddに変換されます。

cabbrev Cdd cdd

To-Do

  • vimからcddに登録できるようにする

参考

Sora Harakami

ホーム > Vim Hacks

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

    Return to page top