Arch dm-cryptでデバイスを透過的に暗号化する

前座

先日ThinkPad X1を購入しました。 初期のOSとして当然Windowsが入っているわけですが、最近は初期状態でBitLockerによるデバイスの暗号化が施されているようですね。
少し感動しました。

ちなみに、Linuxを入れるためにSecureBootを無効にするとTPM(セキュリティチップ)にある鍵にアクセスできなくなりWindows起動時にデバイスをオートアンロックできなくなります。
その場合はhttps://account.microsoft.com/devices/recoverykeyに載っている49文字の鍵を入力することでアンロックできます。

本題

さて、PCを新調したのでLinuxを入れたいわけですが、Windowsだとデバイスが暗号化されているのにわざわざ手間を掛けて入れたLinuxは生でデータを保存しているというのはなんだかかっこ悪いですね。
そういう訳で、Linuxでのデバイス暗号化を行います。

この記事はArchWikiを参考にしています。ざっくりと解説はしますが詳細はWikiを見てください。
また、Archをインストールしたことがある方を基準に話を進めます。

インストールガイド - ArchWiki
- ディスク暗号化 - ArchWiki
- - dm-crypt - ArchWiki
- - - dm-crypt/ドライブの準備 - ArchWiki
- - - dm-crypt/デバイスの暗号化 - ArchWiki
- - - dm-crypt/システム設定 - ArchWiki
- - - - mkinitcpio - ArchWiki
- - - dm-crypt/スワップの暗号化 - ArchWiki

バイスを暗号化するには

ディスク暗号化 - ArchWikiによるとLinuxにおける暗号化にはスタックファイルシステムの暗号化とブロックデバイスの暗号化があるようです。
今回はdm-cryptによるブロックデバイスの暗号化を行います。

dm-cryptはLinux標準のdevice-mapperを用いて透過的な暗号化デバイスの利用を可能にします。
dm-cryptにはLUKSモード(Linux Unified Key Setup Mode)とplainモードがあります。
詳細は省きますがplainモードよりLUKSモードのほうが賢く便利です。

LUKS

  • 必要な情報の全てをディスクに保存する
  • 1つのデバイスに対して複数の鍵を保存できる
  • 利用開始後に鍵の追加・削除・変更が可能
  • 鍵はパスフレーズとキーファイルの2種類がある

plainを使うメリットは恐らく無いためLUKSを使いましょう。

作業の流れ

この記事はdm-crypt - ArchWikiを参考に作業を進めていきます。

  1. Archインストール準備のパーティショニングまでを済ませる
  2. dm-cryptで暗号化対象のパーティションをセットアップ
  3. セットアップしたパーティションマッピング
  4. マッピングしたデバイスに通常通りファイルシステムを作成しマウント
  5. 以降は非暗号化時と同様
  6. /をアンロックするためのGRUBとinitramfsの設定

1はパーティションを切るところまです。
ファイルシステム作成やfstabの生成はまだ待ってください。 どの様にパーティションを切るかは自由ですが、ルートパーティションとブート(ESP)パーティションは分けてください。
2によってデバイス暗号化の下地を作ります。
3で暗号化したパーティションを通常のパーティションと同じ様に扱えるようにします。
具体的には /dev/mapper にデバイスを作成します。
4では3で作成された /dev/mapper にあるデバイスに通常通りファイルシステムを作成しマウントします。
3でマッピングされたデバイスに対して操作を行うことで透過的に暗号デバイスを使用できます。
一部の操作では特別な手順を必要としますが、通常は非暗号化時とまったく同じ操作が可能です。

完成形

Windowsとのデュアルブートにしたかったため、パーティション構成は以下の様になりました。
マッピングされたデバイスlsblk コマンドではnvme0n1p5とcrypthomeのように表示されます。
/, /home, /boot, swapWindowsパーティションがあります。
スワップパーティションについては今回は暗号化せずそのまま使います。
暗号化されているのは //homeの2つです。
/boot にはWindowsLinuxブートローダーが入っています。

NAME           TYPE  MOUNTPOINT
nvme0n1        disk  
├─nvme0n1p1    part  /boot (ESPパーティション)
├─nvme0n1p2    part  windows-reserved
├─nvme0n1p3    part  windows-recovery
├─nvme0n1p4    part  windows
├─nvme0n1p5    part
│ └─crypthome  crypt /home
├─nvme0n1p6    part 
│ └─cryptroot  crypt /
└─nvme0n1p9    part  [SWAP]

