Reputation: 539
Can someone tell me how to do the opposite of this mapping in Vim:
nnoremap <leader>iw :let _s=@/<Bar>:let _s2=line(".")<Bar>:%s/^\s*/&&/ge<Bar>:let @/=_s<Bar>:nohl<Bar>exe ':'._s2<CR>
As a clarification: This mapping doubles (the &&
part) the number of whitespace characters at the beginning of each line.
Only the whitespace before the first regular character is affected.
Current search string is kept in tact (via the _s
variable).
The cursor position is restored after this transformation (via the _s2
variable).
So, basically, I’m searching for a mapping that will undo this one if they are executed one after another.
I’m having trouble in figuring out how to limit this new operation to work only on whitespace before the first regular character.
Upvotes: 4
Views: 344
Reputation: 28934
The following substitute command inverses the effect of its counterpart that doubles the leading whitespace.
:%s/^\(\s*\)\1/\1/
A mapping to be constructed for this command needs to follow the same pattern as the one used in the question (except for the substitution to execute, of course). To reduce repetition in the definitions, one can separate the state-preserving code into a small function:
nnoremap <silent> <leader>> :call PinnedCursorDo('%s/^\s*/&&/')<cr>
nnoremap <silent> <leader><lt> :call PinnedCursorDo('%s/^\(\s*\)\1/\1/')<cr>
function! PinnedCursorDo(cmd)
let [s, c] = [@/, getpos('.')]
exe a:cmd
let @/ = s
call setpos('.', c)
endfunction
Upvotes: 6
Reputation: 15715
Your original substitution is this (I have replaced the /
delimiters with #
for readability):
%s#^\s*#&&#
And here is my proposed inverse substitution (take a deep breath...):
%s#^\s*#\=matchstr(submatch(0),'^.\{'.string(float2nr(len(submatch(0))/2)).'\}')#
Let's say the matched string (submatch(0)
) contains n
whitespace characters. What I am doing is calculating half this number (n/2
= string(float2nr(len(submatch(0))/2))
) and then extracting that many characters from the match (essentially matchstr(n/2)
). This ensures we get precisely half the whitespace we started with (which may be a mixture of spaces and tabs).
If you know the whitespace will contain ONLY spaces or ONLY tabs, this could be simplified somewhat, for example:
%s#^\s*#\=repeat(" ",indent(".")/2)#
On another note, I would recommend reformulating your maps to make them more readable and therefore easier to modify and maintain. My approach would be to define two functions:
function! DoubleWS()
let pos = getpos('.')
let reg = getreg('@')
exe '%s/^\s*/&&/e'
call setreg('@',reg)
call setpos('.',pos)
endfunction
function! HalfWS()
let pos = getpos('.')
let reg = getreg('@')
exe '%s#^\s*#\=matchstr(submatch(0),"^.\\{".string(float2nr(len(submatch(0))/2))."\}")#e'
call setreg('@',reg)
call setpos('.',pos)
endfunction
Note that the get/set
pos/reg
functions are a much more robust way of maintaining cursor position and register. You can then map these functions as you wish:
nnoremap <silent> <leader>iw :call DoubleWS()<CR>
nnoremap <silent> <leader>rw :call HalfWS()<CR>
Hope that helps!
Upvotes: 2