You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义Markdown链接文本对象在Vim脚本/命令模式下无法执行操作

自定义Markdown文本对象在脚本/命令模式失效的问题解决

我为Markdown链接创建了自定义文本对象il,正常模式下手动执行yil/dil等操作能正确选中并操作链接,但在函数脚本里调用normal! yil,或者通过映射执行时完全失效。

原自定义文本对象代码

function! markdown#InLink()
    let l:magic = &magic
    set magic

    let l:currentLineNumber = line('.')

    " markdown link regex pattern
    let l:pattern = '\[[^\]]\+\]([^)]\+)'

    " move cursor to end of the markdown link
    if (!search(l:pattern, 'ce', l:currentLineNumber))
        " if it fails, there was not match on the line, so return prematurely
        return
    endif

    " start visually selecting from end of the markdown link
    normal! v

    " move cursor to beginning of the markdown link
    call search(l:pattern, 'cb', l:currentLineNumber)

    " restore magic
    let &magic = l:magic
endfunction

xnoremap <silent> il :<c-u>call markdown#InLink()<cr>
onoremap <silent> il :<c-u>call markdown#InLink()<cr>

原复制链接函数代码

function! markdown#YankLink()
    normal! yil
endfunction

nnoremap <buffer> <Leader>yl :call markdown#YankLink()<CR>

问题原因

原函数依赖search()命令的光标移动和交互模式下的视觉选中同步,在脚本/命令模式执行时,search()的位置变更无法和normal! v的选中操作正确同步,导致选中范围失效。另外,onoremap的操作符等待模式在非交互环境下,原逻辑没有处理光标位置的正确绑定。

解决方案

修改markdown#InLink()函数,改用直接匹配行内文本的方式定位链接范围,并区分操作符等待模式和视觉模式的处理逻辑:

function! markdown#InLink()
    let l:magic = &magic
    set magic

    let l:current_line = getline('.')
    let l:link_pattern = '\[[^\]]\+\]([^)]\+)'
    let l:match_start = match(l:current_line, l:link_pattern)
    
    " 当前行无匹配链接则退出
    if l:match_start == -1
        let &magic = l:magic
        return 0
    endif
    
    let l:match_end = matchend(l:current_line, l:link_pattern)
    let l:current_row = line('.')

    " 区分操作符等待模式(如y/d之后调用il)和视觉模式
    if !empty(v:operator)
        " 操作符模式下:先定位到链接起始,再选中到结束
        call setpos('.', [0, l:current_row, l:match_start + 1, 0])
        normal! v
        call setpos('.', [0, l:current_row, l:match_end, 0])
    else
        " 视觉模式下:先定位到链接结束,再反向选中到起始
        call setpos('.', [0, l:current_row, l:match_end, 0])
        normal! v
        call setpos('.', [0, l:current_row, l:match_start + 1, 0])
    endif

    let &magic = l:magic
    return 1
endfunction

xnoremap <silent> il :<c-u>call markdown#InLink()<cr>
onoremap <silent> il :<c-u>call markdown#InLink()<cr>

修改说明

  1. 用match()和matchend()替代search(),直接在当前行内查找链接的起始和结束位置,避免跨搜索的光标同步问题
  2. 通过v:operator变量判断当前是否处于操作符等待模式(比如执行y后等待文本对象),分别处理光标定位逻辑
  3. 用setpos()直接设置光标位置,确保在脚本执行时也能准确定位并选中链接范围

修改后,执行<Leader>yl或者:execute 'normal! yil'即可正常复制Markdown链接。


内容的提问来源于stack exchange,提问作者ankur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 21:40:39