I've published a Vim plugin that nudges visually selected lines up or down, which for the most part seems to function as designed. However, I believe there are likely ways that this plugin could be improved.


  • How do I register a movement that takes an optional amount?

In other words how can this plugin differentiate between Shift^J and 3Shift^J?

  • Have I made any critical mistakes?

  • Are there any features that should be added?


This plugin is tested on Linux based operating systems, but suggestions on how to make it OS agnostic are certainly welcomed; regardless Vim should be installed prior to using this plugin, eg...

sudo apt-get install vim


Source code is maintained on GitHub, as are the documentation files and README.md. Included within this question are the TLDR and source .vim script code files.

mkdir -vp ~/git/hub/vim-utilities

cd ~/git/hub/vim-utilities

git clone [email protected]:vim-utilities/nudge-lines.git

If not utilizing some form of Vim plugin manager, then the linked-install.sh script may be run on most 'nix devices...


... which will symbolically link plugin scripts and documentation, and update Vim documentation tags.


After visually selecting a line, or range of lines, use the following keyboard shortcuts:

  • Ctrl^k Nudge visual selection up

  • Shift^K Nudge visual selection up and reformat tabs

  • Ctrl^j Nudge visual selection down

  • Shift^J Nudge visual selection down and reformat tabs

Source Code


#!/usr/bin/env vim

vnoremap <S-K> :call Nudge_Selection__Laterally(-1, 'reformat')<CR>
vnoremap <C-K> :call Nudge_Selection__Laterally(-1)<CR>

vnoremap <S-J> :call Nudge_Selection__Laterally(1, 'reformat')<CR>
vnoremap <C-J> :call Nudge_Selection__Laterally(1)<CR>

" Nudges visually selected lines up or down
" @param {number} a:amount - Signed integer of lines to nudge selection by
" @param {string[]} a:0 - Optionally reformat tabs
" @example
"   :'<,'> call Nudge_Selection__Laterally(-1, 'reformat')
" @author S0AndS0
" @license AGPL-3.0
function! Nudge_Selection__Laterally(amount, ...) abort range
  let l:not_visual_mode = visualmode() != 'V'
  let l:at_limits__lower = a:lastline == line('$')
  let l:at_limits__upper = a:firstline == 1
  if l:not_visual_mode
  \ || (a:amount > 0 && l:at_limits__lower)
  \ || (a:amount < 0 && l:at_limits__upper)
    normal! gv

  if a:amount > 0
    let l:move_command = "'>+" . a:amount
  elseif a:amount < 0
    let l:move_command = "'<-" . (1 - a:amount)
    throw 'amount must be greater or less than 0'

  execute "'<,'>move " . l:move_command

  if a:0 == 0
    normal! gv
    normal! gv=gv

  silent! foldopen!

Side note, I'm not sure what's going on with syntax highlighting here, but as of latest revisions this plugin seem to function without error.

  • \$\begingroup\$ Let me compare to copy feature (y shortcut). You usualy press y And then an up or down arrow, optionally some numbers between to denote number of lines. Yours could behave similary. Visualy select lines, press Shift+j, nothing happens yet, press arrow up, all moves one line up, press 6 and arrow down, all moves 6 lines down... Its more flexible IMHO And you have saved Shift+k for a different feature... \$\endgroup\$
    – slepic
    Sep 17, 2020 at 5:04


Your Answer

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

Browse other questions tagged or ask your own question.