Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swap selection around a pivot

Tags:

vim

(NOT a possible duplicate of Swap text around equal sign :)

Very often I find myself swapping things around. Which is a pain in the ass.

Let say just after I write this piece of code

tmp = realloc (words, sizeof (char **) * (*count + 1));

I notice there are just too many asterisks in a row, don't like it, and want to swap the two factors around the multiplication asterisk.

Or, I write

#if !defined(_CONSOLE_H_) && defined (__MINGW32__)

but I suddenly realize that defined (__MINGW32__) must come first for some reason.

I think it would be cool if I could do something like this:

(On a character x, [x] indicate cursor position. <S> stands for this hypothetic "swap around" command)

#if [!]defined(_CONSOLE_H_) && defined (__MINGW32__)

command: vf&<S>$ => select from cursor to pivot (the word &&), and swap the selection around with the text till end of line.

Or, for the former example:

tmp = realloc (words, [s]izeof (char **) * (*count + 1));

command: v3f*<S>f) => select from here to third *, swap with text forward to ).

For me, it would be incredibly useful. Is there something like this out there, or I have to write my own plug-in?

Thanks!

EDIT -

As @ib. says in the comments to his answer, I need to be more specific as to what is the pivot.

The pivot could also be a character, for example here:

static char ** tokenize_input (char * input, PDWORD count);

I may want to swap the two arguments around the ",". More precisely, the ", ".

So maybe I'll need two commands:

<s> - char-wise - the pivot is the last character of the selection;
<S> - word-wise - the pivot is the last word of the selection.

Thanks! (ps. what about the last Word? :)

like image 612
Metz Avatar asked Feb 21 '23 08:02

Metz


2 Answers

This seems to work here, let me know how it does:

function! Swap(...) "{{{
  let start_v = col("'<")
  let end_v = col("'>")
  let mv = ''
  let isMv = 0
  while !isMv
    let char = s:GetChar()
    if char == '<Esc>'
      return ''
    endif
    let mv .= char
    let isMv = s:IsMovement(mv)
    echon mv."\r"
  endwhile
  if isMv == 2
    return ''
  endif
  exec "normal! ".end_v.'|'.mv
  let lhs = '\%'.start_v.'c\(.\{-}\S\)'
  if !a:0
    let pivot = '\(\s*\%'.(end_v).'c.\s*\)'
  else
    let pivot = '\(\s*'.a:1.'*\%'.(end_v).'c'.a:1.'\+\s*\)'
  endif
  let rhs = '\(.*\%#.\)'
  exec 's/'.lhs.pivot.rhs.'/\3\2\1/'
endfunction "Swap }}}

function! s:GetChar() "{{{
  let char = getchar()
  if type(char) == type(0) && char < 33
    return '<Esc>'
  elseif char
    let char = nr2char(char)
  endif
  return char
endfunction "GetChar }}}

function! s:IsMovement(mv) "{{{
  let ft = a:mv =~ '^\C\d*[fFtT].$'
  let ft_partial = a:mv =~ '^\C\d*\%([fFtT].\?\)\?$'
  let right = a:mv =~ '^\d*[l$|;,]\|g[m$]$'
  let right_partial = a:mv =~ '^\d*\%([l$|;,]\|g[m$]\?\)\?$'
  if !right_partial && !ft_partial
    return 2
  endif
  return ft || right
endfunction "IsMovement2Right }}}

" Use last char as pivot. e.g.: the comma in the given example.
vmap <silent> <leader>s1 :<C-U>call Swap()<CR>
" Use \S\+ (WORD) as pivot. e.g.: &&
vmap <silent> <leader>ss :<C-U>call Swap('\S')<CR>
" Use \w\+ as pivot.
vmap <silent> <leader>sw :<C-U>call Swap('\w')<CR>

No need to press enter to specify the movement now. The 'pivot' always includes any surrounding whitespace and it can be defined with a single character class given as argument to Swap() in the mapping.

If you want to see it highlighted, check https://gist.github.com/1921196

like image 71
Raimondi Avatar answered Mar 04 '23 22:03

Raimondi


I use the following plugin which has been inspired by a vim tip.

First I delete (in one stroke -- diw also counts as one, while xx does not) the first part I want to swap. Then I visually select the second part. And finally I hit g".

Et voilà! The two texts have been swapped.

Of course, It's not as simple as choosing the pivot. Which is normal, as it's not only about a pivot: we have to specify at one time or another where both texts to swap begin and end, plus the cases when the pivot is zero-length(ed?).

like image 30
Luc Hermitte Avatar answered Mar 04 '23 21:03

Luc Hermitte