how to understand these vim scripts - vim

I have two question about understand those vim script. please give some help,
Question 1:
I download a.vim plugin, and i try to read this plugin, how to understand the below variable definition? the first line I can understand, but the second line, I don't know exactly "g:alternateExtensions_{'aspx.cs'}" means.
" E.g. let g:alternateExtensions_CPP = "inc,h,H,HPP,hpp"
" let g:alternateExtensions_{'aspx.cs'} = "aspx"
Question 2:
how to understand "SID" before the function name, using like below function definition and function call.
function! <SID>AddAlternateExtensionMapping(extension, alternates)
//omit define body
call <SID>AddAlternateExtensionMapping('h',"c,cpp,cxx,cc,CC")
call <SID>AddAlternateExtensionMapping('H',"C,CPP,CXX,CC")
thanks for you kindly help.

let g:alternateExtensions_{'aspx.cs'} = "aspx"
That is an inline expansion of a Vimscript expression into a variable name, a rather obscure feature that is rarely used since Vim version 7. See :help curly-braces-names for details. It is usually used to interpolate a variable, not a string literal like here ('aspx.cs'). Furthermore, this here yields an error, because periods are forbidden in variable names. Newer plugins would use a List or Dictionary variable, but those data types weren't available when a.vim was written.
To avoid polluting the function namespace, plugin-internal functions should be script-local, i.e. have the prefix s:. To invoke these from a mapping, the special <SID> prefix has to be used instead of s:, because <SID> internally gets translated into something that keeps the script's ID, whereas the pure s:, when executed as part of the mapping, has lost its association to the script that defined it.
Some plugin authors don't fully understand this unfortunate and accidental complexity of Vim's scoping implementation either, and they put the <SID> prefix also in front of the function name (which works, too). Though it's slightly more correct and recommended to write it like this:
" Define and invoke script-local function.
function! s:AddAlternateExtensionMapping(extension, alternates)
...
call s:AddAlternateExtensionMapping('h',"c,cpp,cxx,cc,CC")
" Only in a mapping, the special <SID> prefix is actually necessary.
nmap <Leader>a :call <SID>AddAlternateExtensionMapping('h',"c,cpp,cxx,cc,CC")

<SID> is explained in :help <SID>:
When defining a function in a script, "s:" can be prepended to the name to
make it local to the script. But when a mapping is executed from outside of
the script, it doesn't know in which script the function was defined. To
avoid this problem, use "<SID>" instead of "s:". The same translation is done
as for mappings. This makes it possible to define a call to the function in
a mapping.
When a local function is executed, it runs in the context of the script it was
defined in. This means that new functions and mappings it defines can also
use "s:" or "<SID>" and it will use the same unique number as when the
function itself was defined. Also, the "s:var" local script variables can be
used.
That number is the one you see on the left when you do :scriptnames, IIRC.

Related

vim mapping a plugin and providing argument coming from an external script

I use ConqueGdb plugin on a fairly frequent basis for my debugging needs. I decided to set a mapping for it to make my life a little easier. Below is what my mapping looks like -
map gd :ConqueGdb ./binary_name !script_which_returns_pid_of_binary
OR
map gd: ConqueGdb ./binary_name str2nr(system('~/bin/which_pid.sh'))
I noticed that the script in this case is not getting evaluated but instead being pasted as text. Then I tried again by wrapping this script in a function which returns the pid -
map gd :ConqueGdb ./binary_name call GETPID()
Same issue persisted.
Finally, I created a function and within in, I added the
ConqueGdb ./binary_name pid_variable
But here too the same issue prevails (i.e. pid_variable gets passed as text rather than being evaluated to the value it holds).
What am I doing wrong and how can I get vim to use the value stored in the variable rather than assume it is plain text?
TIA.
It seems you're looking for :exe
I guess something like:
exe ':ConqueGdb ./binary_name'. str2nr(system('~/bin/which_pid.sh'))
Instead of ./binary_name you could also use a variable that you assign somewhere else (like a local vimrc that acts as a plugin that defines your project (preferences & more))

Should I use function or function! in vim scripts?

I think I understand the difference between function and function!: if a function with the same name already exists function! silently replaces it, but function yields an error.
I end up using function! always. Because if I use simple function sooner or later it returns and bites me with:
E122: Function my_lib#MyHandyFunction already exists, add ! to replace it
Are there any situations when one should use simple function without !?
In scripts, it doesn't hurt to use :function!, but you should use script-local (s:Foo) or autoload-scoped (myscript#Foo) functions to properly namespace them. So, the override error for :function is helpful to alert you to redefinitions of global functions, but in scripts, you shouldn't need this precaution.
You have to use :function! when you want to reload the script during development (instead of restarting the whole Vim). (And plugins like my ReloadScript plugin can deal with the include guards.)
Another empirical point: Most of the plugins I have use :function!, probably for the easy reload.
The same goes for :command! and :normal!, where (usually), the version with ! should be used.
You should normally use function. Doing such, you would at least recognize when there's a name collision.
When using function! by default, you don't have any feedback that you're about to override an existing function (i.e. change existing functionality)!
Just have a look at the error message you've posted:
E122: Function my_lib#MyHandyFunction already exists, add ! to replace it
This means: careful, dude! If you use function! now, the users of my_lib#MyHandyFunction will experience things they never expected!

