ハイパーマッスルエンジニア

Vim、ShellScriptについてよく書く

Neovim: サクッとFloatingWindowにテキストを表示するプラグイン nvim-popup-message

github.com

https://github.com/Rasukarusan/blog-assets/blob/master/vim-popup-message/demo.gif?raw=true
:messagesの最後の行をサクッと表示している

これなに

  • NeoVim専用プラグイン
  • サクッとFloatingWindowを表示する
  • 現在カーソル位置のすぐ下(または上)にFloatingWindowを作成
  • カーソル移動したらウィンドウは即消える
  • 引数で渡したテキストをFloatingWindowに載せる

インストール

Plug 'Rasukarusan/vim-popup-message'

呼び出し方

call popup_message#open(<args>)に表示したいメッセージをいれてあげればOK。

" 'MM'と入力するとメッセージを表示する
nnoremap MM :call popup_message#open('Any messages!')<CR>

関数をぶち込んでもOK。

" メッセージを返す関数
function! s:get_message()
  return 'Any messages!'
endfunction

nnoremap MM :call popup_message#open(<SID>get_message())<CR>

なんで作ったか

  • FloatingWindowはめちゃくちゃ便利だけど作成が面倒
  • Pluginとしての構造でVimScriptを作ってみたかった

元々は「:messagesの最後の行だけFloatingWindowで表示する」というものを作っていたが、汎用的なものを求めた結果こうなった。

FloatingWindowはめちゃくちゃ便利だけど作成が面倒

FloatingWindowを作るには下記のような記述が必要になる。

let config = {
  \'relative': 'editor',
  \ 'row': row,
  \ 'col': col,
  \ 'width': width,
  \ 'height': height,
  \ 'anchor': 'NW',
  \ 'style': 'minimal',
  \}
let buf = nvim_create_buf(v:false, v:true)
let win_id = nvim_open_win(buf, v:true, config)

そこまで多くないと思うかもしれないが上記の他に、位置を示すrow, colや大きさを示すwidth, heightを決める必要があって、その計算がなかなか面倒。 現在カーソル位置のすぐ下にパッと出したいだけなのにいちいち計算しないといけないのがきつい。

Pluginとしての構造でVimScriptを作ってみたかった

今まで自作のVimScriptはvimrcに全部ぶちこんでいたので、Pluginとして作ってみたかった。あとvimrcに全部書くスタイルだと、共通で使いたい関数がある場合に記述場所を上の方にしないと不便など、面倒な問題がいくつか出てきたため。

Pluginの作り方については下記の記事が大変参考になった。

knowledge.sakura.ad.jp

終わり

カーソル移動やINSERTモードに入ったときに関数を呼ぶ、というのがどうやってやるのかがわかったり、プラグイン固有の変数の値をユーザーが設定できるようにする方法など結構知らなかったことを知れたのでやってよかった。

Neovim: VirtualTextを使ってVim上に注釈を出す

VirtualTextとは

NeoVim 3.2で導入された注釈機能。行の最後にバッファとは別にテキストを表示することができる。

https://camo.githubusercontent.com/de52369c7356b9121f6983fd574c329470836e28/687474703a2f2f692e696d6775722e636f6d2f384167726449332e676966

github.com

使い方

nvim_buf_set_virtual_text({buffer}, {ns_id}, {line}, {chunks}, {opts})
Column Column
buffer Buffer番号、カレントバッファなら0を指定
ns_id NamespaceId、任意の数値または0を指定すると適当に割り振られる。nvim_create_namespace()で作るのがベター。
line VirtualTextを出したい行
chunks VirtualTextに出したい文字列とハイライトグループをセットにした配列
opts 現在使われていない

とりあえず試してみる。

" VirtualTextを作成
:let ns_id = nvim_buf_set_virtual_text(0, 0, 0, [['Hello World']], {})

" 作成したVirtualTextを消す
:call nvim_buf_clear_namespace(0, ns_id, 0, -1)

f:id:rasukarusan:20210111174045p:plain
画像では伝わりづらいが"Hello World"の部分がVirtualText

ちゃんと書くとこんな感じ。

" カレントバッファの現在行にVirtualTextを作成
let line = line('.')
let buffer = bufnr('')
" ns_idは重複等避けるためnvim_create_namespace()で作成
let ns_id = nvim_create_namespace('my-virtual')

call nvim_buf_set_virtual_text(buffer, ns_id, line-1, [['Hello World']], {})

色を付ける、ハイライトグループを指定。

ハイライトグループの一覧はこちらが参考になる。

" ハイライトグループである'ErrorMsg'を指定
call nvim_buf_set_virtual_text(buffer, ns_id, line-1, [['Hello World', 'ErrorMsg']], {})

f:id:rasukarusan:20210111174523p:plain
'ErrorMsg'の色になる

自分で作成したハイライトグループも指定可能。

hi Hoge guibg=#123456
call nvim_buf_set_virtual_text(buffer, ns_id, line-1, [['Hello World', 'Hoge']], {})

