" ----------------------------------------------------------------------------
" Functions
" ----------------------------------------------------------------------------
function! g:UltiSnips_Complete()
call UltiSnips#ExpandSnippet()
if g:ulti_expand_res == 0
if pumvisible()
return "\<c-n>"
else
call UltiSnips#JumpForwards()
if g:ulti_jump_forwards_res == 0
return "\<tab>"
endif
endif
endif
return ""
endfunction
"
" ----------------------------------------------------------------------------
" Autocmds
" ----------------------------------------------------------------------------
augroup relativenumber
autocmd InsertEnter,focusLost * :set norelativenumber
autocmd InsertLeave,focusGained * :set relativenumber
augroup END
autocmd BufEnter * exec "inoremap <silent> " . g:UltiSnipsExpandTrigger . " <c-r>=g:UltiSnips_Complete()<cr>"
The above code is what comprises the end of my .vimrc file. This function use to work but after some updates (either YouCompleteMe or UltiSnips, unsure) the only time these work is when I first open vim in a directory and then open a file from there. I think it has something to do with the autocmd, but to be honest I don't know where to even start. I've tried changing the autocmd event to BufRead but, unfortunately, that made no difference. Any help is appreciated, thanks!
EDIT: If you think there is a better place I could post this question or you need more details, please tell me! I'm happy to help.
It seems BufEnter runs before VimEnter. This is a problem since SnipMate puts up its mappings in VimEnter. So the very first buffer has the SnipMate mapping instead of your custom one. And when you switch to a new buffer the autocmd run again putting the mapping in place for the new buffer.
To fix this just make the BufEnter mapping a buffer local mapping. This means that the mapping won't get over written by the SnipMate mapping. And the buffer local mapping has precedence when vim looks to run different mappings.
autocmd BufEnter * exec "inoremap <buffer> <silent> " . g:UltiSnipsExpandTrigger . " <cr>=g:UltiSnips_Complete()<cr>"
There are a few corrections that I can suggest :
You don't need to put the mapping in an autocmd BufEnter *, if you want this mapping to be available everywhere, just add the
exec "inoremap <silent>" g:UltiSnipsExpandTrigger "<c-r>=g:UltiSnips_Complete()<cr>"`
From the looks of it, what you really need is an <expr> mapping, something like this should work better :
exec "inoremap <expr> <silent>" g:UltiSnipsExpandTrigger g:UltiSnips_Complete()
NOTE: exec takes in multiple args separated by spaces, if you want to just add a space between strings just add the space, you should use concatenation only when you want to avoid the space that exec would add automatically to it's space separated args.
Edit:
Updated the expression mapping so that <expr> must be the first argument.
Since g:UltiSnipsExpandTrigger is defined by UltiSnips plugin, it is not yet available / defined within your vimrc, it being sourced before all plugins. You should hence put this snippet of code in after/plugin/ultisnips_complete.vim. Then you shouldn't get the error.
Related
folks!
I'm using vim as an IDE for writing code in bash, Python and C. Besides, I have a key map to execute my current buffer depending on the file type. Here is a responsible parts of my .vimrc:
...
autocmd FileType python call Python_source()
autocmd FileType sh call Bash_source()
...
" Read lw (lispwords) modelise from current buffer and pass it as command arguments
func! LWargs()
set lw=''
doautocmd BufRead
if len(&lw) > 0 && len(&lw) < 512
return ' ' . &lw
endif
return ''
endfunc
func! Python_source()
setlocal number cursorline
setlocal shiftwidth=2
setlocal foldmethod=indent
map <F9> :w \| :exe '!python' '%:p' . LWargs()<CR>
imap <F9> <Esc> :w \| :exe '!python' '%:p' . LWargs()<CR>
" Comments on Ctrl-C
map <C-C> :call ToggleComment('#')<CR>
imap <C-C> <Esc>:call ToggleComment('#')<CR>li
endfunc
func! Bash_source()
setlocal number cursorline
setlocal shiftwidth=4
map <F9> :w \| :!./%<CR>
imap <F9> <Esc> :w \| :!./%<CR>
map <C-C> :call ToggleComment('#')<CR>
imap <C-C> <Esc>:call ToggleComment('#')<CR>li
endfunc
...
So, when I press F9, the magic happen and my code is executed with arguments passed by the LWargs. The only problem is after the program exits, vim will jump to the beginning of the file forcing me to position cursor back the line I was working and making my life harder. Is there is any way to prevent vim from jumping around?
The issue is with the doautocmd BufRead command in your LWargs(), which resets the cursor position to the top of the file.
(At least in my case, the command used to restore cursor position upon opening Vim is the one causing the cursor to move. You can inspect the list from :autocmd BufEnter * to see if you can spot a similar command or another one that might be causing the cursor to move. Looking again, it turns out I was also getting that same rule from my Linux distribution...)
A good way to prevent this is to use winsaveview() to save the cursor location and the window view in general (which line is at the top, whether your window is scrolled right to a column when word wrap is off) and winrestview() to restore it.
Here's an updated LWargs() that won't move your cursor:
function! LWargs()
set lw=
let saved_view = winsaveview()
doautocmd BufRead
call winrestview(saved_view)
if len(&lw) > 0 && len(&lw) < 512
return ' ' . &lw
endif
return ''
endfunction
Note also that the correct syntax is set lw=, if you use set lw='' you'll be setting a two-character string with two single quotes.
I have the following on my .vimrc
au FileType ruby nnoremap <expr> <leader>t ':!rspec ' . expand('%') . ':' . line('.')
This executes rspec on the line specified, and gives me option to edit the line before pressing enter. But I have to be on the test file so it get the file name and line number correctly.
While developing I run nnoremap <leader>t :!rspec some/spec/file.rb:123 manually to run the test I want from anywhere in the code. So I can code and fire the test without need to visit the test file.
The problem is that if I visit another ruby file the mapping in .vimrc runs again and I loose the nnoremap command I used before. Is there a command to only map (in normal mode) if there isn't already a map for that sequence?
Regards,
This should be a buffer-local mapping. Use <buffer>:
au FileType ruby nnoremap <buffer> <expr> <leader>t ':!rspec ' . expand('%') . ':' . line('.')
We can do better!
Use an augroup and make is self clearing to make it safe to re-:source.
augroup ruby_testing
autocmd!
autocmd FileType ruby nnoremap <buffer> <expr> <leader>t ':!rspec ' . expand('%') . ':' . line('.')
augroup END
Even better forgo the autocmd and put this mapping in the after directory. Add the following line to ~/.vim/after/ftplugin/ruby.vim:
nnoremap <buffer> <expr> <leader>t ':!rspec ' . expand('%') . ':' . line('.')
For more help see:
:h :map-local
:h :augroup
:h after-directory
Vim has the :help :map-<unique> modifier which makes the mapping fail if such mapping already exists.
au FileType ruby nnoremap <unique> <expr> <leader>t ...
You can suppress the error message with :silent!:
au FileType ruby silent! nnoremap <unique> <expr> <leader>t ...
Alternatively (and this is slightly better because it doesn't suppress any other errors in the mapping definition, like syntax errors), you could explicitly check for the mapping's existence via maparg():
au FileType ruby if empty(maparg('<leader>t', 'n')) | nnoremap <expr> <leader>t ... | endif
Note that this literally implements that (I understand) you're asking for; i.e. the first Ruby file will define the mapping, and any subsequent Ruby files are ignored; the mapping will always use the first file's name and line number. If you instead want to have different right-hand sides for the same mapping, depending on the currently edited file, the solution is a buffer-local mapping as per #PeterRincker's answer. But with that, you have to be inside the original Ruby buffer to trigger the correct mapping.
A remedy for that might be to recall the executed command-line from the command-line history (should happen automatically for your incomplete mapping, else via histadd()), so that you can easily recall the command from another file.
I like using code cleanup scripts; perldidy; uglifierjs, etc. I previously ran them all like this in my vimrc...
map <leader>pt :!perltidy<CR>
map <leader>jt :!uglifyjs -b<CR>
map <leader>pjt :!python -mjson.tool<CR>
map <leader>ct :!column -t<CR>
How this functionally works; it runs currently selected text throguh the CLI program, and replaces the selection with the output. Works wonderfully; but as you can see I now have to stop and think what beautifier I want to run and remember the nemonic I have for it. This led me to think there has to be a better way. So I tried did this...
map <leader>jt :call RunTidy()<cr>
function! RunTidy()
if (&ft == "javascript")
echo 'is js..'
:'<,'>!ulifyjs -b
endif
if (&ft == "json")
echo 'is json'
:'<,'>!python -mjson.tool
endif
endfunction
The problem being this just doesn't work; executing once for each line and not replacing contents.. Anyone aware of a better way to do this? I feel like this should be a solved problem...
augroup filters
autocmd!
autocmd FileType perl map <buffer> <leader>t :!perltidy<CR>
autocmd FileType javascript map <buffer> <leader>t :!uglifyjs -b<CR>
autocmd FileType json map <buffer> <leader>t :!python -mjson.tool<CR>
augroup END
or put those mappings, all with the same lhs, in different ~/.vim/after/ftplugin/{filetype}.vim.
Use ftplugins. You need to have filetype plugin indent on in your vimrc.
Create files in ~/.vim/ftplugin/<filetype>.vim. These files will be sourced when the file type is set.
For example using javascript put the following in ~/.vim/ftplugin/javascript.vim
noremap <buffer> <leader>jt :!uglifyjs -b<CR>
to map <leader>jt to :!uglifyjs -b<CR> in all javascript buffers. This will not show up in other filetypes.
You would do the same for all other filetypes.
You can do the same for file type specific settings by using setlocal.
Take a look at :h ftplugin
I've been using the following technique in my .vimrc to remove extra whitespace at the end of a line...
autocmd BufWritePre * :%s/\s\+$//e
But I realised I didn't want that to happen with Markdown files (e.g. either .md or .markdown) so I have the following VimScript...
fun! StripTrailingWhiteSpace()
" don't strip on these filetypes
if &ft =~ 'md\|markdown'
return
endif
%s/\s\+$//e
endfun
autocmd bufwritepre * :call StripTrailingWhiteSpace()
But that still removes the whitespace for all files.
So I then tried the following (which seemed better as it was shorter)...
let blacklist = ['md', 'markdown']
autocmd BufWritePre * if index(blacklist, &ft) < 0 | :%s/\s\+$//e
But, again, that still removes the whitespace for all files?
Neither of these techniques seem to work? They leave the whitespace still in the file?
Any ideas on how I can do this (at the moment I'm having to edit Markdown files in a separate writing app rather than Vim and that's quite annoying).
The first function should work except you should not be looking for md. ft is short for filetype which is markdown for Markdown files.
By changing it as follows it works fine. (Tested on Vim 7.4)
fun! StripTrailingWhiteSpace()
" don't strip on these filetypes
if &ft =~ 'markdown'
return
endif
%s/\s\+$//e
endfun
autocmd bufwritepre * :call StripTrailingWhiteSpace()
Thanks Kevin Sjoberg for the initial response, but it turns out I have a bigger issue.
Which is if I run :set filetype? from within my Markdown file then it reports back that Vim thinks the filetype is modula2
I'm not sure how I can fix this, so if any one knows then please do comment!
As a temp fix I've used the following work around...
fun! StripTrailingWhitespace()
" don't strip on these filetypes
if &ft =~ 'modula2\|markdown'
return
endif
%s/\s\+$//e
endfun
autocmd BufWritePre * call StripTrailingWhitespace()
...so it checks for both modula2 (whatever that is?) AND markdown files.
UPDATE: well it seems this is a known issue https://github.com/tpope/vim-markdown/issues/15 so I tried the suggested fix...
au BufRead,BufNewFile *.md set syntax=markdown
...but that didn't help, Vim still interpreted the file as modular2
Also tried adding the following into my vimrc file...
au! BufNewFile,BufRead *.md setf markdown
...but that didn't work to change the format.
UPDATE 2
Fixed it, the suggestion by both https://github.com/tpope/vim-markdown/issues/15 and Kevin Sjoberg were almost there.
I just added into my vimrc file a modified version of their suggestion au Bufread,BufNewFile *.md set filetype=markdown
Your implementation is simple, but has some shortcomings (e.g. it clobbers the last search pattern / search history). If you're not against installing a plugin, you can try my DeleteTrailingWhitespace plugin. With it, you can exclude certain buffers by setting a flag, e.g.:
autocmd FileType markdown let b:DeleteTrailingWhitespace = 0
I realize you solved the problem which was more than just the function you were trying to setup.
However, I liked your initial "blacklist" approach with a filetypes list. So for anyone else landing here that likes that, this is what I have.
It also saves your cursor position, and puts it back after running the removal of whitespace.
function! StripTrailingWhitespace()
" Preparation: save last search, and cursor position.
let _s=#/
let l = line(".")
let c = col(".")
" do the business:
%s/\s\+$//e
" clean up: restore previous search history, and cursor position
let #/=_s
call cursor(l, c)
endfunction
let noStripWhiteSpaceTypes = ['markdown']
autocmd BufWritePre * if index(noStripWhiteSpaceTypes, &ft) < 0 | call StripTrailingWhitespace() | endif
I’ve installed the winmanager, NERDTree and BufExplorer plugins. Now, I have <F8> set to toggle the winmanager display, using the following code in my .vimrc:
" mapping for triggering winmanager plugin
nnoremap <silent> <F8> :if IsWinManagerVisible() <BAR>WMToggle<CR><BAR> else<BAR> WMToggle<CR>:q<CR> endif <CR><CR>
This works fine.
What I want to do is to have winmanager show up automatically if the filetype is .c or .cpp. I added this to my .vimrc:
autocmd FileType c,cpp nested "\<F8>"
but it does not work.
Any help? Thanks in advance!
<F8> is a normal-mode mapping, but :autocmd expects an Ex command on its right-hand side. You need to use :normal (without a ! here, to allow mappings to take effect), and :execute to interpret the special key code:
:autocmd FileType c,cpp nested execute "normal \<F8>"
But I think it is cleaner to avoid the additional redirection and duplicate the mapping's commands instead:
:autocmd FileType c,cpp nested if IsWinManagerVisible() |exe 'WMToggle'| else| exe 'WMToggle' | quit | endif