5

In insert mode, typing normally moves the cursor right and pushes text to the right of the cursor farther to the right on the line. I am typing some right-aligned text and am wondering - is there a way to tell vim to leave the cursor where it is while inserting, and move text preceding the cursor to the left? (Discarding whatever falls off the beginning of the line is fine.) I can't find a way on SO or wikia, and there may not be one, but I thought I'd check.

The closest thing I have found is gR<Ctl-D> (virtual replace mode), but that lets me blithely overwrite what is past the cursor position where I started.

Use case: I have this situation:

<line of source code that does something> <tricky part>
        comment explaining tricky part(*) ^^^^^^^^^^^^^

and want to add text at the place marked (*) without moving the ^^^^^^^^^^^^^, by pushing comment explaining tricky part to the left.

  • I'm not sure that would be a good solution but according to :help arabic it is possible to write text right aligned when the option +arabic is enabled, maybe you could search for something this way... (Also I tend to think that if your purpose is just commenting code, you might not need a right-aligned presentation and write it in a regular way but thats only my opinion ;-) ) – statox Jul 1 '15 at 15:17
  • @statox set rightleft does that, but that also means that everything is reversed; so Hello, world becomes dlrow ,olleH ;-) – Martin Tournoij Jul 1 '15 at 17:20
  • @Carpetsmoker: Oh I didn't know that but that makes sense... Actually that's why I made it a comment and not an answer because that was just a thought and I never used it :-) – statox Jul 1 '15 at 18:29
5

(EDITED version)

"============ in your vimrc =================

command! TogglePush  :call TogglePush()
inoremap <Insert> <Esc>:TogglePush<CR>a
nnoremap <Insert> :TogglePush<CR>

command! EnablePush  :call EnablePush()
command! DisablePush :call DisablePush()

"============= in a script file =============

let s:push_enabled = 0
function! TogglePush()
    if (s:push_enabled)
        call DisablePush()
        let s:push_enabled = 0
        echo "PUSH disabled"
    else
        call EnablePush()
        let s:push_enabled = 1
        echo "PUSH enabled"
    endif
endfunction

function! DisablePush()
    augroup PushLeft
        autocmd!
    augroup END
    let s:push_enabled = 0
endfunction

function! EnablePush()
    augroup PushLeft
        autocmd!
        autocmd TextChangedI * call AfterChange()
    augroup END
    let s:push_enabled = 1
    let s:cursor_column = getpos(".")[2]
endfunction


let s:cursor_column = -1

function! AfterChange()
    let l = getline(".")
    let lnum = getpos(".")[1]
    let c = getpos(".")[2] "get cursor column
    let col_diff = c - s:cursor_column
    if (col_diff > 0) " at actual text insertion
        call setline('.', strpart(getline('.'), col_diff))
    else
        let filler = ''
        while col_diff < 0
            let filler .= ' '
            let col_diff += 1
        endwhile
        call setline('.', filler . getline('.'))
    endif
        call cursor(lnum, s:cursor_column)
endfunction

This is a somewhat improved version of the original answer. When you enable the push (in the example below, pressing the Insert key, or with :EnablePush from vim cmd-line), the current column number is saved as the "pinned cursor column" position and an 'after-text-was-changed-in-insert-mode' event is enabled, that will try to keep the cursor on that column, and preserve the text after the cursor. The text before the cursor is altered like follows:

  • if characters were added, all text before the cursor is moved to the left, and the characters at the beginning of the line disappear (i.e. they "fall" behind the left margin).
  • if backspace was pressed, text before the cursor is moved to the right, by inserting space chars at line start.

NOTES:

  • This adds a "pin cursor to column" functionality.
  • If cursor doesn't seem to be on the right column, it means there are some real tabs on the left of the cursor, and what you see is the "virtual cursor" position (I didn't use virtcol() etc.).
  • I've tried this but TextChangedI doesn't seem to be firing. I put the code in ~/vimfiles/plugin/pushleft.vim and ~/vimfiles/vimrc. I started with vim -u NONE and manually sourced those two. :TogglePush does print the enabled/disabled message, but text entry is normal even when push is enabled. When I set verbose=9, I see CursorMovedI as text is entered. When I hit Esc, I see TextChanged, but I never see TextChangedI. Any thoughts? Vim is VIM - Vi IMproved 7.4 (2013 Aug 10, compiled Aug 10 2013 14:33:40). Thanks! – cxw Jul 6 '15 at 12:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.