_:
let
  map = mode: key: action: {
    inherit mode key action;
  };
  luamap =
    mode: key: action:
    map mode key "<cmd>lua ${action}<cr>";
  telescope = "require('telescope.builtin')";
in
{
  programs.nixvim.keymaps = [
    (map "" "<f2>" "<cmd>Lspsaga rename<cr>")
    (map "" "<leader>o" "<cmd>Lspsaga outline<cr>")
    (map "" "<leader>." "<cmd>Lspsaga code_action<cr>")

    # splitting
    (map "n" "<leader>s" "<cmd>vertical sb<cr>")

    # closing
    (map "n" "<leader>w" "<cmd>BufferClose<cr>") # single buffer
    (map "n" "<leader>cb" "<cmd>BufferClose<cr>") # single buffer

    (map "n" "<leader>ct" "<cmd>CloseBuffer<cr>") # buffer or extra tab
    (map "n" "<leader>q" "<cmd>CloseBuffer<cr>") # buffer or extra tab

    (map "n" "<leader>co" "<cmd>silent! BufferCloseAllButVisible<cr>") # other buffers
    (map "n" "<leader>cl" "<cmd>silent! BufferCloseBuffersLeft<cr>") # other buffers (left)
    (map "n" "<leader>cr" "<cmd>silent! BufferCloseBuffersRight<cr>") # other buffers (right)

    # moving
    (map "n" "mL" "<cmd>BufferMovePrevious<cr>") # left
    (map "n" "mr" "<cmd>BufferMoveNext<cr>") # right
    (map "n" "m0" "<cmd>BufferMoveStart<cr>") # start
    (map "n" "m$" "<cmd>BufferMoveEnd<cr>") # end

    (map "n" "<leader>jb" "<cmd>BufferPick<cr>") # jump to tab

    # jumplist
    # (map "n" "<leader><Tab>" "<C-o>")
    # (map "n" "<leader><S-Tab>" "<C-i>")
    (luamap "n" "<leader>r" "${telescope}.jumplist()")

    # pickers
    (luamap "n" "<leader><leader>" "${telescope}.find_files()")
    (luamap "n" "<leader>f" "${telescope}.live_grep()")
    (luamap "n" "<leader>t" "${telescope}.lsp_document_symbols()")
    (luamap "n" "<leader>T" "${telescope}.lsp_dynamic_workspace_symbols()")
    # last used pickers/searches
    (luamap "n" "<leader>h" "${telescope}.pickers()")
    # open buffers
    (luamap "n" "<leader>b" "${telescope}.buffers({sort_mru = true})")

    # diagnostics
    (map "n" "<leader>d" "<cmd>Trouble diagnostics toggle filter.buf=0<cr>")
    (map "n" "<leader>ad" "<cmd>Trouble diagnostics toggle<cr>")
    (luamap "n" "]d"
      "vim.diagnostic.goto_next({ severity = vim.diagnostic.severity.ERROR, wrap=true })"
    )
    (luamap "n" "[d"
      "vim.diagnostic.goto_prev({ severity = vim.diagnostic.severity.ERROR, wrap=true })"
    )
    (luamap "n" "]w" "vim.diagnostic.goto_next({ wrap=true })")
    (luamap "n" "[w" "vim.diagnostic.goto_prev({ wrap=true })")

    # docs with control-d just like in autocomplete
    (map "n" "<C-d>" "K")

    # expand macro
    (map "n" "<leader>em" "<cmd>RustLsp expandMacro<cr>")

    # easier quitting etc
    (map "ca" "W" "w")
    (map "ca" "X" "x")

    (map "ca" "Q" "CloseBuffer")
    (map "ca" "q" "CloseBuffer")

    # navigation
    (map "" "<leader><Left>" "<C-w><Left>")
    (map "" "<leader><Right>" "<C-w><Right>")
    (map "" "<leader><Up>" "<C-w><Up>")
    (map "" "<leader><Down>" "<C-w><Down>")
    (map "" "<leader><Down>" "<C-w><Down>")

    # {
    # key = "/";
    # action = "<cmd>lua require('spectre').open_file_search({select_word=true})<CR>";
    # }

    (map "n" "t" "<cmd>Neotree toggle<cr>")

    # tab for indent/dedent
    (map "n" "<tab>" ">>_")
    (map "n" "<S-tab>" "<<_")
    (map "i" "<S-tab>" "<c-d>")
    (map "v" "<tab>" ">gv")
    (map "v" "<S-tab>" "<gv")

    # to avoid many typos
    (map "n" ";" ":")
  ];
}
