Vim popup window not showing on successive events - vim

I have the following plugin I wrote to test popup windows
"plugin/morning/morning.vim"
augroup morning_command_line_popup
autocmd!
au CmdlineLeave * call morning#close()
au CmdlineEnter * call morning#open()
au CmdlineChanged * call morning#update()
augroup END
"autoload/morning.vim"
function morning#open()
let opt = {}
let opt.border = []
let opt.minwidth = &columns / 3
let g:morning_window_handle = popup_create(":echo 'hi'", opt)
endfunction
function morning#close()
call popup_close(g:morning_window_handle)
endfunction
function morning#update()
call popup_show(g:morning_window_handle)
endfunction
The first time I enter the command line, the popup window appears. Every successive time that I enter the command line, it doesn't appear. I know the events are being fired and the functions are being called because if I put echom commands in the functions the messages show. Anyone know what could be wrong?

I figured out that if I redraw after showing the popup window, it seems to work consistently. I don't know if there's a reason why it won't redraw normally, or if doing this is extremely inefficient. This also has the side effect of overwriting wildmenu.
function morning#open()
let opt = {}
let opt.border = []
let opt.minwidth = &columns / 3
let g:morning_window_handle = popup_create(":echo 'hi'", opt)
redraw
endfunction
function morning#close()
call popup_close(g:morning_window_handle)
endfunction
function morning#update()
call popup_settext(g:morning_window_handle, string(getcmdline()))
call popup_show(g:morning_window_handle)
redraw
endfunction

Related

VIM: How to remain netrw sidebar window when I open a new tab?

I have the following in .vimrc:
let g:netrw_banner = 0
let g:netrw_liststyle = 3
let g:netrw_browse_split = 4
let g:netrw_altv = 1
let g:netrw_winsize = 25
augroup ProjectDrawer
autocmd!
autocmd VimEnter * :Vexplore
augroup END
It gives me a nice explorer sidebar when I launch vim. But the problem is when I open a new tab with :tabnew, it lost the sidebar in the new tab. How can I make :tabnew to have the exact same sidebar? Thanks a lot.
The corresponding event is :help TabNew; if you add that to VimEnter, you should be good to go:
autocmd VimEnter,TabNew * :Vexplore
Unfortunately, that didn't work for me; it just opened an empty window split. Delaying the creation helps:
autocmd VimEnter * :Vexplore
autocmd TabNew * call feedkeys(":Vexplore\<CR>", 'n')
Note that all these instances will be separate from each other; its contents won't be synchronized.

Different VimResized autocmds for Shrinking and Growing

autocmd VimResized * <foo> will run the command <foo> whenever the vim application's window is resized.
Is there a way to run different commands depending on whether the resize is a shrink or a grow?
And, if so, are there any caveats for console vim?
A window is 2-dimensionnal, so the concept of shrinking or growing is quite imprecise : are you talking about the height, or about the width, or about the area?
Let's assume you're talking about the area.
A simple way to do it is to save the last size of the window, on startup and on each time the win is resized; then you just have to compare the last size and the new one, each time the win is resized:
" Defines a command to save the current dims:
command! SaveVimDims let g:last_lines=&lines | let g:last_columns=&columns
" Saves the dims on startup:
au VimEnter * SaveVimDims
" Calls the func below, each the win is resized:
au VimResized * call VimResized_Func()
function! VimResized_Func()
" Gets the area of the last dims:
let last_area = g:last_lines * g:last_columns
" Saves the new dims:
SaveVimDims
" Gets the area of the new dims:
let cur_area = g:last_lines * g:last_columns
" Compares the areas:
if cur_area < last_area
" do something when shrinking
else
" do something when growing
endif
endf
This was only tested with Gvim; I never use Vim in console. Hope it'll work as well

Prevent cursor jumping to bottom when the buffer is replaced

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

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']

Vim script: Buffer/CheatSheet Toggle

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. :)

Resources