将Vim中每行开头的空格数减半

时间:2012-02-02 19:21:40

标签: vim removing-whitespace

有人可以告诉我如何在Vim中执行与此映射相反的操作:

nnoremap <leader>iw :let _s=@/<Bar>:let _s2=line(".")<Bar>:%s/^\s*/&&/ge<Bar>:let @/=_s<Bar>:nohl<Bar>exe ':'._s2<CR>

作为澄清,此映射将每行开头的空白数加倍(&&部分)。 只有第一个常规字符前的空格才会受到影响。 保留当前搜索字符串(变量_s)。 此转换后,位置将恢复(变量_s2

所以基本上我正在寻找一个映射,如果它们一个接一个地执行就会撤消这个映射。

我在弄清楚如何限制这个新操作只能在第一个常规字符之前的空格上工作时遇到了麻烦。

2 个答案:

答案 0 :(得分:5)

以下替换命令会反转其对应的效果 领先的空白加倍。

:%s/^\(\s*\)\1/\1/

要为此命令构建的映射需要遵循相同的规则 模式作为问题陈述中使用的模式(除了 当然,替换执行)。减少重复 定义,可以将状态保留代码分成小的 功能:

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

答案 1 :(得分:2)

您的原始替换为此(我已将/分隔符替换为#以便于阅读):

%s#^\s*#&&#

这是我提出的反向替代(深呼吸......):

%s#^\s*#\=matchstr(submatch(0),'^.\{'.string(float2nr(len(submatch(0))/2)).'\}')#

假设匹配的字符串(submatch(0))包含n个空格字符。我正在做的是计算这个数字的一​​半(n/2 = string(float2nr(len(submatch(0))/2))),然后从匹配中提取那么多字符(主要是matchstr(n/2))。这可以确保我们获得正好一半的空白(可能是空格和制表符的混合)。

如果您知道空格将仅包含空格或仅包含选项卡,则可以稍微简化一下,例如:

%s#^\s*#\=repeat(" ",indent(".")/2)#

另一方面,我建议您重新制作地图,使其更易读,因此更容易修改和维护。我的方法是定义两个函数:

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

请注意,get/set pos/reg函数是维护光标位置和寄存器的更强大的方法。然后,您可以根据需要映射这些功能:

nnoremap <silent> <leader>iw :call DoubleWS()<CR>
nnoremap <silent> <leader>rw :call HalfWS()<CR>

希望有所帮助!