Vimのカバレッジを見る

この記事はVim Advent Calendar 2017の2日目の記事です。

Vimカバレッジcoveralls.iocodecov.io上で見ることができますが、手元で見たくなることもあると思います。

そんな時はMakefileに書いてある通り、次のようにしてカバレッジを計測し、生成されたhtmlから見ることができます。

# Vimのリポジトリ直下に移動

# 各種フラグに--coverageをつけてビルド、他はお好みで
CFLAGS=--coverage LDFLAGS=--coverage ./configure --with-features=huge && make

cd ./src

# まずはゼロカバレッジの初期データを作る
lcov -c -i -b . -d objects -o objects/coverage_base.info

# テストを走らせてカバレッジ情報を作る
make test
lcov -c -b . -d objects/ -o objects/coverage_test.info

# 初期データとテストのカバレッジ情報を結合する
lcov -a objects/coverage_base.info -a objects/coverage_test.info -o objects/coverage_total.info

# 結果をobjects/index.htmlとして生成する
genhtml objects/coverage_total.info -o objects

ビルド時に--coverageをつけることでgcovが有効になり、実行された行が記録されるようになります。
lcov(と付属のgenhtml)はそのフロントエンドツールです。

さて、実行された行が記録されるということは、Vimで特定の操作をした際にどの関数が呼ばれたのか調べることができる、ということです。

しかし、gcovはプログラムの終了直前に保存処理を行うため、目的の操作だけの結果を知るには

  1. Vimの起動
  2. コマンドやキー入力
  3. Vimの終了

の順で操作をしつつ、1と3は除外する必要があります。
幸いgenhtmlには-b(--baseline-file)オプションがあるので次のようにすれば実現できます。

# 上と同じ
lcov -c -i -b . -d objects -o objects/coverage_base.info

# 起動と終了のカバレッジを取得
VIMRUNTIME=../runtime ./vim --clean -c 'q'
lcov -c -b . -d objects/ -o objects/coverage_quit.info
lcov -a objects/coverage_base.info -a objects/coverage_quit.info -o objects/coverage_baseline.info

# カバレッジをリセット
lcov -z -d objects/

# 起動と:smileと終了のカバレッジを取得
VIMRUNTIME=../runtime ./vim --clean -c 'smile | q'
lcov -c -b . -d objects/ -o objects/coverage_smile.info
lcov -a objects/coverage_base.info -a objects/coverage_smile.info -o objects/coverage_result.info

# 生成されるのはcoverage_result.infoからcoverage_baseline.info分のカウントを減らした結果
genhtml -b objects/coverage_baseline.info objects/coverage_result.info -o objects

というわけで:smile*1にはsyntax.c以下のソースが使われていることがわかりました。

f:id:daisuzu:20171202170437p:plain

処理を正確に追うにはデバッガを使うのが確実だとは思いますが、ソースの構造がある程度わかっていないと難しかったりもします。

f:id:daisuzu:20171202173329p:plain

なので、さらっと概要と知りたい時やソースコードリーディングのお供にでも是非カバレッジを活用してみてください。

:smile
                            oooo$$$$$$$$$$$$oooo
                        oo$$$$$$$$$$$$$$$$$$$$$$$$o
                     oo$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o         o$   $$ o$
     o $ oo        o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o       $$ $$ $$o$
  oo $ $ "$      o$$$$$$$$$    $$$$$$$$$$$$$    $$$$$$$$$o       $$$o$$o$
  "$$$$$$o$     o$$$$$$$$$      $$$$$$$$$$$      $$$$$$$$$$o    $$$$$$$$
    $$$$$$$    $$$$$$$$$$$      $$$$$$$$$$$      $$$$$$$$$$$$$$$$$$$$$$$
    $$$$$$$$$$$$$$$$$$$$$$$    $$$$$$$$$$$$$    $$$$$$$$$$$$$$  """$$$
     "$$$""""$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$     "$$$
      $$$   o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$     "$$$o
     o$$"   $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$       $$$o
     $$$    $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" "$$$$$$ooooo$$$$o
    o$$$oooo$$$$$  $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$   o$$$$$$$$$$$$$$$$$
    $$$$$$$$"$$$$   $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$     $$$$""""""""
   """"       $$$$    "$$$$$$$$$$$$$$$$$$$$$$$$$$$$"      o$$$
              "$$$o     """$$$$$$$$$$$$$$$$$$"$$"         $$$
                $$$o          "$$""$$$$$$""""           o$$$
                 $$$$o                                o$$$"
                  "$$$$o      o$$$$$$o"$$$$o        o$$$$
                    "$$$$$oo     ""$$$$o$$$$$o   o$$$$""
                       ""$$$$$oooo  "$$$o$$$$$$$$$"""
                          ""$$$$$$$oo $$$$$$$$$$
                                  """"$$$$$$$$$$$
                                      $$$$$$$$$$$$
                                       $$$$$$$$$$"
                                        "$$$""""