f:id:rasukarusan:20210111174705p:plain
自分で作成したグループの色(#123456)になる

複数のテキストを表示する。

call nvim_buf_set_virtual_text(buffer, ns_id, line-1, [['Hello', 'ErrorMsg'], ['World', 'Hoge']], {})

f:id:rasukarusan:20210111174850p:plain
'Hello'と'World'で別のハイライトを指定

VirtualTextの使いみち

Syntaxチェック

https://github.com/Rasukarusan/blog-assets/blob/master/nvim-virtual-text/ale.gif?raw=true
ale + textlintでシンタックスエラーをリアルタイムに表示
github.com

オプションであるg:ale_virtualtext_cursor1にするとVirtualTextが有効になる。

" VirtualTextを有効にする
let g:ale_virtualtext_cursor = 1
" prefixの文字列
let g:ale_virtualtext_prefix = ' --> '

カーソルを合わせるだけでどんなエラーが出ているのかわかるので便利っちゃ便利。

カラーコード表示

https://camo.githubusercontent.com/cfc61743d38e4e166546d004d07bf9cc853d72a282f2c19a56576f0f030bb7bc/68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f50696b5169616b6a324e597846446d63646c2f67697068792e676966
カラーコードを視覚的に表示

github.com

カラーコードのビジュアルをVirtualTextとして表示する。これはめっちゃいい。

VirtualTextの特徴

  • EOL(行の最後)に表示される
  • テキストの折返しはされず、途中で見切れる
  • Bufferではないので:ls!には残らない

なので長文は適しておらず、行によってはVirtualTextが表示できない可能性もある。
行の最後に表示されるので、フォーマットが決まっている場合や、色表示のようにピンポイントで表示するようなことに適している感じ。

ただFloatingWindowとは違い、Bufferではないので複数表示させたとしても動作が重くならない点は利点。

https://github.com/Rasukarusan/blog-assets/blob/master/nvim-virtual-text/floatingWindow_vs_virtualText.gif?raw=true
左:VirtualText、右:FloatingWindow。どちらも同じタイミングで作成/削除を行った。VirtualTextは作成も削除も一瞬。

終わり

今の所どうやって使うかはあまり思い浮かばないけど、ピンポイントで使う場面がありそう。
使い方が簡単なのもいい。リアルタイム翻訳とかに使うとビジュアル的に良さそう。

Exコマンドの補完をfzfでやる

VimのExコマンドって結構長いものであったり、どんなコマンドがあったか全然覚えられない。
TABで一応補完は出てくるが、補完を出した後にインタラクティブに絞り込みたいときって結構あると思う。

fzfでExコマンドのフィルタリングをできるようにした

https://github.com/Rasukarusan/blog-assets/blob/master/vim-ex-completion/demo.gif?raw=true
コマンドラインモードでTABを押すとfzfの絞り込みを開始できる

TABでいきなりfzfを出してもいいし、途中まで入力してから絞り込みを開始してもいい。

環境

  • NeoVim 0.43
  • tmux 3.2rc

ソース

:でコマンドラインモードに入ったらTABでfzfの絞り込みが始まるようになっている。

" =============================================
" Exコマンドの補完をfzfでする
" =============================================
function! CompletionExCmdWithFzf()
    let currentCmdLine = getcmdline()
    let isVisualMode = stridx(currentCmdLine, "'<,'>") != -1 
    let isCall = stridx(currentCmdLine, 'call ') != -1 
    let type = 'command'
    let prefix = ''

    if isCall == 1
      let cmdLines = split(currentCmdLine, ' ')
      let currentCmdLine = len(cmdLines) > 1 ? cmdLines[1] : ''
      let type = 'function'
      let prefix = 'call '
    elseif isVisualMode == 1
      let cmdLines = split(currentCmdLine, '>')
      let currentCmdLine = len(cmdLines) > 1 ? cmdLines[1] : ''
      let type = 'command'
      let prefix = "'<,'>"
    endif

    let result = fzf#run({
      \'source': getcompletion(currentCmdLine, type),
      \ 'tmux': '-p60%,60%',
      \ 'options': '--no-multi --bind tab:down'
      \}
    \)
    if len(result) == 0
      return ''
    endif

    " fzf#runの結果はlist型で返されるので、そのままコマンドラインに返すと^@が末尾に付与される
    " ^@を削除するためjoin()している
    return prefix . join(result, '')
endfunction
cnoremap <TAB> <C-\>eCompletionExCmdWithFzf()<CR>

注意したいのは、tmux popupが使用できる端末に限る。
tmux popupについては別記事で書いたのでそちらを見てもらいたい。

www.rasukarusan.com

いくつかハマったポイントをまとめておく。

コマンドラインモードの入力を取得する

:を打った後につづく文字列を取得するにはgetcmdline()を使う。 ただ、getcmdline()CTRL-\ e {expr}でしか使えない。なのでcnoremap <TAB> <C-\>eCompletionExCmdWithFzf()<CR>という形にしている。

https://vim-jp.org/vimdoc-ja/eval.html#getcmdline()

CTRL-\ e {expr}内でfzfと対話を行う

通常fzfで絞り込みを行いときは下記のようにする。

let result = fzf#run({
  \'source': getcompletion(currentCmdLine, type), 
  \ 'down': '50%'
  \}
\)

ただ、CTRL-\ e {expr}の関数内でfzfを使うと下記のようなエラーが出て使えない。

function CompletionExCmdWithFzf[11]..fzf#run[58]..<SNR>121_execute_term[3]..<SNR>121_split の処理中にエラーが検出されました:
行   31:
E523: ここでは許可されません: botright 33new

全然調べていないので合ってるかわからんが、どうも画面を分割してしまうようなことは駄目っぽい。

ただ、抜け道があってfzfの起動方法をtmux popupにすることで対話が可能になる。

let result = fzf#run({
  \'source': getcompletion(currentCmdLine, type), 
  \ 'tmux': '-p90%,60%'
  \}
\)

tmux popup の思わぬ恩恵がここにある。最高ですね。

終わり

coc.nvimのコマンドとかdeinのコマンドとか長くて打つのがしんどかったけど、これで楽になった。

Vimに生命を吹き込む(NeoVim×FloatingWindow)

f:id:rasukarusan:20191208172849p:plain
Vimに生命を吹き込む(NeoVim×FloatingWindow)

はじめに

この記事はHameeアドベントカレンダー13日目の記事です。

最近VimからNeoVimに乗り換えた。
VimPluginの王ShougoさんのPluginもいくつかNeoVim対応が増えてきていた(というかNeoVimにした方が楽な状態になってた)ので、NeoVimにしてみた。

NeoVimの中でも特に気になったのがFloating Window。今回はこれの紹介とVimScriptを開発していく上でのちょっとしたTipsを書いていく。

Floating WindowでVimに「生命」を吹き込みたかった

FloatingWindowで遊んで出来上がったものがこれ。
pasteするときに上から降らしたり、deleteするときに一個ずつバラバラにしたりして、さもVimが生きているかのような振る舞いを目指した。

https://user-images.githubusercontent.com/17779386/69538032-bbfabd80-0fc4-11ea-85e2-4da46b2317b8.gif
TETRIS Pasteと名付けた

https://rasukarusan.github.io/blog-assets/neovim-floating-window-tips/randomcolor.gif
floating windowでdeleteをTETRISっぽく

ソースはこちら github.com

github.com

これを紹介したかっただけなので今回の記事はもう満足してる。お疲れサマンサ。

Floating Windowって何?

画面分割などでウィンドウをタイル型に並べるのではなく、重なるように配置できるNeoVimの新機能。イメージ的には補完表示で出てくるウィンドウに近い。
FloatingWindowで作成されたウィンドウはVimの通常のバッファとほぼ同じなので、移動などの操作もhjklだし、勿論Vimコマンドも使用できる。 詳しくは下記サイトが参考になる。 https://rhysd.hatenablog.com/entry/2019/03/10/230119

https://rasukarusan.github.io/blog-assets/advent-calender-2019-hamee/neovim_floating.gif
floating windowで関数説明やGitのログを表示できる

FloatingWindowはNeoVimにしか入っていないが、通常のVimでも「ポップアップウィンドウ」という名前で導入されている。 生成の仕方はそれぞれ違うが、たぶん同じようなことができるんだろう。

VimScriptで遊ぶときのTips

今回のFloatingWindowのように、Vimで新しい機能が導入されたときや、.vimrcに自作関数を作りたいときに、どうやって遊んでいくか。自分はこうやっているよというのを紹介したい。

