Anti-pattern of vimrc

file encoding

In Vim script, :scriptencoding exists to specify a file encoding. Generally it should define :scriptencoding when you use multibyte character. When you don't use multibyte character, you don't have to define :scriptencoding. Therefore you have to use :scriptencoding at the head of your vimrc.

Bad pattern

" use multibyte charactor.
let g:indentLine_char = '|'

Good pattern

scriptencoding utf-8
let g:indentLine_char = '|'

:scriptencoding and :set encoding

Vim can not recognize the character code of your vimrc when :scriptencoding is defined before :set encoding. When writing :set encoding, it should be described before :scriptencoding.

Bad pattern

scriptencoding utf-8
set encoding=utf-8

Good pattern

set encoding=utf-8
scriptencoding utf-8

Abbreviate option

I often see a vimrc using an abbreviate option and the same strict option. It's a bad coding. You should use a strict option only.

Bad pattern(1)

" 'modeline' of abbreviation.
set ml

Good pattern(1)

set modeline

Bad pattern(2)

augroup vimrc
  " 'autocmd' of abbreviation.
  au!
  au FileType vim  * :set expandtab
augroup END

Good pattern(2)

augroup vimrc
  autocmd!
  autocmd FileType vim  * :set expandtab
augroup END

If you don't know an abbreviate option, please you try :h ml in your Vim. And you will know a strict name of the abbreviate option.

   *'modeline'* *'ml'* *'nomodeline'* *'noml'*

Scope of variable

I often see a vimrc using g:mapleader and mapleader. It's a bad coding. You should use a variable with a scope.

Bad pattern(1)

let mapleader = ' '

Good pattern(1)

let g:mapleader = ' '

Bad pattern(2)

for i in range(0,2)
  execute printf('source .local_%d.vim', i)
endfor

Good pattern(2)

for s:i in range(0,2)
  execute printf('source .local_%d.vim', s:i)
endfor

When you use a variable without a scope outside a function, implicitly its scope is global scope(g:). Generally when you use a variable outside a function, you have to define it with script scope(s:).

:autocmd without belonged to groups

In Vim script, :autocmd exists. Its syntax is following syntax.

:au[tocmd] [group] {event} {pat} [nested] {cmd}

:autocmd can omit [group]. It is a bad coding and that makes your Vim heavy.

Bad pattern

autocmd FileType cpp setlocal expandtab
autocmd FileType make setlocal noexpandtab

Good pattern

" define a group `vimrc` and initialize.
augroup vimrc
  autocmd!
augroup END

" register autocmds to group `vimrc`.
autocmd vimrc FileType cpp setlocal expandtab
autocmd vimrc FileType make setlocal noexpandtab

Why is it heavy?

If you define autocmds without a group, your Vim registers the same autocmd each :source ~/.vimrc. And your Vim executes the same autocmds each occurring a Event(e.g. FileType). In one word, it's heavy.

「zshでsource .vimrc」を対応するvimrcにする。

以前、素で以下の記事のsourceコマンドがVimのコマンドかと思ってリプライを返してしまったことがある。

なので、どうにかしてzsh上でのsource .vimrcを防げないかなっと考えた結果、以下の1行をvimrcの先頭で書いて置けばよいことがわかった。

"return" 2>&- || "exit"

この先頭のダブルクォートがキモで、Vim script上ではコメント扱いになり、zshbash上ではコマンドとして正常終了するという仕組みである。便利!

ちなみに、なぜわざわざreturnとexitをしているかというと、returnのみだと終了コードがエラーで終わってしまい、exitだと端末自体が終了してしまうため、これらの問題を対処した結果このようなコードとなっています。

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

scriptencodingset encoding

上項で「vimrcの先頭で宣言するのがいいでしょう。」と言いましたが、 scriptencodingset encodingの順番には気を付けましょう。 Vim文字コードの内部的な仕組み上、set encodingscriptencodingより あとに記述すると文字コードを上手く扱うことができません。 よって、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:mapleadermapleaderをそれぞれ宣言しているのを見かけるけど、これは全く同じ変数を参照します。

悪いパターン

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に良く書かれるマップにはcmapimapnmapがある。でもこれは展開されるマップです。 これらにはそれぞれcnoremapinoremapnnnoremapと展開されないマップも存在します。 これを上手く使いこなせていない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 2012164日目の記事ですでに説明されていますけど、まぁ一応。 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読書会ではプラグイン関連のノウハウがいろいろと発言されているのですが...まとめるとキリがない。  

MinGWでVimをコンパイルしようとしたらビルドできなかった。(windres: unexpected version string length 68 != 32 + 8)

ちょっとVim scriptことで調べものしていて、Vim7.3(v7-3-002)の本体のソースコードをビルドしていたら、 以下のような感じでビルドエラーになってしまった。

$windres --preprocessor="gcc -E -xc" -DRC_INVOKED gobj/vimres.res gobj/vimrc.o

windres: unexpected version string length 68 != 32 + 8

これについてググってみると、どうやらMinGWbinutilsが新しいとダメっぽい。

mingw-get remove binutils

mingw-get install binutils=2.22-1

こうしたら無事にビルドできた。

コマンドラインから「インターネット プロトコル バージョン 4(TCP/IPv4)のプロパティ」を設定する。

f:id:rbtnn:20130919014027p:plain

A


$ netsh interface ipv4 set address name="ローカル エリア接続" source=dhcp

B


$ netsh interface ipv4 set address name="ローカル エリア接続" address=192.168.0.144
mask=255.255.255.0 gateway=192.168.0.254

C


$ netsh interface ipv4 set dnsservers name="ローカル エリア接続" source=dhcp

D


$ netsh interface ipv4 set dnsservers name="ローカル エリア接続" source=static address=10.23.4.3 register=primary
$ netsh interface ipv4 add dnsservers name="ローカル エリア接続" address=10.46.51.3