将VimScript按键映射转为函数:视觉模式自动高亮失效问题
问题:视觉模式下光标移动自动高亮选中内容匹配项失效
我想要编写一个VimScript函数,使其在视觉模式下每次光标移动时自动高亮当前选中内容的所有匹配项。参考的按键映射可正常实现功能:
xnoremap <silent> <cr> "*y:silent! let searchTerm = '\V'.substitute(escape(@*, '\/'), "\n", '\\n', "g") <bar> let @/ = searchTerm <bar> echo '/'.@/ <bar> call histadd("search", searchTerm) <bar> set hls<cr>
但将其转为如下函数后无法工作:
function! HighlightVisual(mode) if mode()=~#"^[vV\<C-v>]" call feedkeys('"*y') let searchTerm = '\V'.substitute(escape(@*, '\/'), "\n", '\\n', "g") let @/ = searchTerm echo '/'.@/ call histadd("search", searchTerm) set hls endif endfunction autocmd CursorMoved * call HighlightVisual(mode())
错误原因分析
feedkeys()异步执行导致变量赋值错误:feedkeys()是异步触发按键操作的,函数调用它后会立刻执行后续代码,此时@*寄存器还没被新的选中内容赋值,导致searchTerm为空或仍是旧值,高亮逻辑失效。- 视觉模式下文本获取方式错误:不该用模拟按键复制的方式获取选中内容,Vim提供了直接获取视觉选中区域的内置方法,无需依赖异步按键操作。
修正后的函数实现
function! HighlightVisual() let current_mode = mode() " 判断当前是否处于视觉模式(普通视觉、行视觉、块视觉) if current_mode =~# '^[vV<C-v>]' " 获取视觉选中区域的起始和结束位置 let [start_line, start_col] = getpos("'<")[1:2] let [end_line, end_col] = getpos("'>")[1:2] let selected_text = '' " 处理单行选中的情况 if start_line == end_line let selected_text = getline(start_line)[start_col-1 : end_col-1] " 处理多行选中的情况 else let selected_text = getline(start_line)[start_col-1:] for line in range(start_line+1, end_line-1) let selected_text .= "\n" . getline(line) endfor let selected_text .= "\n" . getline(end_line)[:end_col-1] endif " 格式化搜索字符串,保持和原映射一致的逻辑 let searchTerm = '\V' . substitute(escape(selected_text, '\/'), "\n", '\\n', "g") let @/ = searchTerm echo '/' . @/ call histadd("search", searchTerm) set hls endif endfunction " 光标移动时触发高亮函数 autocmd CursorMoved * call HighlightVisual()
修正说明
- 移除了
feedkeys()调用,改用getpos()结合getline()直接提取视觉选中的文本,避免异步操作带来的时序问题。 - 兼容单行、多行选中的场景,确保选中文本提取准确。
- 简化函数参数传递,直接在函数内获取当前模式,逻辑更清晰。
内容的提问来源于stack exchange,提问作者lobelk
相关产品推荐
相关产品推荐

