.vimrc を読むための vim script の基本

vim 使いになって1年.
いい加減 vim script から逃げられないと悟った.

まずは .vimrc をさらっと読めるようになりたい.
手を動かしながら vim script を学ぼう.

いつも通り厳密性は回避してゆるくふわっと.

概要は次の通り.

動作環境

  • mac mojave 10.14.5
  • vim 8.0

モチベーション

モチベーションはこれ.

  • .vimrc を理解したい

plugin 設定の度に .vimrc に呪文を書いてきた.
そろそろ呪文の中身を理解したい.

もう少し高尚なモチベーションに言い換えるとこんな感じ.

  • plugin 設定内容 (.vimrc) を理解したい
  • color scheme 視認性を高くしたい
  • 不具合発生時のこれをやめたい
    • vim script がわからないからググって人柱を探す

本記事はこれらを実現する足掛り的な位置付け.

対象読者

対象読者はこんな人.

  • vim がメインエディタの人
  • .vimrc が呪文に見える人

方針

方針はこんな感じ.

ゴール

ゴールはこれ.

  • 自分の .vimrc がそれなりに読めるようになる

やり方

やり方はこれ.

  • help を読む
  • ポイントだなって箇所で手を動かして動作確認

本記事では次の箇所だけ紹介

  • あ、これはメモしなきゃ って箇所

script の実行は次のいずれかの手順で行う.

  • .vimrc 先頭に記述して :source ~/.vimrc で実行
  • sample.vim を用意して実行

help を読む

help を読もう.
vim 不明点調査の第一歩はこれが最善.

参考

大変勉強になりました. ありがとうございます.

今回最も参照した help は usr_41.txt

help に Write a Vim script って章がある.
次の操作で対象ファイルへ到達可能.

# help を開く
:h

# ファイルを探そう
:Ex

# / で検索
/usr_41

# enter で開く

: から始まるコマンドは Ex コマンド

vim で何気なく使ってるコマンドは Ex コマンド というらしい.

  • cf.)
    • :help
    • :echo
    • :set

vim script とは Ex コマンドから : を除いたコマンドを並べたもの

vim script を書くときは : は付けない.
何気なく書いてた .vimrc は全部そうなってる.

.vimrc は vim 起動時に自動で読み込まれる

.vimrc は vim で何らかのファイルを開いた時点で読み込まれる.
help の Introduction にはこうある ↓

Your first experience with Vim scripts is the vimrc file.
Vim reads it when it starts up and executes the commands.

usr_41.txt

たしかに, .vimrc に知らない内に書いていた呪文は vim script だったね.

基本構文は Ex コマンド 引数

基本構文はコレ.

  • Ex コマンド 引数

試してみる.
.vimrc 先頭にこう書く

echo "hoge"

Ex コマンドは echo
引数は "hoge"

シェルから適当にファイルを開く

$ vi vim_script.md

hoge
Press ENTER or type command to continue

hoge って出力されてる.

引数をいくつ取れるかはコマンドによる.
基本構文は大体こんな感じ.

変数 & 変数のスコープ

変数 & 変数のスコープについて.
この辺を見てゆく.

  • let, unlet
    • 変数使用時, 破棄時 に使う prefix
  • スコープ
    • g
    • l
    • s
    • a
    • b
    • w

let, unlet

let, unlet は変数使用時・削除時の prefix.

  • let: 変数宣言
    • = 前後に半角スペース ok
  • unlet: 変数破棄

次の文字が使える.

  • アルファベット
  • 数字
    • 数字で開始することはできない
  • アンダースコア

.vimrc に書いて試してみる.

let x = "foo"
echo x
unlet x
echo x

適当なファイルを開いてもいいけど, コマンドで再読み込みしてみる.

:source ~/.vimrc

foo
Error detected while processing /Users/takimoto/.vimrc:
line    7:
E121: Undefined variable: x
E15: Invalid expression: x

最初の echo xfoo が表示された.
次の echo x ではそんな変数ないよと怒られた.
期待通り.

スコープ

使い方は スコープ: って感じ.
:(コロン) は必須.

変数のスコープはいくつかある.
ファイル内外の変数汚染(重複定義とか)回避に必須.

記号 スコープ
g global
l function local
s script local
a function argument
b buffer local
w window local

b, w について.
buffer と window について知らないと b, w は使い分けられないかも.

  • buffer
    • vim のメモリに展開されたデータは全部 buffer 扱い
      • e.g.) vim で開いたファイルとか
    • :ls で buffer 一覧が見れる
  • window
    • buffer を表示するのが window
      • :h window の summary に簡単にまとまってる

