A plugin that makes Neovim more friendly to non-English input methods 🤝
vim.g.mapleader
and map.g.localleader
before langmapper.setup()
;Examples of CLI utilities:
- im-select for Mac and Windows
- xkb-switch for Linux
With Lazy.nvim:
return {
'Wansmer/langmapper.nvim',
lazy = false,
priority = 1, -- High priority is needed if you will use `autoremap()`
config = function()
require('langmapper').setup({--[[ your config ]]})
end,
}
With Packer.nvim:
use({
'Wansmer/langmapper.nvim',
config = function()
require('langmapper').setup({--[[ your config ]]})
end,
})
After all the contents of your init.lua
(optional):
-- code
require('langmapper').automapping({ global = true, buffer = true })
-- end of init.lua
First, make sure you have a langmap
configured. Langmapper only handles key
mappings. All other movement commands depend on the langmap
.
Set up your layout
in config, set hack_keymap
to true
and load Langmapper
the first of the sheet of plugins, then call langmapper.setup(opts)
.
Under such conditions, all subsequent calls to vim.keymap.set
,
vim.keymap.del
, vim.api.nvim_(buf)_set_keymap
and
vim.api.nvim_(buf)_del_keymap
will be wrapped with a special function,
which will automatically translate mappings and register them.
This means that even in the case of lazy-loading, the mapping setup will still be processed and the translated mapping will be registered for it.
If you need to handle built-in and vim script mappings too, call the
langmapper.automapping({ buffer = false })
function at the very end of
your init.lua
. (buffer to false
, because nvim_buf_set_keymap
already hacked 😎)
Set up your layout
in config, set hack_keymap
to false,
and call langmapper.setup(opts)
.
-- this function completely repeats contract of vim.keymap.set
local map = require('langmapper').map
map('n', '<Leader>e', '<Cmd>Neotree toggle focus<Cr>')
-- Neo-tree config.
-- It will return a table with 'translated' keys and same values.
local map = require('langmapper.utils')
local window_mappings = mapper.trans_dict({
['o'] = 'open',
['sg'] = 'split_with_window_picker',
['<leader>d'] = 'copy',
})
Add langmapper.autoremap({ global = true, buffer = true })
to the end of your
init.lua
.
It will autotranslate all registered mappings from nvim_get_keymap()
and
nvim_buf_get_keymap()
.
But it cannot handle mappings of lazy loaded plugins.
NOTE: all keys, that you're using in
keys = {}
inlazy.nvim
also will be translated.
folke/which-key.nvim
which-key
uses nvim_feedkeys
to execute the sequence entered by the user.
This imposes restrictions on the execution of commands related to operators,
text objects and movements, since nvim_feedkeys
does not handle the value
of your vim.opt.langmap
. Therefore, the entered sequence must be
translated back into English characters.
Here example how to integrate Langmapper to LazyNvim.
Some other plugins that work with user input can also be hacked in this way. You can find some hacks or share your own it this discussion.
Usage:
local langmapper = require('langmapper')
langmapper.map(...)
langmapper.automapping(...)
-- etc
automapping()
Gets the output of nvim_get_keymap
for all modes listed in the
automapping_modes
, and sets the translated mappings using nvim_feedkeys
.
Then sets event handlers { 'BufWinEnter', 'LspAttach' }
to do the same with
outputting nvim_buf_get_keymap
for each open buffer.
Must be called at the very end of init.lua
, after all plugins have been loaded
and all key bindings have been set.
This function also handles mappings made via vim script.
Does not handle mappings for lazy-loaded plugins. To avoid it, see
hack_keymap
.
NOTE: If you use
hack_keymap
, there are only one reason to use this function it is auto-handling built-in mappings (e.g., for netrw, like 'gx') and if you have mappings (or plugins with mappings) on vim script.
---@param opts {global=boolean|nil, buffer=boolean|nil}
function M.automapping(opts)
map()/del()
Wrappers of vim.keymap.set
\ vim.keymap.del
with same contract.
map()
- Sets the given lhs
, then translates it to the configured input
methods, and maps it with the same options.
E.g.:
map('i', 'jk', '<Esc>')
will execute vim.keymap.set('i', 'jk', '<Esc>)
and vim.keymap.set('i', 'ол', <Esc>)
.
map('n', '<leader>a', ':echo 123')
will execute vim.keymap.set('n', '<leader>a', ':echo 123')
and vim.keymap.set('n', '<leader>ф', ':echo 123')
.
lhs
with <Plug>
, <Sid>
and <Snr>
will not translate and will be mapped as is.
del()
works in the same way, but with mappings removing. Also, del()
is
wrapped with a safetely call (pcall
) to avoid errors on duplicate characters
(helpful when usingnvim-cmp
).
---@param mode string|table Same mode short names as |nvim_set_keymap()|
---@param lhs string Left-hand side |{lhs}| of the mapping.
---@param rhs string|function Right-hand side |{rhs}| of the mapping. Can also be a Lua function.
---@param opts table|nil A table of |:map-arguments|.
function M.map(mode, lhs, rhs, opts)
---@param mode string|table Same mode short names as |nvim_set_keymap()|
---@param lhs string Left-hand side |{lhs}| of the mapping.
---@param opts table|nil A table of optional arguments:
--- - buffer: (number or boolean) Remove a mapping from the given buffer.
--- When "true" or 0, use the current buffer.
function M.del(mode, lhs, opts)
hack_get_keymap()
Hack get_keymap
functions. See :h nvim_set_keymap()
and :h nvim_buf_set_keymap()
.
After this hack, nvim_set_keymap/nvim_buf_set_keymap
will return only
latin mappings (without translated mappings). Very useful for work with
nvim-cmp
(see #8)
Usage:
local langmapper = require("langmapper")
langmapper.setup()
langmapper.hack_get_keymap()
Original keymap's functions, that were wrapped with translation functions if
hack_keymap
is true
:
-- When you don't need some mapping to be translated. For example, I don't translate `jk`.
`original_set_keymap()` -- vim.api.nvim_set_keymap
`original_buf_set_keymap() -- vim.api.nvim_buf_set_keymap
`original_del_keymap()` -- vim.api.nvim_del_keymap
`original_buf_del_keymap()` -- vim.api.nvim_buf_del_keymap
`put_back_keymap()` -- Set original functions back
NOTE: No original
vim.keymap.set/del
becausenvim_set/del_keymap
is used inside
Another functions-wrappers with translates and same contracts:
`wrap_nvim_set_keymap()`
`wrap_nvim_del_keymap()`
`wrap_nvim_buf_set_keymap()`
`wrap_nvim_buf_del_keymap()`
translate_keycode()
Translate 'lhs' to 'to_lang' layout. If in 'to_lang' layout no specified
default_layout
, uses global default_layout
To translate back to English
characters, set 'to_lang' to default
and pass the name of the layout to
translate from as the third parameter.
---@param lhs string Left-hand side |{lhs}| of the mapping.
---@param to_lang string Name of layout or 'default' if need translating back to English layout
---@param from_lang? string Name of layout.
---@return string
function M.translate_keycode(lhs, to_lang, from_lang)
Example:
local utils = require('langmapper.utils')
local keycode = '<leader>gh'
local tr_keycode = utils.translate_keycode(keycode, 'ru') -- '<leader>пр'
trans_dict()
Translates each key of table for all layouts in use_layouts
option
(recursive).
---@param dict table Dict-like table
---@return table
function M.trans_dict(dict)
Example:
local keycode_dict = { ['s'] = false, ['<leader>'] = { ['d'] = 'copy' }, ['<S-TAB>'] = 'prev_source' }
local result = utils.trans_dict(keycode_dict)
-- {
-- ['s'] = false,
-- ['ы'] = false,
-- ['<leader>'] = {
-- ['d'] = 'copy',
-- ['в'] = 'copy',
-- },
-- ['<S-TAB>'] = 'prev_source',
-- }
trans_list()
Translates each value of the list for all layouts in use_layouts
option.
Non-string value is ignored. Translated value will be added to the end.
---@param dict table Dict-like table
---@return table
function M.trans_list(dict)
Example:
local keycode_list = { '<leader>d', 'ab', '<S-Tab>' }
local translated = utils.trans_list(keycode_list)
-- { '<leader>d', 'ab', '<S-Tab>', '<leader>в', 'фи' }