如何在选择时突出显示matchit.vim定义的匹配“%”名称(例如if / end,for / end)?


10

目前,我的Vim用青色背景和白色前景突出显示匹配的括号,方括号,引号等-可以在之间移动光标%。多亏了matchit.vim,我也可以%在if / end,for / end等之间切换。但是,这些在选择时未突出显示。

如何在选择时自动突出显示这些匹配对,就像用括号自动完成一样?

此外,如何使用修改用于这些对的背景色:highlight

提前致谢。


我已经通过下面的@Tommy A更新了答案,以说明指定的matchit.vim组不正确,以及其他情况下%操作员永远不会将光标返回到原始位置。检查“ while”循环中的差异。建议阅读此线程的任何人都使用此版本,以避免无限循环:

function! s:get_match_lines(line) abort
  " Loop until `%` returns the original line number; abort if
  " (1) the % operator keeps us on the same line, or
  " (2) the % operator doesn't return us to the same line after some nubmer of jumps
  let a:tolerance=25
  let a:badbreak=1
  let a:linebefore=-1
  let lines = []
  while a:tolerance && a:linebefore != line('.')
    let a:linebefore=line('.')
    let a:tolerance-=1
    normal %
    if line('.') == a:line
      " Note that the current line number is never added to the `lines`
      " list. a:line is the input argument 'line'; a is the FUNCTION BUFFER
      let a:badbreak=0
      break
    endif
    call add(lines, line('.'))
  endwhile
  "Return to original line no matter what, return list of lines to highlight
  execute "normal ".a:line."gg"
  if a:badbreak==1
    return []
  else
    return lines
  endif
endfunction

function! s:hl_matching_lines() abort
  " `b:hl_last_line` prevents running the script again while the cursor is
  " moved on the same line.  Otherwise, the cursor won't move if the current
  " line has matching pairs of something.
  if exists('b:hl_last_line') && b:hl_last_line == line('.')
    return
  endif
  let b:hl_last_line = line('.')
  " Save the window's state.
  let view = winsaveview()
  " Delete a previous match highlight.  `12345` is used for the match ID.
  " It can be anything as long as it's unique.
  silent! call matchdelete(12345)
  " Try to get matching lines from the current cursor position.
  let lines = s:get_match_lines(view.lnum)
  if empty(lines)
    " It's possible that the line has another matching line, but can't be
    " matched at the current column.  Move the cursor to column 1 to try
    " one more time.
    call cursor(view.lnum, 1)
    let lines = s:get_match_lines(view.lnum)
  endif
  if len(lines)
    " Since the current line is not in the `lines` list, only the other
    " lines are highlighted.  If you want to highlight the current line as
    " well:
    " call add(lines, view.lnum)
    if exists('*matchaddpos')
      " If matchaddpos() is availble, use it to highlight the lines since it's
      " faster than using a pattern in matchadd().
      call matchaddpos('MatchLine', lines, 0, 12345)
    else
      " Highlight the matching lines using the \%l atom.  The `MatchLine`
      " highlight group is used.
      call matchadd('MatchLine', join(map(lines, '''\%''.v:val.''l'''), '\|'), 0, 12345)
    endif
  endif
  " Restore the window's state.
  call winrestview(view)
endfunction
function! s:hl_matching_lines_clear() abort
  silent! call matchdelete(12345)
  unlet! b:hl_last_line
endfunction

" The highlight group that's used for highlighting matched lines.  By
" default, it will be the same as the `MatchParen` group.
highlight default link MatchLine MatchParen
augroup matching_lines
  autocmd!
  " Highlight lines as the cursor moves.
  autocmd CursorMoved * call s:hl_matching_lines()
  " Remove the highlight while in insert mode.
  autocmd InsertEnter * call s:hl_matching_lines_clear()
  " Remove the highlight after TextChanged.
  autocmd TextChanged,TextChangedI * call s:hl_matching_lines_clear()
augroup END

2
我知道这是一个老问题,但是我刚才看到它突然出现在首页上。只想提及我的新插件对决就是为了以一种更强大的方式完全做到这一点:github.com/andymass/vim-matchup(以及对matchit的许多其他改进)。
质谱

看起来真的很有用,谢谢您做到这一点!我会尝试一下。
卢克·戴维斯

Answers:


12

我以为这个主意很有趣,所以我试了一下。在密集文件(例如HTML)中,它将特别有用。

