views:

794

answers:

6

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,kgJ:nohl

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.

+5  A: 

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.

CapnNefarious
+3  A: 

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.

Mark Biek
A: 

You can use dwwP or dWwP as Mark and CapnNefarious have said, but I have a few notes of my own:

  • If the cursor is on the first letter of the second word, as in the example you gave, you can use dwbP (or dWbP to handle punctuation);
  • If the cursor is in the middle of the word, you can use dawbP/daWbP.
Bruno Gomes
+4  A: 

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*
camflan
+1  A: 

camflan: can you upload your .vimrc? seems interesting. sorry I can't comment to your reply (stackoverflow requires 50 points for comments)

Achille
Always available online at http://media.camronflanders.com/.vimrcit's a mess and probably could use some work/cleanup/etc. Any suggestions are welcome :)
camflan
A: 

Although not a general solution, you can solve (1)(2)(3) by putting the cursor anywhere in (2) and typing da(f)p

DennisP