読者です 読者をやめる 読者になる 読者になる

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はソースが読みやすかったので変更が用意でした。
見習いたいです。

btrfsで空き領域が開放されずにのディスク使用率が100%になる。

1週間ほど前、btrfsのデバイスが容量が空いているのにも関わらず使用率が100%のまま変わらない事があった。

いろいろやった気がするが、ほとんど思い出せないので覚えてる限りの解決法を載せておく。

注意点

btrfsは空き領域をプールするのでdfコマンドで見れる情報が正しいとは限らない。
ルートディレクトリの情報を見る場合は下記のコマンド。
filesystemは短縮してfでもいい

btrfs filesystem df -h /

解決策

btrfsのsubmoduleを削除したら領域が開放された。

「領域が開放されないときはsubmoduleを削除しろ」と言うサイトは幾つかあったが、submoduleなんて機能は使った覚えがなかったので無視していた。
しかし、自分で直接使った記憶はなくてもDockerが使っていた
Dockerが領域を開放しなかったわけではないだろうが、submoduleを全部消したら解決。
消えて困るコンテナはなかったので楽だった。

削除方法は覚えてないのでBtrfs - ArchWikiでも見てください。

letを使ってシェルで条件演算子(三項演算子)

ポイント
  • シェル変数は$を省略する
  • 未定義の変数は0として扱われる
  • 文字列は扱えない
  • シェルと反対にtrueは1、falseが0
$ let 'a = 1? 11 : 22'
$ echo $a
11

$ let 'a = 0? 11 : 22'
$ echo $a
22

$ let 'a = 3==3? 11: 22'
$ echo $a
11

条件演算子以外にもインクリメントや復号代入も可能です。

$ let 'b++'
$ echo $b
1

$ let 'c += 3'
$ echo $c
3

ちなみに$((expr))でも同様の事が可能。

$ echo $((d -= 10))
echo -10
$ echo $d
-10

Have a nice shell life!