匹配线

下面的脚本只是让matchit.vim我们在记录行号时做些什么。说明在脚本的注释中。

matchlines.vim

function! s:get_match_lines(line) abort
  let lines = []

  " Loop until `%` returns the original line number
  while 1
    normal %
    if line('.') == a:line
      " Note that the current line number is never added to the `lines`
      " list.
      break
    endif
    call add(lines, line('.'))
  endwhile

  return lines
endfunction

function! s:hl_matching_lines() abort
  " `b:hl_last_line` prevents running the script again while the cursor is
  " moved on the same line.  Otherwise, the cursor won't move if the current
  " line has matching pairs of something.
  if exists('b:hl_last_line') && b:hl_last_line == line('.')
    return
  endif

  let b:hl_last_line = line('.')

  " Save the window's state.
  let view = winsaveview()

  " Delete a previous match highlight.  `12345` is used for the match ID.
  " It can be anything as long as it's unique.
  silent! call matchdelete(12345)

  " Try to get matching lines from the current cursor position.
  let lines = s:get_match_lines(view.lnum)

  if empty(lines)
    " It's possible that the line has another matching line, but can't be
    " matched at the current column.  Move the cursor to column 1 to try
    " one more time.
    call cursor(view.lnum, 1)
    let lines = s:get_match_lines(view.lnum)
  endif

  if len(lines)
    " Since the current line is not in the `lines` list, only the other
    " lines are highlighted.  If you want to highlight the current line as
    " well:
    " call add(lines, view.lnum)
    if exists('*matchaddpos')
      " If matchaddpos() is availble, use it to highlight the lines since it's
      " faster than using a pattern in matchadd().
      call matchaddpos('MatchLine', lines, 0, 12345)
    else
      " Highlight the matching lines using the \%l atom.  The `MatchLine`
      " highlight group is used.
      call matchadd('MatchLine', join(map(lines, '''\%''.v:val.''l'''), '\|'), 0, 12345)
    endif
  endif

  " Restore the window's state.
  call winrestview(view)
endfunction

function! s:hl_matching_lines_clear() abort
  silent! call matchdelete(12345)
  unlet! b:hl_last_line
endfunction


" The highlight group that's used for highlighting matched lines.  By
" default, it will be the same as the `MatchParen` group.
highlight default link MatchLine MatchParen

augroup matching_lines
  autocmd!
  " Highlight lines as the cursor moves.
  autocmd CursorMoved * call s:hl_matching_lines()
  " Remove the highlight while in insert mode.
  autocmd InsertEnter * call s:hl_matching_lines_clear()
  " Remove the highlight after TextChanged.
  autocmd TextChanged,TextChangedI * call s:hl_matching_lines_clear()
augroup END

不过,我不太喜欢这种情况CursorMoved。我认为最好将它作为可以在需要时使用的关键地图:

nnoremap <silent> <leader>l :<c-u>call <sid>hl_matching_lines()<cr>

您可以改用该matchaddpos函数。它的速度稍快一些,如果您仍然突出显示整条线,将会使事情稍微简化一些。
Karl YngveLervåg'16

1
@KarlYngveLervåg好点。我下意识地避免使用它,因为它仍然是一个相对较新的功能(我认为是v7.4.330),它一次又一次地把我咬了。我将更新答案以使用它。
汤米

这绝对是完美的,非常感谢!Vimscript的做法也很好;将尝试了解每一行。我想如果您是第一个编写这种实用程序的人,这可能会很受欢迎。
卢克·戴维斯

@LukeDavis我注意到有一个不希望有的效果:它将弄乱跳转列表。我提出了一种通过使用<c-o>找到匹配项并以某种方式起作用的次数来修复它的方法。问题是matchit.vim中存在一个错误,该错误将窗口的顶行添加到跳转列表中。 它已经得到承认,但是似乎并没有急于修复它。
汤米

@TommyA嘿,再次感谢您使用此实用程序。实际上,我在计算机上发现CursorMove autocmd的延迟可以忽略不计。我更新了您的函数s:get_match_lines(line)以帮助防止无限循环,这在某些怪异的情况下对我来说已成为一个大问题。不幸的matchit.vim是充满了缺陷。请参阅上面的编辑,如果您有任何建议,请告诉我;我是vimscript初学者。
卢克·戴维斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.