¿Cómo puedo resaltar los nombres de "%" coincidentes (por ejemplo, si / end, para / end) definidos por matchit.vim en la selección?

10

Actualmente, mi Vim resalta paréntesis, corchetes, comillas, etc. coincidentes con el fondo cian y el primer plano blanco; el cursor se puede mover entre ellos con %. Gracias a mi matchit.vim, también puedo cambiar %entre if / end, for / end, etc., sin embargo, estos no están resaltados en la selección.

¿Cómo puedo resaltar automáticamente estos pares coincidentes tras la selección, como se hace automáticamente con paréntesis?

Además, ¿cómo puedo modificar el color de fondo utilizado para estos pares :highlight?

Gracias por adelantado.


He actualizado la respuesta de @Tommy A a continuación para tener en cuenta los matchit.vimgrupos mal especificados y otras situaciones en las %que el operador no regresa el cursor a la posición original. Vea las diferencias en el ciclo "while". Se recomienda a cualquiera que lea este hilo que use esta versión para evitar bucles infinitos:

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
fuente
2
Sé que esta es una vieja pregunta, pero acabo de verla aparecer en la portada hace un momento. Solo quiero mencionar que mi nuevo complemento de complemento está diseñado para hacer exactamente esto, de una manera más robusta: github.com/andymass/vim-matchup (junto con muchas otras mejoras sobre matchit).
Misa del
Parece realmente útil, ¡gracias por hacer esto! Voy a tratar de salir.
Luke Davis,

Respuestas:

12

Pensé que esta idea era interesante, así que le di una oportunidad. Será particularmente útil en archivos densos, como HTML.

líneas de partido

El siguiente script simplemente permite matchit.vimhacer lo que hace mientras graba los números de línea. Las explicaciones están en los comentarios del guión.

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

Sin CursorMovedembargo, realmente no me gusta que esto suceda . Creo que es mejor como un mapa clave que se puede usar cuando lo necesito:

nnoremap <silent> <leader>l :<c-u>call <sid>hl_matching_lines()<cr>
Tommy A
fuente
Podrías usar la matchaddposfunción en su lugar. Es un poco más rápido y si resaltas toda la línea de todos modos, simplificará un poco las cosas.
Karl Yngve Lervåg
1
@ KarlYngveLervåg Buen punto. Inconscientemente lo evito porque todavía es una función relativamente nueva (v7.4.330, creo) y una vez me mordió el culo. Actualizaré la respuesta para usarlo.
Tommy A
Esto es absolutamente perfecto, ¡muchas gracias! Buena práctica de Vimscript también; intentará entender cada línea. Me imagino que esto podría ser bastante popular si eres el primero en escribir este tipo de utilidad.
Luke Davis
@LukeDavis Hay un efecto no deseado que viene de esto que noté: arruinará la lista de saltos. Se me ocurrió una forma de solucionarlo usando <c-o>la cantidad de veces que se encontró una coincidencia y funciona de alguna manera. El problema es que hay un error en matchit.vim que agrega la línea superior de la ventana a la lista de salto. Se ha reconocido , pero no parece haber ninguna prisa por solucionarlo.
Tommy A
@TommyA Hola, gracias de nuevo por esta utilidad. De hecho, encuentro en mi computadora que el retraso con el autocmd CursorMove es bastante insignificante. Actualicé su función s:get_match_lines(line)para ayudar a proteger contra bucles infinitos, lo que se estaba convirtiendo en un gran problema para mí en ciertos contextos extraños. Lamentablemente matchit.vimestá lleno de defectos. Vea mi edición arriba y avíseme si tiene alguna sugerencia; Soy un principiante vimscript.
Luke Davis