FloatingWindowで遊ぶときのTipsは別記事に上げたので興味ある人はそっちをどうぞ。FloatingWindowの話はここで終わりだ。

1.VimScriptの実行の仕方

例えば下記サイトに書かれているコードを試したいとする。

qiita.com

上記サイトから抜粋。

" 新しい floating window を開いて、そのウィンドウにフォーカスを移す。
" 第2引数が v:true になっているのでフォーカスが移ります。
call nvim_open_win(bufnr(''), v:true, {'relative': 'cursor', 'height': 3, 'width': 10, 'row': 1, 'col': 1})
" Hoge を定義(黄色地に黒文字になります)
hi Hoge guifg=#2e3440 guibg=#ebcb8b
" Hoge をデフォルトのハイライトにする(Normal の代わりに使う)。
set winhighlight=Normal:Hoge

このコードを試したいと思ったらやることは3つ。

  1. どこでもいいのでvim test.vimでスクリプトを作成する
  2. コピぺする
  3. :source %でスクリプトを実行する

こんな感じ。

https://rasukarusan.github.io/blog-assets/advent-calender-2019-hamee/source.gif
:source %でスクリプトを実行する

source %%はVimではカレントバッファのファイル名を示すので、「現在開いているファイルを読み込む(実行する)」に等しい。%以外にも色々あるので:help cmdline-specialで調べてみると面白い。

2. もっと楽に実行する

:source %をキーマップするととっても楽。
自分は.vimrcに以下を記載して、Shift+Sで実行できるようにしている。

" 現在開いているスクリプトを読み込む
nnoremap S :source %<CR>

同様に、.vimrcに追加したものをすぐに反映したいときは以下でいける

" source ~/.vimrcを簡略化
nnoremap rr :source ~/.vimrc<CR>

3. 関数化して呼び出す

VimScriptで関数を作るときはTest()のように先頭を大文字にしてグローバル関数して定義するか、s:test()のようにs:をつけてローカル関数にするかなどいくつかやり方がある。基本はこの2つでいいんじゃねえかな。

function! Test()
    echo "Test"
endfunction

function! s:test()
    echo "test"
endfunction

function! s:test_return()
    return 5
endfunction

call Test()
call s:test()

let i = s:test_return()
echo i

" Test
" test
" 5

ただ、s:の関数を呼び出すときは少し注意が必要で、nnoremapなどのmap系で使う場合はs:ではなく<SID>test()としなければならない。

nnoremap Test :call <SID>test()<CR>

参考 https://whileimautomaton.net/2008/06/30070800

おまけ

ちなみにNeoVimに乗り換えるのはめちゃくちゃ楽で、.vimrcinit.vimにするだけでOK。NeoVim自体もbrew でインストール可能なので一回やってみてもいいかもね。
.vimrcの内容もほぼ書き換えなしでそのまま移行できる。自分は1,2箇所の書き換えのみでスルッと移行できた。

以下の3ステップで導入完了となる。

# NeoVimインストール
$ brew install neovim
# 今まで使っていた.vimrcをそのまま流用
$ ln -sf ~/dotfiles/.vimrc ~/.config/nvim/init.vim
# NeoVim起動
$ nvim

終わり

NeoVimはおもしろい。

NeoVimのFloating Windowで遊ぶためのTips

f:id:rasukarusan:20191205203259p:plain
NeoVimのFloating Windowで遊ぶためのTips

この記事はVim3 Advent Calendar 2019 6日目の記事です。

NeoVimに乗り換えるきっかけにもなった、Floating Window。
このFloating Window、めちゃくちゃ遊べる要素があるなと思い、こんなものを作って遊んでいた。

https://user-images.githubusercontent.com/17779386/69766815-1199ba80-11bd-11ea-9a90-da266c66e44f.gif
私がfloating windowで遊ぶ様子

遊んでいるときに色々役立ったVim関数をまとめたいと思う。

こちらも合わせてどうぞ。

www.rasukarusan.com

文字列幅を取得する

  • 文字数と同じ幅のfloating windowを生成したいときに利用

文字の幅や文字数を取得する関数として

  • strwidth()
  • strlen()
  • strdisplaywidth()
  • strlen(substitute('ほげほげ', '.', 'x', 'g'))

など色々あるが、マルチバイトの文字列などを考えなくて済むのはstrdisplaywidth()だった。
文字列とウィンドウはイコールではないことを知っておこう。

https://rasukarusan.github.io/blog-assets/neovim-floating-window-tips/strdisplaywidth.gif
現在行の文字列と同じ幅のfloating windowを生成

floating windowの位置、大きさを変更する

  • redrawがポイント。while文中などでは再描画が必要になる

floating windowの位置を変更するにはnvim_win_set_config()で新たにconfigをセットしてあげればよい。幅や高さを変えたいときはconfigをセットする必要はなく、nvim_win_set_width()nvim_win_set_height()を使えば良い。
ただ、移動をアニメーションっぽく見せるためにはconfigをセットした後にredrawsleepが必要になる。
redrawが無いと、関数を実行した後にvimが再描画されるため、移動後のfloating windowしか表示されない。
また、sleepがないと一瞬で描画が終わってしまうため、アニメーションっぽくならない。

下記は右下に1ずつ移動していくvimscript。

" floating windowを生成
let config = { 
    \'relative': 'editor',
    \ 'row': 10,
    \ 'col': 10,
    \ 'width': 10,
    \ 'height': 10,
    \ 'anchor': 'NW',
    \ 'style': 'minimal',
    \}
let buf = nvim_create_buf(v:false, v:true)
let win_id = nvim_open_win(buf, v:false, config)

" floating windowを移動させる
let i = 0
while i < 10
    let newConfig = {
        \'relative': config.relative, 
        \'row': config.row + i, 
        \'col': config.col + i
        \}
    call nvim_win_set_config(win_id, newConfig)
    let i += 1
    redraw
    sleep 100ms
endwhile

https://rasukarusan.github.io/blog-assets/neovim-floating-window-tips/strdisplaywidth.gif
右下に1ずつ移動していく

floating windowの背景色をランダムな色でセットする

  • 複数のfloating windowを生成するときに区別できるとデバッグがしやすい

これはもうちょっとスマートに書けると思うが、デバッグ用なのでまあいいかなと甘んじてる。
1桁目(%x)と残り5桁(%05x)で分けているのは、そうした方が似た色が出にくいから。

" 60000はざっくり計算。00000~fffffの10進数以内だったら何でもいい。
let color = '#' . printf("%x", Random(16)) . printf("%05x", Random(60000))
let hl_name = 'ClipBG' . i
execute 'hi' hl_name 'guifg=#ffffff' 'guibg=' . color
call nvim_win_set_option(win_id, 'winhighlight', 'Normal:'.hl_name)


