【问题标题】:Substituting zero-width match in vim script在 vim 脚本中替换零宽度匹配
【发布时间】:2010-11-16 17:15:18
【问题描述】:

我编写了这个脚本,将光标周围的许多空格替换为一个空格。但是,当我在光标周围没有空格的情况下使用它时,这不起作用。在我看来,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

问题是光标移动到下一个字符。它应该留在同一个地方。

任何提示和/或提示?

【问题讨论】:

    标签: regex vim


    【解决方案1】:

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

    nnoremap &lt;leader&gt;6 d/\S&lt;CR&gt;

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

    【讨论】:

      【解决方案2】:

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

      :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
      

      其中大部分是 cmets,但关键是您查看替换前后的行长度并相应地决定新的光标位置。如果您愿意,可以通过比较 len(getline(".")) 之前和之后的方法来使用您的方法。

      编辑

      如果希望光标在空格字符后结束,修改该行:

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

      看起来像这样:

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

      编辑 (2)

      我已经更改了上面的脚本以考虑您的 cmets,以便光标位置仅根据光标位置之前的空格数进行调整。这是通过创建第二个正则表达式来完成的,该表达式从行中提取光标之前的空格(仅此而已),然后根据空格数调整光标位置。

      【讨论】:

      • 这个功能差不多。有一个问题:它会将光标移动到空格字符的前面而不是后面。
      • 啊,我不知道你想让光标在哪里结束,所以我随便挑了一个。请参阅上面的编辑。
      • 经过一些额外的研究,您的功能似乎根本不起作用。如果有很多空格,两个单词之间可能会有很多光标位置。光标应该移动光标前面的空格数才能到达正确的位置,而不是删除的空格总数。如果您将光标放在单词之间的其他位置尝试此操作,您可以自己看到。但我想我已经找到了获得正确结果的方法。
      【解决方案3】:

      这个函数是基于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。

      【讨论】:

      • 我已经修改了我的函数以另一种方式执行此操作(通过在更改行之前计算光标前的空格数)。
      【解决方案4】:

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

      编辑:关于光标定位问题:您现在所做的是在进行替换之前将位置设置在空格的开头,然后将其向前移动一个位置,使其位于空格之后。尝试将其设置在比赛的结束,如下所示:

      search(' *', 'bce')
      

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

      【讨论】:

      • 艾伦是正确的,你想要 *.使用该修改会导致脚本按照您描述的方式运行。
      • 我刚试过这个,但还有一个问题。我将用示例更新问题。
      猜你喜欢
      • 1970-01-01
      • 2021-07-17
      • 2014-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-24
      • 2017-06-23
      相关资源
      最近更新 更多