改行時に\begin{hoge}を認識して\end{hoge}を補完する


この記事の目標

vim を使って Latex のコードを書く方へ,少しだけ便利な設定を共有します.
\begin{hoge} の文章で改行をしたときに \end{hoge} を補完するように vimrc に設定を追記します.

環境・前提事項

私の VIM は バージョン 8.2.200 です.プラグインは使っていません.
最新バージョンでしか使えない機能など,特別なものは含まれていないと思います.
ただし "\begin{hoge}\begin{fuga}" と一行に複数回 "\begin{}" を登場させる方には,
意図通りの動作にならない可能性が高いです.
VIMのバージョンが7の場合,意図通りに動きませんでした.
バージョン7と8では count(...) 関数の使い方が異なるようです.
下記をご利用の際にはバージョンを8以上にしてください.(2021/04/13追記)

実践

vimrc に以下の内容を追記します.

function! s:AutoEndComp()
 let l:line_char=getline('.')
 if count(l:line_char,"\\begin{")
  let l:len=stridx(l:line_char,"}")-(stridx(l:line_char,"\\begin{")+7)
  let l:obj_name=strpart(l:line_char,stridx(l:line_char,"\\begin{")+7,l:len)
  let l:command="\<CR>\\end{"..l:obj_name.."}\<Esc>O "
  call feedkeys(l:command,'n')
 else
  call feedkeys("\<CR>",'n')
 endif
endfunction
inoremap <CR> <C-\><C-O>:call <SID>AutoEndComp()<CR>

解説

改行すると関数 AutoEndComp() が呼び出されます.この関数の大まかなアルゴリズムは以下の通りです.
まず,関数 getline() によりカーソルがある行を抜き出し,文字列変数 line_char に格納します.変数の前にある「l:」はローカル変数であることを明示する接頭辞です.この接頭辞はこちらのページ

接頭辞を関数内で省略した場合は「l:」になる.

と解説されている通り,省略することもできます.

次に関数 count() により文字列変数 line_char に文字列 "\begin{" がいくつ含まれるかを調べます.
ひとつも含まれていない場合は else 節が実行され,通常の改行となります.
ひとつでも含まれている場合は else 前の部分が実行され,以下の処理となります.

  1. \begin{hoge} の "hoge" の部分の文字数をカウントします.そのために関数 stridx(str, "extr") を使っています.この関数は,文字列変数 str における文字列 "extr" が始まる位置を返します.この関数をうまいこと使い "hoge" の文字数を変数 len に格納します.
  2. "hoge" の部分の具体的な名前を調べます.そのために関数 strpart(str, "extr", len) を使います.この関数は,文字列変数 str の中で文字列 "extr" が始まる位置から文字数 len だけ文字列を抽出します.抽出した文字列を文字列変数 obj_name に格納します.
  3. \end{hoge} を補完するために関数 feedkeys() を使用します.第一引数に改行したときの振る舞いを与えます.ここでは見栄えを優先して,文字列変数 command を導入しています.let により文字列を結合する際には,let str=char1..char2のように,ピリオドを二つ使います.

気がついた点,不備などありましたら,ぜひご指摘ください.


(5月9日追記)文字列を結合する方法について
訂正前: let str=char1.char2のように,ピリオドを使います
訂正後: let str=char1..char2のように,ピリオドを二つ使います
文字列の結合はこちらのページで解説されている通り,
ピリオド二つの利用が推奨されていました.
実践 に示したソースコードの該当箇所も書き換えました.
@htsign 様にご指摘いただきました.改めて感謝します.