1. パーティショニング

上の完成形を目標にパーティションを切ります。
/boot パーティションUEFIのESPパーティションです。
最初からある(Windowsによって作られた)ものを使い回します。
既に中身がありますが、これを消すとWindowsが起動しなくなるので注意しましょう。
自分で作成するのは /, /home, swapの3つです。
ArchLinuxのisoで適当なCLIを使うのもいいですが、私はManjaro Linux(XFCE)をライブ起動してGPartedを使うのをおすすめします。
軽くて使い勝手がいいのでレスキューツールとして持ち歩くといいでしょう。

2. 暗号化パーティションのセットアップ

dm-crypt/デバイスの暗号化 - ArchWiki

dm-cryptの操作にはcryptsetupを使用します。

pacman -S cryptsetup

以下のようにパーティションを指定してluksFormatします。
実行するとパスフレーズの入力が求められます。

# /
cryptsetup -y -v --key-size=512 luksFormat /dev/nvme0n1p6
# /home
cryptsetup -y -v --key-size=512 luksFormat /dev/nvme0n1p5

--key-sizeで指定した値の半分の値が実際のサイズになります。
この例ではAES-256を使用しています。
また、cryptsetup luksFormat device /path/to/mykeyfile のようにするとパスフレーズの代わりにキーファイルを使用できます。
LUKSのオプションについてはLUKS モードの暗号化オプション - ArchWikiを見てください。

LUKSの魅力は複数の鍵を登録できることです。
せっかくなので初期のパスフレーズ(もしくはキーファイル)とは別のキーファイルを追加してみましょう。
以下のコマンドで2048ビットのキーファイルを生成できます。
保存場所(of=)は適当に選んでください。

dd bs=512 count=4 if=/dev/urandom of=/luks_keyfile
chmod 400 /luks_keyfile

既存の暗号化デバイスに新たな鍵を登録するには以下のようにします。

cryptsetup luksAddKey /dev/nvme0n1p6 /luks_keyfile
cryptsetup luksAddKey /dev/nvme0n1p5 /luks_keyfile

鍵は最大8つまで登録することができます。 現在登録されている鍵を確認してみましょう。

cryptsetup luksDump /dev/nvme0n1p6 | grep BLED

3. セットアップしたパーティションマッピング

暗号化したパーティションはそのままでは使用できません。
通常のパーティションと同じ様に扱えるようにするためにマッピングします。
cryptrootcrypthome などの名前は自由に付けて構いません。

cryptsetup open /dev/nvme0n1p6 cryptroot
cryptsetup open /dev/nvme0n1p5 crypthome

/dev/mapper/ を確認してみましょう。
指定した名前で新たなデバイスができているはずです。

ls /dev/mapper/

これで暗号化に関する作業はほとんど終了しました。
以降、パーティションに対する操作は全て/dev/mapper/cryptrootなどの/dev/mapper/下のデバイスに対して行います。
/dev/nvme0n1p6などのluksFormatしたデバイスを直接操作してはいけません。

4. ファイルシステム作成とマウント

通常と同じ様にファイルシステムを作成します。
おすすめはBtrfsです。

mkfs.btrfs /dev/mapper/cryptroot
mkfs.btrfs /dev/mapper/crypthome

/, /home, /boot をそれぞれマウントしてみましょう。
下記の-oから始まるマウントオプションは私がBtrfsで使っているものです。 省略して構いません。
/bootは暗号化していないため、そのままのパスを使います。

mount -o noatime,compress=lzo,space_cache,ssd /dev/mapper/cryptroot /mnt
mount -o noatime,compress=lzo,space_cache,ssd /dev/mapper/crypthome /mnt/home
mount /dev/nvme0n1p1 /mnt/boot

5. システムのインストール

通常通りにシステムをインストールしましょう。
注意としてはこの記事では/ をオートアンロックするためにGRUBを使います。

今回の場合は/homeをオートアンロックするためにcrypttabを使います。
オートアンロックに使うキーファイルは/mnt/下の適当なディレクトリにコピーしておきましょう。

echo 'crypthome  /dev/nvme0n1p5 /etc/luks_keyfile' >> /mnt/etc/crypttab
cp /luks_keyfile /mnt/etc/

例では分かり易さを優先してデバイスをパス指定していますが、馴れている方はUUIDを使いましょう。

この記事で前提とする/bootのファイルツリーの抜粋を示しておきます。