" 指定した整数内で乱数を生成
function Random(max) abort
  return str2nr(matchstr(reltimestr(reltime()), '\v\.@<=\d+')[1:]) % a:max
endfunction

https://rasukarusan.github.io/blog-assets/neovim-floating-window-tips/strdisplaywidth.gif
複数のfloating windowを区別するために色付けする

参考にさせて頂いたサイト

Draw color wheel in NeoVim · GitHub
Native Vim Random number script - Stack Overflow

window_idからwindow_numを取得する

これ単体だとあまり使わないが、後述する「指定のfloating windowにコマンドを実行する」ときに必要となる。

function! s:winid2tabnr(win_id) abort
  return win_id2tabwin(a:win_id)[1]
endfunction

このコードは下記のサイトから頂きました。

koturn.hatenablog.com

ここのサイトめちゃくちゃ綺麗にまとまっていて、かつほぼ全ての相互変換を網羅してるので本当に助かりました、感謝です。

指定したfloating windowにコマンドを実行する

  • 指定のfloating windowにexecuteしたいときに必要となる

floating windowでガッツリ遊ぼうと思ったら、特定のfloating windowにコマンドを実行したいってことが結構ある。 そんなときはwindoを使う。

" :help windo
:{window number}windo {cmd}

例えばメインウィンドウにフォーカスを戻したいなら:0windo :となる。
0番は最初に開いたウィンドウとなるのでおそらくメインウィンドウだろう。cmd:を渡しているのはShellScriptと同じ感覚で何もしないことを示す。 関数として作っておくと何かと便利(引数にwindow numberを渡してもいいと思う)

" メインウィンドウにattach
function! s:focus_to_main_window()
    execute "0windo :"
endfunction

また、これは前述のwindow_idからwindow_numberを取得するものと一緒に使うことが多い。 対象のfloating windowに文字列を挿入する、みたいなときはこうする。

" 指定したウィンドウIDのウィンドウの番号を得る
function! s:winid2tabnr(win_id) abort
  return win_id2tabwin(a:win_id)[1]
endfunction

" 指定したfloating windowの現在行に'hoge'を挿入
let win = s:winid2tabnr(win_id)
execute win . 'windo ' . "call setline('.', 'hoge')"

windoを連発するのが面倒な場合、下記のように一旦指定のfloating windowにフォーカスを移してから実行していくと楽。

let win = s:winid2tabnr(win_id)
" 指定のwindowにフォーカスを移す
execute win . 'windo :'
" フォーカスを移した先のwindowで実行される
call setline(".", "hoge")

メインウィンドウ以外のウィンドウを全て閉じる

Ctrl-w Ctrl-o

生成したwindowを全て閉じるときに便利。:lsを実行すると消えていることがわかる。ただし完全には削除されない。 :ls!を実行するとvim上には残っていることがわかる。
完全に消すには

:bw[N]

でNにバッファ番号を指定すればOK。

こちらのサイトを参考にさせて頂きました。

さわさわと vim バッファ削除 バッファ一括削除

floatをintに変換

  • floating windowのconfigでcolとrowがfloatだが、計算するときにはint型で欲しい

floating windowを移動させるときなど、ある程度計算を行った後にcolやrowに値をぶち込むことが多い。
その計算途中でfloatだと面倒なので必要になった。

" float型をint型に変換
let col = float2nr(config.col) + 10

終わり

所詮小さいScriptなので、なんとなく頭の中でこうやればできるなと思うことはできるが、いざ実装しようとすると何の関数を使えばいいのか、そもそもあるのかわからなくてめげそうになる。
ただ、Vimは先人様たちが必要だと思ったのは大体入っている。もしない場合は他で代替可能だという無言のメッセージだと捉えれば、楽しくVimScript開発できる。

VimのMarkdownPreviewプラグイン「iamcco/markdown-preview」がグラフ、UML、Latexが描画できて色々やばい

「vim markdown」とか「vim plantuml」って調べると2015年とか古い記事が多く見つかる。
なんか直近で良い感じのないかな〜と探していたら素晴らしいプラグインを見つけた。

GitHub - iamcco/markdown-preview.nvim: markdown preview plugin for (neo)vim

これ最高。PlantUML書けたらいいかなあと思っていたがそれ以上に色々描画できて、もうNumbersとかスプレッドシートとかいらずにこれ一本でいいんじゃねってなったので紹介したい。

これ一体何?

MarkdownのPreview用プラグイン。ただ内部でnodeが動いているので描画にJSが使えてとてもリッチに表示される。
他のPreviewプラグインと圧倒的に違うのは描画の種類が豊富なところ。いわゆる「テーブル」とか「チェックマーク」だけじゃなく、グラフ、Latex、シーケンス図など様々なものに対応している。
また、プレビュー用のCSSがGithubを強く意識されていて、READMEを書く時などpushしたあともほぼ見た目が変わらないのもGOOD。

インストール方法

deinでインストール

call dein#add('iamcco/markdown-preview.nvim', {'on_ft': ['markdown', 'pandoc.markdown', 'rmd'], 'build': 'cd app & yarn install' })

yarnがインストールされていないとエラーが出るのでインストールしておく

$ brew install yarn

使い方

markdownのファイル(.md)を開いて:MarkdownPreviewと打てば終了。
ブラウザが開いてプレビューされる。

グラフを表示

https://rasukarusan.github.io/blog-assets/markdown-preview/chart.gif

markdown中でchartでコードブロックを作成してその中に書く。 記法はchart.jsと同じ。
ただ一個気をつけるのはJSON形式で書いていくこと。

```chart
{
"type": "bar",
    "data": {
      "labels": ["8月1日", "8月2日", "8月3日", "8月4日", "8月5日", "8月6日", "8月7日"],
      "datasets": [
        {
          "label": "A店 来客数",
          "data": [62, 65, 93, 85, 51, 66, 47],
          "backgroundColor": "rgba(219,39,91,0.5)"
        },{
          "label": "B店 来客数",
          "data": [55, 45, 73, 75, 41, 45, 58],
          "backgroundColor": "rgba(130,201,169,0.5)"
        },{
          "label": "C店 来客数",
          "data": [33, 45, 62, 55, 31, 45, 38],
          "backgroundColor": "rgba(255,183,76,0.5)"
        }
      ]
    },
    "options": {}
}
```

勿論"type":"bar""type:"pie"に変えれば、円グラフで表示できる。

f:id:rasukarusan:20190825160717p:plain:w400
円グラフも表示可能

コードはこちらから拝借しました。

PlantUMLでUML図を書く

https://rasukarusan.github.io/blog-assets/markdown-preview/plantuml.gif

plantumlを書く場合はコードブロックで囲まずそのまま書く。