Press ENTER or type command to continue

*1:v8.0.1362

VimConf 2017でコントリビューターになる話をしてきた

VimConf 2017 - An international Vim Conference

speakerdeck.com

という発表をしてきました。

最初は25分も話すことなんてあるかな?なんて思ってたら意外と良い感じの時間になりました。
少し足りなかったところを少し補足しておくと、

gitコマンド

  • v8.0.0512の調査で使ったgit bisect
# <bad> <good>の順番で指定する
git bisect start v8.0.0104 v8.0.0000

あとは都度makeし、手動で補完させてgoodかbadを判定していきました。
※コマンドからの補完だと再現しなかったため

活動時間帯について

  • 不具合の発見〜1次調査・応急処置
    • 職場
  • 2次調査〜パッチ作成

patchを送った後について

  • 質問とかツッコミは無かったの?
    • 全く無かった
    • パッチが小さかったからかも
    • もしくはタイミングが良かったからとか

場合によってはなかなか取り込まれないこともあったりするようです。

MacVimの不具合って?

マルチバイト文字が潰れて表示されてしまうという問題を修正しました。 github.com

コントリビュートする内容について

  • 新機能を追加したいんだけど
    • 色々大変だと思いますが、新機能を作れるくらいのパッションがあれば大丈夫だと思います!
  • ドキュメントや軽微な(typo・インデント)修正でも良いの?
    • OKです!

ちなみにコントリビュートに対するスタンスは最近聴いた

tech.gunosy.io

にすごく共感するものがあります。

vim-jpについて

vim-jpに気軽に相談してみましょう」なんて勝手に言ってしまって良いのかな?
とか思っていたんですが、なんと本日vim-jpのメンバー入りをしました。
もし何かあれば出来る範囲でサポートしていきたいです!
ということを本番でも話せれば良かったんですが、アドリブ力が無く。。。

     

という感じです。

スライドの英訳を助けてくれた ujihisa さん、
発表の翻訳をしてくださった sandkatt さん、
そしてスタッフのみなさま、
ありがとうございました!お疲れ様でした。

プレゼン資料にハイライトされたコードを貼り付ける

プレゼン資料を作っててコードを綺麗に表示させたくなったんだけど、 何かをインストールしたりとかは面倒だったのでvim*1を使ってやることにした。

以下のコマンドでブラウザが開くのでコピペするだけ。

" mac
:TOhtml | w | !open %

" win
:TOhtml | w | !start %

" linux
:TOhtml | w | !xdg-open %

調べてみたらプラグインが見つかったけどmac用だしインストールが面倒なので試していない…

github.com

*1:自分が使うPCには常にインストールされているし

Meguro.vim #4でプラグインを作った

Meguro.vim #4で自分用の:VimFilerSimpleに代わるプラグインを作りました。

github.com

vimfilerは機能が豊富でそこまで不満があったわけではないのですが、

  • 常に全ての機能を必要としているわけではない
  • unite.vimに依存している

ので、一番使う頻度の高い ファイルをツリー形式で表示する だけのプラグインが欲しかったからです。

f:id:daisuzu:20170715163006p:plain

実装としてはtreeコマンドの結果を

というものです。

マッピングは一切用意していないため、ファイルを開く時には標準機能のgfなどを使うことになります。
また、プラグインの起動もTreeコマンドしか提供していないため、使い方に合わせたマッピングをvimrcに追加していくというデザインにしています。

" Example:

" 垂直分割してツリーを表示し、ウィンドウの幅を32にする
nnoremap <silent> <Leader>vt
      \ :<C-u>execute 'vertical '. v:count .'Tree'
      \ <Bar> vertical resize 32
      \ <CR>

" ツリーを閉じずにファイルを開く
nnoremap <silent> <C-w>e
      \ :<C-u>let @a = fnameescape(expand('<cfile>'))
      \ <Bar> wincmd w 
      \ <Bar> execute 'edit ' . @a
      \ <CR>

Vimでファイルを開く方法(基本編)

