GNU screenからVimへのペーストを、インデント崩れを回避しつつ簡潔な操作で行う
Vim Advent Calendar 2013の59日目(2014-01-28)の記事です。
GNU screenを使う目的の一つは、キーボード操作によるコピー&ペーストです。
GNU screenの他windowのシェル等でコピーした文字列をvimにペーストすることがよくあります。
が、GNU screen側の操作でペースト(CTRL-O .
に設定してます)すると、vim側の'smartindent'が効いてインデントが崩れることがあります。
そうなった時は、以下の操作をしていましたが、繁雑なので、簡潔な操作で行う方法を調べてみました。
Esc
でInsert modeを抜けて、u
でundoして、:se paste
して、i
でInsert modeに入って、- 再度GNU screenのペースト操作(
CTRL-O .
)をして、 Esc
してInsert modeを抜けて、:se nopaste
する。
(なお、GNU screenのペースト操作でペーストする際は、vim側が日本語入力モードになっていたら、日本語入力モードをオフにする操作も必要になります。この点も解決したいところです。:se paste
すれば問題ないですが。)
方法1: 'pastetoggle'を使用
setpastetoggle=<C-Q>
autocmd InsertLeave * setnopaste
- Insert modeで、
CTRL-Q
により、set pasteして、 - GNU screenのペースト操作
Esc
してInsert modeを抜けると、set nopasteされる。
方法2: :a!
や:i!
を使用
:a!
や:i!
すればインデントは崩れない、という記述も見かけるのですが、手元ではなぜか駄目だったので:se paste
を使っていました。
少し試してみたところ、:set smartindent
だけしていると駄目で、:set autoindent
も必要なようです。
:a!
- GNU screenのペースト操作
<CR>
で改行して、.<CR>
で終了
終了方法がちょっと癖がある印象です。
方法3: Xのselectionを使う
vimで+xterm_clipboard featureが有効になっている場合、xterm内のvimでも、普通に"*p
等の操作でXからのペーストが可能。
"*p
もしくは、Insert modeで、
CTRL-R CTRL-O *
ただし、GNU screenのペーストバッファをXのselectionに入れる設定や、操作(例えば以下の設定を~/.screenrcに入れてCTRL-O y
)が必要。
bind y eval writebuf screen 'stuff "xsel -i -p < $HOME/tmp/screen-xchg; exit^M"'
X無しの場合(Windowsからsshでリモートに接続している場合等)は使用不可。
方法4: fakeclipを使用
Vim側で"&p
すれば、GNU screenのペーストバッファからペースト可能。
"&p
tmuxやX、Mac OS X、Cygwinにも対応。Vim側操作で、各システムからのペーストや各システムへのyankが可能。
方法5: fakeclipと同様の自作script
(車輪の再発明をしてました。fakeclipを失念してて、既に同じものがありそうだと思って、「GNU screen vim paste」あたりで少しぐぐっても見つけられず、自分で書いてました。「tmux vim」でぐぐってfakeclipを発見。)
gp
fakeclipに比べて、以下の違いがあります。
- ペーストする文字列に改行が含まれていたら(最後が改行で終わっていなくても)、
:put
を使ってlinewiseにペースト。 行の途中にいる時に複数行をペーストする場合、 linewiseの方が使いやすい気がするので。 (ただし、"*p
はlinewiseではないので、整合性はなくなりますが。) - screenへのyankは、レジスタ指定無しでyank操作をした後、
screenのペーストバッファへのコピーを行う操作(
gy
)を行う形。 これにより、screen側にyankするつもりだったのに、"&
を付けずにyank操作をしてしまった場合に、"&
を付けて再度yank操作をし直さなくても、gy
を入力するだけで良くなります。 - screenへのyank時に、screenのreadbufが、「Slurped 28 characters into buffer」 のようなメッセージを出すと少し目ざわりなので、出さないようにmsgwait 0を追加。
" GNU screenのペーストバッファの内容を、Vim側に読み込む。" ~/.screenrcで以下の設定をしている前提" bufferfile $HOME/tmp/screen-xchglets:screenfile ='~/tmp/screen-xchg'function!s:PasteFromScreen()silent!screen -X writebuf
lets= system('cat ' . s:screenfile)calls:PasteStr(s)endfunctionnnoremap<silent>gp :<C-U>call<SID>PasteFromScreen()<CR>function!s:PasteStr(str)let save_reg = @@
let @@ =a:str
if stridx(a:str,"\n")>=0put" ペースト後のカーソル位置を通常の`p`の場合と合わせる
normal!'[
else
normal!pendiflet @@ = save_reg
endfunctionfunction!s:YankToScreen()call writefile(split(@@,'\n',1), expand(s:screenfile),'b')" suppress message like "Slurped 28 characters into buffer"silent!screen -X eval 'msgwait 0' readbuf 'msgwait 5'"silent execute '!screen -X register . "$(cat ' . s:screenfile . ')"'endfunctionnnoremap<silent> gy :<C-U>call<SID>YankToScreen()<CR>if has('xterm_clipboard')" linewiseにしたいのでnnoremap<silent> gP :<C-U>call<SID>PasteStr(@*)<CR>"nnoremap <silent> gP "*pnnoremap<silent> gY :<C-U>let @* = @@<CR>elsefunction!s:PasteFromX()lets= system('xsel -o -p')lets= iconv(s,'utf-8',&encoding)" &encがeuc-jpの場合用calls:PasteStr(s)endfunctionnnoremap<silent> gP :<C-U>call<SID>PasteFromX()<CR>nnoremap<silent> gY :<C-U>call system('xsel -i -p', @@)<CR>endif" XXX: vimが-X付きで起動されたかの判定もするなら以下。" もっと簡単に判定できるならいいけど、ここまでやらなくてもいい気が。" function! s:has_xterm_clipboard()" if !has('xterm_clipboard')" return 0" endif" let expect = strftime('%c')" call system('xsel -i -p', expect)" let actual = @*" if actual ==# expect" return 1" endif" return 0" endfunction
おまけ: ペースト直後に、ペーストした複数行のインデントを調整
通常のp
を使う場合でも、ペースト直後にインデントの増減をすることが多いのですが、']
を打つのが少し面倒なので。
" paste直後に、pasteした複数行のインデントを増減nnoremap<p<']
nnoremap>p>']
" pastetoggleや、`:a!`用。redoできるように、最初に先頭に移動nnoremap<P'[<']
nnoremap>P'[>']
gvim使用時も同じ操作ができるようにする
gvim使用時も同じ操作ができるように、~/.gvimrcに以下の設定を入れておきます。
(Windows上のgvim用でも同様に、~/_gvimrcに設定を入れておきます。)
nnoremap<silent>gp"*p
nmap <silent> gP gpnnoremap<silent> gy :<C-U>let @* = @@<CR>
nmap <silent> gY gy
参考:GNU screenのペーストバッファ内容を、vimで編集した内容に更新する
普段は日本語入力IMを起動していなくて、lynxやコマンドラインで少しだけ日本語が入力したい場合、以下の操作をしていました。
(なお、lynxは、テキストフィールドでも、CTRL-X e
でvimを起動して編集できるようになったので、以下の操作は不要。)
- screenの別windowを作って
- vimを起動して
- vim内で日本語を入力して(tcvimeを使ってIM無しでvimだけで日本語入力)
- 入力した日本語をscreenのコピー操作(対象文字列の選択操作も含む)でコピー
- screenの元のwindowに切り替えて
- screenのペースト操作でペースト
- (その後、一時的にvimを起動したwindowの後始末)
繁雑なので、以下の手順でできるようにするシェルスクリプトです。
screenのペーストバッファ内容を、vimを起動して編集した内容に更新します。
- screenの操作で、編集用vimを起動(
CTRL-O v
) - vim内で日本語を入力して
- vimを終了(
:x
)。 (終了により、vimで編集した内容がscreenのペーストバッファに入り、 vim用のwindowも閉じられる。) - screenのペースト操作でペースト
~/.screenrcの設定:
bind v eval screen 'stuff "vs; exit^M"'
上で呼んでいる~/bin/vsの内容は以下。
#!/bin/sh# edit file and copy to screen's buffer
cp /dev/null $HOME/tmp/.sv
$EDITOR$HOME/tmp/.sv
screen -X register . "`cat $HOME/tmp/.sv|nkf -e`"