Get Vim to open new blocks like Sublime Text 2 - vim

In sublime text 2 when you:
BLOCK { <Return>
It generates (where the pipe is the cursor):
BLOCK {
|
}
How can I get Vim to behave this way?
I have autoindent on, and smartindent off because with smartindent it does this on return:
BLOCK {
|}
To be more clear, I'm specifically looking for 2 returns, moving up a line, and tabbing in (2 soft tabs to be specific). I already have it auto-matching characters like {, (, [ etc.

A simple mapping will work for most purposes:
imap {<cr> {<cr>}<c-o>O
Depending on plugins, some users may need inoremap instead of imap.

Before it was with TextMate, now it's with ST2.
You have basically two paths before you.
The "dumb" path
One could come up with dozens of variations of this method: you simply create a mapping that executes the series of key presses needed to reach your goal:
inoremap {<CR> {<cr><cr>}<C-o>k<tab>
I called it "dumb" but it doesn't mean that you would be dumb to use it: it's low-tech, has no dependencies, is easy to customize and it can be mapped to anything you like.
The "smart" method
This method involves the use of a plugin. I use DelimitMate but there are many others, choose your poison.

I did some quick digging for vim addons (which are often the solution to this sort of problem). I don't think I've found what you want: there are a few addons that come close, but nothing that inserts the extra newline before the closing brace.
You could do something like
imap { {<return><return>}<up><tab>
but this will get awkward if you are working in a language that uses braces in other situations. You could instead react to the newline:
inoremap <return> <return><return>}<up><tab>
Of course this will trigger on EVERY entered newline, rather than just those following an opening brace. To get it to check that the brace is the last character of the current line, you can:
Have a function (in ~/.vimrc or somewhere in ~/.vim/plugin) that looks like
function! CloseBraceIfOpened()
if getline(".")[-1:] == '{'
" insert a space and then delete it to preserve autoindent level
exec "normal o "
normal x
normal o}
normal k
else
normal o
endif
endfunction
also do
inoremap <buffer> <enter> <esc>:call CloseBraceIfOpened()<enter>A
Note that this imap is buffer-specific, so that mapping will only apply to the buffer you are in when you run it. To have it apply to all buffers, remove <buffer>.
If you are really ambitious/particular, you can do tests in the function to see if the code in the current line really opens a block.
To get the indentation working the way you want it, turn on the 'autoindent' and 'smartindent' settings.
: set autoindent smartindent
To have it on by default, add
set autoindent smartindent
to ~/.vimrc.

I use the following map:
inoremap {{ {<CR><CR>}<ESC>kcc
so instead of using {<CR> I use this mapping. Besides that I also use the plugin mentioned by romainl, DelimitMate for other mappings with braces.

I had the same problem and delimitMate solves it. After installing it you can enable it with:
let g:delimitMate_expand_cr = 1
There are lot's of hacks that delivers the SublimeText experience. Because I got frustrated I've created a project that includes all those features in a single vim distribution (without the need of installing/compiling external plugins/tools).
You can check it out from here: https://github.com/fatih/subvim

Related

Include all default binds in vimrc?

Maybe this goes against the traditional zen of VI, but I get very overwhelmed when it comes to working with rebinding keys in Vi, in particular what can/cannot be bound. I've had a particular headache since moving to nvim with selecting/copying/pasting using visual mode, as well as no longer having access to Home/End to jump to beginning/end of a line while in insert mode.
I was thinking that a way around this, much like I do with my tmux config, is issue a command to unbind all keys in my vimrc, then rebind them all. That way, there's no confusion to what a binding is, and I can easy change and reference a change anytime. Am I going about this all wrong?
You can't really do this. Vim treats "built-in mappings" different from "user-defined mappings". You can't really "unmap" a built-in mapping.
Technically, it stores built-in mappings in a different C struct than user-defined mappings defined with :map. When you do :unmap, it just removes it from the user-defined struct (incidentally, I gave a brief − but incomplete − overview of how this works last week in How to find out what a key is mapped to? at vi.SE).
The only thing you can do is something like:
" Remap all ascii characters; everything below 33 is a control character
for i in range(33, 127)
" | needs some extra love
if i == 124 | continue | endif
execute 'nnoremap ' . nr2char(i) . ' <Nop>'
execute 'nnoremap <C-' . nr2char(i) . '> <Nop>'
endfor
" The above won't remap stuff like `<F1>`, `<Up>`
nnoremap <Bar> <Nop>
nnoremap <F1> <Nop>
nnoremap <Up> <Nop>
" ... etc ... You can use a loop for this as well...
" Now make our own mappings
nnoremap : :
" ...etc...
And the same for vnoremap, etc. but this won't remap <C-w><C-w>, gJ, etc. so you'll need to add even more exceptions for that (the "second key" for these mappings isn't even in a struct, but is a switch/case!)
However. don't do this. Because now rely on your vimrc that you and only you can understand. Just learn the default mappings. This will mean you can use any Vim installation out-of-the-box, and your mappings won't be somehow "better" than the default mappings.
You can put this line near the top of your vimrc to reset all options to their default value:
set all&
But you can't realistically hope to "unbind" the "default bindings" with a single command because:
they are not "bindings", they are "commands",
there's no such command anyway.
You could remap every default command to <nop> (:help <nop>) but that sounds like a lot of work for very little benefit.

Setting mouse=a and mapping <esc> to :noh breaks mouse working on vim? [duplicate]

I am a happy VIM user, although I admit I'm quite far from being fluent. I found this nice post:
Vim clear last search highlighting and I thought I'd become a better person if I didn't have to hammer away a random character sequence every time I did a search. Now, I'm also using the vimrc config from here:
http://amix.dk/vim/vimrc.html
and the problem I have is that when I add the line nnoremap <esc> :noh<return><esc> to it (it doesn't seem to make a difference where I put it) I get awkward behaviour when I use arrows in command mode, namely letters from A to D appear in a newline and I get switched to insert mode.
There has to be some mapping conflict but for the life of me I can't figure out where it is.
EDIT: As it follows from the answers it turns out the Ultimate vimrc part is not relevant, the mentioned nnoremap command will cause altered arrow behaviour even if it's the only vimrc entry. Changing title to a more informative one.
PS. I know I shouldn't use arrows, hopefully I'll get there one day.
The mapping
nnoremap <esc> :noh<return><esc>
will conflict with so called "grey keys" and I believe that it should be used either in GVim only or in terminal Vim by someone who does not use special keys like arrows.
From what I know (and guess) how Vim processes keys, I would say that it's impossible to do anything with this. For Vim to recognize special key all its components should go in a row, so when you press Arrow Left Vim gets the following sequence of codes:
<esc> [ D
But after your mapping Arrow Left becomes:
: n o h l <cr> <esc>
[ D
Vim sees two separate sequences and treats <esc> as a single press of Escape key, thus next two codes of Left Arrow key lose their special meaning.
So I suggest you to map :noh to some other key sequence (e.g. to one starting with <leader>, see :help mapleader; I don't recommend you to use F-keys, using them is as bad as using of arrow keys).
The cause had been explained well, but solution was not mentioned. However there is a straight one.
If you’ll tell to Vim explicitly that there are key sequences starting from <esc>[
:nnoremap <silent><esc> :noh<CR>
:nnoremap <esc>[ <esc>[
than when single <esc> will be pressed Vim will wait for a second (or different time, see :h 'timeoutlen') or for a next key (second <esc> for example) and only then replace it with :noh<CR>.
This solution preserves the ESC mapping to :nohlsearch.
The comment on this answer explaining why this is happening tells us that the root cause is the TermResponse behavior of vim. This can be compensated for by wrapping the mapping in an autocommand for the TermResponse event.
This ensures that the binding doesn't happen until after the term response is set, which prevents Esc from also sending a string like ]>1;3201;0c to vim.
Change your line in vimrc to this:
augroup no_highlight
autocmd TermResponse * nnoremap <esc> :noh<return><esc>
augroup END
The augroup commands are not strictly necessary, but they prevent multiple mappings when you reload your vimrc without quitting vim.
EDIT: If you also use a graphical vim like Gvim or Macvim, the TermResponse event will not fire. Assuming you use a single vimrc, you'll need some additional code like
if has('gui_running')
nnoremap <silent> <esc> :nohlsearch<return><esc>
else
" code from above
augroup no_highlight
autocmd TermResponse * nnoremap <esc> :noh<return><esc>
augroup END
end
Problem is that when you press an arrow terminal emits something like <Esc>OA. Vim part that supports terminal seems to use the same mapping mechanism to do the job as you are using: while nmap <Esc>OA will tell you nothing, call feedkeys("\eOA") will move one line up and call feedkeys("\eOA", 'n') will add letter A beyond current line. With your mapping being noremappable you forbid vim to use <Esc> as a part of the key. The problem is that you need remappable mapping here, but can have remappable mapping without it being recursive as well only if it starts with {lhs}, but <Esc>:noh<CR>OA is not going to work. I thought the following code will (it uses <expr> and function with side effect to make <Esc> be the first character of the actual {rhs} and still launch :noh), but in fact it does not:
function s:NoHlSearch()
nohlsearch
return "\e"
endfunction
nmap <expr> <Esc> <SID>NoHlSearch()
. I have no other idea how to solve the problem of having non-recursive remappable mapping which includes {lhs} but not at the start.
I have had good luck with this
if $TERM =~ 'xterm'
set noek
endif
nnoremap <silent> <esc> <esc>:noh<cr>
The disadvantage is that function keys can not be used in insert mode.
:h ek

How to prevent <Esc> from waiting for more input in Insert Mode

When I leave insert mode by pressing Esc, there is a half-second pause before Vim actually returns to normal mode.
Normally this wouldn't be an issue, since pressing a normal mode command like j after pressing Esc executes the normal-mode command immediately (without the above-mentioned wait), but I have the mapping inoremap <Esc> <Esc>:w<CR>, so that every time I leave insert mode the file is written. I would like the write to occur immediately when I press Esc, but instead there is that half-second pause.
I'm assuming that the pause is because Vim is waiting for more input before it decides that I just meant to type a single, simple Esc. This must be because there is a mapping somewhere who's first character is <Esc>, but I've looked in my .vimrc and there is no such mapping.
Furthermore, I even ran :map <Esc>, and it returned No such mapping. So, if there is no such mapping, why does Vim appear to be waiting for more input, and how can I avoid that behavior?
Extra Information
It appears that this is not reproduceable, so here is some more information in case anyone really wants to get to the bottom of this:
I am using Steve Francia's spf13 distribution of Vim, with my own .vimrc.local on top of it. I have also installed several additional plugins using Vundle.
Notes: .vimrc.local is sourced last in .vimrc.
UPDATE (3/19/2014)
I found a far better solution than trying to hunt down all the mappings that start with <Esc>, courtesy of Powerline, from the Tips & Tricks section of the docs. Put this somewhere in your .vimrc:
" leave insert mode quickly
if ! has('gui_running')
set ttimeoutlen=10
augroup FastEscape
autocmd!
au InsertEnter * set timeoutlen=0
au InsertLeave * set timeoutlen=1000
augroup END
endif
Note that this will make it impossible to use mappings that start with <Esc> while in insert mode, but there shouldn't be any of those anyway, because of the problem this solves.
I found the lines in spf13's .vimrc that were causing the problem:
" Fix home and end keybindings for screen, particularly on mac
" - for some reason this fixes the arrow keys too. huh.
map ^[F $
imap ^[F $
map ^[H g0
imap ^[H g0
The reason I couldn't find them before is because they weren't mapped using <Esc>, but ^[ instead. Very irritating! Hope this helps some equally disgruntled spf13 user :)
UPDATE:
If removing those mappings doesn't work, then it's probably a mapping from a plugin.
Type :verbose map <Esc> to get a list of all mappings involving Esc. The verbose part instructs Vim to print where the mapping was set. That should help find out what's causing the problem.
Also, the command unmap <Esc> might be useful—it removes any mappings for the Esc key.
Note that unmap does not remove mappings in all modes; type :h unmap for more info.
In my case, it was tmux injecting that delay (this came as a complete surprise for me!).
I fixed it by adding set -g escape-time 0 to my tmux.conf.
This may not strictly help the author, but this question comes up first when searching for this issue with many different keyword combinations, so I hope it helps someone.
Source: first comment from here.
I'm not exactly sure what the problem is with the mapping you describe, in my opinion it should be fine. However, I think that what you want to accomplish can be reached in a better way. Your mapping is basically an attempt at creating a InsertLeave event, which Vim actually already has built in!
Try adding something like this to your .vimrc:
au InsertLeave * if &mod && expand('%')!=''|write|endif
As an added bonus, this one only saves your buffer if it has a filename and was actually modified.
set timeoutlen=1000 ttimeoutlen=0
timeoutlen - mapping delays.
ttimeoutlen - key code delays.
Source

Vim inline remap to check first character

I am trying to do a comment remap in Vim with an inline if to check if it's already commented or not. This is what I have already and of course it's not working ha ha:
imap <c-c> <Esc>^:if getline(".")[col(".")-1] == '/' i<Delete><Delete> else i// endif
What I want to do is check the first character if it's a / or not. If it's a / then delete the first two characters on that line, if it's not a / then add two // in front of the line.
What I had originally was this:
imap <c-c> <Esc>^i//
And that worked perfectly, but what I want is to be able to comment/uncomment at a whim.
I completely agree with #Peter Rincker's answer warning against doing this in insert mode, and pointing you to fully-featured plugins.
However, I couldn't resist writing this function to do precisely what you ask for. I find it easier to deal with this kind of mapping with functions. As an added bonus, it returns you to insert mode in the same position on the line as you started (which has been shifted by inserting or deleting the characters).
function! ToggleComment()
let pos=getpos(".")
let win=winsaveview()
if getline(".") =~ '\s*\/\/'
normal! ^2x
let pos[2]-=1
else
normal! ^i//
let pos[2]+=3
endif
call winrestview(win)
call setpos(".",pos)
startinsert
endfunction
inoremap <c-c> <Esc>:call ToggleComment()<CR>
Notice the modifications to pos to ensure the cursor is returned to the correct column. The command startinsert is useful in this type of function to return to insert mode. It is always safer to use noremap for mappings, unless there is a very good reason not to.
This seems to work well, but it is not very Vim-like, and you might find other plugins more flexible in the long run.
There are many commenting plugins for vim:
commentary.vim
tComment
EnhCommentify
NERD Commenter
and many more at www.vim.org
I would highly suggest you take a look at some these plugins first before you decide to roll your own. It will save you great effort.
As a side note you typically would want to comment/uncomment in normal mode not insert mode. This is not only the vim way, but will also provide a nicer undo history.
If you are dead set on creating your own mappings I suggest you create a function to do all the hard work and have your mapping call that function via :call. If you think you can get by with simple logic that doesn't need a function then you can use an expression mapping (see :h map-<expr>). You may want organize into a plugin as it could be large. If that is the case look at :h write-plugin to give you a feel
for writing plugins the proper way.
Example of a simple expression mapping for toggling comments:
nnoremap <expr> <leader>c getline(".") =~ '\m^\s*\/\/' ? '^"_2x' : 'I//<esc>`['
there's also this vimtip! http://vim.wikia.com/wiki/Comment/UnComment_visually_selected_text
i use the bottom one with the
...
noremap <silent> ,c :<C-B>sil <C-E>s/^/<C-R>=escape(b:comment_leader,'\/')<CR>/<CR>:noh<CR>
noremap <silent> ,u :<C-B>sil <C-E>s/^\V<C-R>=escape(b:comment_leader,'\/')<CR>//e<CR>:noh<CR>
,c comments out a region
,u uncomments a region

Pressing "Home" in Vim on an Indented Line

I have a bad habit of using the 'home' key to go back to the beginning of a line. As I recently started using vim I noticed that when I press the home key on a lined that is indented, it returns me to the very beginning of the line. In Notepad++ (the editor I used to use) it would return me to the beginning of the code on that line, right after the indent.
Is there some way to replicate this behavior in vim? Usually, when I'm pressing home it's in the Insert mode for me to (usually) stick a variable there.
I have set smartindent in my vimrc, with set noautoindent as a "tips" page told me to make sure to disable autoindent (although it didn't seem to be enabled in the first place - perhaps that option is extraneous.)
There are two usual ways to go to the "beginning" of a line in Vim:
0 (zero) go to the first column of text
^ go to the first non-whitespace on the line
I find that using 0w is usually the most convenient way for me to go to the first nonblank character on a line, it's the same number of keys as ^ and is easier to reach. (Of course, if there are no leading spaces on the line, don't press w.)
You could remap Home to be the same as ^ (the docs say Home's default function is equivalent to the movement command 1|):
:map <Home> ^
:imap <Home> <Esc>^i
Which should make the insert mode mapping be equivalent to escaping out of insert mode, pressing ^ and then returning to insert mode. I don't know about the best method of mapping a motion command for use inside insert mode, so this may break something, but it seems to work.
As to your indentation settings, they shouldn't have an effect on movement controls, but I also think you probably would prefer to have them set differently. autoindent just keeps your current indentation for new lines (so if you place 4 spaces at the beginning of a line, after you press return your new line will also have 4 spaces placed in front of it). I don't know why you wouldn't want that, since it's pretty useful in pretty much any programming language, or even just freeform text. smartindent on the other hand implements a couple of hard-coded lightly C-ish indentation rules, like indenting after an opening {, and deindenting after a closing }, but doesn't automatically carry over indentation from previous lines. The docs recommend keeping autoindent on if you use smartindent.
However, smartindent is useless for languages that don't meet its hard-coded rules, or even actively harmful (like when it automatically removes indentation from any line starting with a '#', which it thinks is a preprocessor directive but is wrong for python programmers trying to write an indented comment).
So vim also includes a more advanced indentation mode, filetype indentation, which allows flexible indentation rules on a per-language/filetype basis and is the preferred indentation mode for most people (even for C-like languages). If you do use filetype indentation, it's best to turn off smartindent (otherwise it can interfere with the filetype indentation, like moving all comment lines to column 0 in python files).
Personally, I always have autoindent on, use filetype when available, and never use smartindent. My .vimrc includes:
set autoindent " doesn't interfere with filetype indents, and is useful for text
if has("autocmd")
" Enable file type detection and indentation
filetype plugin indent on
set nosmartindent
endif
I imagine there's something you could do to have smartindent turned on only when filetype indenting doesn't exist for a filetype, if you're editing that many different C-like languages with no filetype indentation available.
Here’s what I have in my .vimrc. This maps Home to move to the beginning of the
text if you are anywhere in the line, and column 0 if you are at the beginning
of the text.
function ExtendedHome()
let column = col('.')
normal! ^
if column == col('.')
normal! 0
endif
endfunction
noremap <silent> <Home> :call ExtendedHome()<CR>
inoremap <silent> <Home> <C-O>:call ExtendedHome()<CR>
Note: I am using a keyboard layout that maps Home to Alt Gr+A, that why I’m using this. If you have to leave the letter field of your keyboard to reach Home, you should probably go to normal mode instead.
You could also use _ in Normal mode to go to the first non-whitespace character of the current line. You can also use a count with this motion.
_ <underscore> [count] - 1 lines downward,
on the first non-blank character linewise.
Try pressing 0 (also see :help 0)
also, this might help:
:imap <C-Home> <esc>0a

Resources