我有一个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>
但是,在我的有限测试中(我只搜索了几千行中的the
或foobar
这样的简单模式),似乎while
循环是通常比s///gen
慢。有时候只是一点点,有时甚至更多(例如8倍)。
如何防止Vim Ex命令花费太多时间,在一般情况下,或至少在此特定时间内?
答案 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
命令一起快速触发。)这会添加一个额外的映射,并将责任放在用户身上,但至少对我而言,我很少需要摘要,而不是总是和跳到比赛一起。