在vim脚本中替换零宽度匹配

时间:2009-08-04 15:14:16

标签: regex vim

我编写了这个脚本,用一个空格替换了光标周围的许多空格。但是当我在光标周围没有空格的情况下使用它时,这不起作用。在我看来,Vim并没有取代零宽度匹配。

function JustOneSpace()
    let save_cursor = getpos(".")
    let pos = searchpos(' \+', 'bc')
    s/\s*\%#\s*/ /e
    let save_cursor[2] = pos[1] + 1 
    call setpos('.', save_cursor)
endfunction

nmap <space> :call JustOneSpace()<cr>

以下是一些示例(管道|是游标):

这一行

hello     |      world

变为

hello |world

但这一行

hello wo|rld

不会成为

hello wo |rld

更新:通过将功能更改为以下内容,它适用于上述示例。

function JustOneSpace()
    let save_cursor = getpos(".")
    let pos = searchpos(' *', 'bc')
    s/\s*\%#\s*/ /e
    let save_cursor[2] = pos[1] + 1 
    call setpos('.', save_cursor)
endfunction

这一行

hello |world

变为

hello w|orld

问题是游标移动到下一个角色。它应该留在同一个地方。

任何指针和/或提示?

4 个答案:

答案 0 :(得分:4)

我认为你的脚本唯一的问题是位置保存似乎不正确。你基本上可以做你想做的事情:

:s/\s*\%#\s*/ /e

与您问题中的(正确)代码相同。您可以简单地将其映射到:

:nmap <space> :s/\s*\%#\s*/ /e<CR>

如果你想保存位置,它会变得有点复杂。可能最好的选择是使用这样的东西:

function! JustOneSpace()
    " Get the current contents of the current line
    let current_line = getline(".")
    " Get the current cursor position
    let cursor_position = getpos(".")
    " Generate a match using the column number of the current cursor position
    let matchRE = '\(\s*\)\%' . cursor_position[2] . 'c\s*'
    " Find the number of spaces that precede the cursor
    let isolate_preceding_spacesRE = '^.\{-}' . matchRE . '.*$'
    let preceding_spaces = substitute(current_line, isolate_preceding_spacesRE, '\1', "")
    " Modify the line by replacing with one space
    let modified_line = substitute(current_line, matchRE, " ", "")
    " Modify the cursor position to handle the change in string length
    let cursor_position[2] -= len(preceding_spaces) - 1
    " Set the line in the window
    call setline(".", modified_line)
    " Reset the cursor position
    call setpos(".", cursor_position)
endfunction

大多数是评论,但关键是你要看替换前后的行长,并相应地决定新的光标位置。如果您愿意,可以通过比较前后len(getline("."))来使用您的方法执行此操作。

修改

如果希望光标在空格字符后面结束,请修改以下行:

    let cursor_position[2] -= len(current_line) - len(modified_line)

这样看起来像这样:

    let cursor_position[2] -= (len(current_line) - len(modified_line)) - 1

编辑(2)

我已经更改了上面的脚本以考虑您的注释,使得光标位置仅通过光标位置之前的空格数来调整。这是通过创建第二个正则表达式来完成的,该表达式从行中提取光标之前的空格(而不是其他任何内容),然后通过空格数调整光标位置。

答案 1 :(得分:3)

我不使用vim,但如果你想匹配零个或多个空格,你不应该使用' *'而不是' \+'吗?

编辑:重新定位光标定位问题:你现在正在做的是在进行替换之前在空白的开头设置位置,然后将其向前移动一个位置,使其在空格之后。请尝试在匹配的 end 中进行设置,如下所示:

search(' *', 'bce')

这样,在光标位置之前会发生任何添加或删除。在大多数编辑器中,光标位置会自动移动以跟踪此类更改。你不应该做任何getpos / setpos的东西。

答案 2 :(得分:0)

此功能基于Alanswer

function JustOneSpace()
    " Get the current contents of the current line
    let current_line = getline(".")

    " Get the current cursor position
    let cursor_position = getpos(".")

    " Generate a match using the column number of the current cursor position
    let matchre = '\s*\%' . cursor_position[2] . 'c\s*'
    let pos = match(current_line, matchre) + 2

    " Modify the line by replacing with one space
    let modified_line = substitute(current_line, matchre, " ", "")

    " Modify the cursor position to handle the change in string length
    let cursor_position[2] = pos

    " Set the line in the window
    call setline(".", modified_line)
    " Reset the cursor position
    call setpos(".", cursor_position)
endfunction

使用正常线和修改线之间的差异,我找到第一个空格的位置,该位置将与替换的正则表达式匹配。然后我将光标位置设置为该位置+ 1。

答案 3 :(得分:0)

我使用的这个简单的几乎相同:

nnoremap <leader>6 d/\S<CR>

将光标放在要删除空格的位置,并删除光标和下一个文本后的所有空格。