How to understand `{'key: function('1')}` in VimScript? - vim

Rescently I am in favor of a Vim plugin called Vundle. a dict named as g:bundle has a item:
{'path': function('1')}
If I call the item.path(), Vundle can invoke "s:bundle.path()" in vundle/config.vim :
func! s:bundle.path()
return s:expand_path(g:bundle_dir.'/'.self.name)
endf
So, could you tell me the usage about parameter "1" of anonymous function in Vimscript?
Updated:
Thanks for Mr. Karkat.
I use :function {1} command, whose result is:
function 1() dict
return s:expand_path(g:bundle_dir.'/'.self.name)
endfunction
the function block is same as s:bundle.path(), it proves that number in braces means Funcref:
The function will then get a nusmber and the value of dict.len is a
Funcref that references this function. The function can only be used
through a Funcref. It will automatically be deleted when there is no
Funcref remaining that refers to it.
Referance:
https://github.com/gmarik/Vundle.vim/blob/master/autoload/vundle/config.vim#L106
http://vimdoc.sourceforge.net/htmldoc/eval.html#Dictionary-function

What you see is an anonymous Dictionary function. What this means is that for the Dictionary {'path': function('1')} (let's call it foo, and we know that it's an element of g:bundles), there has been defined this:
let foo = {}
function foo.path() dict
return "foo"
endfunction
You can find out more about the function definition via
:function {1}

Related

How to suppress printing "0" in vim when using skeleton/eval dynamic templates

When I load a template with a function with no explicitly specified return value in the template's placeholder-eval block, Vim executes the function but also types "0" instead of the placeholder. The "0" is a function's exit code I assume.
I'd like to suppress printing of "0".
This is in my .vimrc:
augroup templates
au!
" read in template files
autocmd BufNewFile *.* silent! execute '0r $HOME/.vim/templates/skeleton.'.expand("<afile>:e")
" parse special text in the templates after the read
autocmd BufNewFile * %s#\[:VIM_EVAL:\]\(.\{-\}\)\[:END_EVAL:\]#\=eval(submatch(1))#ge
augroup END
When I create a file with an extension for which I've got a skeleton file in my ~/.vim/templates directory, Vim reads the content of the template file in a buffer and substitutes the contents of [:VIM_EVAL:] "tags" or "placeholders" by evaluating it with eval(). If the placeholder is a function with no return value specified, like cursor(100,1) below, Vim will type "0" in place of this VIM_EVAL placeholder. How do I avoid such behavior?
My template file looks like this:
#!/usr/bin/perl
#[:VIM_EVAL:]MyCoolFunctionWithReturnValue()[:END_EVAL:] #this function works as I expect
use strict;
use warnings;
[:VIM_EVAL:]cursor(100,1)[:END_EVAL:] # this built-in vim function movers the cursor but also types "0" digit on the line before the cursor.
The problem is not because cursor() doesn't return any value, but actually because cursor() will return 0 for success and -1 for failure.
From :help cursor():
Returns 0 when the position could be set, -1 otherwise.
So one possible solution is to wrap that into a function that returns an empty string instead.
function! MoveCursor(lnum, col)
call cursor(a:lnum, a:col)
return ''
endfunction
And then refer to that function in your template:
[:VIM_EVAL:]MoveCursor(100,1)[:END_EVAL:]
An alternative that doesn't involve creating a wrapper function is to use an expression that will call the function you want, but still evaluate to an empty string. Examples are:
[:VIM_EVAL:]cursor(100,1) ? '' : ''[:END_EVAL:]
Or:
[:VIM_EVAL:][cursor(100,1), ''][-1][:END_EVAL:]
The first uses the ? : operator to define what to return depending on whether the expression is true or false. But in this case we return an empty string either way.
The second creates an array (so it evaluates every expression for each element) and then pick the last element, which is an empty string.
Either of these should work well for your particular use case.

Vim - Overwrite Plugin Scoped Function