@startuml
title 家計簿アプリの家計簿取得シーケンス

participant "ユーザー"

box "家計簿システム" #LightBlue
    participant "Play"
    participant "Redis"
    participant "MySQL"
end box

"ユーザー" -> "Play" : 家計簿を取得
"Play" -> "Redis" : ユーザーID、今月という時間をキーに取得
    alt キャッシュヒット
        "Play" <-- "Redis" : 家計簿情報または家計簿がないという情報
    else キャッシュミス
        "Play" <-- "Redis" : キャッシュされていないという情報
        "Play" -> "MySQL" : 家計簿取得
        "Play" <-- "MySQL"
        alt 家計簿情報あり
            "Play" ->> "Redis" : 非同期に\n家計簿情報を格納
        else 家計簿情報なし
            "Play" ->> "Redis" : 非同期に\n家計簿がないという情報を格納
        end
    end
"ユーザー" <-- "Play" : 家計簿情報または家計簿がないという情報
@enduml

コードはこちらから拝借しました。

ガントチャートの表示

https://rasukarusan.github.io/blog-assets/markdown-preview/gantt.gif

ガントチャートはmermaidでコードブロックを作成してその中に書く。mermaid.jsを使用しているのでガントチャートの他にもシーケンス図やフローチャートも書ける。

```mermaid
gantt
      title ガントチャート
      dateFormat YYYY-MM-DD
      section 製品A開発セクション
      設計        : done,   task1, 2017-12-11,  2017-12-15
      実装        : active, task2, 2017-12-18,  5d
      単体テスト  :         task3, after task2, 5d
      結合テスト  :         task4, after task3, 5d
```

コードはこちらから拝借しました。

かわいいシーケンス図

f:id:rasukarusan:20190825160822p:plain:w400
かわいいシーケンス図

これ個人的に一番好きなんだけど、手書き風のシーケンス図が書ける。 どうやらsequence-diagramsというJSのライブラリがあるらしく、それで描画される。 sequence-diagramsでコードブロックを作成してその中に書いていけばOK。

```sequence-diagrams
Andrew->China: Says
Note right of China: China thinks\nabout it
China-->Andrew: How are you?
Andrew->>China: I am good thanks!
```

latex記法で数式を表示

f:id:rasukarusan:20190825160801p:plain
数式を美しく表示

Latexで美しく数式を表示したいときもイケる。 Latex記法でそのままmarkdown中に書いてしまえばOK。

$\sqrt{3x-1}+(1+x)^2$

$$\begin{array}{c}

\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} &
= \frac{4\pi}{c}\vec{\mathbf{j}}    \nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\

\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\

\nabla \cdot \vec{\mathbf{B}} & = 0

\end{array}$$

終わり

markdownのプラグインは色々あるけどこれ一択で良いと思う。

Vimでカーソル位置下の単語をGoogle検索する

プログラムを書いていると「お、なんだこの関数?」と思いググることってよくあると思う。
そんな時一々単語をコピーし、ブラウザを開き、検索バーに打ち込んで検索している自分に腹が立った。なぜこんな無駄な動きをしているんだ。
プログラムに限らずREADMEなどドキュメントを読んでいるときも多々遭遇する。

息を吸うようにググりたい

私はエディタがVimなので本来ならターミナルから動くことは許されない。
唯一「ググる」という行為は許しているが、それでもブラウザに行くまでの動作は限りなく短いほうがよい。

今までのググるまでの道のりは以下の通り。

  1. 調べたい単語が見つかる
  2. 単語をコピーする
  3. ブラウザに移動する
  4. 検索バーにペーストする

長過ぎる...

理想は「調べたい単語が出てきた時点で既に検索が終了している」状態が望ましい。

関数作った

ということで理想に限りなく近い関数を作った。もちろんVimScriptだ。

f:id:rasukarusan:20190309011318g:plain
息を吸うように検索する

" カーソル下の単語をGoogleで検索する
function! s:search_by_google() 
    let line = line(".")
    let col  = col(".")
    let searchWord = expand("<cword>")
    if searchWord  != ''
        execute 'read !open https://www.google.co.jp/search\?q\=' . searchWord
        execute 'call cursor(' . line . ',' . col . ')'
    endif
endfunction
command! SearchByGoogle call s:search_by_google()
nnoremap <silent> <Space>g :SearchByGoogle<CR>

調べたい単語の上で<SPACE> gをタイプすれば検索された状態のブラウザを開く。 割と理想に近いと思う。

解説

特に難しいこともしていないが一応。
上の関数をシンプルに書くと以下。

" カーソル下の単語をGoogleで検索する
function! Search_by_google() 
    let searchWord = expand("<cword>")
    execute 'read !open https://www.google.co.jp/search\?q\=' . searchWord
endfunction

他の部分はエラー判定やキーバインドの設定なので無視して構わない。
調べたい単語の上で:call Search_by_google()と打てばググることができる。

やっていることは

  1. カーソル下の単語を取得
  2. Vimを開いたままシェルコマンドを実行

のみである。

1. カーソル下の単語を取得

これは

expand("<cword>>")

でゲットできる。他にもファイルパスとか取得できたりするので:help expandで調べてみると楽しいと思う。

2. Vimを開いたままシェルコマンドを実行

execute 'read !open https://www.google.co.jp/search\?q\=' . searchWord

肝はread。これを付けないで!open https://...と実行すると、

press enter or type command to continue

# もしくは

続けるにはENTERを押すかコマンドを入力してください

と出力され、VimからShellに操作を奪われてしまう。

これを回避するためにreadを使う。

readって何?

詳しくは:help readで調べてくれればいいが、ざっくりいえばコマンドの出力をVimにそのまま貼り付けるコマンド。

例えば

:read !ls

" もしくは
:r !ls

と実行すると、lsの結果がVimに出力される。

もしくはreadを使わなくても、Shift+V→:!lsのようにVisualモードに切り替えてから実行しても同じ結果が得られる。
shellコマンドのopenは出力を返すものではないので、Vim上に出力されるものがない=Shellに操作を奪われずVim上で実行したように見える。

ただ、readでコマンドを実行するとカーソル位置が行の最初に移動してしまうため、

let line = line(".")
let col  = col(".")
execute 'call cursor(' . line . ',' . col . ')'

でカーソル位置を実行したときの位置まで戻している。

終わり

:helpで調べてくれればいいが」と何回も言っているが、本当に:helpは有能なので騙されたと思って実行してみてほしい。きっと楽しいから。

でもVimで欲しい情報を得たい時、phpとかをググるより難しいな。欲しい情報が中々出てこない。
今回だったらshellに操作を奪われないためにどうするかに結構悩み、「"press enter or type command to continue" vim」でググっていたがわからなかったので、とりあえずvim.jpを読んでみた。偶然readを見つけることができたからいいが、もうちょっとググり力を上げたい。