この記事はVim Advent Calendar 2016の4日目の記事です。

Vimでファイルを開くのに

プラグインを使っている人はたくさんいると思います。
これらのプラグインは大変便利なインターフェースを提供してくれているのですが、 依存しすぎてしまうと標準機能でファイルを開く方法を忘れてしまうかもしれません。

...さすがにそんなVimmerはいないと思いますが、ファイルを開く

をおさらいしてみましょう。

コマンドで開く

以下のコマンド*1の引数としてファイル名を指定します。

コマンド 説明
:e[dit] 現在のバッファでファイルを開く
:sp[lit] 水平分割してファイルを開く
:vs[plit] 垂直分割してファイルを開く
:tabe[dit] 新しいタブページを作成してファイルを開く

このとき、

  • <Tab>(または<CTRL-I>)でファイル名の補完
    • 動作はwildmode(:help wildmode)で変更可能
  • <CTRL-D>で入力にマッチするファイル名の一覧表示

をすることができます。

また、対象のファイル名を探すのに

  • ***などのワイルドカード(:help wildcard)
  • :p:hなどのファイル名修飾子(:help filename-modifiers)

を用いることもできます。

" <CTRL-D>で一覧を表示し、
:e */index<CTRL-D>
public/index.html  src/index.css      src/index.js

" <Tab>を押すと最初の候補が選択される
:e */index<Tab>
:e public/index.html

" もう1度<Tab>を押すと次の候補が選択される
:e public/index.html<Tab>
:e src/index.css
  • /usr/lib/golang/src/配下のreader.goを探す
:e /usr/lib/golang/src/**/reader.go<CTRL-D>
/usr/lib/golang/src/archive/tar/reader.go
/usr/lib/golang/src/archive/zip/reader.go
/usr/lib/golang/src/bytes/reader.go
/usr/lib/golang/src/compress/lzw/reader.go
/usr/lib/golang/src/compress/zlib/reader.go
/usr/lib/golang/src/debug/elf/reader.go
/usr/lib/golang/src/encoding/csv/reader.go
/usr/lib/golang/src/go/doc/reader.go
/usr/lib/golang/src/image/gif/reader.go
/usr/lib/golang/src/image/jpeg/reader.go
/usr/lib/golang/src/image/png/reader.go
/usr/lib/golang/src/mime/quotedprintable/reader.go
/usr/lib/golang/src/net/textproto/reader.go
/usr/lib/golang/src/strings/reader.go
/usr/lib/golang/src/testing/iotest/reader.go
" カレントディレクトリが/rootの状態で
:pwd
/root

" swagger-codegen/samples/server/petstore/go-api-server/main.goを開いている時
:echo expand('%:p')
/root/swagger-codegen/samples/server/petstore/go-api-server/main.go

" 最初の:hで"/main.go"、次の:hで"/go-api-server"が除去される
:e %:h:h/**/pom.xml<CTRL-D>
swagger-codegen/samples/server/petstore/java-inflector/pom.xml
swagger-codegen/samples/server/petstore/java-msf4j/pom.xml
swagger-codegen/samples/server/petstore/jaxrs/jersey1/pom.xml
swagger-codegen/samples/server/petstore/jaxrs/jersey2/pom.xml
swagger-codegen/samples/server/petstore/jaxrs-cxf/pom.xml
swagger-codegen/samples/server/petstore/jaxrs-cxf-cdi/pom.xml
swagger-codegen/samples/server/petstore/jaxrs-resteasy/default/pom.xml
swagger-codegen/samples/server/petstore/jaxrs-resteasy/joda/pom.xml
swagger-codegen/samples/server/petstore/jaxrs-spec/pom.xml
swagger-codegen/samples/server/petstore/scalatra/pom.xml
swagger-codegen/samples/server/petstore/spring-mvc/pom.xml
swagger-codegen/samples/server/petstore/spring-mvc-j8-async/pom.xml
swagger-codegen/samples/server/petstore/springboot/pom.xml
swagger-codegen/samples/server/petstore/undertow/pom.xml

キーマッピングで開く

ファイル名がある場所にカーソルを移動し、以下のキー*2を入力します。

キー 説明
gf 現在のバッファでファイルを開く
CTRL-W f 水平分割してファイルを開く
CTRL-W CTRL-F
CTRL-W gf 新しいタブページを作成してファイルを開く

現在開いているバッファにファイル名があれば良いですが、無い場合は:r!lsfindなどの外部コマンドを用いてファイル一覧を作成してしまいましょう。

