which-key.nvim icon indicating copy to clipboard operation
which-key.nvim copied to clipboard

[Proposal] A better DSL for keymappings

Open I60R opened this issue 4 years ago • 2 comments

The current is bad because of the way how lua tables works: duplicate keys aren't supported, so with { ',' = { "<Leader>", mode = "n" }, ',' = { "<Leader>", mode = "v" } } only the latter would be registered.

This may be quite confusing for users not familiar enough with lua.

Moreover, nested structure encourages registering all mappings at once separately from plugins they depends on.


So, I've implemented a DSL on top of it that gives more freedom:

--- This is how I generate bufferline mappings for each mode
for n = 1, 9 do
  local function focus_nth_buffer() require('bufferline').go_to_buffer(n) end
  map ['<M-' .. n .. '>'] = { focus_nth_buffer, "Go to (" .. n .. ") buffer", remap = false, silent = true }
end
map ['<M-`>'] = { '<Cmd>BufferLinePick<CR>', "Pick buffer" }
map ['<M-Left>'] = { '<Cmd>BufferLineCyclePrev<CR>', "Previous buffer" }
map ['<M-Right>'] = { '<Cmd>BufferLineCycleNext<CR>', "Next buffer" }
map ['<M-q>'] = { '<Cmd>b # | bd #<CR>', "Close buffer" }
map:register { modes = 'nicxsot' }
--- This is how I register LSP keybindings
map ['<Leader>wl'] = { print_workspace_folders, "Workspace folders" }
map ['<Leader>wa'] = { vim.lsp.buf.add_workspace_folder, "Add workspace folder" }
map ['<Leader>wr'] = { vim.lsp.buf.remove_workspace_folder, "Remove workdspace folder" }

map ['gD'] = { vim.lsp.buf.declaration, "Declaration" }
map ['gd'] = { vim.lsp.buf.definition, "Definition" }
map ['K'] = { vim.lsp.buf.hover, "Hover" }
map ['gi'] = { vim.lsp.buf.implementation, "Implementation" }
map ['<C-k>'] = { vim.lsp.buf.signature_help, "Signature" }
map ['<Leader>D'] = { vim.lsp.buf.type_definition, "Type" }
map ['<Leader>rn'] = { vim.lsp.buf.rename, "Rename" }
map ['gr'] = { vim.lsp.buf.references, "References" }
map ['<Leader>e' ] = { vim.lsp.diagnostic.show_line_diagnostics, "Diagnostics" }
map ['[d'] = { vim.lsp.diagnostic.goto_prev, "Previous diagnostic" }
map [']d'] = { vim.lsp.diagnostic.goto_next, "Next diagnostic" }
map ['<Leader>q'] = { vim.lsp.diagnostic.set_loclist, "Set loclist"

-- Set some keybinds conditional on server capabilities
if client.resolved_capabilities.document_formatting then
  map ['<Leader>f'] = { vim.lsp.buf.formatting, "Document formatting" }
elseif client.resolved_capabilities.document_range_formatting then
  map ['<Leader>f'] = { vim.lsp.buf.range_formatting, "Range formatting" }
end

map:register { silent = true, remap = false }
--- This is how plugin installation looks like
use {
  'phaazon/hop.nvim',
  config = function()
    local hop = require('hop')
    hop.setup {
      inclusive_jump = true,
      uppercase_labels = true,
    }

    map ['F'] = { function() vim.cmd 'norm 0<CR>'; hop.hint_lines_skip_whitespace() end, "Jump to a line" }
    map ['f'] = { function() hop.hint_char1() end, "Jump to a letter" }
    map:split { modes = 'o' }

    map ['F'] = { function() hop.hint_lines_skip_whitespace(); vim.cmd 'norm zz' end, "Jump to a line and focus" }
    map ['f'] = { function() hop.hint_char1() end, "Jump to a letter" }
    map:register { modes = 'n' }
  end
}

Let me know if you would like to add something like that to your plugin

I60R avatar Nov 21 '21 14:11 I60R

This looks amazing!! I second this being added

DEliasVCruz avatar Nov 23 '21 01:11 DEliasVCruz

Upd: now it looks like this:

for n = 1, 9 do
  local function focus_nth_buffer() require('bufferline').go_to_buffer(n) end
  (map "Go to (" .. n .. ") buffer")
    .alt [n] = { focus_nth_buffer, remap = false, silent = true }
end
(map "Pick a buffer")
  .alt ['`'] = 'BufferLinePick'
(map "Previous buffer")
  .alt ['Left'] = 'BufferLineCyclePrev'
(map "Next buffer")
  .alt ['Right'] = 'BufferLineCycleNext'
(map "Close buffer")
  .alt ['q'] = 'b # | bd #'
(map "Previous buffer")
  ['<F13>'] = 'BufferLineCyclePrev'
(map "Next buffer")
  ['<F14>'] = 'BufferLineCycleNext'
map:register { as = 'cmd', modes = 'nicxsot' }

local function print_workspace_folders()
  print(vim.inspect(vim.lsp.buf.list_workspace_folders()))
end
(map "Workspace folders")
  ['<Leader>wl'] = print_workspace_folders;
(map "Add workspace folder")
  ['<Leader>wa'] = vim.lsp.buf.add_workspace_folder;
(map "Remove workdspace folder")
  ['<Leader>wr'] = vim.lsp.buf.remove_workspace_folder;

(map "Declaration")
  ['gD'] = vim.lsp.buf.declaration;
(map "Definition")
  ['gd'] = vim.lsp.buf.definition;
(map "Hover")
  ['K'] = vim.lsp.buf.hover;
(map "Implementation")
  ['gi'] = vim.lsp.buf.implementation;
(map "Signature")
  .ctrl ['k'] = vim.lsp.buf.signature_help;
(map "Type")
  ['<Leader>D'] = vim.lsp.buf.type_definition;
(map "Rename")
  ['<Leader>rn'] = vim.lsp.buf.rename;
(map "References")
  ['gr'] = vim.lsp.buf.references;
(map "Diagnostics")
  ['<Leader>e' ] = vim.lsp.diagnostic.show_line_diagnostics;
(map "Previous diagnostic")
  ['[d'] = vim.lsp.diagnostic.goto_prev;
(map "Next diagnostic")
  [']d'] = vim.lsp.diagnostic.goto_next;
(map "Set loclist")
  ['<Leader>q'] = vim.lsp.diagnostic.set_loclist;

-- Set some keybinds conditional on server capabilities
if client.resolved_capabilities.document_formatting then
  (map "Document formatting")
    ['<Leader>f'] = vim.lsp.buf.formatting;
elseif client.resolved_capabilities.document_range_formatting then
  (map "Range formatting")
    ['<Leader>f'] = vim.lsp.buf.range_formatting;
end

map:register { silent = true, remap = false }

use {
  'phaazon/hop.nvim',
  config = function()
    local hop = require('hop')
    hop.setup {
      inclusive_jump = true,
      uppercase_labels = true,
    };
    (map "Jump to a line")
      ['F'] = function() vim.cmd 'norm 0<CR>'; hop.hint_lines_skip_whitespace() end
    (map "Jump to a letter")
      ['f'] = function() hop.hint_char1() end
    map:split { modes = 'o' };

    (map "Jump to a line and focus on it")
      ['F'] = function() hop.hint_lines_skip_whitespace(); vim.cmd 'norm zz' end
    (map "Jump to a letter")
      ['f'] = function() hop.hint_char1() end
    map:register { modes = 'n' }
  end
}

I60R avatar Dec 20 '21 23:12 I60R