调用Vim %范围函数时如何阻止光标自动跳转至文件首行
问题根因
你在函数内部使用winsaveview()不生效的原因非常明确:当对带range标记的函数执行范围调用(比如:%call、可视选区选中后的:'<,'>call)时,Vim会在正式进入函数执行代码之前,先把光标移动到传入范围的起始行。等你跑到函数第一行执行winsaveview()时,拿到的已经是光标被跳转后的视图状态,当然恢复不到调用前的位置。
解决方案
核心原则:视图保存的逻辑必须放在范围调用触发之前,不能写在带range的函数体内部。
方案1:自定义命令封装调用逻辑(推荐,无侵入)
不需要修改你原有带range的函数实现,通过自定义命令把保存视图、范围调用、恢复视图的逻辑封装在一起即可:
" 原有带range的函数保持不变 function! HelloWorld() range echo "hello world" endfunction " 自定义调用命令,默认范围为整个文件,支持可视选区调用 command! -range=% CallHelloWorld \ let l:save_view = winsaveview() | \ <line1>,<line2>call HelloWorld() | \ call winrestview(l:save_view)
后续直接执行:CallHelloWorld就能实现原来:%call HelloWorld()的效果,光标会停留在调用前的位置,不会自动跳转;如果是选中一段文本后执行该命令,也会自动把选中范围传入函数,完全兼容原有range逻辑。
方案2:临时命令行调用写法
如果只是单次临时调用,不需要长期保留映射,直接在命令行把保存、调用、恢复三个步骤串起来执行即可:
:let v=winsaveview()|%call HelloWorld()|call winrestview(v)
这个写法生效的原因是winsaveview()在%call触发前就完成了视图保存,此时光标还停留在调用前的位置,后续哪怕范围调用把光标挪走,最后也能正确恢复。
注意:不要尝试在带range的函数内部通过标记、行号变量等方式记录位置,所有函数内部的记录逻辑都晚于Vim自动移动光标的时机,不可能拿到调用前的光标位置。
内容的提问来源于stack exchange,提问作者Kaiwen
相关产品推荐
相关产品推荐

