Is it possible to do this with a Vim command?
[] = Cursor Normal Mode
[ = Cursor Insert Mode
Before
Text []
After
Text
[
Before
Text []
After
[
Text
I've changed the [count] behavior of o / O with the following mapping. I think this does what you want:
" o/O Start insert mode with [count] blank lines.
" The default behavior repeats the insertion [count]
" times, which is not so useful.
function! s:NewLineInsertExpr( isUndoCount, command )
if ! v:count
return a:command
endif
let l:reverse = { 'o': 'O', 'O' : 'o' }
" First insert a temporary '$' marker at the next line (which is necessary
" to keep the indent from the current line), then insert <count> empty lines
" in between. Finally, go back to the previously inserted temporary '$' and
" enter insert mode by substituting this character.
" Note: <C-\><C-n> prevents a move back into insert mode when triggered via
" |i_CTRL-O|.
return (a:isUndoCount && v:count ? "\<C-\>\<C-n>" : '') .
\ a:command . "$\<Esc>m`" .
\ v:count . l:reverse[a:command] . "\<Esc>" .
\ 'g``"_s'
endfunction
nnoremap <silent> <expr> o <SID>NewLineInsertExpr(1, 'o')
nnoremap <silent> <expr> O <SID>NewLineInsertExpr(1, 'O')
do these two mapping help?
nnoremap <leader>O O<ESC>O
nnoremap <leader>o o<cr>
the first by pressing <leader>O will add two empty lines above current line, and bring you to INSERT mode. The 2nd one by pressing <leader>o will add two lines after your current.
Related
Vim often creates lines with lots of contiguous whitespace, so I'm surprised there isn't an easy way to backwards delete until the end of the previous word. Am I missing something obvious?
hello this is a line |
deletes back to here:
hello this is a line|
or non-whitespace is before the cursor:
hello this is a line|
deletes back to here:
hello this is a |
That way I could map a key in insert mode and just back delete words or not-words.
While vim doesn't ship with a command like this, it ships with commands that are very close to what you want: ge and gE, look them up in the help. Luckily this means it's trivial to wrap these in the following normal, visual and operator pending mappings:
noremap <expr> <leader>e 'h'.v:count1.'gel'
noremap <expr> <leader>E 'h'.v:count1.'gEl'
onoremap <expr> <silent> <leader>e ':<c-u>normal! vh'.v:count1.'gel<cr>'
onoremap <expr> <silent> <leader>E ':<c-u>normal! vh'.v:count1.'gEl<cr>'
Obviously map those to whatever you want, personally I have the first one mapped to <BS>. The difference between e and E is that they invoke ge (moving by word) and gE (moving by WORD) respectively. See :h word and :h WORD for details on what that means.
These aren't exactly what you asked for: they don't adjust for where we are when we start so e.g. deleting using this operator will change
this is a line|
to
this is a|
instead of
this is a |
You could certainly fix that if you wanted to write a bit more complex vimscript but it's hardly worth if for one space.
The stuff moved over by the operator-pending map will also subtly change depending on the value of 'selection' if that bothers you.
Edit: This is how I would create the exact solution asked for
function! s:Backup(mode)
let str = a:mode == 2 ? 'g' : ''
let str .= a:mode ? 'v' : ''
let at_end = col('.') >= col('$') - 1
let str .= getline('.') =~ '\%'.(col('.') - !at_end).'c\S' ? 'b' : 'gel'
let str .= !at_end && a:mode == 1 ? 'oho' : ''
execute 'normal!' str
endfunction
This doesn't delete by itself, but composes with any vim command, such as the d, y, c operators, and works in all modes as long as you pass the correct parameter. To replicate the mapping used in the askers answer:
inoremap <silent> <c-b> <c-o>d:<c-u>call <SID>Backup(1)<cr>
And here are bindings for normal, visual, operator-pending mode on <leader>e
nnoremap <silent> <leader>e :<c-u>call <SID>Backup(0)<cr>
onoremap <silent> <leader>e :<c-u>call <SID>Backup(1)<cr>
xnoremap <silent> <leader>e :<c-u>call <SID>Backup(2)<cr>
N.B. mappings must be defined in the same script as the function to use s: and <SID>, otherwise remove those.
Edit 2: fixed the function name :/
Edit 3: adjusted to fix operation when not at end of line. Apparently vim thinks the cursor is in a different place when appending at the end of the line as it does when inserting.
Okay, this function does what I need:
function! BackspaceContiguousInsertMode()
python << EOF
import vim
try:
line = vim.current.line
row, deleteto = vim.current.window.cursor
if deleteto==0:
vim.current.line = ""
vim.command("startinsert")
elif line[deleteto] == " ":
deletefrom=deleteto
while deletefrom-1>0 and vim.current.line[deletefrom-1] == " ":
deletefrom=deletefrom-1
vim.current.line=line[:deletefrom] + line[deleteto+1:]
if len(line)-1 == deleteto:
vim.current.window.cursor = (row,deletefrom+1)
vim.command("startinsert!")
else:
vim.current.window.cursor = (row,deletefrom)
vim.command("startinsert")
else:
eol=deleteto+1 >= len(line)
if not eol and line[deleteto+1] != " ":
trailingline = line[deleteto+1:]
vim.current.line=line[:deleteto+1] + " " + trailingline
vim.command("normal diw")
leadingto=len(vim.current.line)-len(trailingline)-1
vim.current.line=vim.current.line[:leadingto] + trailingline
vim.command("startinsert")
elif eol:
vim.command("normal diw")
vim.command("startinsert!")
else:
vim.command("normal diw")
vim.command("startinsert")
except Exception as e:
print("Error: {}".format(e))
EOF
endfunction
inoremap <C-b> <Esc>:call BackspaceContiguousInsertMode()<CR>
From insert mode (which is where I wanted to invoke it from) I can press <C-b> to back delete each word block or whitespace block to the beginning of the line. Given the following line, each time I press <C-b> this is what happens:
alskdjf a;sjdf a kjkdjd |kja sdf
alskdjf a;sjdf a kjkdjd|kja sdf
alskdjf a;sjdf a |kja sdf
alskdjf a;sjdf a|kja sdf
alskdjf a;sjdf |kja sdf
alskdjf a;sjdf|kja sdf
alskdjf a;|kja sdf
alskdjf a|kja sdf
alskdjf |kja sdf
alskdjf|kja sdf
|kja sdf
|... (stays on the same line)
I would to like to insert x number of character # at the beginning of a line followed by a space. My goal is to put headers for markdown easily:
Before:
Title
After typing 5+F5
##### Title
I just managed to insert # with F5 key: nnoremap <F5> #='I#<C-V><Esc>'<CR>h
Many thanks!
In my opinion this one is a little more clear if you use vimscript instead of the editing commands. The following mapping works like this:
If you don't give a count, it inserts "# " at the beginning of the line.
If you give a count, it inserts count * "#" followed by " " at the beginning of the line.
Mapping:
nmap <F5> :<C-u>call setline('.', substitute(getline('.'), '^', repeat('#', v:count1) . ' ', ''))<Enter>
In response to your comment below, here is a version that will modify the current line to have exactly <count> hashes followed by one space - even if it already has hashes and a space. If <count> is zero, both the hashes and space are removed.
nmap <F5> :<C-u>call setline('.', substitute(getline('.'), '\v^(#+ )?', repeat('#', v:count) . repeat(' ', min([1, v:count])), ''))<Enter>
And because This is getting really busy for a one line map, here it is with a function instead:
function SetMarkdownHeader()
let current_line = getline('.')
let hashes = repeat('#', v:count)
let maybe_space = repeat(' ', min([v:count, 1]))
let new_line = substitute(current_line, '\v^(#+ )?', hashes . maybe_space, '')
call setline('.', new_line)
endfunction
nmap <f5> :<C-u>call SetMarkdownHeader()<Enter>
I recently fell in love with the f, F, t, and T commands in vim. Now I find myself frequently wanting to insert something at a position that can easily be navigated to with one of these commands, at least frequently enough that I would want to make the entire action "find character and insert text" repeatable via .. Currently, I can repeat the insert action, but I have to retype the find-character movement at every line where I want to repeat the insert.
Is there a command that combines the actions of these movement commands with the action of dropping into insert mode? Or, if not, is it possible to define such a command in my .vimrc?
First, you can repeat the last f/t/F/T motion via ; (reverse via ,), so you can repeat with two keys: ;.
If that's not good enough, the repeat.vim plugin can be used to build a custom mapping that repeats just like the built-in commands:
"<Leader>it{char} Insert text before the [count]'th occurrence of {char}
" to the right.
"<Leader>if{char} Insert text after the [count]'th occurrence of {char}
" to the right.
" These mappings can be repeated atomically, this is
" faster than ";."
function! s:InsertAtCharPrepare( motion, moveOffMotion, repeatMapping )
augroup InsertAtChar
autocmd!
" Enter insert mode automatically after the f/t motion.
" XXX: :startinsert doesn't work on the first movement somehow, use
" feedkeys() instead.
autocmd CursorMoved <buffer> call feedkeys('a', 'n')
" Prime repeat.vim after insertion is done.
execute printf('autocmd InsertLeave <buffer> %scall repeat#set(%s, %d) | autocmd! InsertAtChar',
\ (v:count1 <= 1 || empty(a:moveOffMotion) ? '' : 'execute "normal!" ' . string(a:moveOffMotion) . '|'),
\ string(a:repeatMapping),
\ v:count1
\)
" Abort in case something unexpected happens.
autocmd WinLeave,BufLeave <buffer> autocmd! InsertAtChar
augroup END
return a:motion
endfunction
function! s:InsertAtCharRepeat( moveOffMotion, repeatMapping )
let l:count = v:count1 " Save the original count to pass this on to repeat.vim.
execute 'normal!' l:count . ';.' . (l:count <= 1 ? '' : a:moveOffMotion)
call repeat#set(a:repeatMapping, l:count)
endfunction
" With "t" and [count] > 1, we need to move off from before {char} (where we're
" left when leaving insert mode) onto {char}, so that a repeat will happen
" before the next occurrence, not on the same again.
nnoremap <silent> <Plug>(InsertUntilCharRepeat) :<C-u>call <SID>InsertAtCharRepeat('l', "\<lt>Plug>(InsertUntilCharRepeat)")<CR>
nnoremap <silent> <Plug>(InsertFromCharRepeat) :<C-u>call <SID>InsertAtCharRepeat('', "\<lt>Plug>(InsertFromCharRepeat)")<CR>
nnoremap <expr> <Leader>it <SID>InsertAtCharPrepare('t', 'l', "\<lt>Plug>(InsertUntilCharRepeat)")
nnoremap <expr> <Leader>if <SID>InsertAtCharPrepare('f', '', "\<lt>Plug>(InsertFromCharRepeat)")
In many text editors, pressing down arrow when the cursor is on the last line will move the cursor to the end of the line. Is there a way to do this in vim (for down arrow or j)?
This is as opposed to simply pressing $.
This should do what you want.
nnoremap <expr> j (line('.') == line('$')) ? '$' : 'j'
nnoremap <expr> <DOWN> (line('.') == line('$')) ? '$' : '<DOWN>'
We check to see if the current line is the last line. If it is we map j to $ if it isn't we map j to j. (Same logic for <DOWN>)
When I'm using vim I generally never want to move to a punctuation mark when I press w or b to go forwards or backwards. So I'm wondering if there's a setting or something to change this functionality?
e.g. If I've got some code like
object.method(args)
and my cursor is at the [o] in "object" then I want w to move to the [m] in "method", and another w to move to the [a] in "args". I don't want it to land on the [.] or the [(]. If I've ever wanted to move to a punctuation char I've always used f or F to jump straight to it. I've never personally wanted to move to a punctuation char when I move through words and I just realized this is really bugging me.
I too find that I would like a movement that is more inclusive that w, but not as inclusive as W. In particular, I would like a movement that only considers tokens beginning with alphanumeric characters as significant.
So I came up with the following:
" <SPACE> : forward to next word beginning with alphanumeric char
" <S-SPACE> : backward to prev word beginning with alphanumeric char
" <C-SPACE> : same as above (as <S-SPACE> not available in console Vim
" <BS> : back to prev word ending with alphanumeric char
function! <SID>GotoPattern(pattern, dir) range
let g:_saved_search_reg = #/
let l:flags = "We"
if a:dir == "b"
let l:flags .= "b"
endif
for i in range(v:count1)
call search(a:pattern, l:flags)
endfor
let #/ = g:_saved_search_reg
endfunction
nnoremap <silent> <SPACE> :<C-U>call <SID>GotoPattern('\(^\\|\<\)[A-Za-z0-9_]', 'f')<CR>
vnoremap <silent> <SPACE> :<C-U>let g:_saved_search_reg=#/<CR>gv/\(^\\|\<\)[A-Za-z0-9_]<CR>:<C-U>let #/=g:_saved_search_reg<CR>gv
nnoremap <silent> <S-SPACE> :<C-U>call <SID>GotoPattern('\(^\\|\<\)[A-Za-z0-9_]', 'b')<CR>
vnoremap <silent> <S-SPACE> :<C-U>let g:_saved_search_reg=#/<CR>gv?\(^\\|\<\)[A-Za-z0-9_]<CR>:<C-U>let #/=g:_saved_search_reg<CR>gv
nnoremap <silent> <BS> :call <SID>GotoPattern('[A-Za-z0-9_]\(\>\\|$\)', 'b')<CR>
vnoremap <silent> <BS> :<C-U>let g:_saved_search_reg=#/<CR>gv?[A-Za-z0-9_]\(\>\\|$\)<CR>:<C-U>let #/=g:_saved_search_reg<CR>gv
" Redundant mapping of <C-SPACE> to <S-SPACE> so that
" above mappings are available in console Vim.
"noremap <C-#> <C-B>
if has("gui_running")
map <silent> <C-Space> <S-SPACE>
else
if has("unix")
map <Nul> <S-SPACE>
else
map <C-#> <S-SPACE>
endif
endif
I have had this for a long time now, and I find that I use <SPACE>/<C-SPACE> movements so much more than w and W; it just seems more useful when coding. You can, of course, map the commands to whatever keys you find useful or more appropriate.
Even running the risk of creating a script for something that's built-in (like
I did last time), here is a little function that may help accomplishing
this.
function! JumpToNextWord()
normal w
while strpart(getline('.'), col('.')-1, 1) !~ '\w'
normal w
endwhile
endfunction
Basically, what it does is executing the standard w and repeating it
if the character under the cursor is not in a word character (feel free to
change that pattern.
If you add that and a little map in your .vimrc:
nnoremap <silent> ,w :call JumpToNextWord()<CR>
It should work.