2
Paragraph 1.

Paragraph 2.

Paragraph 3.

Paragraph 4.

Here is how } and { currently work for me:

  1. Put the cursor on the P of the 2nd paragraph.
  2. Press }. The cursor will jump to the position between paragraphs 2 and 3.
  3. Press } again. The cursor will jump to the position between paragraphs 3 and 4.

And here is how I expect them to work:

  1. Put the cursor on the P of the 2nd paragraph.
  2. Press }. The cursor will jump on the P in the 3rd paragraph.
  3. Press } again. The cursor will jump on the P in the 4th paragraph.

My question:

  • Does it really make sense how Vim jumps between paragraphs by default?

  • How to make it jump as I described above?

2 Answers 2

3

Does the default Vim behaviour make sense? I guess that depends on your preferences. One downside of your proposed behaviour is that something like d} will now delete the first line of the next paragraph too, and visually selecting paragraphs with v}} is harder, as it will select the first line of the next paragraph. I suspect this is the reason it works like it does.

AFAIK there isn't any way to make { and } behave like you want, but it's not very hard to override that; here's a punt which seems to work well in some quick testing.

I have not used it extensively, so there may be caveats. It's based on the jumpy.vim code though, which is reasonably well tested so I think it should work well, but could be it turns out the search pattern needs some tweaking.

With this d} won't delete the first line of the next paragraph, but blank lines will be removed (but d{ will leave an extra blank line), and V will select it. I think that's probably fixable though, if you want it (i.e. go one line up if mode is x or o).

fun! s:jump(mode, dir) abort
    " Pattern to find:
    "
    "   \n        Newline
    "   \s*\n     Any whitespace, followed by a newline (i.e. a blank line)
    "   \s*\zs    Skip any whitespace, and \zs sets the start of the pattern so
    "             the cursor is on the right position.
    let pattern = '\n\s*\n\s*\zs'

    " Get motion count; done here as some commands later on will reset it.
    let count = v:count1

    " Set context mark so user can jump back with '' or ``.
    normal! m'

    " Start visual selection or re-select previously selected.
    if a:mode is# 'x'
        normal! gv
    endif

    let save = winsaveview()
    for i in range(count)
        let loc = search(pattern, 'Wz' .. (a:dir is# 'prev' ? 'b' : ''))
        if loc > 0
            " Jump to first non-whitespace if cursor is on leading whitespace.
            if getline('.')[:col('.') - 1] =~# '^\s*$'
                normal! ^
            endif
            continue
        endif

        " Jump to top or bottom of file if we're at the first or last match.
        if i is count - 1
            exe 'keepjumps normal! ' .. (a:dir is# 'next' ? 'G' : 'gg')
        else
            call winrestview(save)
        endif

        break
    endfor
endfun

" Mappings for normal mode (nnoremap), operator-pending mode (onoremap),
" and visual/select mode (xnoremap).
nnoremap <silent> { :call <SID>jump('n', 'prev')<CR>
nnoremap <silent> } :call <SID>jump('n', 'next')<CR>
onoremap <silent> { :call <SID>jump('o', 'prev')<CR>
onoremap <silent> } :call <SID>jump('o', 'next')<CR>
xnoremap <silent> { :call <SID>jump('x', 'prev')<CR>
xnoremap <silent> } :call <SID>jump('x', 'next')<CR>
2
  • I was thinking of a mapping that used normal! } ({) as needed and then adjusted. I'm actually having a hard time digesting your function, lol
    – D. Ben Knoble
    Aug 17, 2022 at 13:50
  • 1
    That'll probably work too @D.BenKnoble; maybe it's better – certainly simpler, although I'm not sure if it will cover all edge cases. This is just what I already had from jumpy.vim and was easy to modify for this 😅 Aug 17, 2022 at 16:53
1

I understand that you would like that the paragraph starts after a empty line. And that in presence of multiple contiguous empty lines the paragraph starts after these empty lines.

But here is what the doc is saying:

                            *paragraph*
A paragraph begins after each empty line, and also at each of a set of
paragraph macros, specified by the pairs of characters in the 'paragraphs'
option.  The default is "IPLPPPQPP TPHPLIPpLpItpplpipbp", which corresponds to
the macros ".IP", ".LP", etc.  (These are nroff macros, so the dot must be in
the first column).  A section boundary is also a paragraph boundary.
Note that a blank line (only containing white space) is NOT a paragraph
boundary.
Also note that this does not include a '{' or '}' in the first column.  When
the '{' flag is in 'cpoptions' then '{' in the first column is used as a
paragraph boundary |posix|.

Here is a way to map { and } to the move you propose:

function! PreviousParagraph()
  let cursor = getpos('.') 
  let cursor[1] = cursor[1] - 1
  if cursor[1] < 1
    return
  endif
  exe cursor[1]

  let line = getline(cursor[1])
  while len(line) <= 0
     let cursor[1] = cursor[1] - 1
     if cursor[1] < 1
       break
     endif
     exe cursor[1]
     let line = getline(cursor[1])
  endwhile
  while len(line) > 0
     exe cursor[1]
     let cursor[1] = cursor[1] - 1
     if cursor[1] < 1
       break
     endif
     let line = getline(cursor[1])
  endwhile
endfunction

function! NextParagraph()
  let cursor = getpos('.') 
  let line = getline(cursor[1])
  let num_lines = line('$')
  while len(line) > 0
     let cursor[1] = cursor[1] + 1
     if cursor[1] > num_lines
       break
     endif
     exe cursor[1]
     let line = getline(cursor[1])
  endwhile
  while len(line) <= 0
     let cursor[1] = cursor[1] + 1
     if cursor[1] > num_lines
       break
     endif
     exe cursor[1]
     let line = getline(cursor[1])
  endwhile
endfunction

nnoremap <silent> { :call PreviousParagraph()<CR>
nnoremap <silent> } :call NextParagraph()<CR>
3
  • Could you point me where it said there that when you press } or {, Vim puts the cursor on the empty line above a paragraph and not on the paragraph itself?
    – user90726
    Aug 17, 2022 at 10:05
  • You are right the documentation is vague here :-) But it do not describe alternative ways to define paragraphs which gives little hope. Aug 17, 2022 at 10:36
  • What you could do is to reimplement { and } but to be complete you would have to introduce another text object and make sure Vim use it when it used to use paragraphs e.g.: ip, ... Aug 17, 2022 at 10:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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