まあでも道中で知らないVimのコマンド知ることできたし、こういう遠回りは積極的にしていきたい。

【Tips】Vimで先頭と末尾に文字を挿入する

コマンド一発で先頭と末尾に文字を挿入したいときってあるじゃん。
そんな時にどうするかって話。

方法

とりあえず結論から話すと以下のコマンドでいける。

:%s/^/先頭に入れたい文字/g | %s/$/行末に入れたい文字/g

パイプで繋げれば良かったんやな。 %sのところは'<,'>とか自由に変えてくれればいい。

どんなときに必要か

例えば以下のようなデータファイルがあるとする。

username.txt

Aaron
Adam
Adrian
Aidan
Alexander
Andrew
.....

これをプログラム中で配列に持ちたい時、例えばphpならこんな形にしたい。

<?php

$usernames = array(
    'Aaron',
    'Adam',
    'Adrian',
    'Aidan',
    'Alexander',
    'Andrew',
);

先頭と末尾にシングルクォートないしダブルクォートを挿入したい時って結構あると思うのですよ。

今まで

  1. Ctrl+Iで先頭に文字挿入
  2. gvで再度選択し$Aで行末に文字挿入

という流れでしていた。とっても面倒くさい。

全然思考のスピードでタイプできてないし、もっと長い行数の場合に一々最下行まで選択しに行かなければならないのがナンセンスだった。

f:id:rasukarusan:20190228214115g:plain
とっても面倒くさい

これから

シングルクォートを入れたい場合は以下で一撃だ。

:%s/^/'/g | %s/$/'/g

f:id:rasukarusan:20190228004427g:plain
コマンド版

ただこれだと打つのが面倒くさいし、全選択になってしまうので使い勝手が非常に悪い。

さらにこれから

範囲選択した時/しない時の両方に対応するため関数にした。

" 行頭と行末に文字列を挿入
" 範囲選択をしていない場合全範囲が対象となる
" ex.) InTH <div> <\/div>
function! s:insert_head_and_tail(...) range
    let head = a:1 " 行頭に入れたい文字列
    let tail = a:2 " 行末に入れたい文字列

    " 範囲選択中かで実行するコマンドが違うので分岐
    if a:firstline == a:lastline
        execute ':%s/^/'.head.'/g | %s/$/'.tail.'/g'
    else 
        execute ':'.a:firstline.','.a:lastline.'s/^/'.head.'/g | '.a:firstline.','.a:lastline."s/$/".tail.'/g'
    endif
endfunction
command! -nargs=+ -range InTH <line1>,<line2> call s:insert_head_and_tail(<f-args>)

f:id:rasukarusan:20190228213755g:plain
関数版

" 行頭に「"」、行末に「",」を挿入する時  
:InTH " ",

終わり

vim楽しいなあ。shell芸みたいに今回のも色々やり方あるんだろうなあ。
alias名が毎回適当になってしまうのが若干悩み。

vim-fzfで自作関数を実行する

vim-fzf超便利。vimを使っていてまだ入れてない人は今すぐ入れたほういい。

github.com

何ができるようになるかってvim内でfzfが使える。例えばファイルを開きたい場合にめちゃくちゃ早く開ける。
こんな感じ↓(私はCtrl-pにキーバインドして使っています)

f:id:rasukarusan:20181015152034g:plain

fzf-vimに標準で入っている関数だけでも十分便利だが、自分で作った関数にfzfで選択したものを食わせたいと思ったのでやってみる。

やりたいこと

現在開いているファイルの別ブランチ版を別タブで表示したい。例えば今のブランチがtestでhoge.phpを開いている時、masterのhoge.phpを見たい場合、今までは以下のような手順を踏んでやっていた。

  1. Ctrl-zでシェルに戻る(vimから抜ける)
  2. git checkout masterでmasterブランチに移動
  3. fgvimに戻ってLで開く

動作的にはこんな感じ。

f:id:rasukarusan:20181015151507g:plain

めちゃくちゃ面倒くせえ....ただmasterブランチのファイルを見たいだけなのに一々shellに戻るのが非常にナンセンス。
理想の動きは「shellに戻らず、vim内でブランチを選ぶだけ」。
ということで実現のために必要なことをまとめてみる。

  1. ブランチ名を指定したら今開いているファイルをそのブランチ版で開く自作関数を作る
  2. 任意のコマンドの結果をvim内のfzfで表示
  3. fzfで選択した結果をvim自作関数に突っ込む

1. ブランチ名を指定したら今開いているファイルをそのブランチ版で開く自作関数を作る

これはvimプラグインtpope/vim-fugitiveを入れてたら余裕でした。

:Gtabedit ブランチ名:%

で現在開いているファイルを別ブランチ版で開くことができます。
作った関数はこちら。

.vimrc

" Gtabeditを簡単に実行できるようにした関数 "
function! s:alias_Gtabedit(...)
    if a:0 == 0
        let branch_name = 'master'
    else
        let branch_name = a:1
    endif
    execute ':Gtabedit '.branch_name.':%'
endfunction

" :Co ブランチ名 でそのブランチのソースをタブ表示 "
command! -nargs=? Co call s:alias_Gtabedit(<f-args>)

これで:Co hoge_branchのようにブランチを指定するだけで現在開いているファイルを別ブランチ版で見ることが可能に。
ちなみにCocheckou-outの略です。masterブランチを参照することが多かったので引数に何も指定しない場合は自動でmasterブランチを指定するようにしました。

2. 任意のコマンドの結果をvim内のfzfで表示

さてここからが本番です。vim-fzfにどうやって任意のコマンド結果を食わせることができるのか調べてみました。

READMEを見た限り割とシンプルでした。 他にも色々とオプションがあるようですが、以下のものだけ知っておけばとりあえず実現できそうです。

  call fzf#run({
    \ 'source': '任意のshellコマンド',
    \ 'sink': '選択したものに対して行いたいvimコマンド',
    \ 'down': '40%'  " fzfを表示させる場所・幅。up / down / left / rightを指定可能。
    \ })

例えば:call fzf#run({ 'source': 'ls', 'sink': 'tabedit', 'down': '40%' })と打てばlsの結果がfzfで表示され、選択するとそのファイルを別タブで開くことができます。

3. fzfで選択した結果をvim自作関数に突っ込む

ということであとはsourceにブランチを表示するコマンドを指定し、sinkに1で作った自作関数を指定させてやればフィニッシュです。

  call fzf#run({
    \ 'source': 'git branch -a',
    \ 'sink': function('s:alias_Gtabedit'),
    \ 'down': '40%'
    \ })

