lyokha / vim-xkbswitch

vim plugin for automatic keyboard layout switching in insert mode
MIT License
481 stars 23 forks source link

mapping duplicate for script local mapping #61

Closed Konfekt closed 1 year ago

Konfekt commented 1 year ago

If the original mapping uses, say, a script local function nnoremap ; :<c-u>call <sid>function()<cr>, then the duplicate mapping, say ö, does not work because the script local SID is no longer correct that where the duplicate mapping is defined. Could patch 8.2.4861 resolve this?

lyokha commented 1 year ago

Not sure that I understood what you mean. It works for me. I use NVIM v0.8.1, but it also worked when I used vim 8.

If I put in init.vim/.vimrc

fun! <SID>simple_fun()
    echoerr "simple_fun"
endfun

imap <silent> ;  <C-o>:call <SID>simple_fun()<CR>

and then type in a buffer ; or ж (this Russian letter is placed on the same key as ;), I get the simple_fun mesage in both cases. To check imaps, type

:imap ;
i   ;          <C-O>:call<SNR>4_simple_fun<CR>
imap ж
i  ж        @<C-O>:call<SNR>4_simple_fun<CR>

Flag @ means that the mapping is buffer-local, this shouldn't be a problem.

You used <c-u> in the mapping, did you mean to use <c-o> instead?

Konfekt commented 1 year ago

Hello, and thank you for assisting.

With Vim 9.0.813 and https://github.com/kana/vim-arpeggio and vimrc including

let g:XkbSwitchEnabled = 1
let g:XkbSwitchIMappings = ['ru']

call arpeggio#load()
Arpeggio inoremap <silent> jk          <Esc>

Then imap л shows

i  л            @<SNR>366_chord_key('k')

and typing л gives

<SNR>366_chord_key('k')

In more detail,

echo maparg('л', 'i', 0, 1)

gives

{'lhs': 'л', 'mode': 'i', 'expr': 0, 'sid': 301, 'lnum': 512, 'noremap': 0, 'nowait': 0, 'rhs': '<SNR>366_chord_key(''k'')', 'lhsraw': 'л', 'abbr': 0, 'script': 0, 'buffer': 1, 'silent': 1, 'mode_bits': 16, 'scriptversion': 1}
lyokha commented 1 year ago

I guess, to make this work correctly the mapping should read

i  л            @<C-O>:call <SNR>366_chord_key('k')<CR>

which means that the original (not yet translated) mapping should read

i  k            <C-O>:call <SNR>366_chord_key('k')<CR>

Note that it has <C-O>: before call and <CR> at the end.

Konfekt commented 1 year ago

To be honest, I think the problem is that

call arpeggio#load()
Arpeggio inoremap <silent> jk          <Esc>

is only fired after VimEnter which might be too late for vim-xkbswitch?

I wonder why it is not an <expr> mapping. Maybe <c-r>= is used but not shown somehow.

Konfekt commented 1 year ago

Not true. The original mapping, as expected, reads

{'lhs': 'k', 'mode': 'i', 'expr': 1, 'sid': 366, 'lnum': 205, 'noremap': 0, 'nowait': 0, 'rhs': '<SID>chord_key(''k'')', 'lhsraw': 'k', 'abbr': 0, 'script': 0, 'buffer': 0, 'silent': 0, 'mode_bits': 16, 'scriptversion': 1}

That is, has the expr flag set. Maybe vim-xkbswitch does not yet take account of it?

Here's a mapping to save a copy of mapping with all its flags

function! SaveMap(mode, from, to) abort
  let from = maparg(a:from, a:mode, 0, 1)
  if empty(from)
    exe a:mode . "noremap " . a:to . a:from
  else
    exe
          \ a:mode . (from.noremap ? 'nore' : '') . 'map ' . 
          \ (from.silent ? '<silent>' : '') .
          \ (from.buffer ? '<buffer>' : '') .
          \ (from.nowait ? '<nowait>' : '') .
          \ (from.expr   ? '<expr>' : '') .
          \ substitute(a:to, '\c<sid>', '<SNR>' . from.sid . '_', 'g') . ' ' . substitute(from.rhs, '\c<sid>', '<SNR>' . from.sid . '_', 'g')
  endif
endfunction

Say,

call SaveMap("i", "<Space>", "<plug>(Space)")

would then copy the mapping of <Space> to <plug>(Space).

lyokha commented 1 year ago

Try to replace line 509 in plugin/xkbswitch.vim from

                    \ '^[[:blank:]*&@]*[a-zA-Z][a-zA-Z0-9_#]*(.*)$') != -1 ?

to

                    \ '^[[:blank:]*&@]*\(<SNR>\|[a-zA-Z]\)[a-zA-Z0-9_#]*(.*)$') != -1 ?

and say if it works for you.

lyokha commented 1 year ago

This should work, but unexpectedly. It will print k in Russian layout too, as the value of the expression could not be translated. I tested this. Any thoughts how this can be fixed?

lyokha commented 1 year ago

I have a solution for Arpeggio which does not use duplicate mappings from vim-xkbswitch. Just add

Arpeggio inoremap <silent> ол          <Esc>

into .vimrc, and this just works!

But your finding that <SNR> is not translated as <expr> in duplicate mappings of vim-xkbswitch is really good, and I am going to commit the related change.

lyokha commented 1 year ago

SaveMap()

Unfortunately, the plugin cannot use this because it simply does not know all the mappings declared in .vimrc and other plugins. Instead, it reads imaps from output of command :imap where the original information about mappings is partially lost. Therefore, it must use some heuristics to restore the original declarations. Particularly, to guess that the original mapping was <expr>, it merely checks that the mapping's value is a function call, i.e. it starts from a letter allowed for the functions ([a-zA-Z]) and ends withs something in brackets ((.*)). This did not count for <SNR> functions, nor counted this for non-function-like expressions. I am going to add <SNR>, but non-finction-like expressions are not feasible to be guessed robustly from the output of :imap, alas.

The <SNR> fix won't help you with Arpeggio because duplicate mappings translate only keys, not values of the mappings. But solution with manual Arpeggio translation works well, and it looks to me good enough in the case of declarations of multiple keys at once such as Arpeggio mappings.

Konfekt commented 1 year ago

Thank you for the fix and the explanations!