Search code examples
vimindentationauto-indent

Preserve cursor position when using ==


I am trying to make Vim indent lines like Emacs (that is, "make the current line the correct indent" instead of "insert tab character"). Vim can do this with = (or ==) for one line). I have imap <Tab> <Esc>==i in my .vimrc but this makes the cursor move to the first non-space character on the line. I would like the cursor position to be preserved, so I can just hit tab and go back to typing without having to adjust the cursor again. Is this possible?

Example

  • What I have now (| represents the cursor):

    function f() {
    doso|mething();
    }
    

    Tab

    function f() {
        |dosomething();
    }
    
  • What I would like:

    function f() {
    doso|mething();
    }
    

    Tab

    function f() {
        doso|mething();
    }
    

    Also

    function f() {
     |  dosomething();
    }
    

    Tab

    function f() {
       |dosomething();
    }
    

Solution

  • I don't believe there's an "easy" way to do this (that is, with strictly built-in functionality) but a simple function does it just fine. In your .vimrc file:

    function! DoIndent()       
        " Check if we are at the *very* end of the line
        if col(".") == col("$") - 1
            let l:needsAppend = 1
        else
            let l:needsAppend = 0
        endif
    
        " Move to location where insert mode was last exited
        normal `^
    
        " Save the distance from the first nonblank column
        let l:colsFromBlank = col(".")
        normal ^
        let l:colsFromBlank = l:colsFromBlank - col(".")
    
        " If that distance is less than 0 (cursor is left of nonblank col) make it 0
        if l:colsFromBlank < 0                            
            let l:colsFromBlank = 0
        endif
    
        " Align line
        normal ==
    
        " Move proper number of times to the right if needed
        if l:colsFromBlank > 0
            execute "normal " . l:colsFromBlank . "l"
        endif
    
        " Start either insert or line append
        if l:needsAppend == 0
            startinsert
        else
            startinsert!
        endif
    endfunction                                   
    
    " Map <Tab> to call this function                                          
    inoremap <Tab> <ESC>:call DoIndent()<CR>