さらに:Coで呼び出したいので以下のように設定。(1で書いたcommand! -nargs=? Co call s:alias_Gtabedit(<f-args>)は削除しておく)

function! s:fzf_alias_Gtabedit()
  call fzf#run({
    \ 'source': 'git branch -a',
    \ 'sink': function('s:alias_Gtabedit'),
    \ 'down': '40%'
    \ })
endfunction
command! Co call s:fzf_alias_Gtabedit()

実行画面

f:id:rasukarusan:20181015160339g:plain

まとめ

fzfになんでも食わせられるようになって一気に幅が広がった気がする。
vim-shellとかvim-fzfとか色々疎通ができるようになってくるとアイデアが湧き出てきて本当に楽しい。

Xcode9.2でXVim2を動くようにする

巷に転がっている方法ではなぜか動かなったが、苦戦しつつも解決できたので誰かの助けになればと思い書く。
結論から言うと

  1. XVim2のブランチがXcode9.2対応ブランチでなかった
  2. Xcode/Plug-ins/XVim2/XVim2/Info.plistの15〜19行目にUUIDが記述されているが、ここに筆者が使っているXcodeのUUIDがないこと

が原因だった。

手順

ざっくりすると以下の手順。

  1. Xcode9.2対応ブランチに切り替える
  2. Xcode9.2のUUIDを追加しているコミットを取り込む
  3. make、update_xcode_pluginsをしてインストール

コマンドまとめ

コミット番号とか変わるはずがないので以下のコマンドを打っていけば動作するはず。 急いでいる方はこちらをどうぞ。

# Xcodeのプラグイン置き場に移動
cd ~/Library/Application\ Support/Developer/Shared/Xcode/Plug-ins
# XVim2を落としてくる
git clone git://github.com/XVimProject/XVim2 XVim2
# ここからはXVim2ディレクトリの中で作業を行う
cd XVim2
# Xcode9.2対応ブランチを作成
git checkout -b xcode9.2 refs/tags/xcode9.2
# XCode9.2のUDIDを追加しているプルリクエストをローカルに持ってくる
git fetch origin pull/96/head:unchartedworks
# UDIDを追加したコミットを取り込む
git cherry-pick e85d3ed83018022d571474ce3bdaad3278a940f7
# XVim2をXcodeにインストール
make
update_xcode_plugins
defaults delete com.apple.dt.Xcode DVTPlugInManagerNonApplePlugIns-Xcode-9.2

以上で無事XVim2が動くはず。

詰まった箇所1:masterブランチはXcode9.2に対応していない

git logを見ていたら以下のコミットを発見

commit cca1fff54ed29798b54eb80a906260e88be4e366
Author: pebble8888 <pebble8888@gmail.com>
Date:   Sun Apr 1 15:01:47 2018 +0900

    support Xcode9.3 (not support Xcode9.2 and lower)

おやおやまさかな、、、と思い初心に帰ってREADMEを見る。

tags xcode9.2 xcode9.4 Please use appropriate tags or branches.

最初からREADME見ろよと言いたい。「適切なブランチを使ってね」と書いてある。

Xcode9.2対応ブランチに切り替える方法

git logをして「tag」で検索すると以下のような箇所が見つかるためコミット番号をコピーしてcheckoutする。 f:id:rasukarusan:20180908181637p:plain

Xcode9.2対応ブランチに切り替える

git checkout 66c6dc44edfca8a032b78294a03721ee7c51dd95 -b xcode9.2

もしくはシンプルにこっち。

git checkout -b xcode9.2 refs/tags/xcode9.2

Xcode9.2のUUIDを追加しているコミットを取り込む

ブランチを切り替えただけではXVim2は動かなかった。あとで気づいたがどうやら筆者のXcodeのUUIDがXVim2/Info.plistに記述されていないことが原因だった。
以下の記事を参考に該当のコミットを取り込む。

qiita.com

プルリクをローカルに引っ張ってくる

git chery-pickするために該当コミットをしているブランチをローカルに落とす必要がある。

git fetch origin pull/96/head:unchartedworks

コミットを取り込む

githubプルリクからコミット番号を確認してgit cherry-pickでコミットを取り込む

f:id:rasukarusan:20180908181811p:plain

git cherry-pick e85d3ed83018022d571474ce3bdaad3278a940f7

詰まった箇所2:update_xcode_plugins --unsignの罠にはまる

あとはmakeしてupdate_xcode_plugins --unsignを終了!と思いきや壁にぶつかる。 どこのページを見てもupdate_xcode_plugins --unsignのコマンドを実行しているが、以下のエラーが出る。

▶update_xcode_plugins --unsign
Looking for Xcode...

Didn't find any signed Xcode 8+ on your system.

念の為以下のコマンドでインストールしなおした。

sudo gem install update_xcode_plugins

しかし結果は変わらない。一度restoreした。

update_xcode_plugins --restore

ふとここでなんのためにupdate_xcode_plugins --unsignをするのか気になったので調べてみた。

medium.com

どうやらサードパーティプラグインXcodeで利用する場合、署名を変更しないと、Xcodeの起動がすごく遅くなる、とのこと。 それなら別にとりあえずXVim2を使いたいなら--unsignしなくてもいいんじゃね?と思ったので--unsignを外してみた。

▶update_xcode_plugins
Found:
- Xcode (9.2) [B395D63E-9166-4CD6-9287-6889D507AD6A]: /Applications/Xcode.app

Plugins:
- XVim2 (1.0)

Updating...

Finished! 🎉

It seems that you have Xcode 8+ installed!
Some plugins might not work on recent versions of Xcode because of library validation.
See https://github.com/alcatraz/Alcatraz/issues/475

Run `update_xcode_plugins --unsign` to fix this.

なんかうまくいっているっぽいのでこれで良しとした。

Xcode起動時にXVim2を読み込む

defaults delete com.apple.dt.Xcode DVTPlugInManagerNonApplePlugIns-Xcode-9.2

を打ち込みXcodeを起動する。すると以下のようなアラートが表示されるので「Load Bundle」をクリック。

f:id:rasukarusan:20180908214305p:plain

プロジェクトを開くと、、、

f:id:rasukarusan:20180908182048g:plain

感無量。 てかgit cherry-pickとか使わず普通にInfo.plistに書き込めばよかったんじゃね...

Vimでもデバッグできるんすわ(Vagrant+PHP+Vdebug)

Vimでもデバッグしたい

最近var_dumpやChrome.phpを使ったブラウザに変数を表示させるデバッグ方法に疲れてきたのでVimにデバッガ入れてみるかという話。 こんな感じでブレークポイント入れたり変数の確認とかのデバッグができます。

f:id:rasukarusan:20180729220611g:plain

環境

  • Vagrant(1.94)
  • PHP(5.16)
  • Mac(El Capitan)
  • Vim(8.1:Vagrantではなくローカルにインストールされているものです)

