Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to emulate Emacs’ transpose-words in Vim?

Emacs has a useful transpose-words command which lets one exchange the word before the cursor with the word after the cursor, preserving punctuation.

For example, ‘stack |overflow’ + M-t = ‘overflow stack|’ (‘|’ is the cursor position).

<a>|<p> becomes <p><a|>.

Is it possible to emulate it in Vim? I know I can use dwwP, but it doesn’t work well with punctuation.

Update: No, dwwP is really not a solution. Imagine:

SOME_BOOST_PP_BLACK_MAGIC( (a)(b)(c) )
//             with cursor here ^

Emacs’ M-t would have exchanged b and c, resulting in (a)(c)(b).

What works is /\w yiwNviwpnviwgp. But it spoils "" and "/. Is there a cleaner solution?

Update²:

Solved

:nmap gn :s,\v(\w+)(\W*%#\W*)(\w+),\3\2\1\r,<CR>kgJ:nohl<CR>

Imperfect, but works.

Thanks Camflan for bringing the %# item to my attention. Of course, it’s all on the wiki, but I didn’t realize it could solve the problem of exact (Emacs got it completely right) duplication of the transpose-words feature.

like image 411
Roman Odaisky Avatar asked Sep 26 '08 19:09

Roman Odaisky


4 Answers

These are from my .vimrc and work well for me.

" swap two words
:vnoremap <C-X> <Esc>`.``gvP``P
" Swap word with next word
nmap <silent> gw    "_yiw:s/\(\%#\w\+\)\(\_W\+\)\(\w\+\)/\3\2\1/<cr><c-o><c-l> *N*
like image 188
camflan Avatar answered Nov 19 '22 06:11

camflan


Depending on the situation, you can use the W or B commands, as in dWwP. The "capital" versions skip to the next/previous space, including punctuation. The f and t commands can help, as well, for specifying the end of the deleted range.

There's also a discussion on the Vim Tips Wiki about various swapping techniques.

like image 24
Sam Martin Avatar answered Nov 19 '22 05:11

Sam Martin


There's a tip on http://vim.wikia.com/wiki/VimTip10. But I choose to roll my own. My snippet has two obvious advantages over the method mentioned in the tip: 1) it works when the cursor isn't in a word. 2) it won't high-light the entire screen.

It works almost like emacs 'transpose-words', except that when transposition is impossible, it does nothing. (emacs 'transpose-words' would blink and change cursor position to the beginning of current word.)

"transpose words (like emacs `transpose-words')
function! TransposeWords()
    if search('\w\+\%#\w*\W\+\w\+')
    elseif search('\w\+\W\+\%#\W*\w\+')
    endif
    let l:pos = getpos('.')
    exec 'silent! :s/\(\%#\w\+\)\(\W\+\)\(\w\+\)/\3\2\1/'
    call setpos('.', l:pos)
    let l:_ = search('\(\%#\w\+\W\+\)\@<=\w\+')
    normal el
endfunction

nmap <silent> <M-right> :call TransposeWords()<CR>
imap <silent> <M-right> <C-O>:call TransposeWords()<CR>
like image 2
Ji Han Avatar answered Nov 19 '22 07:11

Ji Han


In the middle of a line, go to the first letter of the first word, then do

dw wP

At the end of a line (ie the last two words of the line), go to the space between the words and do

2dw bhP

From the handy Equivalence of VIM & Emacs commands


You could add shortcut keys for those by adding something like the following to your vimrc file:

map L dwwP
map M 2dwbhP 

In that case, SHIFT-L (in command-mode) would switch words in the middle of the line and SHIFT-M would do it at the end.

NB: This works best with space-separated words and doesn't handle the OP's specific case very well.

like image 5
Mark Biek Avatar answered Nov 19 '22 07:11

Mark Biek