Quantcast
Channel: Vimタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 5608

Vimの undojoin で :%!autopep8 や :%!clang-format を快適に

$
0
0

前置 (plugin の話はありません)

 Vim でプログラムを編集していると、バッファ全体に外部コマンドを適用したい時があります。たとえば、python の autopep8 や c/c++ の clang-format など。
 これらのフォーマッタには整形範囲を限定するオプションがあり、ここは手動で整形したい、ここは機械的な整形で、と使い分けができます。

autopep8
autopep8 --line-range 100 110
clang-format
clang-format -lines=100:110

 ところで、これらのコマンドは部分的な整形であっても、入力にはファイル全体を渡す必要があります。そうすると Vim からは :%!を使って、

:%!autopep8 --line-range 100110

とすれば、一応目的を達します。
問題はここから・・・

問題1. 範囲指定が面倒だ。

 これは、a:firstlinea:lastline (:h func-range) を使った関数を定義し、visual モードの選択範囲を渡せばよいので、割合簡単に解決しました。

.vimrc
" autopep8function! s:FormatAutopep8() range
    let cmd =':silent %! autopep8 '
        \ .'--line-range '.a:firstline.' '.a:lastline.' '
        \ .'-'
    execute cmd
endfunction

augroup Formater
    au!auFileType python
        \ :xnoremap <buffer><silent>f :call<SID>FormatAutopep8()<cr>
augroup END

こうして、visual モードで整形したい部分を選択し、fを押すとその部分だけ整形されるようになりました。が、

問題2. 実行時や undo/redo 時にカーソルが飛んでいく。

 実行すると、カーソルが1行目へ飛んでしまいます。これは使いづらい。
 また、結果が期待と違っていたからと uすれば、カーソルが最終行に飛び、さらに <c-r>してもカーソルが最終行に。この辺りの挙動が何だか良くわからないですが、全然嬉しくない。

 今回の場合、整形前後を目diff (という言葉はあるのかな)するのが小さな楽しみでもあるので、実行時も undo/redo 時もカーソル位置は動いて欲しくないのです。

 実行後のカーソル位置の保存は、winsaveview()winrestview()を使えば実現できますが、undo/redo 時にカーソル位置を留めるのが難しい。
 いろいろ調べると undojoin (:h undojoin「以降の変更を直前の undo ブロックにつなげる。」) が正解に近そうな感じだったので、試行錯誤の結果、このようになりました。

.vimrc
function! s:KeepPosExec(cmd)let save_view = winsaveview()silent! execute "normal! i\<space>\<bs>\<esc>"|undojoin| execute a:cmd
    call winrestview(save_view)endfunction" autopep8function! s:FormatAutopep8() range
    let cmd =':silent %! autopep8 '
        \ .'--line-range '.a:firstline.' '.a:lastline.' '
        \ .'-'call s:KeepPosExec(cmd)endfunction

augroup Formater
    au!auFileType python
        \ :xnoremap <buffer><silent>f :call<SID>FormatAutopep8()<cr>
augroup END

 execute "normal! i\<space>\<bs>\<esc>"は何もしない、一見意味のないコマンドですが、現在のカーソル位置をundoブロックに記憶させる意味があります。なお、execute "normal! i\<esc>"では効果ありませんでした。
 ここに undojoinexecuteを繋げた形、
silent! execute "normal! i\<space>\<bs>\<esc>" | undojoin | execute a:cmdでは、全体がひとつの undo ブロックとして記憶されます。そしてその起点は normal!〜ですから、uしたとき戻ってくるのは現在位置、という仕組みです。

結果

こうなりました。同種のコマンドなら同様に対応できそうです。
ただ、"normal! i\<space>\<bs>\<esc>"という力技、なんとかならないかな・・・

.vimrc
function! s:KeepPosExec(cmd)let save_view = winsaveview()silent! execute "normal! i\<space>\<bs>\<esc>"|undojoin| execute a:cmd
    call winrestview(save_view)endfunction" clang-formatfunction! s:FormatClang() range
    let cmd =':silent %! clang-format '
        \ .'-lines='.a:firstline.':'.a:lastline.' '
        \ .'-style="{
        \   AccessModifierOffset                : -4,
        \   AlwaysBreakTemplateDeclarations     : true   ,
        \   Standard                            : C++11,
        \   ColumnLimit                         : 100,
        \   BreakBeforeBraces                   : Attach ,
        \   IndentWidth                         : 4,
        \   UseTab                              : Never  ,
        \   AllowShortIfStatementsOnASingleLine : false  ,
        \   AlignConsecutiveAssignments         : true   ,
        \   AlignConsecutiveDeclarations        : true   ,
        \   AlignEscapedNewlinesLeft            : true   ,
        \   IndentCaseLabels                    : true   ,
        \ }"'
    call s:KeepPosExec(cmd)endfunction" autopep8function! s:FormatAutopep8() range
    let cmd =':silent %! autopep8 '
        \ .'--line-range '.a:firstline.' '.a:lastline.' '
        \ .'-'call s:KeepPosExec(cmd)endfunction

augroup Formater
    au!auFileTypec,cpp,objc,objcpp
        \ :xnoremap <buffer><silent>f :call<SID>FormatClang()<cr>auFileType python
        \ :xnoremap <buffer><silent>f :call<SID>FormatAutopep8()<cr>
augroup END

Viewing all articles
Browse latest Browse all 5608

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>