Come posso evidenziare i nomi "%" corrispondenti (ad es. If / end, for / end) definiti da matchit.vim alla selezione?


10

Attualmente il mio Vim evidenzia abbinando parentesi, parentesi, virgolette, ecc. Con sfondo ciano e primo piano bianco - il cursore può essere spostato tra questi con %. Grazie a my matchit.vim, posso anche passare %tra if / end, for / end, ecc., Tuttavia questi non sono evidenziati nella selezione.

Come posso evidenziare automaticamente queste coppie corrispondenti alla selezione, come avviene automaticamente con le parentesi?

Inoltre, come posso modificare il colore di sfondo usato per queste coppie usando :highlight?

Grazie in anticipo.


Ho aggiornato la risposta di @Tommy A di seguito per tenere conto dei matchit.vimgruppi mal specificati e di altre situazioni in cui l' %operatore non riporta mai il cursore nella posizione originale. Scopri le differenze nel ciclo "while". A chiunque legga questa discussione si consiglia di utilizzare questa versione, per evitare cicli infiniti:

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
So che questa è una vecchia domanda, ma l'ho appena vista apparire in prima pagina un momento fa. Voglio solo menzionare che il mio nuovo match-up di plugin è progettato per fare esattamente questo, in un modo più robusto: github.com/andymass/vim-matchup (insieme a molti altri miglioramenti rispetto a matchit).
Messa del

Sembra davvero utile, grazie per averlo fatto! Lo proverò.
Luke Davis,

Risposte:


12

Ho pensato che questa idea fosse interessante, quindi ho provato. Sarà particolarmente utile in file densi, come HTML.

linee di corrispondenza

Il seguente script consente semplicemente di matchit.vimfare ciò che fa durante la registrazione dei numeri di riga. Le spiegazioni sono nei commenti della sceneggiatura.

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

CursorMovedTuttavia, non mi piace proprio questo . Penso che sia meglio come una mappa chiave che può essere utilizzata quando ne ho bisogno:

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

È possibile utilizzare matchaddposinvece la funzione. È leggermente più veloce e se evidenzi comunque l'intera linea, semplificherà un po 'le cose.
Karl Yngve Lervåg,

1
@ KarlYngveLervåg Un buon punto. Lo evito inconsciamente perché è ancora una funzione relativamente nuova (v7.4.330 credo) e mi ha morso nel culo una volta. Aggiornerò la risposta per usarla.
Tommy A

Questo è assolutamente perfetto, grazie mille! Buona pratica anche con Vimscript; proverò a capire ogni riga. Immagino che questo potrebbe essere abbastanza popolare se sei il primo a scrivere questo tipo di utilità.
Luke Davis,

@LukeDavis C'è un effetto indesiderato che deriva da questo che ho notato: rovinerà la lista dei salti. Mi è venuto in mente un modo per risolvere il problema usando <c-o>per il numero di volte in cui è stata trovata una corrispondenza e funziona in qualche modo. Il problema è che c'è un bug in matchit.vim che aggiunge la riga superiore della finestra alla jump list. È stato riconosciuto , ma non sembra esserci alcuna fretta per risolverlo.
Tommy A

@ TommyA Ehi, grazie ancora per questa utility. In realtà trovo sul mio computer il ritardo con CursorMove autocmd è piuttosto trascurabile. Ho aggiornato la tua funzione s:get_match_lines(line)per aiutarti a evitare infiniti loop, che stava diventando un grosso problema per me in determinati contesti strani. Purtroppo matchit.vimè pieno di difetti. Vedi la mia modifica sopra e fammi sapere se hai qualche suggerimento; Sono un principiante di vimscript.
Luke Davis,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.