在子替换表达式中引发异常时,Vim会替换空字符串

时间:2019-02-10 17:27:10

标签: vim

更新

提交的issue已在Vim 8.1.1061中修复。


我正在编写一个简短的脚本,以增加Vim中.srt文件的延迟。

实际上,核心功能可以是单行替换命令,用于搜索时间码,并引用一个助手函数,该助手函数将其参数转换为毫秒,进行数学运算,然后将结果转换回时间码格式。

但是,我想做一个额外的检查-如果任何新值是负数,则抛出一个异常,该异常我想处理。给定一个格式正确的字幕文件,其时间代码行按正确的顺序排列,我们可以假设在第一个匹配项(如果有的话)就立即抛出异常,因此无需担心任何状态更改-至少我是这么想的。

fun! s:DelayTimecodes(delay)
    try
        let saved_view = winsaveview()
        let timecode = '\v\d{2}:\d{2}:\d{2},\d{3}'
        exe 'keepjumps keeppatterns %s/' . timecode . '/'
                    \ . '\=s:DelayedTimecode(submatch(0), a:delay)/g'    
    catch 'illegal timecode value'
        redraw | echo 'Cannot apply: the given delay time would result'
                    \ . ' in negative timecode value(s)'
    finally
        call winrestview(saved_view)
    endtry
endfun

但是,当DelayedTimecode表达式内的\=抛出异常时,第一条匹配行上的匹配仍将替换为空字符串。作为一个临时解决方案,我编写了一个循环,逐行进行替换,首先创建整个延迟的行(这可能会引发异常),然后将其传递给替换命令。效果很好,但设计起来却很荒谬。

这背后的原因是什么?有什么方法可以避免这种行为,而无需撤消替换命令并清除历史记录,这有点麻烦?

1 个答案:

答案 0 :(得分:1)

sub-replace-expression不能很好地处理异常。您可以在Vim的bug tracker上提出问题,也可以在vim_dev mailing list上直接讨论此问题,但是这种情况有可能被归类为特定于实现的行为,并且被搁置一旁。

最简单的解决方法是只返回原始匹配项(submatch(0),而不是引发异常(使:substitute成为无操作),并设置一个内部标志使表达式执行对于:substitute中的表达式的每次后续调用都相同。然后,您可以像在:catch中一样,检查该标志并打印错误消息。

如果要避免任何缓冲区修改(也将其标记为已修改),则必须:call search()来找到第一个时间码实例,并将其提取(getline()matchstr() ),然后在实际的:substitute之前进行检查。