I'm trying to make a small extension/bug fix to a plugin I rely on (referenced as
Plugin in the code snips), therefore I'm constrained by its existing API.
I've got the functionality I want somewhat working, however due to inconsistent typing to what the
Plugin's API is expecting, it is not quite working fully.
So here's my somewhat functional code addition to
Plugin; with the context explained beneath:
function! s:conditional_func(mode, ActionFnRef, key) if a:mode.Enabled call a:ActionFnRef(a:key) else call Plugin#Pass#Action(a:key) endif endfunction function! s:nop_exception_register(mode, key) let existing_keymap = Plugin#Get#Keymap() let ActionFnRef = existing_keymap[a:mode][a:key] call Plugin#Keymap#Register(function('s:conditional_func', \ [a:mode, ActionFnRef]), \ "Nop", a:key) endfunction " s:nop_exception_register is later called via another function
a:mode.Enabledis a boolean for the status of
a:ActionFnRefwill be some existing Plugin function, which I only want called (with its own "key-pressed" argument
a:mode.Enabled == 1
Plugin#Pass#Actionis a Plugin function that effectively performs no operation when passed
a:key(in practice, it provides the user with a feedback message, but that's unimportant here)
dictof all existing keymaps, grouped by their modes, then by the actual keys within each mode
thereby the actual function defining the action to be performed when a key is pressed in its mode is captured by the assignment to
Plugin#Keymap#Registeris the built-in API call for registering actions and their keys in the correct mode
The function signature for
Plugin#Keymap#Register is actually (v:t_string, v:t_string, v:t_string), and NOT (v:t_func, v:t_string, v:t_string).
Normally, the name of the action function is passed as a string to
Plugin#Keymap#Register, which generates the Funcref itself by calling
I'm able to have the action working as desired (i.e. conditionally depending on the value of
s:mode.Enabled) because I guess calling
function with a Funcref like I did just returns the same Funcref.
Plugin#Keymap#Register also generates a
dict map of help descriptions, which uses the passed string names of the action functions as its keys. Therefore passing it a Funcref like I did eventually causes
Plugin code executed downstream of that to error. The help map can still be successfully initialized, which I've confirmed by inspecting it. It will contain items like:
Where items keyed by
'B' will work correctly, but
'D' (added by my hack) not quite.
- Create a named Partial inside
s:nop_exception_register, and trying to pass its name as a string to
function! s:nop_exception_register(mode, key) let existing_keymap = Plugin#Get#Keymap() let ActionFnRef = existing_keymap[a:mode][a:key] let CondPartial = function('s:conditional_func', [a:mode, ActionFnRef]) call Plugin#Keymap#Register("CondPartial", "Nop", a:key) endfunction
This causes a Vim error before it's even fully loaded, with
E700: Unknown function: CondPartial displayed in the terminal.
- Place the logic of 's:conditional_func' inside of
s:nop_exception_registeras a closure and pass that to
function! s:nop_exception_register(mode, key) let existing_keymap = Plugin#Get#Keymap() let ActionFnRef = existing_keymap[a:mode][a:key] func! s:_cond_call_action(key) closure if a:mode.Enabled call ActionFnRef(a:key) else call Plugin#Pass#Action(a:key) endif endfunc call Plugin#Keymap#Register("s:_cond_call_action", "Nop", a:key) endfunction
E700: Unknown function: s:_cond_call_action
Also tried to use
eval(), something like
string(eval("function('s:cond_return_func', [a:mode, ActionFnRef])")), where
s:cond_return_funcbasically returns a Partial like
CondPartialfrom workaround 1 above. This also errors, not on Vim startup, but at runtime when the relevant part of the
Plugincode is called:
E117: Unknown function: <evaluated representation of s:cond_return_func>
I've also considered "dereferencing" a Funcref to its string name if somehow possible, like how in Python one can do
<funcname>.__name__to get the string identifier of the function. Though intuitively this seems unlikely to be possible in Vimscript, as I don't believe functions/funcrefs in Vimscript are like objects in Python. But ofc, please correct me if I'm simply being ignorant here.
So basically, to me it looks like my problem is not fully understanding how to resolve variable/function scopes in Vimscript.
Also I know the two functions from the first code snippet I showed can almost certainly be refactored into one, which I plan on doing, once this more glaring issue is resolved.
I apologize in advance for this long and verbose post, but I just wanted to make sure the problem is fully described. I'm pretty much at my wit's end, please help.