-
Notifications
You must be signed in to change notification settings - Fork 269
feat(pick): make key query process respect the 'iminsert' option #2026
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
feat(pick): make key query process respect the 'iminsert' option #2026
Conversation
ec72a3a
to
27bb91c
Compare
Thanks for the PR! I have several issues with supporting this kind of functionality. As I've never used the 'iminsert' option and language mappings, could you please clarify if I understand correctly?
Please, always first create an issue for a new feature request (after searching that this was not already suggested earlier). This usually saves time for both parties involved. |
First of all, I am sorry for rushing this PR. I was upset by the absence of the 'iminsert' handling and very elated after adding it. You are right, I should have started with an issue. Would you like me to close the PR and create an issue for a new feature request first? Now, I will try to make the best of the bad situation. The PR explicitly shows what my suggestion is. Let me try to be more rational, address why I think it is important, and answer your questions. And in the end I hope you can help me understand how we can move forward with implementation. I am sorry in advance for the wall of text below, and I am thankful for your time to review it. So, what is 'iminsert' and why is it important? Well, the 'iminsert' option is a part of the broader 'keymap' option. To think of it, the better name for the PR would be: Essentially, the 'keymap' option is one of the ways Neovim supports multiple input languages. You have a 'langmap' option set in your Neovim setup, so I will make the comparison between 'langmap' and 'keymap'. The 'langmap' allows executing Normal commands while keeping the non-English keyboard mode. Well, 'keymap' is the opposite to 'langmap'. It even says so in the manual (albeit it says that 'langmap' is the opposite of 'keymap'):
When 'keymap=ukrainian-jcuken' is set, and 'iminsert=1', typing
It allows the user to input non-English characters while keeping the English keyboard mode.
The 'iminsert' option specifically controls whether or not such mappings are applied. If 'keymap' is not set, it essentially does nothing. 'CTRL-^' toggles the 'iminsert' option:
'CTRL-^' is not a user-defined mapping, but a standard control character, like 'hjkl' are. I hope I was able to explain the mechanics of the 'keymap' function clearly enough. If you want, you can easily try it out:
Now to address your questions: From usability side, does it indeed require having a dedicated mapping for toggling 'iminsert' value?I would advocate for having a dedicated mapping for toggling 'iminsert' value in 'mini.pick' and setting it to specifically to '<C-^>'. However, as this is only for 'keymap' emulation, it does not need to toggle the 'iminsert' specifically. The 'iminsert' has no effect on The best approach here is to compute it once per picker invocationI do agree that mappings can be created after the first 'mini.pick' call, and that caching them globally may be suboptimal. However, the "l"-type mapping are created using the ":lmap" command, and one can reasonably expect that ":lmap" commands will only be invoked when loading a 'keymap'.
So the "optimal" cache invalidation strategy may be to invalidate the cache on "OptionSet" autocommand with the "keymap" pattern. But, realistically, any strategy may be adopted, as lookup table creation is quite cheap. The pure Lua code is probably easier to understand and more performantSure, I will rewrite the lookup table creation in imperative style. The performance difference is negligible, though. But technically yes, imperative is better, having smaller mean and variance.
What is the advantage of not preferring to switch the keyboard layout?Regarding the advantage of using the 'keymap' option. I use the 'keymap' option all the time. I do a lot of note taking, and I prefer my notes to be in my native tongue. However, almost every one of my notes contains English characters - code blocks, formulas, etc. The wonderful thing about 'keymap' is the fact that it is scriptable. For example, I have the following mapping defined: So I prefer 'keymap' option to switching the keyboard layout. It allows to achieve what essentially is an automatic layout switching, fully self-contained in my Neovim setup. In standard Neovim, 'keymap' option works transparently and everywhere. I am very used to being able to type non-English characters in every Insert mode buffer after pressing 'CTRL-^'. So when I first started using 'mini.pick', I was quite disappointed to discover that it does not work. At first I thought this must be a bug! Other pickers implemented in Lua - specifically "Telescope" and "Snacks.picker" - do create the standard Insert mode buffer, and the 'keymap' option has the expected effect while typing there. Later I learned about the custom key query process, and tried to emulate the 'keymap' effect, and that is where we are now. But it will be fair to admit that it is up to preference. "What is the advantage of using Neovim over VSCode?" - the answer will be different for everyone. So why do I think it is a good feature to add? The advantage is making a picker buffer behave more like a standard Insert mode buffer. While it is powered by a custom key query process, it is reasonable to assume that it is an Insert mode buffer. I see value in making the behavior more in line with the standard set by the core Neovim feature. |
6246944
to
c7dce9d
Compare
Thanks for such a detailed explanation (especially since it looks like not written by AI :) ). I'll be perfectly honest here. To me personally right now using built-in Neovim 'keymap' instead of OS's layout change does look like a matter of taste. And since the latter is more ubiquitous, more flexible (allows more naturally switching between more than two layouts), and already works, I'd close this as not planned. Mostly due to concerns of adding complexity and lines to already the biggest and most complex 'mini.nvim' module. But as you are a fellow Ukrainian and seem to be very passionate about this, I'd be willing to compromise here. Adding this with close to zero performance penalty for the common case of no 'keymap' with as few lines as possible should be doable. For that:
In particular:
If this okay with you, we can go one of two routes:
Both options are completely fine by me. The 1 will require less work for both of us, the 2 will get you full credit and me - another person who has experience with 'mini.nvim' development. |
I would not dare pushing AI content in this situation. Nevertheless, thank you for acknowledging my writing skills :) Reading through your feedback and learning more about your values, it seems that you are also very passionate about 'mini.nvim', keeping it as minimal as possible, and adhering to things that already work. And it seems that you are quite reluctant to adopt the feature in a way that I presented it. It's okay, and I respect that. I checked the issues and discussions once again, and it seems that there are very few issues and discussions regarding natural language and keyboard layouts. And it is fare to say that most (all?) of the 'mini.pick' uses do not really care about the 'keymap' option. So by moving forward even if the performance penalty for the common case is close to zero we would solve my very specific issue. However, as you correctly noted, I would very much like for this improvement to re made. However, I do not want the solution to be forced. And, I think, there might be yet another way, that is less of a compromise and more of an actual improvement/feature. The root cause of my problem is the fact that 'mini.pick' relies on custom key query process, and this process does not follow the Insert mode rules. Instead, the query is essentially built character by character. My initial proposal hooked into this process in the most straightforward way possible - I intercepted the character and changed it, by injecting use case specific code into a general flow. And while we can formalize it slightly according to your suggestion, the core issue with my proposal remains the same. How about we lean into formalizing the character interception instead? In the form of a callback that will fire after a character is retrieved and before it is added to a query. This approach is more general, can be used for more than one thing and is easier to test. Future maintainer will not need to ask themselves "What are these 'keymap' and 'iminsert' options, and why are they here?" The 'keymap' use case can be added to the docs as a new example. And I will be able to solve my issue in my config, not in the 'mini.pick' codebase - by implementing a proper callback. There are multiple possible places to place such a callback - in In the minimal case, it will require 1 new config option, and 1 conditional function call. No built-in mappings. If you like the idea - I can prototype the implementation and we will move on from there. Regarding the implementation - I 100% prefer the route where we work on this together. I want to learn more about the Neovim plugin development lifecycle, specifically setting up testing environment and adding tests. This looks like the great opportunity to do so. And I don't want to miss the opportunity to work on one of the most popular Neovim plugins, with one of the most popular Neovim plugin authors :) |
I don't think I like this idea. It is more general, but it is also relatively complicated to actually use (no "out of the box value"). While I'd imagine it is pretty niche, especially since there are already a mechanism of custom mappings.
Knowing about Neovim's options and capabilities is a good thing for a Neovim plugin maintainer to have. Even/especially if it is not widely used.
Sounds good. But to be fair, I don't think working on 'mini.nvim' will extrapolate well on other plugins: not enough plugins have testing at all while coding practice/design can differ significantly. Start with implementation outlined in this comment and try to look into how to write and run tests. Useful sources:
|
c7dce9d
to
83f9fe0
Compare
lua/mini/pick.lua
Outdated
H.islist = vim.fn.has('nvim-0.10') == 1 and vim.islist or vim.tbl_islist | ||
|
||
H.get_lmap = function() | ||
if vim.o.keymap == '' then return {} end |
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.
Do not check 'keymap' option because language mappings can be present even if 'keymap' is not used.
The change looks okay. Do you need help with writing and/or understanding tests? Edit: Accidentally closed PR, sorry. |
Hi. I am working on tests, yes. I think I more or less got it. However, I stumbled upon the next layer of issues. While the feature as expected and as we discussed with simple keymaps - like the
The behavior is expectedly underwhelming - 'iminsert' does nothing, as every key in
And in such a case the behavior was unexpected for me - with 'iminsert' enabled input was consumed, but there was no output. |
I don't quite follow here. If you are comfortable writing tests, please write it as a separate failing test (in a comment). If not - as a step for me to reproduce the issue. And if reasonable changes are needed in
If that's the case, then I think it is reasonable to filter them out in
The planned approach of what is currently in the PR looks like a strict improvement already. It should make 'mini.pick' work in situations it currently can not while preserving to work where it works now. The fact that implementation doesn't cover all new use cases is a shame, but should not block strict improvements. I'd add (concise!) comments about the limitations to the tests with possibly commented out example of where the test should ideally pass but it currently fails. I'll also ask to not overly cover this functionality with tests. I.e. no need to test all possible and impossible situations. Start with the ones outlined at the end of this comment. |
Language mappings can be present even if 'keymap' is not used.
e33a7f1
to
7a9963e
Compare
validate('<C-c>') | ||
end | ||
|
||
T['iminsert'] = new_set() |
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.
Please don't create separate set and files for such small content. All this can be a single test case under T['start']
(you can put it after 'allows overriding built-in mappings'
).
Probably something like this:
diff --git a/tests/test_pick.lua b/tests/test_pick.lua
index 54bf95b8..2e800a0d 100644
--- a/tests/test_pick.lua
+++ b/tests/test_pick.lua
@@ -893,6 +893,38 @@ T['start()']['allows overriding built-in mappings'] = function()
eq(get_picker_state().caret, 2)
end
+T['start()']['works with language mappings'] = function()
+ child.o.keymap = 'ukrainian-jcuken'
+ eq(child.o.iminsert, 1)
+
+ start_with_items({})
+ type_keys('g', 'h')
+ eq(get_picker_query(), { 'п', 'р' })
+ type_keys('<C-u>')
+
+ -- Should allow changing 'iminsert' while picker is active
+ child.o.iminsert = 0
+ type_keys('g', 'h')
+ eq(get_picker_query(), { 'g', 'h' })
+ type_keys('<C-c>')
+
+ -- Should work with custom "good" language mappings
+ child.o.keymap = ''
+ child.cmd('lmap a 1')
+ child.cmd('lmap b <char-0x1f171>')
+ child.cmd('lmap cc C')
+
+ start_with_items({})
+ type_keys('a', 'b', 'c', 'c')
+ eq(get_picker_query(), { '1', 'b', 'c', 'c' })
+ type_keys('<C-u>')
+
+ -- Should cache language mappings per picker session
+ child.cmd('lmap d 4')
+ type_keys('d')
+ eq(get_picker_query(), { 'd' })
+end
+
T['start()']['respects `window.config`'] = function()
-- As table
start({ source = { items = { 'a', 'b', 'c' } }, window = { config = { border = 'double' } } })
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.
Sure, I can minify the test set. I was following the example of other test sets, but it makes sense to view this feature scope as "small" and include it in the "start" set. Should I create multiple test functions, or condense it all into a single test, as in your example?
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.
The above test case is big enough already (and looks like covering all cases of language mappings, right?). If the tested behavior fits into "work with languge mappings", then add to it. If not - add new test case.
I think only testing of "paste" action is left, which is better done as a separate case in T['Paste']
set.
@yehorb, this takes a bit longer than initially expected. So I merged into a temporary branch to polish this up myself and later merge into |
This is now part of the
Otherwise, thank you for your time and efforts when making this! Lovely to see more Ukrainians using Neovim and contributing to its ecosystem :) |
Oh my, you are too kind. I just carved out some time to polish the feature later today and tomorrow. Now I am a bit embarrassed for leaving the feature unattended so you felt the need to step in. Oh well, looks like the perfect is the enemy of good after all. Thank you for the kind words and the opportunity to contribute. |
Please, don't be. You've done more than most users 🙏 It's just that this was already nearly finished for almost two weeks and I don't like seeing PR number go up :) |
The 'getcharstr' returns a character from the input as is, and it is not affected by the 'iminsert' option.
It is possible, however, to construct a lookup table for ':lmap' mappings and apply it to the returned character on the fly.
This successfully emulates the 'iminsert' option in the 'mini.pick' input field.
However, the 'iminsert' toggle has to be defined in 'mini.pick' configuration as a keybinding, as the standard '<C-^>' has no effect by default.
The lookup table is created only once and then cached. In my non-scientific testing, thie feature has no impact on input latency.
The feature has no effect if the 'keymap' option is not set.
I understand that 'iminsert' is a relatively niche feature to cover, and it was never mentioned in issues or discussions. While you can always change the keyboard layout, and achieve the desired effect, it feels unnatural to me. I write quite a lot in the 'iminsert' mode, and find it easier to invoke than the keyboard layout change.
I thought to raise the issue beforehand, but the implementation is quite trivial, so I decided to raise the PR outright.
I intentionally did not update the documentation or anything. If you want to move forward with the feature, I am ready and willing to follow your lead and bring the change up to standard.