vim call a function from inside a vmap - vim

I have created a vmap text object for selecting the text of a single LaTeX \item:
vmap im ?\\item<CR>o/\\item\\|\\end{itemize}<CR>b$
But this has the annoying feature that I lose my current search term. I have read that search terms are restored when the search happens inside a function call, so I wanted to convert the map to just call a function that would do the searches:
function! ItemizeTextObject()
?\\item
normal o
/\\item|\\end{itemize}
normal b$
endfunction
vmap in :call ItemizeTextObject()<CR>
Unfortunately, this does not work: I get an error ("Pattern not found: \item|\end{itemize}"), no text at all is selected, and a new line is inserted below the line my cursor is on. I tried several variations of this, and had no success.
I think the basic problem is that I have to preserve the visual mode when calling the function (the o in my command should switch to the other end of the selection, but it inserts a new line instead), but I don't know how.
Update:
I try to get the following behaviour: In a text like this:
\begin{itemize}
\item lorem ipsum...
\item piece of text I want to select,
the *CURSOR* is here, and there is more text
that follows
\item lorem ipsum...
\end{itemize}
I want to hit vin, and then the text block in the middle should be selected:
\item piece of text I want to select,
the *CURSOR* is here, and there is more text
that follows
that means the text from the beginning of the previous \item, until but not including the next \item or \end{itemize}.