この辺は必要になったら改めて深追いする.

使ってみる.
.vimrc にこれを書く

let s:cnt = 1
while s:cnt < 5
  echo "s:cnt = ". s:cnt
  let s:cnt = s:cnt + 1
endwhile

:source ~/.vimrc で実行

s:cnt = 1
s:cnt = 2
s:cnt = 3
s:cnt = 4

while の外で一度 let 宣言したのに, while 内で再定義するのはちょっとクセがあるね.

オプション

vim の組み込み変数を オプション って呼ぶらしい.
詳しくは :h options で.

default 設定から変更されているオプションは次のコマンドで確認可能.

:se

オプションは大きく次の2パターン.

  • 値をセットするもの
  • オプション名でスイッチするもの

ちょっと確認.

値をセットするもの

変数と違って, オプション設定時は = 前後に半角スペースを入れられない.

  • e.g.) set syntax=markdown

オプション名でスイッチするもの

たとえば行番号の表示・非表示がこれに該当する.

" 行番号 表示
set nu

" 行番号 非表示
set nonu

setlocal を覚えておく

current buffer, current window のオプションを変更したいときは setlocal を使う.
set はグローバルな変更をしちゃうらしい.

  • cf.) :h setlocal

こういうのがある.
詳しくは help で.

  • 四則演算
  • 論理演算
  • 三項演算子

条件式 & 式の実行

条件式はこんなの

  • if
  • while
  • for

式の実行は exe[cute]

やってみる.

let s:is_true = 1
let s:cmd = "echo "
let s:str = "success!"
if (s:is_true)
    execute s:cmd. "s:str"
endif

success! と表示されれば成功

関数

vim にも組み込み関数があるらしい.
使い方はこれ.

  • function ~ endfunction で囲む
  • 関数名は s: または 大文字 から始める

ここでは自作してみる.
Desktop に sample.vim を用意して次のコマンドを書く.

" ~/Desktop/sample.vim

function! s:echoIsDog (str)
  let l:cmd = "echo "

  " 引数が dog なら true
  let l:result = "no, it is NOT a dog."
  if a:str == "dog"
    let l:result = "yes, it's a dog."
  endif

  exe l:cmd. "l:result"
endfunction

let s:mojiCat = "cat"
let s:mojiDog = "dog"
call s:echoIsDog(s:mojiCat)
call s:echoIsDog(s:mojiDog)

実行

:source ~/Desktop/sample.vim

no, it is NOT a dog.
yes, it's a dog.

簡単に解説.

  • !
    • 定義済み関数だった場合エラーが出る
    • そのエラーメッセージを抑制
  • l:
    • 関数内の変数として宣言
  • a:
    • 関数の引数であることを宣言
  • exe l:cmd. "l:result"
    • exe コマンド (引数)
      • exe で コマンド を実行
      • もし コマンド に引数が必要なら付ける
        • 今回は echo をコマンドとして利用したから引数を付けた
    • 文字連結演算子 . を利用
      • これがないと exe[cute] が引数を2つ持つ表現になっちゃう
  • call
    • 関数呼び出しの Ex コマンド

エラーメッセージの抑制についてはここに書いてあった.

This deletes the script-local variable “s:count” to free up the memory it uses.
If you are not sure if the variable exists, and don’t want an error message when it doesn’t, append !:

:unlet! s:count

usr_41.txt

リストと辞書

データ型について.
ここでは リスト, 辞書 に絞って見てゆく.

リスト

[] を使ったやつ.
こんな感じ.

let aList = [1, 2, 3]
let bList = ['foo', 'bar']

echo aList bList

実行

[1, 2, 3] ['foo', 'bar']

要素の取り出しや配列の連結については help 参照.

辞書

plugin 設定でよく見かけるやつ.
連想配列と同じ意味.

定義はこれ.

  • {<key> : <value>, ...}
    • キー, 値 のペアで表現
    • キーには '' を付ける

改行するときは行頭に \ を入れる.
閉じ波括弧にも \ が必要.

こんな感じ.

let aDict = {'a': 1, 'b': 2, 'c': 3}
let bDict = {
\ 'a': 1,
\ 'b': 2,
\ 'c': 3,
\ }

echo aDict bDict

実行