" 新しいバッファにカレントディレクトリのファイル一覧を作成
:new | r! ls

なお、:newでバッファを作成した場合、デフォルトではgfでファイルを開く際にエラー(E37)になってしまいます。
その場合、

  • set hidden ... ウィンドウ内に表示されなくなる際にバッファを隠す

    • 全てのバッファが対象
    • Vim終了時に未保存バッファが残っているとエラーになる
  • set bufhidden=hide ... ウィンドウ内に表示されなくなる際にバッファを隠す

    • カレントバッファのみが対象
    • Vim終了時に当該バッファが未保存だとエラーになる
  • set buftype=nofile ... バッファを書き込まれる予定のないバッファにする

    • カレントバッファのみが対象
    • Vim終了時に当該バッファが未保存でもエラーにならない

などの設定でバッファを保存せずに切り替えられるようにしてあげる必要があります。

また、ファイル一覧に大量のファイルが表示されてしまった場合

  • /{pattern}で検索したり、
  • :g/{pattern}/dでフィルタしてあげると

目的のファイルが探しやすくなります。

:new | r! find . -type f
" Windowsの場合
:new | r! dir . /b /s /a-d
  • 現在開いているファイルがあるディレクトリ配下のファイル一覧を作成
" findの引数は実行前に<Tab>で展開しておく
:new | r! find %:p:h<Tab> -type f
" Windowsの場合
:new | r! dir %:p:h<Tab> /b /s /a-d
  • ファイル一覧を作成するユーザコマンドの定義

上記のコマンドを毎回入力するのが面倒な場合、.vimrcに以下のようなコマンドを追加しておくと便利です。

" 新しいscratchバッファ(:help special-buffers 参照)を作成する
command! -bar NewScratch new | setlocal buftype=nofile bufhidden=hide noswapfile

" :NewScratchで作成したバッファにfindコマンドの出力内容を挿入する
" FIX: <args>は""で囲まないとスペースの入ったパスが正しく処理できない(derisさん、ありがとうございます)
command! -nargs=1 -complete=dir Files NewScratch | r! find "<args>" -type f
" Windowsの場合
" FIX: <args>は""で囲まないとスペースの入ったパスが正しく処理できない(derisさん、ありがとうございます)
" command! -nargs=1 -complete=dir Files NewScratch | r! dir "<args>" /b /a-d /s

" %:p:hを引数として:Filesを実行する(<args>には%:p:hが展開された状態で渡される)
command! FilesBuffer Files %:p:h

" .を引数として:Filesを実行する
command! FilesCurrent Files .

まとめ

Vimの標準の機能でファイルを開く方法を紹介しました。

単にファイルを開くだけなら十分ですが、インタラクティブ性や非同期性が必要な場面になると物足りないところがあるかもしれません。
そういった時には組み込み関数と合わせて使ってみたり、プラグインを活用すると良いと思います。

*1:詳細は :help edit-a-file, :help opening-window, :help tab-page-commands 参照

*2:詳細は :help window-tag 参照

VimConf 2016 に行ってきた

VimConf 2016に行ってきた。

発表メモ

Introduction to Vim 8.0 by Ken Takata
  • 最近Vimに入った機能の中でjob, timer, packagesは使ったけど *1 *2 Partial, lambda, closureはまだ触っていないので試してみたい
  • 日本人VimmerVim本体に多くのパッチを送っているのは知っていたけど改めて数字で見てみると予想以上にすごかった
  • Vimのテストが充実してきたおかげで新機能の使用例としても参考にできてありがたい
Vim as the MAIN text editor by bird_nitryn
  • 自分もVimに乗り換えた時は同じようにメインエディタにして使いまくった
  • 最近はVimmerを育てる機会の方が多いので参考にしたい
Denite.nvim ~The next generation of unite~ by Shougo
  • unite.vimを使っていてパフォーマンス的な不満は特になかったけどデモに触発されてMacVimでDenite.nvimを試してみたらすごく速かった
  • Decoupled UIはちょっと気になるのでそのうち試してみたい
Go、C、Pythonのためのdeoplete.nvimのソースの紹介と、Neovim専用にpure Goでvim-goをスクラッチした話 by zchee
  • neovimに全然興味なかったけど発表を聞いてとりあえずインストールしてみることにした
  • nvim-goの作りが気になるのでソースを見てみる
