Comment puis-je mettre en évidence les noms «%» correspondants (par exemple, si / end, for / end) définis par matchit.vim lors de la sélection?

10

Actuellement, mon Vim met en évidence les parenthèses, crochets, guillemets, etc. correspondants avec un arrière-plan cyan et un premier plan blanc - le curseur peut être déplacé entre ceux-ci avec %. Grâce à mon matchit.vim, je peux également basculer %entre if / end, for / end, etc. - mais ceux-ci ne sont pas mis en évidence lors de la sélection.

Comment puis-je mettre automatiquement en surbrillance ces paires correspondantes lors de la sélection, comme cela se fait automatiquement avec les parenthèses?

De plus, comment puis-je modifier la couleur d'arrière-plan utilisée pour ces paires à l'aide de :highlight?

Merci d'avance.


J'ai mis à jour la réponse de @Tommy A ci-dessous pour tenir compte des matchit.vimgroupes mal spécifiés et d'autres situations où l' %opérateur ne remet jamais le curseur à sa position d'origine. Découvrez les différences dans la boucle "while". Quiconque lit ce fil est conseillé d'utiliser cette version, pour éviter les boucles infinies:

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
Luke Davis
la source
2
Je sais que c'est une vieille question, mais je viens de la voir apparaître sur la première page il y a un instant. Je veux juste mentionner que ma nouvelle correspondance de plugin est conçue pour faire exactement cela, d'une manière plus robuste: github.com/andymass/vim-matchup (ainsi que de nombreuses autres améliorations par rapport à matchit).
Messe du
Semble vraiment utile, merci d'avoir fait ça! Je vais l'essayer.
Luke Davis

Réponses:

12

Je pensais que cette idée était intéressante, alors je l'ai essayée. Il sera particulièrement utile dans les fichiers denses, tels que HTML.

lignes de correspondance

Le script suivant permet simplement de matchit.vimfaire ce qu'il fait lors de l'enregistrement des numéros de ligne. Les explications se trouvent dans les commentaires du script.

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

CursorMovedCependant, je n'aime pas vraiment que cela se produise . Je pense que c'est mieux comme carte clé qui peut être utilisée quand j'en ai besoin:

nnoremap <silent> <leader>l :<c-u>call <sid>hl_matching_lines()<cr>
Tommy A
la source
Vous pouvez utiliser la matchaddposfonction à la place. C'est un peu plus rapide et si vous mettez en surbrillance la ligne entière de toute façon, cela simplifiera un peu les choses.
Karl Yngve Lervåg
1
@ KarlYngveLervåg Bon point. Je l'évite inconsciemment parce que c'est encore une fonction relativement nouvelle (v7.4.330 je pense) et ça m'a mordu dans le cul une fois. Je mettrai à jour la réponse pour l'utiliser.
Tommy A
C'est absolument parfait, merci beaucoup! Bonne pratique Vimscript aussi; va essayer de comprendre chaque ligne. J'imagine que cela pourrait être très populaire si vous êtes le premier à écrire ce genre d'utilitaire.
Luke Davis
@LukeDavis Il y a un effet indésirable qui en résulte que j'ai remarqué: cela va bousiller la liste de sauts. J'ai trouvé un moyen de le corriger en utilisant <c-o>le nombre de fois qu'une correspondance a été trouvée et cela fonctionne d'une certaine manière. Le problème est qu'il y a un bogue dans matchit.vim qui ajoute la ligne supérieure de la fenêtre à la liste de raccourcis. Il a été reconnu , mais il ne semble pas urgent de le réparer.
Tommy A
@TommyA Hé, merci encore pour cet utilitaire. En fait, je trouve sur mon ordinateur que le délai avec l'autocmd CursorMove est assez négligeable. J'ai mis à jour votre fonction s:get_match_lines(line)pour vous protéger contre les boucles infinies, ce qui devenait un gros problème pour moi dans certains contextes étranges. Malheureusement, il matchit.vimest plein de défauts. Voir ma modification ci-dessus et faites-moi savoir si vous avez des suggestions; Je suis un débutant vimscript.
Luke Davis