Jak mogę podświetlić pasujące nazwy „%” (np. If / end, for / end) zdefiniowane przez matchit.vim przy wyborze?


10

Obecnie mój Vim wyróżnia pasujące nawiasy, nawiasy, cytaty itp. Z cyjanowym tłem i białym planem - kursor można przesuwać między nimi za pomocą %. Dzięki mojemu matchit.vim mogę także przełączać się %pomiędzy if / end, for / end itp. - jednak nie są one podświetlane przy wyborze.

Jak mogę automatycznie podświetlić te pasujące pary po wybraniu, tak jak to robi się automatycznie z nawiasami?

Ponadto, w jaki sposób mogę zmienić kolor tła użyty dla tych par :highlight?

Z góry dziękuję.


Zaktualizowałem odpowiedź @ Tommy A poniżej, aby uwzględnić słabo określone matchit.vimgrupy i inne sytuacje, w których %operator nigdy nie przywraca kursora do pierwotnej pozycji. Sprawdź różnice w pętli „while”. Każdy, kto czyta ten wątek, powinien użyć tej wersji, aby uniknąć nieskończonych pętli:

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
Wiem, że to stare pytanie, ale przed chwilą zobaczyłem, jak wyskakuje na pierwszej stronie. Chcę tylko wspomnieć, że mój nowy zestaw wtyczek został zaprojektowany tak, aby robić to dokładnie w bardziej niezawodny sposób: github.com/andymass/vim-matchup (wraz z wieloma innymi ulepszeniami w stosunku do matchit).
Mass

Wygląda naprawdę przydatnie, dziękuję za zrobienie tego! Wypróbuję to.
Luke Davis,

Odpowiedzi:


12

Pomyślałem, że ten pomysł jest interesujący, więc spróbowałem. Będzie to szczególnie przydatne w gęstych plikach, takich jak HTML.

dopasuj linie

Poniższy skrypt po prostu pozwala matchit.vimrobić to, co robi podczas rejestrowania numerów linii. Objaśnienia znajdują się w komentarzach skryptu.

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

CursorMovedJednak tak naprawdę nie lubię tego . Myślę, że jest to lepsza mapa kluczowa, której można użyć, gdy jej potrzebuję:

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

Zamiast tego możesz użyć tej matchaddposfunkcji. Jest nieco szybszy i jeśli podświetlisz całą linię, trochę to uprości.
Karl Yngve Lervåg,

1
@ KarlYngveLervåg Dobra uwaga. Podświadomie go unikam, ponieważ wciąż jest to stosunkowo nowa funkcja (chyba w wersji 7.4.330) i raz mnie ugryzła w tyłek. Zaktualizuję odpowiedź, aby z niej skorzystać.
Tommy A

To jest absolutnie idealne, wielkie dzięki! Dobra praktyka Vimscript; spróbuje zrozumieć każdą linię. Wyobrażam sobie, że może to być dość popularne, jeśli jako pierwszy napiszesz tego rodzaju narzędzie.
Luke Davis,

@LukeDavis Wynika z tego niepożądany efekt, który zauważyłem: zepsuje listę skoków. Wymyśliłem sposób, aby to naprawić, używając <c-o>liczby razy, gdy znaleziono dopasowanie i działa w pewien sposób. Problem polega na tym, że w matchit.vim występuje błąd, który dodaje górną linię okna do listy skoków. Zostało to potwierdzone , ale wydaje się, że nie ma pośpiechu, aby to naprawić.
Tommy A

@TommyA Hej, jeszcze raz dziękuję za to narzędzie. Na moim komputerze opóźnienie z autocmdem CursorMove jest dość znikome. Zaktualizowałem twoją funkcję, s:get_match_lines(line)aby chronić się przed nieskończonymi pętlami, które stały się dla mnie dużym problemem w niektórych dziwnych kontekstach. Niestety matchit.vimjest pełen wad. Zobacz moją edycję powyżej i daj mi znać, jeśli masz jakieś sugestie; Jestem początkującym vimscriptem.
Luke Davis
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.