エディタの壁を越えるGoの開発ツールの文化と作成法 by tenntenn
  • 自分の近くでは「Goやったことある方」でほとんどの人が手を挙げていた
  • gofmt(ごーふむと)、GOOS(ぐーす)、GOARCH(ごーち)
  • ファイルを生成するツール *3 は作ったから次はファイルを書き換えるツールと思っていたのでgorenameのところがとても参考になった
vim-mode-plus for Atom editor by t9md
  • vim-mode-plusのアーキテクチャとそこに到るまでの考え方がただただすごかった
    • Vimでテキストを編集するということへの捉え方
    • モーションやオペレータの表現
  • persistence-selectionはVimにも欲しい
Vimの日本語ドキュメント by MURAOKA Taro
  • 「翻訳作業に手と目と知恵を貸してくれる人募集中」
    • リファレンス&ユーザー・マニュアル
    • メッセージ
    • メニュー
    • など
  • 脱線しかけたのはAho-Corasick法(文字列探索アルゴリズム)
  • 技術者はSPOFは排除したいはず
Vim script parser written in Go by haya14busa
  • Fixer、Formatterに期待!
  • stacktrace便利そう
  • Vim8とV8(node)は紛らわしい?
僕の友達を紹介するよ by aiya000
Best practices for building Vim plugins by thinca
  • :help design-goalsは知らなかった
  • シャイで有名なthincaさんがハイテンションで発表に懸ける強い想いが感じられた

感想

  • Vimハラスメント」に気をつけようと思った
    • (自分としてはネタのつもりだけど)職場でよく「Vimを使おう」と言っているので...
      • 開発関連のやるべき事をやってくれていれば何も言わないけどできていない
      • Vimだったらそのためにどうすれば良いかはいくらでもサポートできる
      • というのが言い訳
  • 今回はチームのメンバ(2名)に参加してもらう事に成功した
    • 楽しんでもらえたようで良かった

終わりに

発表者のみなさま、スタッフのみなさま、会場を提供してくださったミクシィさん、
ありがとうございました。
来年も是非参加したいです。

neocompleteとneobundleを捨てた

半年ほど使っていなかったのでvimrcを整理するタイミングで完全に消すことにした。
ただ長年ツートップだったプラグインなので消すに至った経緯を軽くメモっておく。

neocomplete

今の自分にはVim標準の補完機能(ins-completion)で十分だった。

  • 自動補完が発動するとVimが固まって使い物にならなくなることがある
    • 特定の環境でたまにしか発生しないけど発生するとすごくストレス
  • 自動補完されないと補完自体ができないと思ってしまう
    • そんなことはなく、基本的には常に手動補完可能
    • そもそも自動補完の対象になっていない補完機能もある
  • if_luaが必須
    • どうせ有効化するけどプラグインのためだけにluaを入れたくはなかった

昔は自動補完が必須だと思っていたけどVimの熟練度が上がったからか、

「補完したくなったらその時にCTRL-X(or N,P)押せばいいじゃん」

と思うようになった。
CTRL-Xが押しにくいと感じていたのもいつの間にか全然気にならなくなっていた。

neobundle

Vim標準のパッケージ機能(packages)を使うことにした。

  • 開発終了宣言されたのでどうするか悩んでいたけどどれも決め手に欠けていた
    1. それでもneobundleを使い続ける
      • どうせいつかは移行しないといけなくなるはず
    2. 順当にdein.vimに移行する
      • 機能にあまり魅力を感じなかったし、安定性や将来性に不安があった
    3. ミニマリストを謳うvim-plugに移行する
      • Installationを見た瞬間にもうめんどくさくなった
  • Vimプラグインマネージャーに求める最低限の機能はただ1つ
  • あったら便利な機能は自分でなんとかするか諦める
    1. プラグインのインストールとアップデート
      • Bitbucketにあるプラグインは使わないのでgitさえ叩ければOK
      • 単純なシェルコマンドならVimに依存する必要もない
      • それでも後処理以外はVimからできるようにはしたけど
    2. 依存関係のあるプラグインを自動で解決してインストールする機能
    3. 遅延読み込み
      • 個別に設定するのが面倒なのでbackground loadingで代用している
      • その方が起動時間の短縮にもなるし

どうもプラグインを管理するプラグインに依存するのに疲れたっぽい。
決定版が出るまでは当分これでいくつもり。

次は脱unite!?

インターフェースは便利なんだけどやっぱり安定性はイマイチ...
(今までできていたことが急にできなくなったりすることがあるし)
いつ無くなっても困らないくらいには依存度を減らすようにしておくかな。