liuchengxu / vim-which-key

:tulip: Vim plugin that shows keybindings in popup
https://liuchengxu.github.io/vim-which-key/
MIT License
1.93k stars 65 forks source link

Support Dictionary-function elements within the description dictionary #209

Closed rene-descartes2021 closed 2 years ago

rene-descartes2021 commented 2 years ago

In writing a C# layer for space-vim, I wanted to overlap/replace/augment the '+lsp' key mappings.

EDIT2: For example, in space-vim the hotkey sequence ' lgd' is '+lsp/+goto/definition' using the existing '+lsp' keymappings using the LSP Client as configured in space-vim, but in .cs files ' lgd' should use OmniSharp-vim (in the C# space-vim layer) to do the same. OmniSharp-vim isn't an LSP Client (it doesn't use the language server protocol (LSP)) but does many of the same things with a language server*, so it makes sense to use the same hotkeys.

The most convenient way I thought to do this was to use a dictionary-function as an element within the vim-which-key description dictionary (see :help dictionary-function). May have pros/cons compared to the technique of BufEnter/BufLeave in #132 and perhaps #48.

The dictionary-function can return a different mapping depending on e.g. the &filetype.

Example:

" <space-vim>/layers/+lang/csharp/config.vim

" nickspoons/omnisharp-vim: {
  " A `Dictionary-function` containing vim-which-key mappings
  function! s:SetOmnisharpHotkeysInWhichkey()
    let s:keep_lsp_mapping = get(s:, 'keep_lsp_mapping', deepcopy(g:spacevim#map#leader#desc['l']))

    let l:omnisharp_map = {
    \ 'name': '+omnisharp',
    \ '.': ['<Plug>(omnisharp_code_action_repeat)', 'repeat'],
    \ 'a': ['<Plug>(omnisharp_code_actions)'      , 'code-action'],
    \ 'c': ['<Plug>(omnisharp_global_code_check)' , 'global-code-check'],
    \ 'f': ['<Plug>(omnisharp_code_format)'       , 'formatting'],
    "\ 'h': ['spacevim#lang#util#Hover()'         , 'hover'],
    \ 'h': ['<Plug>(omnisharp_signature_help)'    , 'hover'],
    \ 'H': ['<Plug>(omnisharp_highlight)'         , 'highlight'],
    \ 'r': ['<Plug>(omnisharp_find_usages)'       , 'references'],
    \ 'R': ['<Plug>(omnisharp_rename)'            , 'rename'],
    "\ 's': ['spacevim#lang#util#DocumentSymbol()', 'document-symbol'],
    \ 's': ['<Plug>(omnisharp_documentation)'     , 'document-symbol'],
    "\ 'S': ['spacevim#lang#util#WorkspaceSymbol()', 'workspace-symbol'],
    \ 'S': ['<Plug>(omnisharp_find_symbol)'       , 'workspace-symbol'],
    \ 'g': {
      \ 'name': '+goto',
      \ 'd': ['<Plug>(omnisharp_go_to_definition)', 'definition'],
      \ 't': ['<Plug>(omnisharp_find_type)'       , 'type-definition'],
      \ 'i': ['<Plug>(omnisharp_find_implementations)', 'implementation'],
      \ 's': ['<Plug>(omnisharp_find_symbol)'     , 'symbol'],
      \ },
    \ 'p': {
      \ 'name': '+preview',
      \ 'd': ['<Plug>(omnisharp_preview_definition)'     , 'definition'],
      \ 'i': ['<Plug>(omnisharp_preview_implementations)', 'implementation'],
      \ },
    \ 'b': {
      \ 'name': '+build',
      \ 'p': ['<Plug>(omnisharp_build_project)'      , 'build-project'],
      \ 's': ['<Plug>(omnisharp_build_solution)'     , 'build-solution'],
      \ 'd': ['<Plug>(omnisharp_debug_project)'      , 'debug-project'],
      \ 'v': ['<Plug>(omnisharp_create_debug_config)', 'create-vimspector-config'],
      \ },
    \ }

    if &filetype ==# 'cs'
      return l:omnisharp_map
    else
      return s:keep_lsp_mapping
    endif
  endfunction

  " Integrate OmniSharp hotkeys with vim-which-key
  if spacevim#load('which-key')
    let s:leader = g:spacevim#map#leader#desc
    let s:leader['l'] = function('s:SetOmnisharpHotkeysInWhichkey')
  endif
" }

EDIT: Very simple changes in vim-which-key. V had to be capitalized so as lower-case variable names can't refer to a funcref. And if V was a funcref it had to be called with V() and the result of that dictionary-function assigned to the right location in s:runtime for processing (calculating layout and etc).

TODO: I can put a simpler example in the wiki and make a new FAQ entry in the README.md.

liuchengxu commented 2 years ago

TODO: I can put a simpler example in the wiki and make a new FAQ entry in the README.md.

That can be really great!

@rene-descartes2021 Thanks for the PR! Looks pretty good to me!

rene-descartes2021 commented 2 years ago

Ok a smaller example would be:

set nocompatible

call plug#begin('~/.vim/plugged')
Plug 'liuchengxu/vim-which-key'
call plug#end()

let mapleader = "\<Space>"

nnoremap <silent> <leader>      :<c-u>WhichKey '<Space>'<CR>
nnoremap <silent> <localleader> :<c-u>WhichKey  ','<CR>

call which_key#register('<Space>', "g:which_key_map")

let g:which_key_map = {}

let g:which_key_map.c = { 'name' : '+coc' }
let g:which_key_map.c.g = { 'name' : '+goto'}
let g:which_key_map.c.g.d = [ '<Plug>(coc-definition)', "coc-definition"]

let g:which_key_map.g = {'name': '+git'}
let g:which_key_map.g = ['Gstatus', 'git-status']

" ~/.vim/ftplugin/cs.vim
" A `Dictionary-function` containing vim-which-key mappings
function! s:MyHotkeyDictionaryFunction()
  let s:keep_lsp_mapping = get(s:, 'keep_lsp_mapping', deepcopy(g:which_key_map['g']))

  if &filetype ==# 'cs'
    let l:key_map = {
    \ 'name': '+goto',
    \ 'd': ['<Plug>(omnisharp_go_to_definition)', 'definition'],
    \ 'f': {
      \ 'name': '+find',
      \ 't': ['<Plug>(omnisharp_find_type)'       , 'type-definition'],
      \ 'i': ['<Plug>(omnisharp_find_implementations)', 'implementation'],
      \ 's': ['<Plug>(omnisharp_find_symbol)'     , 'symbol'],
      \ },
    \ }
    return l:key_map
  else
    return s:keep_lsp_mapping
  endif
endfunction

" Integrate dictionary-function with dictionary used in earlier which_key#register()
let s:leader = g:which_key_map
let s:leader['g'] = function('s:MyHotkeyDictionaryFunction')

I can't edit the wiki, don't have permission, so I added an entry in the README.md FAQ.

liuchengxu commented 2 years ago

Merged, thank you! And now you can edit the wiki @rene-descartes2021 :D