vimrcアンチパターン
この記事はVim Advent Calendar 2014 - Qiita1日目の記事です。
今回は、もう130回も続いているvimrc読書会でよく見られるvimrcのアンチパターン、 まぁ「これは気を付けたほうがいいんじゃない」的なことを私なりにまとめてみようと思う。
vimrcの文字コード
Vim scriptにはscriptencoding
という現在のVim scriptファイルの文字コードを指定するコマンドが存在します。
一般的にscriptencoding
はマルチバイト文字を使う前に宣言します。マルチバイト文字を一切使っていない場合、特に宣言する必要はないでしょう。
なので、マルチバイト文字をvimrc内で使用する場合(コメント内でマルチバイト文字を使用する場合も含みます)、vimrcの先頭で宣言するのがいいでしょう。
悪いパターン
" ミュートにする。 set t_vb= set visualbell set noerrorbells
良いパターン
scriptencoding utf-8 " ミュートにする。 set t_vb= set visualbell set noerrorbells
scriptencoding
とset encoding
上項で「vimrcの先頭で宣言するのがいいでしょう。」と言いましたが、
scriptencoding
とset encoding
の順番には気を付けましょう。
Vimは文字コードの内部的な仕組み上、set encoding
をscriptencoding
より
あとに記述すると文字コードを上手く扱うことができません。
よって、set encoding
を書く場合にはscriptencoding
より前に記述するべきです。
悪いパターン
scriptencoding utf-8 set encoding=utf-8
良いパターン
set encoding=utf-8 scriptencoding utf-8
省略記法のオプション
たまに省略しているオプションと省略してない同じオプションをそれぞれ宣言しているのを見かけるけど、 これは同じ処理を2回も行っていてもったいない処理です。 省略しているオプションと省略してないオプションを混ぜてvimrcに記述するのは精神衛生上可読性的にも非常に悪いです。 省略しないでオプションを設定するようにするといいでしょう。
悪いパターン
" ↓modelineの省略記法 set ml
良いパターン
set modeline
もし、省略しているオプションの正式な名前がわからない場合には:h ml
のようにヘルプを引きましょう。
以下のような感じで省略名と正式な名前の両方が記載されているはずです。
*'modeline'* *'ml'* *'nomodeline'* *'noml'*
スコープのない変数(1)
vimrcに書かれるよくあるパターンして<leader>
の文字を決める変数g:mapleader
があります。
たまにg:mapleader
とmapleader
をそれぞれ宣言しているのを見かけるけど、これは全く同じ変数を参照します。
悪いパターン
let mapleader = ' '
良いパターン
let g:mapleader = ' '
関数外でスコープなし変数を宣言した場合、暗黙的にグローバル変数扱いになります。 関数外で変数を宣言するときはスコープを付けるくせをつけるといいでしょう。
スコープのない変数(2)
関数外でfor文を使う場合、一時変数(下記でいうi
)もlet文で宣言したのと同等な扱いとなります。
ということはスコープのなしでfor文を使えば当然グローバル変数扱いとなりとても邪悪です。
悪いパターン
for i in range(0,2) execute printf('source .local_%d.vim', i) endfor
良いパターン
for s:i in range(0,2) execute printf('source .local_%d.vim', s:i) endfor
let文と同じく、関数外でfor文を使うときはスコープを付けるくせをつけるといいでしょう。
基本的にvimrc内で関数外の変数を宣言する時にはスクリプト変数であるs:
をつけるといいでしょう。
グループに属していないautocmd
autocmdコマンドは以下のような構文で、[group]
を省略することができるのですが、
vimrc内で[group]
を省略すると少々厄介です。
:au[tocmd] [group] {event} {pat} [nested] {cmd}
この[group]
を指定していないautocmdをvimrc内に記述していると、vimrcを再読み込みするたびにそのautocmdが登録されてしまいます。
ということはvimrcを読み込んだ回数だけautocmdが実行されてしまいます。
要するに余計な処理が増え、段々Vimが重くなっていきます。
悪いパターン
autocmd FileType cpp setlocal expandtab autocmd FileType make setlocal noexpandtab
良いパターン
augroup vimrc autocmd! augroup END autocmd vimrc FileType cpp setlocal expandtab autocmd vimrc FileType make setlocal noexpandtab " ...
vimrcの先頭の方で
augroup vimrc autocmd! augroup END
を宣言することでグループvimrc
に属するautocmdを初期化できます。
もうちょっと詳しくいうとautocmd!
が現在のグループに属しているautocmdをすべて登録解除を行います。
その後にグループvimrc
に属したautocmdを使えばOKです。
これでvimrcを再読み込みしてもVimが重くなることもありません。
上記の方法以外にもいくつか書き方があります。
" 別解 augroup vimrc autocmd! autocmd FileType cpp setlocal expandtab autocmd FileType make setlocal noexpandtab " ... augroup END
これは初期化と登録を同時にしてしまう方法です。
毎回、autocmd
の後にグループ名を書かなくていいので若干コーディングが楽になります。
※ グループ名は好きな名前が使えます。vimrc
である必要はありません。
展開されるマップと展開されないマップ
vimrcに良く書かれるマップにはcmap
、imap
、nmap
がある。でもこれは展開されるマップです。
これらにはそれぞれcnoremap
、inoremap
、nnnoremap
と展開されないマップも存在します。
これを上手く使いこなせていないvimrcをちらほら見かけます。
悪いパターン
nmap <leader>c :<C-u>copen<cr>
良いパターン
nnoremap <leader>c :<C-u>copen<cr>
上記の悪いパターンはnmap : ;
などとコロンの挙動を変えてしまうと途端に意図しない挙動になるでしょう。
一般的に展開されるマップを使うのはプラグインが提供している<Plug>(...)
系のマッピングです。
例
smap <C-z> <Plug>(neosnippet_expand_or_jump)
その他のマッピングでは展開されないマップを使う方がよいでしょう。 当然、意図的に展開されるマップを使うなら全くもって問題ないです。
実は必要のないset nocompatible
Vimはvimrcもしくはgvimrcを発見すると自動的にset nocompatible
になります。
なので、vimrcにset nocompatible
を書く必要はありません。
また、set nocompatible
はちょっと厄介な副作用を持っていて、以下のオプションが初期化されてしまいます。
(vim-jp/vimdoc-jaから引用)
vimrc読書会でよく話題に上がる問題としてset nocompatible
するとオプションhistory
が初期化されていまうため、
履歴が削除されてしまうというものです。
オプション + Viの既定値 効果 ~ 'allowrevins' オフ コマンド CTRL-_ なし 'backupcopy' Unix: "yes" バックアップファイルがコピーになる 他: "auto" バップアップはコピーまたはリネーム 'backspace' "" 普通のバックスペース 'backup' オフ バックアップファイルなし 'cindent' オフ C言語ファイルにインデントなし 'cedit' + "" |cmdwin| を開くキーなし 'cpoptions' + (全フラグ) Vi互換のフラグ 'cscopetag' オフ ":tag" に cscope を使わない 'cscopetagorder' 0 |cscopetagorder| を参照 'cscopeverbose' オフ |cscopeverbose| を参照 'digraph' オフ ダイグラフなし 'esckeys' + オフ 挿入モードで <Esc> で始まるキーなし 'expandtab' オフ タブはスペースに展開されない 'fileformats' + "" 自動ファイルタイプ決定なし "dos,unix" (ただし DOS, Windows と OS/2 以外で) 'formatoptions' + "vt" Vi互換の文書整形 'gdefault' オフ ":s" でフラグの既定値に 'g' なし 'history' + 0 コマンドラインの履歴なし 'hkmap' オフ ヘブライ語用キーボードマップなし 'hkmapp' オフ phoneticヘブライ語用キーボードマップなし 'hlsearch' オフ 検索でマッチした文字列に強調なし 'incsearch' オフ インクリメンタル・サーチなし 'indentexpr' "" expression によるインデントなし 'insertmode' オフ 挿入モードでの開始なし 'iskeyword' + "@,48-57,_" キーワードはアルファベットと数字と '_' 'joinspaces' オン ピリオドの後ろには空白を2個挿入 'modeline' + オフ モードラインなし 'more' + オフ リスト表示が止まらない 'revins' オフ 右から左の挿入なし 'ruler' オフ ルーラなし 'scrolljump' 1 ジャンプスクロールなし 'scrolloff' 0 スクロールにオフセットなし 'shiftround' オフ インデントは shiftwidth の整数倍でない 'shortmess' + "" メッセージの短縮なし 'showcmd' + オフ コマンドの文字は表示されない 'showmode' + オフ 現在のモードは表示されない 'smartcase' オフ 大文字小文字の無視は自動にならない 'smartindent' オフ 高度なインデントなし 'smarttab' オフ 高度なタブ挿入なし 'softtabstop' 0 タブは常に 'tabstop' を基準 'startofline' オン いくつかのコマンドで行頭に移動する 'tagrelative' + オフ タグファイル名は相対的でない 'textauto' + オフ 自動改行コード決定なし 'textwidth' 0 自動行分割なし 'tildeop' オフ チルダはオペレータではない 'ttimeout' オフ ターミナルの時間切れなし 'whichwrap' + "" 左から右への移動は行を超えない 'wildchar' + CTRL-E 現在の値が <Tab> のときのみ、コマンド ライン補完に CTRL-E を使う 'writebackup' オンかオフ |+writebackup| 機能に依る
もし、なにかの理由でset nocompatible
したい場合には以下のようにすると良いでしょう。
if &compatible set nocompatible endif
syntax on
の宣言位置
ハイライトを有効にしてくれるsyntax on
(もしくはsyntax enable
)があります。
実はファイルタイプ系ハイライトプラグインを導入している場合、syntax on
の宣言位置を気を付ける必要があります。
syntax on
は現在のruntimepathに含まれている設定をもとにシンタックスを生成しようとします。
なので、runtimepathを初期化するような処理をした後にsyntax on
してもあまり意味はなく、
runtimepathをすべて設定し終えた後にsyntax on
をするべきです。
まぁ、runtimepathを初期化するような処理をvimrcに入れている人は結構まれなので心の片隅に入れとくとよいかと思います。
悪いパターン
" runtimepathを初期化するような処理 set runtimepath=$VIMRUNTIME syntax on " ファイルタイプ系ハイライトプラグイン neoBundle 'kongo2002/fsharp-vim'
良いパターン
" runtimepathを初期化するような処理 set runtimepath=$VIMRUNTIME " ファイルタイプ系ハイライトプラグイン neoBundle 'kongo2002/fsharp-vim' syntax on
同一視されるキー
Vim Advent Calendar 2012の164日目の記事ですでに説明されていますけど、まぁ一応。 Vimは以下のキーを同一視してしまいます。
<C-i>
==<Tab>
<C-m>
==<Enter>
<C-[>
==<ESC>
なので、<Tab>
キーのマッピングしたのに<C-i>
キーのマッピングで上書きしてしまったってことが起こりえます。
inoremap <expr> <Tab> MySuperTab() " ↓これは <Tab> のキーマッピングを上書きしてしまう! inoremap <C-i> <C-o><C-i>
(上記の記事のコードをそのまま引用)
g:vim_indent_cont
アンチパターンとかではないけどあまり知られていないので、g:vim_indent_cont
について説明します。
g:vim_indent_cont
は以下のように\
を入力した時のインデント量をつかさどっている変数です。
この変数はVim script(vimrc)を編集しているときにしか使われないので、まぁVim scriptをいじらない方はどうでもいいですね。
let s:hoge = [ _______\ " ↑この`\`を入力した時のインデント量をつかさどっている変数
以上、Vim Advent Calendar 2014 - Qiita1日目の記事でした。 今回、プラグイン関連のノウハウは一切載せないようにしました。 vimrc読書会ではプラグイン関連のノウハウがいろいろと発言されているのですが...まとめるとキリがない。