/boot
├── BOOT
│   └── BOOT.SDI
├── EFI
│   ├── Boot
│   │   ├── LenovoBT.EFI
│   │   ├── License.txt
│   │   ├── ReadMe.txt
│   │   └── bootx64.efi
│   ├── Microsoft
│   │   ├── Boot
│   │   └── Recovery
│   └── grub
│       └── grubx64.efi
├── grub
│   ├── fonts
│   ├── grub.cfg
│   ├── grubenv
│   └── x86_64-efi
├── initramfs-linux-fallback.img
├── initramfs-linux.img
└── vmlinuz-linux

GRUBのインストールには以下を実行しました。

grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=grub

もし、VirtualBoxEFIを有効にして検証している場合は /boot/EFI/grub/grubx64.efi/boot/EFI/boot/bootx64.efiにリネームしてください。

6. GRUBとinitramfsの設定

dm-crypt/システム設定 - ArchWiki

この記事では/を暗号化しましたが、OSが起動するためにはブートローダーがOSを起動する際にデバイスをアンロックしなければなりません。
起動時のアンロックには主に3つの方法があります。

  1. プロンプトにパスワードを入力
  2. 外部ストレージのキーファイルを読み込む
  3. initramfsにキーファイルを仕込む(あまり意味ない)

1はGRUBからOSを選択した後にプロンプトが表示され、パスワード入力が求められます。
もしあなたが1台のPCを専有(ユーザーが1人)なら、アカウントの自動ログインを有効にすることで今までと同じ回数のパスワード入力でセキュリティを高めることができます。

2はキーファイルを入れたUSBメモリなどを持ち歩くことでパスワード入力を省略できます。
バイスを管理し持ち運ぶ手間を惜しまなければ良い方法です。

3は外部ストレージに入れるべきキーファイルをinitramfsに入れることでパスワード入力や外部ストレージを持ち運ぶ手間を省くことができます。
ただし、initramfsは暗号化されていない/bootにあるため知識のある人間に対する防御力は0です。
GRUBからThinkPadに搭載されているセキュリティチップにアクセスできれば解決しそうですが、それはまた後日検証したいと思います。 解決方法を知っていたら是非教えてください。
この方法に残された唯一の利点はストレージを破棄する際、データの破棄が鍵の削除のみで済むことです。

私は現在パスワード入力を使っていますが、この記事では設定例として多少難しいinitramfsにキーファイルを仕込む方法を示します。
どれも設定方法はほぼ同じです。

/を暗号化した場合のLinux起動は以下です。

  1. 電源が入る
  2. UEFI/bootGRUBを起動
  3. GRUBinitramfsでミニマムなLinuxを起動
  4. /をアンロック(cryptrootにマッピング)
  5. /をルートディレクトリとしてLinuxを起動
  6. /etc/crypttabを読み/homeをアンロック(/dev/mapper/crypthomeマッピング)
  7. /etc/fstabで各パーティションをマウント

mkinitcpio - ArchWiki
起動時にアンロックするためにinitramfsにフックを追加する必要があります。
/etc/mkinitcpio.confを開きHOOKSのudevより後にencryptを挿入してください。
アンロック時にキーボードでパスワードを入力する場合はencryptよりも前にkeyboardを挿入する必要があります。
また、plymouthを無効にする必要があるのでHOOKSからplymouthを削除してください。

以下が設定例です。
BINARIESはbtrfsを使ってる場合に必要です。
もしThinkPad X1(G6)を使っていて、CSMを無効にしているならMODULESi915を追加してください。 FILESにファイルパスを指定すると、initramfs内のミニマムなLinux/の同じパスにファイルが配置されます。
initramfsにキーファイルを含めてオートアンロックする場合はキーファイルのパスを指定してください。

MODULES=(i915)
HOOKS=(base udev autodetect modconf keyboard keymap block encrypt filesystems fsck)
BINARIES=(/usr/bin/btrfs)
FILES=(/etc/luks_keyfile)

設定ファイルを書き換えたらでinitramfsを再生成することで反映されます。

mkinitcpio -p linux

次に、GRUBで暗号化された/を認識するために/etc/default/grubGRUB_CMDLINE_LINUX_DEFAULTカーネルパラメーターを設定しましょう。

GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice=/dev/nvme0n1p6:cryptroot cryptkey=rootfs:/etc/luks_keyfile root=/dev/mapper/cryptroot"