筆者の環境はVagrantでPHPが動作しており、ソースはローカルとVagrantがマウントしている状態。 いわゆるリモートデバッグがしたいという状況です。

死ぬほど参考になったサイト

基本的には以下のサイトに従って導入したが、以下の記事ではうまくいかなかったこともあったのでそれも含めて記載します。

PHPでVim使って開発していてvar_dump()を唱えているならVim Plugin のvdebugを使ってみろって - Qiita

https://blog.code-life.net/blog/2012/10/01/vim-vdebug-xdebug/

VagrantにXdebugをインストール

$ vagrant ssh

# Xdebugをインストール
[vagrant@muscle ~]$ sudo pecl install xdebug-2.0.3

# Cannot find autoconf. Please check your autoconf installation and the $PHP_AUTOCONF
# ERROR: `phpize' failedと出たら以下を実行してください
[vagrant@muscle ~]$ sudo yum install autoconf

筆者の環境ではCentOSのバージョンが低く、普通にyumでインストールできなかったのでXdebugのバージョンを指定してインストールしました。 XdebugのHPから自分のPHPのバージョンに合っているXdebugのバージョンを確認。

php.iniにXdebugの設定を追加

# xdebug.soのパスを確認
[vagrant@muscle ~]$ sudo find / -name 'xdebug.so'

[vagrant@muscle ~]$ sudo vi /usr/local/lib/php.ini
# php.iniに以下を追加
[Xdebug]
zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20192838/xdebug.so ; #<= ここはfindで見つかったxdebug.soのパス
xdebug.default_enable=1
xdebug.remote_enable=1
xdebug.remote_autostart=1
xdebug.remote_host=10.0.2.2 ; #<= Vagrantだとこのホストになるそうです。筆者の環境ではprivate_networkのIPを指定してもだめでした。
xdebug.remote_port=9001
xdebug.remote_log=/tmp/xdebug.log
xdebug.max_nesting_level=10000
xdebug.remote_connect_back=1

Xdebugを入れるとその分メモリを喰うのでphp.iniを編集してメモリの上限を上げておきます

[vagrant@muscle ~]$ sudo vi /usr/local/lib/php.ini
;memory_limit = 50M      ; Maximum amount of memory a script may consume (50MB)
memory_limit = 128M      ; Maximum amount of memory a script may consume (128MB) ; #<= Xdebugでメモリを喰うため

apacheを再起動

# vagrant reloadでも良い
[vagrant@muscle ~]$ sudo service httpd restart
httpd を停止中:                                            [  OK  ]
httpd を起動中:                                            [  OK  ]

インストールされているか確認

[vagrant@muscle ~]$ php -i | grep xdebug
xdebug
xdebug support => enabled
xdebug.auto_trace => Off => Off
xdebug.collect_includes => On => On
....
xdebug.remote_host => 10.0.2.2 => 10.0.2.2
xdebug.remote_log => /tmp/xdebug.log => /tmp/xdebug.log
xdebug.remote_mode => req => req
xdebug.remote_port => 9001 => 9001
xdebug.show_exception_trace => Off => Off
xdebug.show_local_vars => Off => Off
xdebug.show_mem_delta => Off => Off
xdebug.trace_format => 0 => 0
xdebug.trace_options => 0 => 0
xdebug.trace_output_dir => /tmp => /tmp
xdebug.trace_output_name => trace.%c => trace.%c
xdebug.var_display_max_children => 128 => 128
xdebug.var_display_max_data => 512 => 512
xdebug.var_display_max_depth => 3 => 3

上記が表示されていればインストールはOKです。
これでVagrant上の操作は終了です。ここからはローカルの設定です。

VimにVdebugをインストール

Vimでデバッグするのには「Vdebug」というプラグインを使用。 github.com

deinでインストール

call dein#add('joonty/vdebug')

vimrcにVdebugの設定を追加

" ========Vdebug======== "
let g:vdebug_options= {
\    "port" : 9001,
\    "timeout" : 20,
\    "on_close" : 'detach',
\    "break_on_open" : 0,
\    "remote_path" : "",
\    "local_path" : "",
\    "debug_window_level" : 0,
\    "debug_file_level" : 0,
\    "debug_file" : "",
\    "path_maps" : {
\       '/home/yourpath/web' : '/Users/'.$USER.'/workspace/web',
\    },
\    "window_arrangement" : ["DebuggerWatch", "DebuggerStack"]
\}

上記のpath_mapsが重要です。これをVagrant上でマウントしている先のパスと合わせます。左側がVagrantのパス、右側がローカルPCのパスとなります。 また、上記では書かれていませんが"server":"127.0.0.1"という設定をよく見ます。ただ、筆者の環境ではこれを書いている限りVagrantと疎通が取れずデバッグができなかったので削除しました。(remote_pathも同じ理由で空白しています)

以下は筆者の好みでデフォルトから変更しているもの。

キー名 意味
break_on_open いきなりブレークポイントまで行くかどうか
デフォルトは1で行かないようになっている
window_arrangement デバッガ画面に表示する項目
デフォルトは"DebuggerWatch", "DebuggerStack","DebuggerStatus"の3つが指定。

Xdebug helperのインストール

ブラウザからデバッグするために以下のChrome拡張を入れます。 chrome.google.com 設定は特にありません。デバッグしたい画面で以下の画像のように「Debug」を選択するだけです。

いざ実行

F10でブレークポイント、F5でデバッグ開始です。 デバッガ中はF3でステップ実行、F6をデバッグ終了、もう一度F6 を押すとデバッガ画面が閉じます。

F6二回押しで閉じずに:qとかで閉じるとvimが止まるか次回起動時にエラーが出るので注意してください。

vimrcでキーマップを変えることができます。デフォルトは以下になってます。

let g:vdebug_keymap = {
\    "run" : "<F5>",
\    "run_to_cursor" : "<F9>",
\    "step_over" : "<F2>",
\    "step_into" : "<F3>",
\    "step_out" : "<F4>",
\    "close" : "<F6>",
\    "detach" : "<F7>",
\    "set_breakpoint" : "<F10>",
\    "get_context" : "<F11>",
\    "eval_under_cursor" : "<F12>",
\    "eval_visual" : "<Leader>e"
\}

ちなみにこういうデフォルト値とか配列の指定どうやって見つけるの?という話だが、READMEに書いてあるのを見るのが一番だが書いていないときはソースを見に行くのが早道のときもある。 今回でいうなら.vim/dein/repos/github.com/joonty/vdebug/plugin/vdebug.vimを見てデフォルト値とか引数がわかった。vimのプラグインだったらXXX.vimというのを見に行けば大体解決する(と思う)。

非常に快適だ。