I've a plugin in Vim and I don't like the behavior of a single function within it. But it isn't rly a case to open a pull request, but more an extension for it.
I know that overwriting a function ist possible by using a bang as postfix, as soon as the new definition comes after the previous one. But how can I do such thing, if this method is scoped to a script within a plugin?
I wasn't able to find a hint in _Vim_s help, nor by request a search engine. Anybody aware of this topic, at least if he can say that it is simply not possible.
A short example:
plugin/autoload/plugin.vim
...
function! s:foo() {
// behavior I would like to adjust
}
...
~/.vimrc
function! foo() {
// the "correct" behavior
}
Thanks for any help!
Actually it is possible. But as #romainl said, you'd better suggest your patch to the plugin maintainer or ask for a variation point.
Regarding the how.
First, you'll need to identify the script number of this autoload plugin. Let's say that :scriptname says it's 210. In order to do that automatically I have a lh#askvim#scriptid() function in my library plugin that does the job -- see the current definition at the end of the answer.
Then, to override the s:foo() function, you'll need to provide a new definition for
function! <SNR>210_Foo()
new definition
endfunction
(I've just tested it with vim 8.0-1157)
IOW, we can override a script-local function. However, I haven't found how to override a script-local variable directly without a reference to its s: dictionary. We could inject setter/getter functions to a specific variable or a function that returns the local s: dictionary.
lh#askvim#scriptid() current definition is the following
" Function: lh#askvim#execute(command) {{{3
" #since Version 4.0.0
if exists('*execute')
function! lh#askvim#execute(command) abort
return split(execute(a:command), "\n")
endfunction
else
function! lh#askvim#execute(command) abort
return s:beware_running_through_client_server ? [] : split(lh#askvim#exe(a:command), "\n")
endfunction
endif
" Function: lh#askvim#scriptnames() {{{3
function! lh#askvim#scriptnames() abort
let scripts = lh#askvim#execute('scriptnames')
let s:scripts = map(copy(scripts), 'split(v:val, "\\v:=\\s+")')
call lh#list#map_on(s:scripts, 1, 'fnamemodify(v:val, ":p")')
return s:scripts
endfunction
" Function: lh#askvim#scriptid(name) {{{3
function! lh#askvim#scriptid(name, ...) abort
let last_change = get(a:, 1, 0)
if last_change || !exists('s:scripts')
call lh#askvim#scriptnames()
endif
let matches = filter(copy(s:scripts), 'v:val[1] =~ a:name')
if len(matches) > 1
throw "Too many scripts match `".a:name."`: ".string(matches)
elseif empty(matches)
if last_change
throw "No script match `".a:name."`"
else
return lh#askvim#scriptid(a:name, 1)
endif
endif
return matches[0][0]
endfunction
That is not possible.
s:foo() is scoped to the script it belongs to (see :help s:) so it can't be accessed from anywhere else.
Fork it.
Make the desired changes to your fork.
Use your fork instead of the original.
Consider submitting a pull request.

vimscript read input but already pass something

i want to read a input from the user. I do this that way:
let wordUnderCursor = expand("<cword>")
call inputsave()
let filePattern = input('Searchterm: ')
call inputrestore()
Now my goal is to already put something into the searchterm so that the user (me) doesnt has to write the whole searchterm.
So is it possible to prepopulate the input-function with a string?
thx in advance
Check out :help input(); it tells you that there are optional additional arguments; the first one is exactly the default text you've been looking for:
input({prompt} [, {text} [, {completion}]])
[...]
If the optional {text} argument is present and not empty, this
is used for the default reply, as if the user typed this.
For your example:
let filePattern = input('Searchterm: ', wordUnderCursor)
If you don't want the preset, you can remove it via <BS> character-by-character, or in one fell swoop with <C-u> (as in any command-line).

Testing vim plugin private functions

I'm creating a vim plugin which has a couple of private functions and I'm trying to add unitary testing to it using vim-vspec.
What is the best way to invoke these private functions in the test file?
For now, I created a public function that invokes the private one, but I don't think that's a good approach because I'm loosing the point of having a private function. Here's some of the code
" File foo.vim (the plugin)
" Private function
fu! s:foo(arg)
...
endfu
" Public function
fu! InvokeFoo(arg)
call <SID>foo(a:arg)
endfu
" File foo-unittest.vim (the test file)
runtime! plugin/foo.vim
describe 'foo function'
it 'should have some behavior'
call InvokeFoo(...)
" Some expectations ...
end
end
I tried creating maps to the private functions but when I call exe map_combination it doesn't have any effect on the testing buffer.
I found a solution to my question here, and it gives one approach for variables and another for functions.
Variables
For the variables, I used vim's scopes. Calling :help internal-variables:
The scope name by itself can be used as a Dictionary. For example, to
delete all script-local variables:
:for k in keys(s:)
: unlet s:[k]
:endfor
So I access the :s scope by making a getter function to its dictionary:
fun! SScope()
return s:
endfu
And finally an variable s:variable will be accessed by:
let l:scope = SScope()
echom l:scope['variable']
Functions
The functions are a bit more complicated due the <SID> string. If you read the manual you'll get
When executing the map command, Vim will replace <SID> with the
special key code , followed
by a number that's unique for the script, and an underscore. Example:
:map <SID>Add could define a mapping "23_Add".
So, we need to access this unique number and one way to do it is to define a map that will serve as an accesor using maparg inside a function:
fu! SID()
return maparg('<SID>', 'n')
endfu
nnoremap <SID> <SID>
Then, to call the function we will make a little hack:
call call(substitute('s:my_function', '^s:', SID(), ''), [arg1, arg2, ...])

Vim: How to write a function that searches the current buffer?

I am trying to write a function in Vim that searches the current buffer for a certain pattern and returns it. But, I'm failing horribly. Basically, what I want is a function that returns the (PHP) namespace of the file I am working in. The namespace is defined in the file itself:
namespace Foo\Bar;
What I would like is a function that returns the Foo\Bar part as a string. I.e. something that searches like /namespace\s\([^;]\+\) and returns the first submatch.
Edit: Here's the function I build thanks to the help I got:
func! PhpNamespace()
let l:lnr = 0
while l:lnr < line('$')
let l:str = matchstr(getline(l:lnr), '^\s*namespace\s\+[^;]\+')
if len(l:str)
return substitute(l:str, '^\s*namespace\s\+', '', '')
endif
let l:lnr = l:lnr + 1
endwhile
return ''
endfunc
One option is to use searchpos(), which gets you the start position; you then need to extract the text yourself. This is fast and easy, especially if you need to search forward / backward from the cursor position. With the 'n' flag, the cursor position will not change. Otherwise, you have to save and restore the cursor position (getpos('.'), setpos('.', saved_cursor)).
For your problem, it looks like the namespace declaration is likely at the beginning of the file, and is limited to a single line. Then, you could also get individual lines with getline(lnum) in a loop and extract the text with matchstr(), and break out of the loop once you have it.

Resources