{'a': 1, 'b': 2, 'c': 3} {'a': 1, 'b': 2, 'c': 3}

例外

try – catch 構文がある.
必要になったら深追いする.

map

入力キーの動作変更に使うコマンド.
mode 毎に使用するコマンドが異なる.

:h map-overview で種類が見れる.

COMMANDS MODES ~
:nmap :nnoremap :nunmap Normal
:vmap :vnoremap :vunmap Visual and Select
:smap :snoremap :sunmap Select
:xmap :xnoremap :xunmap Visual
:omap :onoremap :ounmap Operator-pending
:map! :noremap! :unmap! Insert and Command-line
:imap :inoremap :iunmap Insert
:lmap :lnoremap :lunmap Insert, Command-line, Lang-Arg
:cmap :cnoremap :cunmap Command-line
:tmap :tnoremap :tunmap Terminal-Job

構文はこんな感じ.

  • map 入力キー 期待する動作のキー

たとえばこんな感じで使う.

if bufwinnr(1)
    map + <C-W>+
    map - <C-W>-
endif

buffer が存在する window だったら if文配下を実行するよー, という内容.
画面分割時, <C-w> を打たなくても, +, - だけで上下方向の resize 可能.

buffer 一覧は :ls で確認可能.

ここでは指定してないけど, map 直後にこんなオプションを指定可能.

  • <buffer>
  • <nowait>
  • <silent>
  • <special>
  • <script>
  • <expr>
  • <unique>

詳しくは :h map-arguments で.

autocmd

とあるイベントで発火するコマンドを登録できるやつ.
とあるイベントは次のコマンドで見れる.

  • cf.) :h {event}

イベントはジャンル毎に整理されている ↓

  • Reading
  • Writing
  • Buffers
  • Options
  • Startup and exit
  • Various

構文はこれ

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

使ってみる.
たとえばこんな動作を実現したい.

  • markdown のファイルを新規作成したら
  • 新しいファイルだよっ と出力
" ~/.vimrc

autocmd BufNewFile *.md echo '新しいファイルだよっ'

BufNewFile がイベント.
*.md がパターン.
echo '新しいファイルだよっ' がコマンド.

適当なファイルを新規作成 & 開く

$ vim hoge.md

hoge.md を開いた瞬間に .vimrc が読み込まれる.
status line に 新しいファイルだよっ って表示されれば成功.

augroup

autocmd 使用には注意点がある (:h autocmd).

:autocmd adds to the list of autocommands regardless of whether they are already present.
When your .vimrc file is sourced twice, the autocommands will appear twice.
To avoid this, define your autocommands in a group, so that you can easily clear them:

    augroup vimrc  
      autocmd!      " Remove all vimrc autocommands  
      au BufNewFile,BufRead *.html so <sfile>:h/html.vim  
    augroup END  

引用: autocmd.txt

定義したコマンドが既に存在するかどうかに関わらず, autocmd を使ったら autocommands リストに登録しちゃうからね.
.vimrc を source コマンドで 2度読んだら autocommands も2回出るからね, と.

↑ これはこういうこと ↓

  • autocmd は上書きされない
  • .vimrc を source した回数だけリストに登録される

どうせなら .vimrc を読み込むたびに次のような動作をして欲しい

  • 既に登録済みの autocmd はクリア
  • autocmd を 1つだけ再登録

これは augroup で実現可能.
augroup では

  • oo ってグループに属する
  • xx ってコマンドは
  • 一旦初期化して再定義するよ

という設定ができる.
使用例は上の説明通り.
autocmd を使う際のお作法だと思っておく.

autoload

もはや基本じゃないけど, .vimrc を読むために必要.
.vimrc で時々見かけるこんな記述は autoload というらしい.

  • call mylib#myfunction(arg)

他のフィルから特定のファイルに書かれた関数を呼び出すための記法.
# はパスのデリミタ. よく使う / みたいなもの.

多くの場合, 次のような記述方法になる.

  • スクリプト名#サブディレクトリ名#関数名()

まとめ

これで .vimrc がもっと楽に読めそう.
今回は以上.

この記事を書いた人

1985年生まれ 東京都在住 フリーランスWEBエンジニア.
30歳からWEBプログラミングを独学で習得. その後エンジニアとして某企業にコミット.
現在は週3日稼働のフリーランスエンジニアとして活躍.
学んだことを中心に web 界隈の人達に向けた情報を発信.

たきもとをフォローする
Vim
たきもと.com

コメント

Secured By miniOrange