2

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 3

1

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.

0

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
4
  • 2
    Or V/another/-<CR>
    – D. Ben Knoble
    Commented Jul 15, 2020 at 21:49
  • Oh, nice trick!
    – Biggybi
    Commented Jul 16, 2020 at 2:12
  • Now I’m wondering if you could automate slightly using a negative match/lookbehind and the - search offset
    – D. Ben Knoble
    Commented Jul 16, 2020 at 2:19
  • @D.BenKnoble I wonder too, but I was not able to do it, so I found another way!
    – Biggybi
    Commented Jul 16, 2020 at 3:12
0

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.

2
  • I'd like to match a set number of characters rather than a word, since I sometimes want to select things that match the first few characters but not an entire word. With matching by word, the selection wouldn't quite work if the cursor is on testing 2 and I want to also select up to test 1. Commented Jul 16, 2020 at 5:53
  • Indeed. I'm not quite sure how to do this though.
    – Biggybi
    Commented Jul 16, 2020 at 6:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.