如何防止Vim Ex命令花费太多时间?

时间:2017-07-27 08:50:17

标签: vim

我有一个Vim映射,它覆盖默认的n命令,在下一个匹配时移动光标,但也显示搜索寄存器的匹配数,以及匹配的索引光标。

它有效,除了当模式变得有点复杂和/或有许多匹配时,脚本可能需要花费很多时间(几秒钟)。结果被缓存,因此在初始n之后,只要缓冲区没有更改,连续的n就不会导致任何减速。

在分析脚本之后,我发现到目前为止花费最多时间的命令是:

let output = execute(a:range.'s///gen')

我在function中使用它来计算任意范围内的匹配数:

fu! s:matches_in_range(range) abort
    let output = execute(a:range.'s///gen')
    return str2nr(matchstr(output, '\d\+'))
endfu

要解决这个问题,我有两个想法,但它们都有问题。

我可以尝试猜测命令是否会花费太多时间,通过计算初始范围的小子范围内的匹配数,然后计算其余行。

但是大多数匹配可能超出了这个子范围,因此测试可能无法检测到总体上需要花费太多时间。

或者,我可以使用while循环和search()函数重构函数。在每次迭代之后,我可以测试自开始以来经过了多长时间,并在超过某个限制时取消。另一个好处是第4个可选参数{timeout},可以传递给search()。如果模式真的太复杂,甚至循环的第一次迭代花费太多时间,这个参数可能会停止该函数。这是它的样子(没有超时参数):

fu! Total_matches() abort
    let view      = winsaveview()
    let total     = 0
    let matchline = search(@/, 'cW')
    let time      = reltime()

    call cursor(1, 1)
    while matchline && total <= 9999
        if reltimefloat(reltime(time)) > 1
            echo 'too many matches'
            call winrestview(view)
            return
        endif
        let total += 1
        let matchline = search(@/, 'W')
    endwhile

    call winrestview(view)
    echo @/.' ['.total.']'
endfu

nno cd :call Total_matches()<cr>

但是,在我的有限测试中(我只搜索了几千行中的thefoobar这样的简单模式),似乎while循环是通常比s///gen慢。有时候只是一点点,有时甚至更多(例如8倍)。

如何防止Vim Ex命令花费太多时间,在一般情况下,或至少在此特定时间内?

1 个答案:

答案 0 :(得分:1)

您已经概述了明显的方法:

  • 一些低级函数(如search())会获取{timeout}值,以避免它们阻止用户操作太长时间。不幸的是,它不是:%s///gn的100%等价物,所以你需要额外的代码(这里:Vimscript中的一个较慢的循环)。

  • 您可以实施启发式算法来估计调用是否会很慢,然后不要这样做。这将永远不会是完美的,需要付出很多努力,但也许还不错。

建议

此附加(搜索摘要)信息对用户有多重要?通常,可能不是必需的,但可能存在用户愿意等待几秒钟的情况。

我将确保主要功能(此处:跳转到下一个匹配的n命令)将始终执行(第一个),并且可以通过按<C-C>来中止摘要生成(也许多次,突破:%s然后退出你的功能)。基本上,这意味着:catch ing Vim:Interrupt,请参阅:help catch-interrupt

我的方法

我已经写了SearchPosition plugin来显示按需这样的摘要信息。它不与n集成,必须始终单独触发。 (默认映射为<A-n>,因此可以与n命令一起快速触发。)这会添加一个额外的映射,并将责任放在用户身上,但至少对我而言,我很少需要摘要,而不是总是和跳到比赛一起。