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']
Related
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.
I often use gq$ to wrap a line in Vim.
For example, if I have set textwidth=80 as my only line in .vimrc, then
option1, option2, option3, option4, option5, option6, option7, option8, option9, option10, option11
wraps to
option1, option2, option3, option4, option5, option6, option7, option8, option9,
option10, option11
However, if I want to wrap a comma-delimited list (without spaces), this command does not work, because Vim considers this line as a single word:
option1,option2,option3,option4,option5,option6,option7,option8,option9,option10,option11
Whereas desired output is:
option1,option2,option3,option4,option5,option6,option7,option8,option9,
option10,option11
How can I allow Vim to wrap by splitting a line on commas? I didn't see anything immediately in :help fo-table that is relevant to my case.
One way to do it is to use Par. It's the best program for reflowing text hands down, but you have to really like abstractions to digest the manual. My cheat sheet for it:
set the environment variable PARINIT:
export PARINIT='grTbEiq B=.,!?_A_a Q=_s>:|'
in my vimrc:
set equalprg=par\ s0\ 72
set formatprg=par\ s0\ 72
function! s:FormatPar()
let old_format = &formatprg
let textwidth = &textwidth > 0 ? &textwidth : 72
let &formatprg = 'par s0 ' . textwidth . (v:count > 0 ? 'h1p' . v:count : '')
normal }{gq}
normal }
let &formatprg = old_format
endfunc
nnoremap <silent> <F2> :<C-u>silent! call <SID>FormatPar()<CR>
With that F2 re-formats the current paragraph, and using it with a count adds a hanging indent (that is 4F2 formats the paragraph with a hanging indent of 4).
It works very well for email messages, comments in code, and the like. It also has no problem with dealing with lists like above.
I have content stored in a variable (out) which I want to replace with the current buffer. I'm currently doing it like this (simplified version):
let splitted = split(out, '\n')
if line('$') > len(splitted)
execute len(splitted) .',$delete'
endif
call setline(1, splitted)
(Detailed: https://github.com/fatih/vim-go/blob/master/autoload/go/fmt.vim#L130)
However setline() here causes slowness on some machines and https://github.com/fatih/vim-go/issues/459. I've profilde it myself but for me setline was not a problem. Anyway, I need a solution which is more faster. So I've come up with several other solutions.
First one is, which puts the output to a register, deletes all lines and then puts it back:
let #a = out
% delete _
put! a
$ delete _
Second solution would be using append() (which was used previously in vim-go https://github.com/fatih/vim-go/commit/99a1732e40e3f064300d544eebd4153dbc3c60c7):
let splitted = split(out, '\n')
%delete _
call append(0, splitted)
$delete _
They both work! However they both also causes a side effect which I'm still couldn't solve and is also written in the title. The problem is described as:
If a buffer is opened in another view (say next to next), and
we call one of the two solutions above, it breaks the cursor of
the other view and jumps to the bottom
Here is a GIF showing it better (whenever I call :w one of the procedures above is called): http://d.pr/i/1buDZ
Is there a way, to replace the content of a buffer, which is fast and doesn't break the layout? Or how can I prevent it with one of the procedures above?
Thanks.
Did you try winsaveview() and winrestview()?
:let old_view=winsaveview()
:% delete _
:put! =out
:$ delete _
:call winrestview(old_view)
However I don't know anything about pasting text in a quicker way
Try using the redraw command.
I have faced similar issues of strange delays a few times, where profiling doesn't shows anything suspicious. But the redraw command solved it in most cases and it doesn't disrupt the window layout (the last time I found this problem was in vim-addon-qf-layout plugin).
If the problem still happens you could try using the following approach, which is slight different from your first example; I've been using it for quite some time without any delays:
function! s:setCurrentLine(content)
silent put =a:content
" delete original line
silent '[-1delete _
endfunction
What about this? It saves the view for each window with the current buffer opened inside, then restores all the views after the modifications. It seems to work for me.
function! BufListSave()
let cur_buf = winbufnr(0)
let cur_tab = tabpagenr()
let buflist = []
for i in range(tabpagenr('$'))
let tab_array = []
let tab_buflist = tabpagebuflist(i+1)
for j in range(len(tab_buflist))
if tab_buflist[j] == cur_buf
exe "tabn ".(i+1)
let cur_win = winnr()
exe (j+1)."wincmd w"
call add(tab_array, {"win":j+1, "view":winsaveview()})
exe cur_win."wincmd w"
endif
endfor
call add(buflist, tab_array)
endfor
exe "tabn ".cur_tab
return buflist
endfunction
function! BufListRest(buflist)
let cur_tab = tabpagenr()
for i in range(len(a:buflist))
let tab_array = a:buflist[i]
if len(tab_array) == 0
continue
endif
exe "tabn ".(i+1)
let cur_win = winnr()
for wi in tab_array
exe "".wi['win']."wincmd w"
call winrestview(wi['view'])
endfor
exe cur_win."wincmd w"
endfor
exe "tabn ".cur_tab
endfunction
function! Do_It()
let buf_list = BufListSave()
%delete _
put! =out
$delete _
call BufListRest(buf_list)
endfunction
function! Do_It_Silently()
silent call Do_It()
endfunction
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
I want to make a vim cheat sheet plugin. It's real simple:
I want to toggle my cheatsheets. A vertsplit toggle, like Taglist or NERDTree.
I want the cheatsheet to be filetype specific. So I toggle my c++ cheatsheet when I have opened a .cpp file.
I want the cheatsheet to be horizontally split. So it shows two files, my syntax cheat sheet and my snippet trigger cheat sheet.
I already have a collection of these cheatsheets, in vimhelp format, but now I have to manually open them.
I haven't really done any vim scripting, but I imagine this would be really simple to put together. I'm sorta sick of googling unrelated codesnippets, so what I'm asking here is:
Could anyone give me a short sum-up of what I need to learn in regards to vim scripting to piece this together. What I have a hard time finding is how to toggle the buffer window.
If you know any intro tutorials that covers the material I need to get this up and running, please provide a link.
tx,
aktivb
The function below may not do exactly what you want, and I haven't tested it, but it should give you some ideas.
The main idea is that the function reads the filetype of the current buffer (you can test this by typing :echo &ft) and then sets the path of the appropriate cheat sheat. If it exists, this path is then opened (read-only and non-modifiable) in a split window. You can then call this function any way you wish, for example by mapping it to the {F5} key as shown.
I'm not sure about the toggling possibilities (is this really easier than just closing the split window?) but you could look at the bufloaded() function, which returns whether or not a given file is currently being accessed.
function! Load_Cheat_Sheet()
let l:ft = &ft
if l:ft == 'html'
let l:path = 'path/to/html/cheat/sheet'
elseif l:ft == 'c'
let l:path = 'path/to/c/cheat/sheet'
elseif l:ft == 'tex'
let l:path = 'path/to/tex/cheat/sheet'
endif
if l:path != '' && filereadable(l:path)
execute ':split +setlocal\ noma\ ro ' l:path
endif
endfunction
map <F5> :call Load_Cheat_Sheet()<CR>
Hope this helps. Just shout if anything is unclear, or you want to know more.
I had forgotten about this until I got a notice about Eduan's answer. Since I posted this question I've done quite a bit of vim scripting, including getting this to work:
let g:cheatsheet_dir = "~/.vim/bundle/cheatsheet/doc/"
let g:cheatsheet_ext = ".cs.txt"
command! -nargs=? -complete=customlist,CheatSheetComplete CS call ToggleCheatSheet(<f-args>)
nmap <F5> :CS<CR>
" strip extension from complete list
function! CheatSheetComplete(A,L,P)
return map(split(globpath(g:cheatsheet_dir, a:A.'*'.g:cheatsheet_ext)),
\ "v:val[".strlen(expand(g:cheatsheet_dir)).
\ ":-".(strlen(g:cheatsheet_ext) + 1)."]")
endfun
" specify cheatsheet or use filetype of open buffer as default
" instead of saving window status in a boolean variable,
" test if the file is open (by name). If a boolean is used,
" you'll run into trouble if you close the window manually with :wq etc
function! ToggleCheatSheet(...)
if a:0
let s:file = g:cheatsheet_dir.a:1.g:cheatsheet_ext
else
if !exists("s:file") || bufwinnr(s:file) == -1
let s:file = g:cheatsheet_dir.&ft.g:cheatsheet_ext
endif
endif
if bufwinnr(s:file) != -1
call ToggleWindowClose(s:file)
else
call ToggleWindowOpen(s:file)
endif
endfun
" stateless open and close so it can be used with other plugins
function! ToggleWindowOpen(file)
let splitr = &splitright
set splitright
exe ":vsp ".a:file
exe ":vertical resize 84"
if !splitr
set splitright
endif
endfun
function! ToggleWindowClose(file)
let w_orig = bufwinnr('%')
let w = bufwinnr(a:file)
exe w.'wincmd w'
exe ':silent wq!'
if w != w_orig
exe w_orig.'wincmd w'
endif
endfun
Thought I would add to Goulash's answer.
I think in order to implement the toggle you would simply use some if statements and a global variable.
let g:cheatsheet_toggle_on=0
if (g:cheatsheet_toggle_on == 0)
" Turn the cheatsheet on
" Also make sure to know that the toggle is on:
let g:cheatsheet_toggle_on=1
elseif (g:cheatsheet_toggle_on=1
" Do whatever you need to turn it off, here
endif
Hope this figures out that logic. :)