Hyprland-dotfiles/nvim/lua/modules/utils/keymap.lua
2024-07-21 02:51:17 -04:00

206 lines
4.8 KiB
Lua

local M = {}
---Shortcut for `nvim_replace_termcodes`.
---@param keys string
---@return string
local function termcodes(keys)
return vim.api.nvim_replace_termcodes(keys, true, true, true)
end
---Returns if two key sequence are equal or not.
---@param a string
---@param b string
---@return boolean
local function keymap_equals(a, b)
return termcodes(a) == termcodes(b)
end
---Get map
---@param mode string
---@param lhs string
---@return table
local function get_map(mode, lhs)
for _, map in ipairs(vim.api.nvim_buf_get_keymap(0, mode)) do
if keymap_equals(map.lhs, lhs) then
vim.api.nvim_buf_del_keymap(0, mode, lhs)
return {
lhs = map.lhs,
rhs = map.rhs or "",
expr = map.expr == 1,
callback = map.callback,
noremap = map.noremap == 1,
script = map.script == 1,
silent = map.silent == 1,
nowait = map.nowait == 1,
buffer = true,
}
end
end
for _, map in ipairs(vim.api.nvim_get_keymap(mode)) do
if keymap_equals(map.lhs, lhs) then
vim.api.nvim_del_keymap(mode, lhs)
return {
lhs = map.lhs,
rhs = map.rhs or "",
expr = map.expr == 1,
callback = map.callback,
noremap = map.noremap == 1,
script = map.script == 1,
silent = map.silent == 1,
nowait = map.nowait == 1,
buffer = false,
}
end
end
return {
lhs = lhs,
rhs = lhs,
expr = false,
callback = nil,
noremap = true,
script = false,
silent = true,
nowait = false,
buffer = false,
}
end
---Returns the function constructed from the passed keymap object on call of
---which the original keymapping will be executed.
---@param map table keymap object
---@return function
local function get_fallback(map)
return function()
local keys, fmode
if map.expr then
if map.callback then
keys = map.callback()
else
keys = vim.api.nvim_eval(map.rhs)
end
elseif map.callback then
map.callback()
return
else
keys = map.rhs
end
keys = termcodes(keys)
fmode = map.noremap and "in" or "im"
vim.api.nvim_feedkeys(keys, fmode, false)
end
end
-- Amends a mapping (i.e., allows fallback when certain conditions are met)
---@param cond string
---@param mode string
---@param lhs string
---@param rhs function
---@param opts? table
local function amend(cond, mode, lhs, rhs, opts)
local map = get_map(mode, lhs)
local fallback = get_fallback(map)
local options = vim.deepcopy(opts) or {}
options.desc = table.concat({
"[" .. cond,
(options.desc and ": " .. options.desc or ""),
"]",
(map.desc and " / " .. map.desc or ""),
})
vim.keymap.set(mode, lhs, function()
rhs(fallback)
end, options)
end
-- Completely replace a mapping
---@param mode string
---@param lhs string
---@param rhs string
---@param opts? table
---@param buf? boolean|number
local function replace(mode, lhs, rhs, opts, buf)
get_map(mode, lhs)
local options = vim.deepcopy(opts) or {}
if buf and type(buf) == "number" then
vim.api.nvim_buf_set_keymap(buf, mode, lhs, rhs, options)
else
vim.api.nvim_set_keymap(mode, lhs, rhs, options)
end
end
---Amend the existing keymap.
---@param cond string
---@param mode string | string[]
---@param lhs string
---@param rhs function
---@param opts? table
local function modes_amend(cond, mode, lhs, rhs, opts)
if type(mode) == "table" then
for _, m in ipairs(mode) do
amend(cond, m, lhs, rhs, opts)
end
else
amend(cond, mode, lhs, rhs, opts)
end
end
---Replace the existing keymap.
---@param mode string | string[]
---@param lhs string
---@param rhs string
---@param opts? table
---@param buf? boolean|number
local function modes_replace(mode, lhs, rhs, opts, buf)
if type(mode) == "table" then
for _, m in ipairs(mode) do
replace(m, lhs, rhs, opts, buf)
end
else
replace(mode, lhs, rhs, opts, buf)
end
end
---Amend the existing keymap.
---@param cond string
---@param global_flag string
---@param mapping table<string, map_rhs>
function M.amend(cond, global_flag, mapping)
for key, value in pairs(mapping) do
local modes, keymap = key:match("([^|]*)|?(.*)")
if type(value) == "table" then
local rhs = value.cmd
local options = value.options
modes_amend(cond, vim.split(modes, ""), keymap, function(fallback)
if _G[global_flag] then
local fmode = options.noremap and "in" or "im"
vim.api.nvim_feedkeys(termcodes(rhs), fmode, false)
else
fallback()
end
end, options)
end
end
end
---Replace the existing keymap.
---@param mapping table<string, map_rhs>
function M.replace(mapping)
for key, value in pairs(mapping) do
local modes, keymap = key:match("([^|]*)|?(.*)")
if type(value) == "table" then
local rhs = value.cmd
local options = value.options
local buffer = value.buffer
modes_replace(vim.split(modes, ""), keymap, rhs, options, buffer)
elseif value == "" or value == false then
for _, m in ipairs(vim.split(modes, "")) do
get_map(m, keymap)
end
end
end
end
return M