TransWikia.com

Visually select consecutive lines with the same starting string

Vi and Vim Asked on August 31, 2021

This is mainly for working on multiple files in vifm (log1.txt, log2.txt, etc.) so lines can be assumed to be sorted, though this should work in normal vim/nvim.

Let’s say we have this:

test 1
test 2
test 3
test 4
test 5
testing 1
testing 2
testing 3
another

I’m on the line with test 3 (normal mode) and want to select everything from test 1 to test 5. I can choose to select every consecutive line (up & down) with the same starting substring test (with a space).

May be I’m on test 3 and want to select every consecutive line with the starting substring test (without a space), which is test 1 till testing 3 but not another.

In such cases, I want to visually select all consecutive lines with the same <count> characters. I can set this to 3,5,10,etc. I know the lines I want to select typically work with 5 starting characters so I can set that as a constant. However, I don’t know beforehand what those 5 characters would be.

How can I make a macro/binding to do such selection?

3 Answers

If you want a command that does that based on the length of the prefix, here's one:

function! Prefix(count)
  if a:count == 0
    return
  endif
  let prefix = strcharpart(getline('.'), 0, a:count)
  let pat = 'V^'.escape(prefix, '')
  echom "pat = [".pat."]"
  let start = line('.')
  let end = start
  let last = line('$')
  while start > 1 && match(getline(start-1), pat) == 0
    let start -= 1
  endwhile
  while end > 1 && match(getline(end+1), pat) == 0
    let end += 1
  endwhile
  execute printf('normal! %dGV%dG', start, end)
endfunction
command! -bar -count=0 Prefix call Prefix(<count>)

You call it as :Prefix 4 to use the first four characters (match both test and testing), or :5Prefix to use the first five (match only test followed by a space.) You can pass the length of the prefix either before or after the command itself.

It works by assembling a pattern based on the first n characters of the line, then iterating through the lines that precede and follow the current one, as long as the pattern still matches. Then finally it visually select the range with the matching lines.

Answered by filbranden on August 31, 2021

Here's a seemingly working solution, but I'm not sure it's very robust. It selects down from the current line (not above).

function! SelectFirstWordBlock(all) abort
  let curpos = getpos('.')
  let curline = curpos[1]
  let nextline = curline + 1
  let firstword = split(trim(getline('.')), 'W+')[0]
  while (a:all == 1 && split(trim(getline(nextline)), 'W+')[0] =~ firstword)
    let nextline += 1
  endwhile
  while (a:all == 0 && split(trim(getline(nextline)), 'W+')[0] ==# firstword)
    let nextline += 1
  endwhile
  normal V
  call cursor(nextline - 1, 0)
endfunction
nnoremap <leader>V :silent! call SelectFirstWordBlock('1')<cr>
nnoremap <leader><c-v> :silent! call SelectFirstWordBlock('0')<cr>

The first mapping matches with a space (test X and not testx), whereas the second matches without it.

Answered by Biggybi on August 31, 2021

The only idea that comes to my mind is to use / to search for another:

  • V : select the first line
  • / : search the first non-matching line (another)
  • k : up a line

Answered by Biggybi on August 31, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP