Vim:映射正常/可视模式移动

3

我有一些在正常模式下移动光标的函数,例如:

function! s:Skip(distance, direction)
    execute "normal! ".string(a:distance).a:direction
endfunction

其中,a:distance是整数,a:direction类似于'h''l'。这在正常模式映射中可以很好地工作,但是我想将这些动作扩展到可视模式。

问题在于,在可视模式下运行的execute "normal! etc..."命令不起作用(即不执行光标移动,就像在正常模式下选择原始和最终光标位置之间的文本)。一个解决方法是:退出可视模式,放置标记,执行skip(),然后从新的光标位置到标记之间进行可视化选择。我不喜欢这个解决方案,不仅因为它需要一个标记,而且因为它也不“感觉”像将正常模式移动转换为可视模式移动的正确方式。

有什么建议吗?我应该指出,我有许多像skip()这样执行execute "normal! movement..."的函数,因此最好有一个通用规则,而不是一次性的、零散的解决方案(如标记,如果是这种情况)。将我执行正常模式移动的方式从execute "normal! etc..."更改为其他方式是完全可以的。

谢谢!


小提示:您不需要使用 string(a:distance);Vim 会自动将数字转换为字符串。 - Ingo Karkat
当您在可视模式下调用函数时,我不明白您想要实现什么。例如,调用4l后,您的光标向左移动4次,您希望选择范围保持不变吗?这意味着新的光标位置位于选择区域的“中间”位置吗?还是您只想更改可视选择区域的4个字符?(取决于您当前的光标位置,即选择区域的开头或结尾) - Kent
我想我想要后者。也就是说,如果 skip()execute "normal! 4l",那么在可视模式下它应该等同于 4l(即它会将光标向左移动4个字符,以便您选择4个字符并将光标停留在第四个字符上)。 - jayflo
2个回答

2
将当前模式通过函数传递(例如通过一个名为isVisual的标志);您的映射知道该模式,或者您可以查询mode()来获取该模式。然后,在可视模式下,通常使用gv重新选择当前选择(因为当您从映射中触发函数时,选择会丢失)。
function! s:Skip(distance, direction, isVisual)
    execute "normal! ".(a:isVisual ? 'gv': '').a:distance.a:direction
endfunction

这将在您的可视模式映射之后保留(扩展)选择。请注意,gv还会将光标恢复到一个选择边界(通常是结尾); 您可能需要考虑到这一点/切换到另一侧(使用o)。


也许我做错了什么,但是像 vmap s execute "normal! gvjj" 这样的东西对我来说也不起作用。 - jayflo
那将是 :vmap s :<C-u>execute "normal! gvjj"<CR>。你仍然需要切换到命令行模式;<C-u> 会移除自动添加的范围。 - Ingo Karkat

2

如果要了解@Ingo Karkat答案中提到的方法,可以查看matchit.vim(在标准vim发行版的macros/目录中)的“实时”示例:

nnoremap <silent> %  :<C-U>call <SID>Match_wrapper('',1,'n') <CR>
nnoremap <silent> g% :<C-U>call <SID>Match_wrapper('',0,'n') <CR>
vnoremap <silent> %  :<C-U>call <SID>Match_wrapper('',1,'v') <CR>m'gv``
vnoremap <silent> g% :<C-U>call <SID>Match_wrapper('',0,'v') <CR>m'gv``
onoremap <silent> %  v:<C-U>call <SID>Match_wrapper('',1,'o') <CR>
onoremap <silent> g% v:<C-U>call <SID>Match_wrapper('',0,'o') <CR>

我使用单字母代码(nvo)来表示模式,而不是isVisual;只是不同的风格。正如在@Ingo Karkat答案后面的评论中所述,<C-U>会删除在从可视模式切换到命令模式时自动输入的范围。还要注意,:vmap使用gv来恢复可视选择。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接