I've used the doc on operatorfunc to come up with the following, which should be (close to) what you want1:
function! ItemizeTextObject(type, ...)
let sel_save = &selection
let &selection = "inclusive"
let reg_save = ##
if a:0 " Invoked from Visual mode, use '< and '> marks.
silent! 1,+1?\\item
norm v | " use V for linewise visual mode
"" use V for linewise visual mode:
"norm V
silent! /\\item\|\\end{itemize}
"elseif a:type == 'line'
"elseif a:type == 'block'
else
silent! 1,+1?\\item
norm v
silent! /\\item
endif
norm b$
let &selection = sel_save
let ## = reg_save
endfunction
silent! unmap in
xnoremap <silent> in :<C-U>call ItemizeTextObject(visualmode(), 1)<CR>
If you want the mapping in both visual and select modes, you should use vnoremap
Notes of things to address:
you can now implement the motion from another mode (fill in the branches in the function)
if wrapscan is on, no search should wrap (perhaps temporarily set nowrapscan?)
you might want to make the operator repeating so you can extend the selection by saying vininin (see https://stackoverflow.com/a/7292271/85371 for an example)
it looks like you want 'linewise' behaviour (due to b$?)
consider using norm V (see comment)
Edit I compared the behaviour with this simple mapping:
xnoremap <silent>in ?\\item<CR>o/\\item\\|\\end{itemize}<CR>b$
1 Disclaimer: I don't know LateX

Related

Function is called several times in vimscript

I want to call a function when user presses qq in visual mode, so I wrote the following code:
function! FindSelectionMeaning()
echo "FindSelectionMeaning"
endfunction
vnoremap <silent> qq :call FindSelectionMeaning()<CR>
The function is called but I have the following questions:
Why does FindSelectionMeaning getting called once for each of the selected lines? I thought that it should be called only once
Is there any way to call script-related function via vnoremap (in this case s:FindSelectionMeaning instead of FindSelectionMeaning)?
Your command was called several times (in fact the number of selected lines ), because, when you press : in visual mode, vim will automatically add range '<,'>, it leads to for each selected line execute the later inputted command. If your function want to be called only once, you can change your mapping like:
vnoremap <silent> qq :<c-u>call FindSelectionMeaning()<CR>
the <c-u> is gonna remove the range info after the :
In fact, you can get selected text in this way, I think it is easier, keep the <c-u> mapping, and change your function:
function! FindSelectionMeaning ()
try
let v_save = #v
normal! gv"vy
return #v
finally
let #v = v_save
endtry
endfunction
This function returns selected text.

How to obtain command completion list

I created unite source cmdmatch which lets you among other things fuzzy complete wildmenu items. I would like to find a better method to obtain the completion list (method I use is given here and is problematic because with large number of completion items screen will be filled up entirely [to see why just press :<c-a> if you didn't remap <c-a>])
The other solution would be to hide vim's cmd line completely while I am grabbing the list although I don't think that is possible in vim (or at least limit the amount of text it displays so it doesn't fill up screen). Any ideas ?
EDIT
First try has the same flickering problem although it looks like it works faster
fu! GetCompletion(input)
call feedkeys(":" . a:input . "FF\<esc>\<cr>")
endf
cno FF <C-A><C-\>eg:Save()<CR>
fu! g:Save()
let g:x = getcmdline()
endf
Result can be seen as:
:call GetCompletion('help a')
:echo x
SOLUTION
let g:cmdmatch = {}
fu! g:cmdmatch.set_c( base ) dict
let self.c = a:base
endfu
fu! GetCommandCompletion( base )
cno [MATCH] <c-a><c-\>eg:cmdmatch.set_c(getcmdline())<cr>
sil! exe 'norm :' . a:base . '[MATCH]'
cu [MATCH]
retu g:unite_cmdmatch.c
endf
Test: echo GetCommandCompletion("help '")
The trick is to execute the entire completion with :silent, so that the output won't actually happen (but completion is still magically done). The following function retrieves the completions already parsed into a List, by triggering the completion and then wrapping the output in :return split('...'). Of course, you can also :return '...' if you need a single string.
function! GetCommandCompletion( base )
silent execute "normal! :" a:base . "\<C-a>')\<C-b>return split('\<CR>"
endfunction
demo
:let cmds = GetCommandCompletion('no')
:echo cmds
['noautocmd', 'nohlsearch', 'noreabbrev', 'noremap', 'noremenu', 'normal']

Spreadsheet style enter in vim?

Here is my data:
1.333840,2,3,4,5,6,7,8
1.xxxxxx,2,3,4,5,6,7,8
1.yyyyyy,2,3,4,5,6,7,8
1.zzzzzz,2,3,4,5,6,7,8
What I would like to do is have Vim remember the location of the column where i inserted. This would be handy in handling fixed entry tables like the one I have above. I desire to stay in insert REPLACE mode but minimize the use of the cursor, overwriting template data like above.
xxxxxx should be 444444
yyyyyy should be 555555
zzzzzz should be 666666
What I would want to do is start at Row 1 Column Position 3 and type
i444444<spreadsheetenter>555555<spreadsheetenter>666666<esc>
Is there a special mode or key binding that would help me do this? Basically keep the return at the same fixed column from when I re-enter into insert mode.
if you want to "overwrite" the template data, you don't want to do it in INSERT mode, you need replace mode. by pressing R
many vim magics are done in NORMAL mode, you should think about it.
For example, for your problem, I would:
cursor(*) at *333840, press R, start overwriting
when finish, press <ESC>jbR, your cursor will go to *xxxxxx, and ready to replace
you could create a mapping for this kind of job, like:
inoremap <leader>g <esc>jbR
it works like:
You could try the following mappings for your <spreadsheetenter>:
:nnoremap <S-CR> g`[j
:inoremap <S-CR> <Esc>g`[jR
Bonus
And here's a more complex insert mode variant that keeps the mode (insert vs. (virtual-) replace):
function! s:GetCurrentInsertMode()
let s:currentInsertMode = mode()
return ''
endfunction
function! s:RestartCurrentInsertMode()
if s:currentInsertMode ==# 'i'
startinsert
elseif s:currentInsertMode ==# 'R'
startreplace
elseif s:currentInsertMode ==# 'Rv'
startgreplace
else
throw 'ASSERT: Unknown mode: ' . string(s:currentInsertMode)
endif
endfunction
inoremap <silent> <expr> <SID>(GetCurrentInsertMode) <SID>GetCurrentInsertMode()
inoremap <silent> <script> <S-CR> <SID>(GetCurrentInsertMode)<C-\><C-n>g`[j:call <SID>RestartCurrentInsertMode()<CR>
Try the csv plugin. It does this by default in (Virtual)Replace mode.

UltiSnips and YouCompleteMe

I have bundles ultisnips and youcompleteme installed on my macvim.
The problem is that ultisnips doesn't work because tab is bound by ycm.
I tried putting let g:UltiSnipsExpandTrigger = "<s-tab>" so that I can trigger the snippet completion with shift-tab, but it doesn't work for some unknown reason. I could use caps as the trigger, but so far I've found no way to do that.
Do any of you use those two add-ons together?
What can I do to make shift-tab work?
Can you recommend another key to trigger snippets?
Another option is using the SuperTab plugin:
" if you use Vundle, load plugins:
Bundle 'ervandew/supertab'
Bundle 'Valloric/YouCompleteMe'
Bundle 'SirVer/ultisnips'
" make YCM compatible with UltiSnips (using supertab)
let g:ycm_key_list_select_completion = ['<C-n>', '<Down>']
let g:ycm_key_list_previous_completion = ['<C-p>', '<Up>']
let g:SuperTabDefaultCompletionType = '<C-n>'
" better key bindings for UltiSnipsExpandTrigger
let g:UltiSnipsExpandTrigger = "<tab>"
let g:UltiSnipsJumpForwardTrigger = "<tab>"
let g:UltiSnipsJumpBackwardTrigger = "<s-tab>"
Here YouCompleteMe is bound to a different combination Ctrln, but then that combination is bound to tab through SuperTab. UltiSnips and SuperTab play nice together, so you can then just bind UltiSnips to tab directly and everything will work out.
Try this suggestion on a page from the YouCompleteMe issue tracker. In your .vimrc:
let g:UltiSnipsExpandTrigger="<c-j>"
While this setting will make expanding a snippet share the default mapping for jumping forward within a snippet, it simulates TextMates' behavior as mentioned in the UltiSnips help tags.
Since I've mapped my Caps Lock key to Ctrl, this mapping works pretty smoothly.
copy the following code to your vimrc, and enjoy. This function will handle all issues between YCM and UltiSnips.
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
au BufEnter * exec "inoremap <silent> " . g:UltiSnipsExpandTrigger . " <C-R>=g:UltiSnips_Complete()<cr>"
let g:UltiSnipsJumpForwardTrigger="<tab>"
let g:UltiSnipsListSnippets="<c-e>"
" this mapping Enter key to <C-y> to chose the current highlight item
" and close the selection list, same as other IDEs.
" CONFLICT with some plugins like tpope/Endwise
inoremap <expr> <CR> pumvisible() ? "\<C-y>" : "\<C-g>u\<CR>"
i have this in my vimrc
"" YouCompleteMe
let g:ycm_key_list_previous_completion=['<Up>']
"" Ultisnips
let g:UltiSnipsExpandTrigger="<c-tab>"
let g:UltiSnipsListSnippets="<c-s-tab>"
thats what i did on my first try, but i misspelled UltiSnips with Ultisnips.. oh well, worked out in the end!
I personally chose to not use <tab> with YouCompleteMe but navigate it manually.
So I added this to my .vimrc:
let g:ycm_key_list_select_completion=[]
let g:ycm_key_list_previous_completion=[]
which simply disables the tab key for YCM. Instead you use the movement keys (arrows or CTRL-N/CTRL-P) and select the entry with CR. UltiSnips works default with tab.
Just putting together answers by Michaelslec, Joey Liu and along with solutions I found in this issue thread and this guy's vimrc, I now have this which solved pretty much all problems.
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
function! g:UltiSnips_Reverse()
call UltiSnips#JumpBackwards()
if g:ulti_jump_backwards_res == 0
return "\<C-P>"
endif
return ""
endfunction
if !exists("g:UltiSnipsJumpForwardTrigger")
let g:UltiSnipsJumpForwardTrigger = "<tab>"
endif
if !exists("g:UltiSnipsJumpBackwardTrigger")
let g:UltiSnipsJumpBackwardTrigger="<s-tab>"
endif
au InsertEnter * exec "inoremap <silent> " . g:UltiSnipsExpandTrigger . " <C-R>=g:UltiSnips_Complete()<cr>"
au InsertEnter * exec "inoremap <silent> " . g:UltiSnipsJumpBackwardTrigger . " <C-R>=g:UltiSnips_Reverse()<cr>"
Based on Siegfried's answer, I am using the following which seems more natural:
let g:ycm_key_list_select_completion = ['<C-j>']
let g:ycm_key_list_previous_completion = ['<C-k>']
let g:UltiSnipsExpandTrigger = "<C-l>"
let g:UltiSnipsJumpForwardTrigger = "<C-j>"
let g:UltiSnipsJumpBackwardTrigger = "<C-k>"
I also use the c-hjkl bindings somewhere else (switching from a pane to another), but that would only be in normal mode, so there's no problem.
Although I know this post is a little old, I have my own function that is a little more optimized than the one given above:
function! g:UltiSnips_Complete()
call UltiSnips#ExpandSnippetOrJump()
if g:ulti_expand_or_jump_res == 0
if pumvisible()
return "\<C-N>"
else
return "\<TAB>"
endif
endif
return ""
endfunction
Of course, if you just keep the settings that Joey Liu provided and then just use this function everything will work just perfectly!
EDIT: Also, I use another function to increase back-stepping functionality between YouCompleteMe and UltiSnips. I'll show you what I mean:
function! g:UltiSnips_Reverse()
call UltiSnips#JumpBackwards()
if g:ulti_jump_backwards_res == 0
return "\<C-P>"
endif
return ""
endfunction
Then just put this in your .vimrc:
au BufEnter * exec "inoremap <silent> " . g:UltiSnipsJumpBackwardTrigger . " <C-R>=g:UltiSnips_Reverse()<cr>"
As well as let g:UltiSnipsJumpBackwardTrigger="<s-tab>" and your set!
I use both of them together. By default YouCompleteMe binds <Tab> and <Down> to select the next completion item and also <S-Tab> and <Up> to select the previous completion item. You can change the YouCompleteMe bindings with the g:ycm_key_list_select_completion and g:ycm_key_list_previous_completion options. Note that the names of these options were recently changed when the option was changed from a single string to a list of strings.
While Many answer works fine in this post, I just want to say that the problem is caused by key binding collision between YCM and UltiSnip, while YCM support UltiSnip snippets by default, it takes the default UltiSnip expand trigger <tab> as its completion select key, so UltiSnip snippets will not be expaned by <tab>. Give them different key binding will solve the problem, I personally use <c-n and <c-p> for YCM and use the default <tab> for UltiSnip. You can get more details with help youcompleteme doc in vim.
I installed the UltiSnips plugin after the YouCompleteMe plugin so I thought they were conflicting, but in reality I had something more interfering:
set paste
Make sure to remove that from .vimrc if it's present.
I use ; to expand UltiSnips, it's so nifty for me
let g:UltiSnipsExpandTrigger = ";"
I use kj. This is what is in my .vimrc:
let g:UltisnipsExpandTrigger="kj".
It rarely happens that I run into word that has kj in it. If this is the case I would just wait a couple of seconds after typing k and that type j.
As mentioned by others, mapping C-j to ultisnips works great.
let g:UltiSnipsExpandTrigger="<c-j>"
Now, if you go a bit further and install xcape and use
xcape -e "Shift_L=Control_R|J"
You unleash the power of using just the shift key for utlitsnips.

How to filter the contents of a Visual selection that does not span an entire line through an external command in Vim?

I would like to filter a visual selection in Vim through a command. The way I know filters always the complete lines over which the visual selection extends:
Selecting a test in the line
this is a test
and typing
:'<,'>!echo "the result"
will result in
the result
But I want:
this is the result
Consider the following mappings that adhere the behavior of the ! linewise
filtering commands (see :helpg \*!\* and :help v_!).
nnoremap <silent> <leader>! :set opfunc=ProgramFilter<cr>g#
vnoremap <silent> <leader>! :<c-u>call ProgramFilter(visualmode(), 1)<cr>
function! ProgramFilter(vt, ...)
let [qr, qt] = [getreg('"'), getregtype('"')]
let [oai, ocin, osi, oinde] = [&ai, &cin, &si, &inde]
setl noai nocin nosi inde=
let [sm, em] = ['[<'[a:0], ']>'[a:0]]
exe 'norm!`' . sm . a:vt . '`' . em . 'x'
call inputsave()
let cmd = input('!')
call inputrestore()
let out = system(cmd, #")
let out = substitute(out, '\n$', '', '')
exe "norm!i\<c-r>=out\r"
let [&ai, &cin, &si, &inde] = [oai, ocin, osi, oinde]
call setreg('"', qr, qt)
endfunction
You can use \%V to match inside the Visual area:
:'<,'>s/\%V.*\%V/\=system('echo -n "the result"')

Resources