koturnの日記

普通の人です.ブログ上のコードはコピペ自由です.

VimでJsonを整形したい

jqという非常に便利なコマンドラインツールがある. これは,jsonの整形を行うものだ. jqコマンド自体については,公式のマニュアルや以下の記事を参考にすればよくわかるだろう.

この便利なjqコマンドをVim上で利用したい. そのように考えた人は既におり,以下のいくつかの記事が参考になる.

これらの記事に書いてあるように,

:%!jq .

と外部コマンドを実行したり,

if executable('jq')
  function! s:jq(...)
    execute '%!jq' (a:0 == 0 ? '.' : a:1)
  endfunction
  command! -bar -nargs=? Jq  call s:jq(<f-args>)
endif

のようなコマンドを定義し,:Jq とコマンドを実行すればよい.

しかし,Json自体に誤りがあった場合は,Jsonを表示しているカレントバッファからJsonが消え,エラーメッセージに置き換えられてしまう. いちいちundoするのも面倒だ. そこで,エラーがある場合は,別のバッファにエラーメッセージを表示し,カレントバッファに変更を加えないようにしたい. 前回書いた記事:VimでC言語のソースコードを整形したいを模倣し,コマンドを改良する.

if executable('jq')
  function! s:jq(has_bang, ...) abort range
    execute 'silent' a:firstline ',' a:lastline '!jq' string(a:0 == 0 ? '.' : a:1)
    if !v:shell_error || a:has_bang
      return
    endif
    let error_lines = filter(getline('1', '$'), 'v:val =~# "^parse error: "')
    " 範囲指定している場合のために,行番号を置き換える
    let error_lines = map(error_lines, 'substitute(v:val, "line \\zs\\(\\d\\+\\)\\ze,", "\\=(submatch(1) + a:firstline - 1)", "")')
    let winheight = len(error_lines) > 10 ? 10 : len(error_lines)
    " カレントバッファがエラーメッセージになっているので,元に戻す
    undo
    " カレントバッファの下に新たにウィンドウを作り,エラーメッセージを表示するバッファを作成する
    execute 'botright' winheight 'new'
    setlocal nobuflisted bufhidden=unload buftype=nofile
    call setline(1, error_lines)
    " エラーメッセージ用バッファのundo履歴を削除(エラーメッセージをundoで消去しないため)
    let save_undolevels = &l:undolevels
    setlocal undolevels=-1
    execute "normal! a \<BS>\<Esc>"
    setlocal nomodified
    let &l:undolevels = save_undolevels
    " エラーメッセージ用バッファは読み取り専用にしておく
    setlocal readonly
  endfunction
  command! -bar -bang -range=% -nargs=? Jq  <line1>,<line2>call s:jq(<bang>0, <f-args>)
endif

前回の記事とほとんど内容は変わらない. フィルタリングに用いるコマンドのエラーを判別し,エラー用の処理を加えることで,いくつかの記事で紹介されているjqを用いるコマンドより便利になったというだけだ.

参考