<SID> with foldexpr

I am reading Learn Vim Script the Hard Way and hit something that confused me whilst doing the exercise to convert the folding functions to script local ones.
I tried to go this:
setlocal foldexpr=<SID>GetPotionFold(v:lnum)
and renamed all the functions to start with s:
To my surprise this didn't work and every line had a fold level of 0? It works if I put GetPotionFold into the global scope. Do you have to use a globally scoped function when assigning it to a option? Why?
The <SID> can be used in a mapping or menu, unfortunately not in an option. (This is a shortcoming in the implementation.)
You'd either have to translate it into the actual <SNR>NNN_ prefix (there's an s:SID() example function at :help <SID>), or use a different scope that is accessible from outside the script that defines the function. It's commendable that you want to avoid clobbering the global function namespace, as this is prone to name clashes.
A nice trick is using the autoload function prefix; it doesn't just work in autoload scripts, but can also be used elsewhere, e.g. in plugin scripts. Just prepend the script's name, and you'll have a function that can be invoked from anywhere, but scoped to the script's name:
:function! MyScriptName#GetPotionFold(lnum)
...
:setlocal foldexpr=MyScriptName#GetPotionFold(v:lnum)
Adding to the previous answer, you could define the function s:SID() to determine the script number as in the help documentation and then use execute to set the fold expression as following:
exe "setlocal foldexpr=<SNR>" . s:SID() . "_GetPotionFold(v:lnum)"

How to distinguish between line-address and line-range in vim?

This is a simple user-defined function:
fun! Foo() range
echo a:firstline a:lastline
endfun
:5call Foo() and :5,5call Foo() give me the same result.
However, :5j and :5,5j give me different results.
Can I write a function which behave like join?
How does join distinguish between line address and line range?
By defining a custom :command, the -range and -count attributes allow you better control over how the range is consumed. However, I think even that won't allow you to exactly duplicate the behavior of :join. The interface for custom Vim commands is not as rich as what is available to built-in commands.
As a workaround, you could use histget('cmd', -1) to get the command-line that invoked your command, and parse the exact command invocation, including the original range (which can then be re-used by passing it to another command, but doing line arithmetic with it is problematic, since it's the raw range, not the actual line numbers). The workaround will only work for interactive commands, is brittle, and demands some effort. Maybe you can avoid the issue altogether by defining two different commands instead.

vim functions with script scope

I had installed Janus with my MacVim setup. In order to learn about how vim scripts work, I've been reading through the vimrc file that Janus uses, and I don't understand how the author of this is using functions. For example, here's one of the functions in the vimrc:
function s:setupWrapping()
set wrap
set wrapmargin=2
set textwidth=72
endfunction
Now, according to the Defining a function section of the vim manual, 'Function names must begin with a capital letter.' According to the Local mappings and functions section of the manual, 'When defining a function in a script, "s:" can be prepended to the name to make it local to the script.' However, there's no mention of being able to begin a function name with a lower case letter when specifying its scope as local to the script.
So, is the function as written syntactically incorrect but works anyway, or is it syntactically correct but I can't find the documentation that says so?
As I understand it, the rule about capitalizing function names is intended to avoid conflicts with vim's built-in functions. There's no possibility of conflict from script-local functions, so it seems reasonable that the restriction would not apply to them, since you must always prefix them with their namespace qualifier.
ZyX corrected me in the comments, pointing out that, contradictory to an earlier revision of this answer, vim does not allow buffer-scope functions to be declared. You can declare a global function with a name like b:function_name, or for that matter _:function_name, but this is confusing and probably a terrible idea, for reasons mentioned in the comments.
Functions declared within a dictionary do not need to be capitalized.
Buffer-scope Funcrefs, and presumably other Funcrefs outside of global or function-level scope ("local" Funcrefs) do not need to be capitalized. But they have limited usefulness anyway, since a Funcref must reference either a global or script-scope function (the latter being syntactically awkward) or a dictionary function; in the latter case you have to call it with call(funcref, args, dict).
But anyway, you're looking for documentation, so I did a :helpgrep capital and found these nuggets of wisdom:
E704: A Funcref variable must start with a capital, "s:", "w:", "t:" or "b:".
E124: « Define a new function by the name {name}. The name must be made of alphanumeric characters and '_', and must start with a capital or "s:" (see above). » The "see above" pointer refers to the sections user-functions and local-function, which provide more detail but don't mention anything about the non-capitalization of script-scope functions. user-functions mentions that The function name must start with an uppercase letter, to avoid confusion with builtin functions.
It may be that the strict rule of always starting a function name with a capital was true before the advent of other scopes, of which script scope seems to have been the first, or at least the first capable of including function declarations. I'm guessing that the parts of the manual which assert such a rule have just not been updated to reflect the state of modern vim.
I suppose you'll never know if there's documentation but you can't find it.
However, I looked at Derek Wyatt's vimrc file on his blog and he consistently uses a capital first letter in function names.
This just proves, only, that he's read the manual too.

Resources