I'm interested to know if there is any way to mock Vimscript functions for testing. I know there are frameworks like vader.vim, but nothing is said about mocking.
You can redefine any existing user function with :function!. So, for example,
function! Mock(name, Hook) abort
" save a reference to original function
let l:Orig = funcref(a:name)
" overwrite function
let l:text =<< trim EOF
function! %s(...) abort closure
let l:oldresult = call(l:Orig, a:000)
let l:newresult = call(a:Hook, [l:Orig, l:oldresult] + a:000)
return l:newresult isnot v:null ? l:newresult : l:oldresult
endfunction
EOF
execute printf(join(l:text, "\n"), a:name)
endfunction
" silly test
function! Test(p1, p2) abort
return a:p1 * a:p2
endfunction
function! MyHook(Orig, result, ...) abort
echo 'Calling' get(a:Orig, 'name') 'with params' a:000 'produced' a:result
" remember: default return value is zero, not v:null
return a:result * 2
endfunction
call Mock('Test', funcref('MyHook'))
echo Test(6, 7)
" Calling Test with params [6, 7] produced 42
" 84
Related
I'm trying to do this
let myDict = { 'lang': 'vimscript' }
func! MyFunction()
echo "self exists => " . exists("self")
endfun
call MyFunction()
let myDict.myFunction = MyFunction
call myDict.myFunction()
From the docs: https://github.com/neovim/neovim/blob/master/runtime/doc/eval.txt#L161
The output
self exists => 0
Error detected while processing /Users/gecko/.vim/plugged/vim-ansible-execute-task/foo.vim:
line 9:
E121: Undefined variable: MyFunction
E15: Invalid expression: MyFunction
line 10:
E716: Key not present in Dictionary: myFunc
Either the example is plain wrong or it silently assumes that MyFunction is a funcref. In both cases, the information is erroneous so you should probably open an issue.
You are supposed to assign a :help funcref, not the function itself:
let myDict = { 'lang': 'vimscript' }
function! MyFunction()
echo "self exists => " . exists("self")
endfunction
call myFunction()
" self exists => 0
let myDict.myFunction = function("MyFunction")
call myDict.myFunction()
" self exists => 0
Note that in this case, the function doesn't get self. If you want self, you must add the dict attribute, but it will make it impossible to call myFunction() directly:
let myDict = { 'lang': 'vimscript' }
function! MyFunction() dict
echo "self exists => " . exists("self")
endfunction
call myFunction()
" E725
let myDict.myFunction = function("MyFunction")
call myDict.myFunction()
" self exists => 1
This may or may not be a problem for you.
See :help dictionary-function and :help anonymous-function for a simpler approach if you don't care about calling myFunction() directly:
let myDict = { 'lang': 'vimscript' }
function! myDict.myFunction()
echo "self exists => " . exists("self")
endfunction
call myDict.myFunction()
" self exists => 1
I'm looking to create a custom omni completion for VIM, and I want to do the actual work of finding possible matches to be done in another program. What would be the best way to go about this?
My idea so far would be to create a vimscript that sends the entire buffer and the location of the cursor to an external script. However I haven't been able to find a more efficient way of getting the buffer than by using join(getline(0, line('$')), '\n'), which is very slow on large files. If you are interested, what I have so far:
AutoComplete vimscript:
fun! MyComplete(findstart, base)
let line = getline('.')
let start = col('.') - 1
while start > 0 && line[start - 1] =~ '\a'
let start -= 1
endwhile
if a:findstart
return start
else
let result = system('java AutoComplete '.shellescape(expand('%')).' '.line('.').' '.start, 'File sourcecode here')
let res = []
for m in split(result)
if m =~ '^' . a:base
call add(res, m)
endif
endfor
return res
endif
endfun
set completefunc=MyComplete
AutoComplete.java:
class AutoComplete {
public static void main(String[] argv) {
// To be expanded into an amazing program...
System.out.print("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
}
}
It's better to create a temporary file and pass its name to an external command. For example, this is from clang_complete plugin:
let l:buf = getline(1, '$')
let l:tempfile = expand('%:p:h') . '/' . localtime() . expand('%:t')
try
call writefile(l:buf, l:tempfile)
catch /^Vim\%((\a\+)\)\=:E482/
" TODO: handle exception
endtry
" TODO: call external program here
call delete(l:tempfile)
See also the QueryCommandComplete plugin, which can be useful for you.
I need to get all the registers that do not have empty strings into a list.
First of all do they exist in a list somewhere already?
Secondly if they do not is there an easier way to get them into a list rather than manually going through them all and checking to see if they are empty?
Maybe I could create a list of the defined registers and use getreg() over that list but I guess I was hoping that the list already existed so I didn't have to create it.
Here is what I came up with:
" Description:
" Get a list of all the non-empty register indexes
" Example Usage:
" :call GetNonEmptyRegsIndex( 'print' ) """ To print the registers
" for n in GetNonEmptyRegsIndex() | echo nr2char(n)." = ".getreg(nr2char(n)) | endfor
" Resources:
" http://www.ibm.com/developerworks/linux/library/l-vim-script-3/index.html
" irs channel #vim <jamessan>
function! GetNonEmptyRegsIndex( ... )
" Create the list of register 'indexes' where the the elements are in char2nr form
let regnum = range(char2nr('a'), char2nr('z'))
let regnum += range(char2nr('0'), char2nr('9'))
let regstr = ['"','-','*','%','/','.','#',':']
let regnum += map(regstr, 'char2nr(v:val)')
" Remove the registers that are empty
"let regnum = filter( regnum, 'getreg(nr2char(v:val)) != ""' )
let regnum = filter( regnum, 'getreg(nr2char(v:val)) !~ "^$"' )
" Remove the registers that are just spaces
let regnum = filter( regnum, 'getreg(nr2char(v:val)) !~ "^\s\+$"' )
" Remove the registers that have no alpha-num
"let regnum = filter( regnum, 'getreg(nr2char(v:val)) !~ "^\W\+$"' )
return regnum
endfunction
Having to convert from string to num and back again makes the code a little ugly but it works.
This is the full working solution with to autocomplete the register info. I might tweak it more later but it gives me the basics that I wanted.
inoremap <F5> <C-R>=AutoCompleteRegs()<CR>
function! AutoCompleteRegs()
call complete( col('.'), GetNonEmptyRegs() )
return ''
endfunction
" Description:
" Get a list of all the non-empty registers.
function! GetNonEmptyRegs()
" Create the list of register 'indexes' where the the elements are in char2nr form
let regIndexNum = GetNonEmptyRegsIndex()
" Convert the list of indexes to values
let regs = map(regIndexNum, 'getreg(nr2char(v:val))')
"let regs = map(regs, 'substitute(v:val, "^(.*)", "\\U\\1", "g")')
"let regs = map(regs, "echo substitute(v:val, '^(.*)', '\U\1', 'g')")
return regs
endfunction
" Description:
" Get a list of all the non-empty register indexes
" Example Usage:
" :call GetNonEmptyRegsIndex( 'print' ) """ To print the registers
" for n in GetNonEmptyRegsIndex() | echo nr2char(n)." = ".getreg(nr2char(n)) | endfor
" Resources:
" http://www.ibm.com/developerworks/linux/library/l-vim-script-3/index.html
" irs channel #vim <jamessan>
function! GetNonEmptyRegsIndex( ... )
" Create the list of register 'indexes' where the the elements are in char2nr form
let regnum = range(char2nr('a'), char2nr('z'))
let regnum += range(char2nr('0'), char2nr('9'))
let regstr = ['"','-','*','%','/','.','#',':']
let regnum += map(regstr, 'char2nr(v:val)')
" Remove the registers that are empty
"let regnum = filter( regnum, 'getreg(nr2char(v:val)) != ""' )
let regnum = filter( regnum, 'getreg(nr2char(v:val)) !~ "^$"' )
" Remove the registers that are just spaces
let regnum = filter( regnum, 'getreg(nr2char(v:val)) !~ "^\s\+$"' )
" Remove the registers that have no alpha-num
"let regnum = filter( regnum, 'getreg(nr2char(v:val)) !~ "^\W\+$"' )
return regnum
endfunction
There is no such list, at least not accessible with vimscript. You can parse the outputs of :silent reg (with silent: no visible output, but still capturable) captured by :redir, but I am not much fond of this command (due to “no nested redirections” and the fact that while target variable will be created at the place where first redir happens, it is populated at the place where second redir happens and referenced by name there, making it possible to append data to any variable or throw errors like E121: Undefined variable). I just don’t like such unpredictable commands.
looking for an advice how to programatically detect, if current Vim's buffer contains at least one fold defined ? Regardless if a fold is open or closed.
Attempting to call mkview only if there is a fold defined in current buffer:
autocmd BufWrite ?* if fold_defined() | mkview | endif
function fold_defined()
???
endfunction
function! HasFold()
let view = winsaveview()
let fold = 0
for move in ['zj', 'zk']
exe 'keepj norm!' move
if foldlevel('.') > 0
let fold = 1
break
endif
endfor
call winrestview(view)
return fold
endfunction
Based on perreal's advice, I did wrote one of possible solutions to my question:
" Detect presence of fold definition in the current buffer
function FoldDefined()
let result = 0
let save_cursor = getpos('.')
call cursor(1,1)
let scanline = line('.')
let lastline = line('$')
while scanline <= lastline
if foldlevel(scanline) > 0
let result = 1
break
endif
let scanline = scanline + 1
endwhile
call setpos('.', save_cursor)
return result
endfunction
function! FoldDefined()
return len(filter(range(1, line('$')), 'foldlevel(v:val)>1'))>0
endfunction
It would be very nice to have an option that would show all the column numbers of the current line or maybe of all the buffer, so I could know where exactly to navigate. Is there such an option or do i have to program it myself (nooo XD)?
:h 'statusline'
It is as easy as defining exactly what you what to see printed. e.g.
" RulerStr() comes from http://www.vanhemert.co.uk/vim/vimacros/ruler2.vim
function! RulerStr()
let columns = &columns
let inc = 0
let str = ""
while (inc < columns)
let inc10 = inc / 10 + 1
let buffer = "."
if (inc10 > 9)
let buffer = ""
endif
let str .= "....+..." . buffer . inc10
let inc += 10
endwhile
let str = strpart(str, 0, columns)
return str
endfunction
let s:saved_stl = {}
function! s:ToggleRuler()
let buf = bufnr('%')
if has_key(s:saved_stl, buf)
let &l:stl = s:saved_stl[buf]
unlet s:saved_stl[buf]
else
let s:saved_stl[buf] = &l:stl
setlocal stl=%{RulerStr()}
endif
endfunction
nnoremap <silent> µ :call <sid>ToggleRuler()<cr>
You can use "set ruler". It will show the line number and column position at the bottom.