programing diff vim function, vim diff mapping - vim

I've found this function:
" Set up a keymapping from <Leader>df to a function call.
" (Note the function doesn't need to be defined beforehand.)
" Run this mapping silently. That is, when I call this mapping,
" don't bother showing "call DiffToggle()" on the command line.
nnoremap <silent> <Leader>df :call DiffToggle()<CR>
" Define a function called DiffToggle.
" The ! overwrites any existing definition by this name.
function! DiffToggle()
" Test the setting 'diff', to see if it's on or off.
" (Any :set option can be tested with &name.
" See :help expr-option.)
if &diff
diffoff
else
diffthis
endif
:endfunction
Now I would like to add an extra condition, if there is some selected text (visual mode) call another command instead of diffthis, Linediff
Reading the function I guess I need some extra set option to test, like they did with &dif but with the visual option. Something like:
function! DiffToggle()
if &dif
diffoff
elseif &visual
Linediff
else
diffthis
endif
:endfunction
This doesn't work, but does anyone have any clue to make it work?
Also, it would be very useful any reference about what and how many setting variables of this kind are in vim.
Edit
I ended up with this in my vimrc, (works):
"LINEDIFF/VIMDIFF
"--------------
nnoremap <silent> <Leader>df :call DiffToggle('n')<CR>
xnoremap <silent> <Leader>df :call DiffToggle('x')<CR>
function! DiffToggle(mode) range
echo "difftoggle..."
if &diff
diffoff
echo "diffoff..."
else
if a:mode=='x'
echo "linediff..."
echo a:firstline."---".a:lastline
call linediff#Linediff(a:firstline, a:lastline)
else
echo "diff..."
diffthis
endif
endif
:endfunction

Simply call a slightly different function from xnoremap <Leader>df ... ? That one will be called when you are in visual mode.
Or, pass the mode as argument to your function:
nnoremap <silent> <Leader>df :call DiffToggle('n')<CR>
xnoremap <silent> <Leader>df :call DiffToggle('x')<CR>
... and check a:mode inside your function, having the following prototype:
function! DiffToggle(mode)

Related

Prevent vim from jumping through source code

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.

How to avoid remap in vim

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.

How to map VIM to run current RSpec file in console?

I'm trying this:
inoremap <F2> :!rspec %
But it doesn't work. Can anyone help?
The "i" in inoremap means "insert mode": your mapping is an insert mode mapping so it obviously won't work in normal mode.
Try nnoremap ("n" for "normal mode") instead and add <CR> at the end to actually execute the command:
inoremap <F2> :!rspec %<CR>
Since we are at it, you could also modify it to "write and run":
nnoremap <F2> :update<bar>!rspec %<CR>
Maybe this is a better solution to check the file with rspec (put these lines in to your .vimrc, it needs nocompatible to be set, works in insert mode):
" Open quickfix window after :make if there was errors.
autocmd QuickFixCmdPost * botright cwindow
inoremap <F2> :call Rspec()<CR>
" Check the file with rspec, don't forget to save it before calling.
function Rspec()
let save_makeprg = &makeprg
compiler rspec
let &makeprg = 'rspec "' . expand( '%' ) . '"'
echo expand( &makeprg )
silent make
let &makeprg = save_makeprg
redraw!
endfunction
It will list the errors in quickfix window.

Vimscript autocmd function fail

" ----------------------------------------------------------------------------
" 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.

Allowing custom motion in a vim map?

I have the following mapping that allows to paste over a word from the yank buffer. (cpw = change paste word): nmap <silent> cpw "_cw<C-R>"<Esc>
What I would like to do is allow commands such as the following:
cpiw (change paste in word -> like the 'iw' motion)
cpaw (change paste a word -> like the 'aw' motion)
for any motion {m} cp{m}
Is this possible to allow in a mapping, so I don't have to write the nmap for each motion that I want to work with it?
Thanks in advance.
EDIT: typo fixes. My solution below
After diligently looking into the map-operator I was successful in making a function that did exactly as I wanted. For any who are interested it is as follows:
"This allows for change paste motion cp{motion}
nmap <silent> cp :set opfunc=ChangePaste<CR>g#
function! ChangePaste(type, ...)
silent exe "normal! `[v`]\"_c"
silent exe "normal! p"
endfunction
EDIT - new version that might be better.
"This allows for change paste motion cp{motion}
nmap <silent> cp :set opfunc=ChangePaste<CR>g#
function! ChangePaste(type, ...)
if a:0 " Invoked from Visual mode, use '< and '> marks.
silent exe "normal! `<" . a:type . "`>\"_c" . #"
elseif a:type == 'line'
silent exe "normal! '[V']\"_c" . #"
elseif a:type == 'block'
silent exe "normal! `[\<C-V>`]\"_c" . #"
else
silent exe "normal! `[v`]\"_c" . #"
endif
endfunction
There is a way to define a custom operator, see :help :map-operator for details.

Resources