-
-
Notifications
You must be signed in to change notification settings - Fork 6.3k
[RDY] Implement dictionary notifications #3603
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
2995453
to
e8e866f
Compare
@ZyX-I other than |
@tarruda |
@tarruda: Also API: vim_set_var, vim_set_vvar. v: variables are mostly set using |
@tarruda Any specific reason to stick with IMO Just my two cents. |
9b65ba3
to
b319bc0
Compare
No reason, but I don't usually spend a lot of time thinking about names. Both |
Can you think of a situation where it would be useful to watch for v: variables? I'm not sure it is safe because these can be set anywhere, and it is not always safe to execute vimscript |
Got that covered, thanks |
b319bc0
to
3e55693
Compare
Other than the documentation and possibly a change in the |
3e55693
to
1fffa42
Compare
I do not like any function names which do not include
. From function named
|
argvars[1].v_type != VAR_STRING || | ||
(argvars[2].v_type != VAR_FUNC && argvars[2].v_type != VAR_STRING)) { | ||
// Wrong argument types | ||
EMSG(_(e_invarg)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be much better to report something more specific: at least, argument number which has invalid type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this kind of validation code extremely repetitive and tend to avoid adding too much detail, especially because in a future PR I want to refactor the function declarations to be more like this:
{"watcheradd", 3, 3, f_watcheradd, false, {VAR_DICT, VAR_STRING, VAR_FUNC | VAR_STRING},
{"watcherdel", 3, 3, f_watcheradd, false, {VAR_DICT, VAR_STRING, VAR_FUNC | VAR_STRING},
Which would allow automatic validation for every new function added. the false
part is to determine if the function is callable from sandbox, and the VAR_* constant values needed to be changed into powers of 2.
To avoid memory leaks, I don't increase refcount on the dict when a watcher is added, so when a dict is garbage collected, all its watchers are silently removed. One way to handle is trigger a special notification(without new or old values) to tell the caller if a dict is garbage collected. Since I'm not sure how to deal with this for cases where a dict is collected by |
Using the same function for supporting makes second argument inconsistent: list does not need patterns. I do not really think that lists need anything, but a “whole list” watcher function: most of time items are appended to a list or removed from it and not assigned (even though it is possible and is rarely used). You can though make a function which watches the whole dictionary also add watchers to a whole list.
? Why do you need this: when dictionary is removed watcher that is watching " Plugin is using g:surround_keys dictionary like currently g:surround_{number} is used:
function s:SurroundKeysChanged(dict, key, value)
if a:dict isnot# g: | throw 'AssertionError:Invalid dictionary' | endif
if a:key isnot# 'surround_keys' | throw 'AssertionError:Unexpected key' | endif
if has_key(a:value, 'new')
if type(a:value.new) == type({})
call watcheradd(a:opts.new, function('s:SurroundKeyChanged'))
else
echohl ErrorMsg
echomsg 'Invalid g:surround_keys type: expected dictionary'
echohl NONE
endif
else
" Do something on removal
endif
endfunction
function s:SurroundKeyChanged(dict, key, value)
if a:dict isnot# g:surround_keys | throw 'AssertionError:Invalid dictionary' | endif
" Do something
endfunction
call dictkeywatcheradd(g:, 'surround_keys', function('s:SurroundKeysChanged')) (BTW: note that I am also passing dictionary to make it possible to reuse one function for multiple dictionaries. Key and value pair is not enough.) |
The second function is being called whenever key inside dictionary changes, but it is not called at all when dictionary is garbage-collected. |
Considering these arguments, I'm fine with
👍 makes sense, thanks |
The {callback} receives two arguments: | ||
|
||
- The key which changed. | ||
- A dictionary containg the new and old values for the key. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also highly suggest to pass also a dictionary being watched. And I think you may simplify the code thus, because in this case only needed things are
- Dictionary being watched.
- Changed key.
- (optional) Old value. (Or new value, I did not check when you call this watcher: before or after value changed.)
I.e. function signature will be watcher(dict, key, ...)
. This saves you up to four allocates (dictionary, dictionary item for old value, dictionary item for new value, hash table allocate). Passing dictionary for which notification was created allows using notifications not only for options: e.g. I can imagine how you can use this feature to implement monkey-patching in some OOP implementations (i.e. you have a class dictionary where you put new method, but since instance dictionaries are separate to attach new method to all instances you need to patch them. Patching may be done in notification function, but it would be very, very inconvenient to implement without passing dict
argument).
And there is another argument against patterns, especially against glob patterns: when hearing about pattern match user will expect everything to work which works in regular globs. But regular globs in Vim use shell (will be fixed if luaviml will ever make it into release), and also this will highly complicate creating the DFA. Non-pattern variant is more simple to code. |
There's no need to call the shell for simple autocmd-like patterns. In fact, fileio.c already has methods for converting a glob pattern into a compiled regular expression, and About using a non-pattern variant to simplify having a fast implementation: In practice it will only offer an small improvement. Even if the user has 1000 plugins and each one has one watcher on As for the DFA approach, it is not very complex. We already have it implemented in the regexp engine, but it needs to be refactored to allow better reuse in other parts of the code |
@tarruda And what about my suggestion regarding new callback signature? |
The dictionary is already passed as the self argument, but it may be a good On Sun, Nov 8, 2015, 11:14 AM Nikolai Aleksandrovich Pavlov <
|
@tarruda Proposal was not only about the dictionary. When passed only as self it is convenient to use your variant with |
They are also difficult to debug ( |
There's a reason I pass both function! Cb(a:key, a:value)
if !has_key(a:value, 'old')
" Key inserted
elseif !has_key(a:value, 'new')
" Key deleted
else
" Key updated
endif
endfunction Without the function! Cb(a:key)
if has_key(self, a:key)
" Key inserted or updated, need to check if an old parameter was passed
if a:0 > 1
" Key updated and old value in a:2
else
" Key inserted
endif
else
" Key deleted and old value in a:2
endif
endfunction An alternative would be to pass another argument that specifies the type of event, but this would also be unclean.
I'm not sure I understand, are you saying that any function that has the FWIW I considered implementing #2203 as an autocommand(first @ZyX-I suggestion) and it would actually have been easier, but found it much cleaner to use callbacks with setup/remove functions. Autocommands would require at least 3 extra v: variables and there would be no way to watch arbitrary dictionaries, only top-level variables(Though most plugins just use g: variables for settings and I doubt that will change, so the usefulness of watching nested dictionaries is questionable). There is one advantage of using autocommands though: It has a better chance of it being merged back into Vim. Since now there is an But since we're still alpha, I will move this PR forward and if someone sends a patch to Vim and it is merged we can adapt to be more compatible. |
249a61f
to
df37aa6
Compare
If this is the only objection, it seems not worth the cost in terms of API weight. Also, there is no Vim precedent for prefixing a function name with "dict" or "list". Existing function names to draw from:
We should allow watching function calls in the future. What if
Vim draws from unix conventions where possible ( |
You forgot
Not sure what exactly @justinmk meant, but I think it is the problem with anonymous and not dictionary functions. And they also do work with Ah, and you have to somehow determine the number that serves as a function name. Though this is easy, but not very convenient. |
@justinmk Also when talking about funcrefs I was thinking about function redifinition with |
The goal is to implement #2203, which will provide a flexible mechanism for implementing UI options(though it may be useful for plugins in general)