Vim Tips Wiki
Tip 103 Printable Monobook Previous Next

created  · complexity intermediate · author Paul Wright, Thore Karlsen · version 6.0

When working with Python and other languages which do not use braces, it is useful to be able to jump to and from lines which have the same indentation as the line you are currently on. The following mappings in your vimrc will help:

nnoremap <M-,> :call search('^'. matchstr(getline('.'), '\(^\s*\)') .'\%<' . line('.') . 'l\S', 'be')<CR>
nnoremap <M-.> :call search('^'. matchstr(getline('.'), '\(^\s*\)') .'\%>' . line('.') . 'l\S', 'e')<CR>

With these mappings, in normal mode, press Alt-, to find the previous line with the same indent as the current line, or press Alt-. to find the next such line. These mappings should work in gvim, but you may need to choose different keys if working with Vim in a terminal.

The following is a slightly more comprehensive solution.

The example mappings below work as follows:

  • [l and ]l jump to the previous or the next line with the same indentation level as the current line.
  • [L and ]L jump to the previous or the next line with an indentation level lower than the current line.

These movements also work in visual mode and in operator pending mode, meaning that you can enter commands like d]l to delete lines. The motion is specified as being exclusive when in operator pending mode.

These mappings are useful when writing programs in Python, Haskell, or editing XML files. For example, in XML you can jump to the outer enclosing tag, or the next matching tag.

" Jump to the next or previous line that has the same level or a lower
" level of indentation than the current line.
" exclusive (bool): true: Motion is exclusive
" false: Motion is inclusive
" fwd (bool): true: Go to next line
" false: Go to previous line
" lowerlevel (bool): true: Go to line with lower indentation level
" false: Go to line with the same indentation level
" skipblanks (bool): true: Skip blank lines
" false: Don't skip blank lines
function! NextIndent(exclusive, fwd, lowerlevel, skipblanks)
  let line = line('.')
  let column = col('.')
  let lastline = line('$')
  let indent = indent(line)
  let stepvalue = a:fwd ? 1 : -1
  while (line > 0 && line <= lastline)
    let line = line + stepvalue
    if ( ! a:lowerlevel && indent(line) == indent ||
          \ a:lowerlevel && indent(line) < indent)
      if (! a:skipblanks || strlen(getline(line)) > 0)
        if (a:exclusive)
          let line = line - stepvalue
        exe line
        exe "normal " column . "|"

" Moving back and forth between lines of same or lower indentation.
nnoremap <silent> [l :call NextIndent(0, 0, 0, 1)<CR>
nnoremap <silent> ]l :call NextIndent(0, 1, 0, 1)<CR>
nnoremap <silent> [L :call NextIndent(0, 0, 1, 1)<CR>
nnoremap <silent> ]L :call NextIndent(0, 1, 1, 1)<CR>
vnoremap <silent> [l <Esc>:call NextIndent(0, 0, 0, 1)<CR>m'gv''
vnoremap <silent> ]l <Esc>:call NextIndent(0, 1, 0, 1)<CR>m'gv''
vnoremap <silent> [L <Esc>:call NextIndent(0, 0, 1, 1)<CR>m'gv''
vnoremap <silent> ]L <Esc>:call NextIndent(0, 1, 1, 1)<CR>m'gv''
onoremap <silent> [l :call NextIndent(0, 0, 0, 1)<CR>
onoremap <silent> ]l :call NextIndent(0, 1, 0, 1)<CR>
onoremap <silent> [L :call NextIndent(1, 0, 1, 1)<CR>
onoremap <silent> ]L :call NextIndent(1, 1, 1, 1)<CR>


This functionality has now been packaged and wrapped up in a script, IndentWise, which incorporates many enhancements and improvements.

This functionality is also provided by a more recent plugin vindent.vim, which combines functionalities of IndentWise (indentation related motions) and vim-indent-object (indentation related text objects).