mapset() restores entirely from dict#10295
mapset() restores entirely from dict#10295errael wants to merge 1 commit intovim:masterfrom errael:MapsetDictOnly
Conversation
From
From this comment in issue #6118:
But I still don't understand why we would need to return a list of dictionaries, and not just a single dictionary. Although, to be able to return a single dictionary, we would need Example: vim9script
noremap! <F3> aaa
echomsg maparg('<F3>', '!', false, true)
Maybe we could just modify the current example to lock the variable so that it doesn't get unexpectedly changed: diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index e6e6dc272..7360b77c4 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -5364,7 +5364,7 @@ mapset({mode}, {abbr}, {dict}) *mapset()*
{mode} is used to define the mode in which the mapping is set,
not the "mode" entry in {dict}.
Example for saving and restoring a mapping: >
- let save_map = maparg('K', 'n', 0, 1)
+ const save_map = maparg('K', 'n', 0, 1)
nnoremap K somethingelse
...
call mapset('n', 0, save_map)Unrelated, but there are other pitfalls with First, a mapping can be global or local to a buffer, but there is no way to specify to Second, for a non-mapped key, vim9script
const map_save = maparg('<F3>', 'n', false, true)
nnoremap <F3> <Cmd>echomsg 'this is just a temporary mapping'<CR>
mapset('n', false, map_save)
Third, for a buffer-local mapping, vim9script
edit one
nnoremap <buffer> <F3> <ScriptCmd>echomsg 'should be restored in buffer one'<CR>
const map_save = maparg('<F3>', 'n', false, true)
nunmap <buffer><F3>
edit two
mapset('n', false, map_save)
feedkeys("\<F3>")Notice that the mapping has been restored in the buffer There might be other pitfalls like the fact that |
|
maplist() only finds buffer-local mappings in the current buffer. Thus to restore these one has to be in that same buffer. |
|
Nit: arg_string_or_dict_any() arguments are too long, should be using the one argument per line formatting. dict_get_string() with "save" FALSE returns the pointer into the typval, thus a change to the dict may make it invalid. |
|
Related to issue #6118, I'm wondering if the new
Unmapping an lhs is messy. I've got the following. Adding a
That's a feature which allows copying local mappings between buffers. :-)
I don't really understand that or the implications. |
Got it. That file is heavily infected with that issue. |
|
Tests on the way, and looks like there are merge conflicts... |
|
Make sure to note/check
What else? |
Codecov Report
@@ Coverage Diff @@
## master #10295 +/- ##
==========================================
+ Coverage 80.98% 82.46% +1.48%
==========================================
Files 161 148 -13
Lines 185638 171751 -13887
Branches 41973 38832 -3141
==========================================
- Hits 150335 141639 -8696
+ Misses 22778 17500 -5278
- Partials 12525 12612 +87
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
|
I closed the issue #6118, because this PR helps a lot, but it doesn't fix it entirely. Before the PR: vim9script
noremap! <C-A> <C-B>
echo 'BEFORE restoration'
noremap! <C-A>
const map_save = {
i: maparg('<C-A>', 'i', false, true),
c: maparg('<C-A>', 'c', false, true)
}
unmap! <C-A>
mapset('i', false, map_save.i)
mapset('c', false, map_save.c)
echo 'AFTER restoration'
noremap! <C-A>The code was needlessly complex (in part because of too many invocations of After the PR: vim9script
noremap! <C-A> <C-B>
echo 'BEFORE restoration'
noremap! <C-A>
const map_save = maparg('<C-A>', 'i', false, true)
unmap! <C-A>
mapset(map_save)
echo 'AFTER restoration'
noremap! <C-A>The code is simpler, and the mapping is restored exactly as it was defined. However, when we've saved the mapping, notice that we had to use one of the submodes from We couldn't write that: Could vim9script
noremap! <C-A> <C-B>
echo 'BEFORE restoration'
noremap! <C-A>
const map_save = maparg('<C-A>', '!', false, true)
unmap! <C-A>
mapset(map_save)
echo 'AFTER restoration'
noremap! <C-A>Expected: Actual: If that's not possible, no worry; the PR is already very helpful; thank you very much for working on this. |
To get It seems you need to know exactly how it is mapped. In which case, using Seems like doing might be the way to go. A generic or specialized ModeFilter could probably be built. Or just spell out a filter lamda. Either way, this seems to be a more robust solution; should pick up any mappings that are used for insert or command. Out of curiosity, what's the vim9 way to make a ModeFilter(lhs, mode_string)? A function that returns a function that takes two args, index and dict, and remembers lhs and fmode_string. |
|
I've thought a little more about this, and I don't think there is an issue anymore. My use case was this:
But I've just realized that I never want to override a mapping in a set of modes (
In Vim9 script (I don't know C)? If so, could you give some pseudo-code showing an example of what you want? |
Good to here
Right, in vim9 script. Say I want to implement a ModeFilter function that is used like in the following to return all the mappings with that returns function that can be used with filter. Would something like this work? Guess the question is whether lhs_pattern and mode_string are captured in the lamda that gets returned. IsContainedIn() needs to be figured out; it probably uses a dict like Hmm, I'm a little unsure of how to set up dict keys of ' ' and '!' as well. |
It should work, because the lambda is then a closure: vim9script
def Func(s: string): func
return () => s
enddef
var Ref = Func('captured')
echo Ref()Untested, but I would try something like this: vim9script
def ModeFilter(lhs_pattern: string, mode_string: string): func
return (_, d) => {
return d.lhs =~ lhs_pattern
&& IsContainedIn(d.mode, mode_string)
}
enddef
const mode_expander = {
'': ['n', 'x', 's', 'o'],
' ': ['n', 'x', 's', 'o'],
'!': ['i', 'c'],
'v': ['x', 's']
}
def IsContainedIn(mode: string, modes: string): bool
for m in modes
if mode =~ get(mode_expander, m, m)->join('\|')
return true
endif
endfor
return false
enddef |
|
Without looking at the code - it should work to use maplist() to find a mapping (or a few of them), store the list of dicts somewhere, do some random stuff, then use mapset() with those dicts to restore the mappings as they were. No matter what modes the dict contains. |
|
I think this is ready to go, if questions raised in #10295 (comment) are not an issue. |
This didn't get through to the list, github was taking a break this morning when it was posted. |
|
Are there any issues with this patch? |
|
I guess we can now simplify the restoration of the diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
index 63d25db52..3d518a09e 100644
--- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
+++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
@@ -915,7 +915,7 @@ func s:DeleteCommands()
if empty(s:k_map_saved)
nunmap K
else
- call mapset('n', 0, s:k_map_saved)
+ call mapset(s:k_map_saved)
endif
unlet s:k_map_saved
endif |
|
I guess we can now simplify the restoration of the `K` mapping in the
termdebug plugin:
```diff
diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
index 63d25db52..3d518a09e 100644
--- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
+++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
@@ -915,7 +915,7 @@ func s:DeleteCommands()
if empty(s:k_map_saved)
nunmap K
else
- call mapset('n', 0, s:k_map_saved)
+ call mapset(s:k_map_saved)
endif
unlet s:k_map_saved
endif
```
Yes, that should work. I'll make the change.
…--
From "know your smileys":
<|-) Chinese
<|-( Chinese and doesn't like these kind of jokes
/// Bram Moolenaar -- ***@***.*** -- http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
|
Hello, with this patch, are now |
You could try it and open an issue if there's a problem. |
Problem: It is not easy to restore saved mappings. Solution: Make mapset() accept a dict argument. (Ernie Rael, closes vim/vim#10295) vim/vim@51d04d1 Co-authored-by: Ernie Rael <errael@raelity.com>
Problem: It is not easy to restore saved mappings. Solution: Make mapset() accept a dict argument. (Ernie Rael, closes vim/vim#10295) vim/vim@51d04d1 Co-authored-by: Ernie Rael <errael@raelity.com>
Problem: It is not easy to restore saved mappings. Solution: Make mapset() accept a dict argument. (Ernie Rael, closes vim/vim#10295) vim/vim@51d04d1 Co-authored-by: Ernie Rael <errael@raelity.com>
First cut, missing important piece for converting mode
Want something like:
Where some_string could be 'nox', '!', ...
and not only the start of a map command.
more than one mode?
Should we doc that the dict from maparg/maplist should not be modified?
Or give rules on how to safely modify? Maybe punt, ignore issue.
Let brave users figure it out. It does say, ...comes from maparg()/maplist().
For better compile time diagnostics, could introduce
arg_mapset1()tocheck at compile time that if first arg is dict then there are no more args.
Some observations about existing mapset()
whichcan have garbage in the followingmaybe the new get_map_mode_string() can be used for this as well. Not
sure what restrictions on mode should be made for the multi-arg mapset().
wrong message:
Mmaybe
e_dictionary_requiredor
e_calling_dict_function_without_dictionary_str