Normal mode ) } and ( { navigate paragraphs and lines but they don't skip blank lines. (They land on them.)

This is obviously the preferable behavior when writing code, but when writing "normal" text it's infuriating. I would like to jump to the beginning of the next sentence/paragraph respectively, not the empty lines I leave between them for readability while editing the text.

Perhaps a solution would be: detecting current position after the initial movement, and while current position = white-space, do a w normal motion. Regrettably, I don't know how to string these commands in a vim.keymap.set, even if it is the right way to go about this.

This is all in Neovim. (Hence the vim.keymap.set) That being said, both the original behavior and the answer should be vim/neovim agnostic (now and in the future,) and hopefully serve both.

A more detailed explanation:

With the cursor at amet, a } or a ) followed by another ) will land me at the blank line between consequat and Duis. Desired behavior is to land on Duis. Similar behavior for { and ( if starting from Ut Excepteur. Desired behavior is for it also to skip (n) number of blank lines: if there were three or ten blank lines between the paragraphs they should be skipped just the same as one blank line.

To explain the purpose of this, I add blank lines for readability while editing, but it messes with the navigation of the text since neovim interprets blanks lines as "Oh, you are probably writing code and those blank lines are important landmarks for you, let me land you there.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Example text for Vivian (for context, go through the comments.) :

 <Empty first line>
 (1)This is a line without quotes.

(2)”This is a line with quotes,” she said. (3)”This is a line with quotes.”
”This is a line with quotes,” he said.
”This is a line with quotes.”
”This is another line with quotes.”
”This is a line with quotes.” This is a line without quotes. (4)”This is a line with quotes."

This is a line without quotes.<EOF>

Legend: Numbers in parenthesis and <> are not part of the text. I've inserted them to simplify explaining motions. When descending, it jumps from 1 to 2 to 3, then all the way to 4. When ascending, It goes 4 3 2 1. In neither case does it acknowledge the final line.

  • Vim script works just fine with vim.cmd[[..]] you are indeed correct. My reasoning for mentioning neovim is that something might work differently (either now or in the future) in neovim compared to vim and potentially confuse anyone looking at this question.
    – Anthony
    Aug 16, 2023 at 15:11
  • 1
    There was a time (not that long ago) when vimscript was utterly confusing to me. I hope the edit above helps steer people in the right direction. @Rich
    – Anthony
    Aug 16, 2023 at 15:18
  • What did you try?
    – romainl
    Aug 16, 2023 at 16:41
  • @romaini my fiddling with if ... then and nextword (w) was so stupid I wouldn't dare post it anywhere. Vivian 's answer provides the functionality I am looking for and seems way more elegant.
    – Anthony
    Aug 16, 2023 at 17:00

1 Answer 1


You could do:

function! NextParagraph()
  if getbufline('%', line('.')) == ['']
    call search('\v(%^|\S)', 'b')
  call search('\v(%^\n?|\n\n)\s*\zs\S')

nnoremap } <Cmd>call NextParagraph()<CR>

function! PreviousParagraph()
  call search('\v(%^\n?|\n\n)\s*\zs\S', 'b')

nnoremap { <Cmd>call PreviousParagraph()<CR>

function! NextSentence()
  if getbufline('%', line('.')) == ['']
    call search('\v(%^|\S)', 'b')
  call search('\v(%^\n?|\n\n|[.?!](\_\s|["“”]))\_\s*\zs\S')

nnoremap ) <Cmd>call NextSentence()<CR>

function! PreviousSentence()
  call search('\v(%^\n?|\n\n|[.?!](\_\s|["“”]))\_\s*\zs\S', 'b')

nnoremap ( <Cmd>call PreviousSentence()<CR>

The start of a paragraph are defined as a non blank (\S) text following:

  • A blank line (\n\n)
  • The beginning of the buffer (\%^\n\?)

The sentences are defined as a non blank (\S) text following:

  • A blank line (\n\n)
  • The beginning of the buffer (\%^\n\?)
  • A sentence punctuation (., ?, !) followed by a space , a tab, a new line or a quote " ((\_\s|")).

The \zs makes that the cursor goes right before the non blank text and not at the start of the pattern.

The call to search() avoid that the / search register is modified.

  • 2
    This would be greatly improved by explanation
    – D. Ben Knoble
    Aug 16, 2023 at 15:47
  • 1
    I will add them to the main body of the question for clarity (so that anyone else that comes across this can understand with a cursory glance.) I should also say, I did not yet try the edited version of your answer, only the first one that was 3 lines long. What you have now posted might be the answer, I will try immediately after I edit my original question above.
    – Anthony
    Aug 16, 2023 at 16:22
  • 1
    @Vivian your solution works perfectly, I just it tested on a very long Lorem Ipsum with a varying number of blank lines between paragraphs. It behaves precisly as desired, thank you. One final question, is there a reason you haven't added the ( ? Is it harder to achieve with the above code?
    – Anthony
    Aug 16, 2023 at 16:55
  • 1
    @ Vivian you are correct, and I just noticed it wasn't included in the original question. Lax proofreading once more rears its ugly head. Thank you again, I would greatly appreciate it if you could add it to your answer. I will edit the question accordingly.
    – Anthony
    Aug 16, 2023 at 17:07
  • 1
    @ Vivian first off, let me start again by thanking you. And by saying the paragraph movement proved sufficient for me. Regarding the sentence movement, it is still erratic. Less so, compared to the previous iteration, but erratic. When it comes upon: "Placeholder 1." Placeholder 2. "Placeholder 3." It will jump from 1 to 3, or 3 to 1 if you are going the other direction. It will also Jump blocks of "a whole sentence is here," if they are one after the other with no blank lines between them, just like if I was using } ot {.
    – Anthony
    Aug 18, 2023 at 17:48

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.