2

I'm thinking about a feature which I think would improve my workflow a lot. I don't know if it already exists..

The feature is about moving the cursor vertically over code blocks without losing its horizontal position in a speedy way. I would regard it as a more convenient alternative for { and }. Or you could say it would be the vertical equivalent for w/e and b.

What I really don't like about { and } is that I lose the horizontal position of the cursor.

The behavior of the feature for moving the cursor should be like this:

  • Move the cursor up/down to the next line that is preceded or followed by an empty line. (ignore white spaces)
  • Don't lose the horizontal position of the cursor. If the line is shorter put the cursor on the last position of that line, but remember the position it came from. Even if you hit j or k afterwards the cursor should maintain its initial horizontal position. (same behavior as j/k)

Example for moving down: (moving up behaves the same, just in the other direction.)

X0 indicates the initial position.

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAA[X0]AAAAAAA
AAAAAAAAAAAAA
AAAAAAAAA[X1]

BBBBBBBBBBB[X2]BBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBB
BBBBBBBBBBB[X3]BBBBBBBB

CCCCCCCCCCC[X4]CCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCC[X5]

If I hit k on the last line it would go back to position 11, right under X4.

Do you know of such a feature or do you know how I could create it?

0

2 Answers 2

4

Here is a small function, that seems to do what you want:

function! Move(up)
    let curpos = getcurpos()
    let firstline='\(^\s*\n\)\zs\S\+'
    let lastline ='\S\+\ze\n\s*$'
    let flags = 'Wn'. (a:up ? 'b' : '')
    " Move to first or last line of paragraph,
    " or to the beginning/end of file
    let pat = '\('.firstline.'\|'.lastline.'\)\|\%^\|\%$'
    " make sure cursor moves and search
    " does not get stuck on current line
    call cursor(line('.'), a:up ? 1 : col('$'))
    let target=search(pat, flags)
    if target > 0
        let curpos[1]=target
        let curpos[2]=curpos[4]
    endif
    call setpos('.',curpos)
endfu
nnoremap <silent> <leader>j :call Move(0)<cr>
nnoremap <silent> <leader>k :call Move(1)<cr>

This relies on the getcurpos() function, which has been included as patch 7.4.578. If you don't have it, you can use the winsaveview()/winrestview() function, to work around it. This might however mess up the screen, not sure this will work reliably. (with a newer vim, you can throw away the values in the dictionary returned by winsaveview() and only update the other values, but that is not possible with older vims as well).

function! Move(up)
    let curpos = winsaveview()
    let firstline='\(^\s*\n\)\zs\S\+'
    let lastline ='\S\+\ze\n\s*$'
    let flags = 'Wn'. (a:up ? 'b' : '')
    " Move to first or last line of paragraph,
    " or to the beginning/end of file
    let pat = '\('.firstline.'\|'.lastline.'\)\|\%^\|\%$'
    " make sure cursor moves and search
    " does not get stuck on current line
    call cursor(line('.'), a:up ? 1 : col('$'))
    let target=search(pat, flags)
    if target > 0
        let curpos.lnum=target
        let curpos.col=curpos.curswant
    endif
    call winrestview(curpos)
endfu
nnoremap <silent> <leader>j :call Move(0)<cr>
nnoremap <silent> <leader>k :call Move(1)<cr>
8
  • I get an error: Unknown function: getcurpos.
    – Rotareti
    Commented Jul 22, 2016 at 16:55
  • Your Vim is too old, can use winsaveview for that, let me update the question. What version is this? Commented Jul 22, 2016 at 17:02
  • Version is 7.4.53. My distro does only offer this out dated version :/
    – Rotareti
    Commented Jul 22, 2016 at 17:06
  • @Rotareti updated answer Commented Jul 22, 2016 at 17:11
  • Moving up works perfectly! On moving down the cursor gets stuck every 1 to 10 movements. Anyway this is fantastic! I'll try to get an update for vim to fix it.
    – Rotareti
    Commented Jul 22, 2016 at 17:48
0

You can start working from this simple mapping:

:nnoremap J :let c=col('.') <bar> execute "normal! }k".c."<bar>"<CR>

Decomposition

:let c=col('.')     " save the current column into the variable c
<bar>               " chain another command
execute "normal!    " execute a normal command
}k                  " go to next paragraph and up one line
c."<bar>"           " go to the column number `c`

Note: this won't work without a newline after the paragraph.

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.