cryptdeviceは暗号化(luksFormst)したデバイスマッピング後の名前をコロン区切りで指定します。
例ではデバイスのパスを指定していますが、UUID指定も可能です。
cryptdevice=UUID=aaaaaaaa-bbbb-cccc-dddd-eeee99998888:cryptroot

cryptkeyでキーファイルのパスを指定します。
これを省略すると起動時にパスワード入力のためのプロンプトが表示されます。
フォーマットはdevice:fstype:filepathです。
deviceには/dev/sda1や /dev/disk/by-uuid/aaaaaaaa-bbbb-cccc-dddd-eeee11112222を指定します。 外部ストレージにキーファイルを保存する際はUUIDを使うようにしましょう。 そうでないとある日突然パスが変わりOSが起動できなくなる可能性があります。 fstypeにはvfatなどストレージのファイルシステムを指定してください。 filepathは名前の通りキーファイルまでのパスを指定します。 initramfsにキーファイルを仕込む場合はdevicerootfsを指定しfstype`を省略します。

rootマッピング後の/のパスです。 /dev/mapper/下にあるデバイスを指定しましょう。

grubの設定が終わったらgrub.cfgを再生成して変更を反映してください。

grub-mkconfig -o /boot/grub/grub.cfg

これでシステムの暗号化は完了です。
再起動し、暗号化されたデバイス上でLinuxが動作することを確認したらスワップの暗号化暗号化されていない boot パーティションのセキュア化を試してみるのもいいでしょう。

SlackAppを最小化状態で自動起動する for Linux

私はログイン時にSlackを立ち上げてほしいけど、ウィンドが表示されるのは邪魔だなー と思う派です。

PCを起動してターミナルを立ち上げてコマンドを実行! としようとしたときに自動起動したSlackAppに入力が取られて、変なメッセージを送ってしまったことが何度かあります。

執筆時点(2018/5/12)では最小化状態(トレイアイコン)で起動する設定はありませんでした。 ただ、slack コマンドに引数で --startup を渡して起動するとウィンドを表示せず起動できるようです。

Linuxはログイン時に ~/.config/autostart/ にあるデスクトップエントリ(拡張子が .desktop なファイル)を読み込み、アプリケーションを自動起動します。 ユーザが自動起動にしたアプリケーションはここにデスクトップエントリが置かれているはずです。

SlackAppを自動起動にしている場合は slack.desktop と言うファイルがあります。 私の環境では、slack.desktop/usr/share/applications/slack.desktopシンボリックリンクになっていました。 なので、編集するには sudo が必要です。

[Desktop Entry]
Name=Slack
Comment=Slack Desktop
GenericName=Slack Client for Linux
Exec=env LD_PRELOAD=/usr/lib/libcurl.so.3 /usr/bin/slack %U
Icon=slack
Type=Application
StartupNotify=true
Categories=GNOME;GTK;Network;InstantMessaging;
MimeType=x-scheme-handler/slack;

Exec が実際に実行するコマンドを指定しています。 envなんたらは無視して、/usr/bin/slack が本体です。 なので、この直後に --startup を挿入すれば最小化状態で自動起動するようになります。

Exec=env LD_PRELOAD=/usr/lib/libcurl.so.3 /usr/bin/slack --startup %U

おそらく、SlackAppをアップデートすると /usr/share/applications/slack.desktop は上書きされて --startup が消えるので、~/.config/autostart/ にはシンボリックリンクではなく、コピーを置くなどのしたほうがいいです。

clang-renameをVimからサクッと呼び出してC++のリネームを快適にする。

最近Go言語を使ってるんですが、この言語gofmtgorenamegoimportsなどのツールが充実していて良いですね。

C系の言語にも同じようなツールは存在します。

Welcome to Extra Clang Tools’s documentation! — Extra Clang Tools 5 documentation

この内clang-formatは既にVimプラグインが存在します。

C や C++ のコードを自動で整形する clang-format を Vim で - はやくプログラムになりたい

今回はclang-renameVimプラグインを作りました。
clang-renameは識別子をリネームするツールです。

github.com

clang-renameの導入

Arch

公式リポジトリからclang-tools-extraを入れましょう。

Ubuntu

clang-3.5以上のバージョンに含まれています。
ただしclang-renameのバージョンが古いと後述のqualified name機能が使えません。
この機能を使いたい場合はPPAなどからの導入を検討してください。
clang-rename --help-qualified-nameが表示されれば大丈夫です。

使い方

単一ファイルの場合:ClangRenameCurrent:ClangRenameQualifiedNameを使えば用が済むでしょう。

{new_name}を省略した場合は入力プロンプトが表示されます。

  • ClangRenameOffset [{offset} [{new_name}]]

オフセットを指定してリネームします。
例えばファイルの先頭のオフセットは0です。
-1を指定すると現在のカーソル位置をオフセットとして扱います。

  • ClangRenameCurrent [{new_name}]

カーソル位置のシンボル名を変更します。
内部でClangRenameOffsetを実行しています。

  • ClangRenameQualifiedName [{qualified_name} [{new_name}]]

シンボル名を指定してリネームします。
このコマンドを使うにはclang-rename-qualified-nameオプションをサポートしている必要があります。

  • ClangRename [...]

引数によって動作が変わります。
0個か1個ならClangRenameCurrentが実行されます。
2個ならClangRenameQualifiedNameが実行されます。

その他

  • <Plug>(clang_rename-current) ClangRenameCurrentのキーマップ
  • g:clang_rename#flags clang-renameの引数
  • g:clang_rename#compile_flags clang-renameが解析するときに使うコンパイルオプション(-std=c++11とか)

終わりに

実はclang extra toolsには標準でVimEmacs向けのスクリプトが付属してます。
ですが、微妙に使いづらかったので、このプラグインを作りました。

vim-fakearray ダミー配列を手軽に入力するプラグイン作った

github.com

プログラミングをしていると適当な配列が欲しい時ありますよね。
「0〜100の整数からランダムに10個選んだ配列」などなど。
そんなVimmerの要望を叶えるのがvim-fakearrayです。
vim-fakearrayはvim-fakeに依存しています。

vim-fakeについては作者の記事を参考にしてください。
qiita.com

インサートモードに<Plug>(fakearray)のキーマップを割り当てれば、 プロンプトを呼び出してダーミー配列を手軽に挿入できます。

解説

呼び出し階層

<Plug>(fakearray)fakearray#genfakearray#valfake#gen|fake#int|fake#float|fake#chars

fakearray#val

fakearray#val(first, second)は引数によって呼び出すfake#関数を変えます。

first second 呼び出し
整数 整数 fake#int(first, second)
少数or整数 少数or整数 fake#float(first, second)
文字列 整数 fake#chars(fake#int(1, second), first)
文字列 空文字列 '' fake#gen(first)
fake#int fake#float

この2つは第1引数〜第2引数の範囲の値を返しますが、Vimのバージョンによっては正しく動作しないようです。
恐らくVimが内部で扱う整数のサイズに依存しているのでしょう。
64bit整数が追加されたのは7.4.1976のようです。
Vim8.0とNeovim0.2では正常に動作しました。

fake#chars

第1引数は生成する文字数です。
第2引数は'abc'のような適当な文字列の他に、'\w'などの文字セットを使うことが出来ます。
詳細はvim-fakeのドキュメントを見てください。

fake#gen

'job''country''word'などのカテゴリを渡すことが出来ます。

fakearray#gen

fakearray#gen(num, first, second)fakearray#valnum回呼び出して、結果をg:fakearray#separator区切りで結合します。
b:fakearray_separatorも使えます。

(fakearray)

プロンプトに入力する値の個数によって動作が変わります。

入力数 動作
1 fakearray#gen({input num}, g:fakearray#prompt_first, g:fakearray#prompt_second)
2 fakearray#gen({input1 num}, {input2 str}, '')
3 fakearray#gen({input1}, {input2}, {input3})

入力数1と3は見たまんまです。
2のときはfakearray#genの第3引数を''にしているのでfake#genが呼ばれます。
内部で入力値にevalをかけているので文字列を入力するときはクオートを省略してはいけません。

キーマップ

インサートモードのキーマップは悩みますよね。
Emacsバインドにしている人は特に。
プラグインのデフォルトでは<F3>に割り当てていますが、私は<c-/>aに割り当てています。
ただし、<c-/>をテキストとして入力しても動作しないので、代わりに<c-v><c-/>特殊文字を入力する必要があります。

最後に

Vimには乱数を生成する関数がないので、このプラグインではvim-fakeを使いました。
乱数を生成する方法は他にもあります。

初めてvim-themisでテストを書きました。
なかなか面白かったです。

quickrun.vimからPrevimを呼んでMarkdownをプレビューする

PrevimMarkdownプレビュー用のVimプラグインです。
スタンドアロンで動作するのが魅力です。

そんなPrevimをQuickRunで呼び出せたらキーマップの節約になります。
autocmdでMarkdownのときだけキーマップを上書きしてもいいのですが、どうせならスマートに解決。

let g:quickrun_config = {
      \ 'markdown': {
      \   'runner': 'shell',
      \   'outputter': 'null',
      \   'command': ':PrevimOpen',
      \   'exec': '%c',
      \ },
      \ }

ざっと見た感じrunnershellを指定して、command:で始めればVimのコマンドが呼べるようです。

空行をレジスタに送らないVimオペレータ作ってみた

ddで空行を削除したらヤンクしたテキストが上書きされてしまった… なんてことありますよね。
"_ddブラックホールに送れば済む話ですが、空行を消すために毎回レジスタ指定するのは面倒くさい。
そんな思いから、空行(空白のみも含む)を削除したときは自動でブラックホールに送ってくれるオペレータ作ってみました。

nmap d <Plug>(operator-blank2void)
xmap d <Plug>(operator-blank2void)
nmap <silent>dd :exec "normal Vd"<cr>

call operator#user#define('blank2void', 'Operator_blank2void')
function! Operator_blank2void(motion_wise) abort
  let v = operator#user#visual_command_from_wise_name(a:motion_wise)
  if join(getline("'[", "']"), '') =~ '\%^\_s*\%$'
    execute printf('normal! `[%s`]"_d', v)
  else
    execute printf('normal! `[%s`]d', v)
  endif
endfunction

ddのキーマップが冗長に思えますが、これは選択時のチラツキを無くすためです。

switch系プラグイン vim-clurinを強化した話

github.com

switch系プラグインについて

switch系プラグインとは定義されたルールに従ってテキストを置換する類のプラグインです。
よくあるルールはTrueFalseを切り替える、if -> else if -> else の順で切り替えるなどです。

vim-clurinの基本的な使い方は作者であるsynganさんの記事を参照してください。 qiita.com

switch.vimとの違い

変更内容の前に、筆者がvim-clurinを使ってる理由をざっと説明します。
筆者はvim-clurinの前はswitch.vimを使っていました。
筆者が使っていた範囲でのswitch.vimとの大きな違いは以下の2つです。

  1. ファイルタイプ毎の設定をグローバル変数1つで定義できる。
  2. パターンにマッチしたら関数を呼べる。
  3. ぱっと見設定が冗長に感じる
    • 拡張性のために冗長な書き方ができますが、ある程度簡略化して書けます。

関数を呼べるのがなかなか強力です。

改善内容

各変更のサンプルはリポジトリのREADMEを見てください。
GitHubを見る限りsynganさんは現在あまり活動されてないようなので、試す場合は筆者のリポジトリを使ってください。

1. ファイルタイプ別設定のキーに複数言語を指定できる

g:clurinのキーには'c'の用に単一のファイルタイプしか指定できませんでしたが、スペース区切りで複数の言語を指定できるようにしました。
C言語C++共用の設定であればキーは'c cpp'です。もちろん、C言語C++独自の設定と共存できます。

2. デフォルトの設定をファイルタイプ毎に無効に出来る

g:clurin'use_default': 0を指定すれば、デフォルトの設定を全て無効化出来ましたが、ファイルタイプ毎に無効化は出来ませんでした。 各ファイルタイプの設定で、'use_default': 0を指定すれば無効化できるようになりました。

3. 関数内で置換処理を終了できる。

'replace'で関数を指定した場合、'pattern'にマッチしたテキストの置換には指定した関数の戻り値が使用されます。
ただ、複雑な置換をする場合は関数内で置換処理を行って、戻り値は使用しないほうが都合が良いときがあります。
そんな場合は'quit': 1を指定すれば、関数呼び出し後に置換処理を即終了出来ます。

4. 'replace''\2'以降も使えるようにした。

'replace'で使える部分正規表現'\1'だけだったので'\2'以降も使えるようにしました。
ただし、これは破壊的な変更です。
'replace'に指定した関数の第1引数が変わります。

  • before: '\1'の文字列
  • after: '\1'から'\9'までのリスト

最後に

これらの変更でRubydo ~ end{ ~ }の切り替えなど、様々な事が出来るようになりました。
やはり、関数を呼べるのは強力ですね。

vim-clurinはソースが読みやすかったので変更が用意でした。
見習いたいです。