Как я могу выделить совпадающие имена «%» (например, if / end, for / end), определенные matchit.vim при выборе?

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
Я знаю, что это старый вопрос, но я только что видел, как он всплыл на первой странице минуту назад. Просто хочу упомянуть, что мой новый плагин match-up разработан именно для этого, более надежным способом: github.com/andymass/vim-matchup (наряду со многими другими улучшениями по сравнению с matchit).
Масс
Выглядит очень полезно, спасибо за это! Я попробую это.
Люк Дэвис

Ответы:

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функцию вместо этого. Это немного быстрее, и если вы все равно выделите всю строку, это немного упростит ситуацию.
Карл Ингве Лервог
1
@ KarlYngveLervåg Хороший вопрос. Я подсознательно избегаю этого, потому что это все еще относительно новая функция (я думаю, v7.4.330), и она однажды укусила меня в задницу. Я обновлю ответ, чтобы использовать его.
Томми А
Это абсолютно идеально, спасибо большое! Хорошая практика Vimscript тоже; постараюсь понять каждую строку. Я полагаю, что это может быть довольно популярно, если вы будете первым, кто напишет такую ​​утилиту.
Люк Дэвис
@ LukeDavis Из этого я заметил нежелательный эффект: он испортит список переходов. Я придумал способ как-то исправить это, используя <c-o>количество раз, когда совпадение было найдено, и оно работает определенным образом. Проблема в том, что в matchit.vim есть ошибка, которая добавляет верхнюю строку окна в список переходов. Это было признано , но, кажется, нет никакой спешки, чтобы это исправить.
Томми А
@ TommyA Эй, еще раз спасибо за эту утилиту. На самом деле я нахожу на своем компьютере задержку с CursorMove autocmd довольно незначительной. Я обновил вашу функцию, s:get_match_lines(line)чтобы помочь защититься от бесконечных циклов, что становилось большой проблемой для меня в некоторых странных ситуациях. К сожалению matchit.vim, полон недостатков. Смотрите мои изменения выше и дайте мне знать, если у вас есть какие-либо предложения; Я начинающий Vimscript.
Люк Дэвис