Skip to content

Commit

Permalink
fix(fallback): make fallback work with buffer-local mappings (#483)
Browse files Browse the repository at this point in the history
* fix(fallback): make fallback work with buffer-local mappings

* fix: always check global keymaps

* fix: empty dictionary check on fallback keymaps

---------

Co-authored-by: Liam Dyer <[email protected]>
  • Loading branch information
folke and Saghen authored Dec 10, 2024
1 parent b368f77 commit 8b553f6
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 12 deletions.
8 changes: 6 additions & 2 deletions lua/blink/cmp/keymap/apply.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ function apply.keymap_to_current_buffer(keys_to_commands)
for key, commands in pairs(keys_to_commands) do
if #commands == 0 then goto continue end

local fallback = require('blink.cmp.keymap.fallback').wrap('i', key)

apply.set('i', key, function()
for _, command in ipairs(commands) do
-- special case for fallback
if command == 'fallback' then
return require('blink.cmp.keymap.fallback').run_non_blink_keymap('i', key)
return fallback()

-- run user defined functions
elseif type(command) == 'function' then
Expand All @@ -42,11 +44,13 @@ function apply.keymap_to_current_buffer(keys_to_commands)
end
if not has_snippet_command or #commands == 0 then goto continue end

local fallback = require('blink.cmp.keymap.fallback').wrap('s', key)

apply.set('s', key, function()
for _, command in ipairs(keys_to_commands[key] or {}) do
-- special case for fallback
if command == 'fallback' then
return require('blink.cmp.keymap.fallback').run_non_blink_keymap('s', key)
return fallback()

-- run user defined functions
elseif type(command) == 'function' then
Expand Down
50 changes: 40 additions & 10 deletions lua/blink/cmp/keymap/fallback.lua
Original file line number Diff line number Diff line change
@@ -1,29 +1,57 @@
local fallback = {}

--- Gets the first non blink.cmp keymap for the given mode and key
--- Add missing types. Remove when fixed upstream
---@class vim.api.keyset.keymap
---@field lhs string
---@field mode string
---@field rhs? string
---@field lhsraw? string
---@field buffer? number

--- Gets the non blink.cmp global keymap for the given mode and key
--- @param mode string
--- @param key string
--- @return vim.api.keyset.keymap | nil
function fallback.get_non_blink_mapping_for_key(mode, key)
function fallback.get_non_blink_global_mapping_for_key(mode, key)
local normalized_key = vim.api.nvim_replace_termcodes(key, true, true, true)

-- get buffer local and global mappings
local mappings = vim.api.nvim_buf_get_keymap(0, mode)
vim.list_extend(mappings, vim.api.nvim_get_keymap(mode))
-- get global mappings
local mappings = vim.api.nvim_get_keymap(mode)

for _, mapping in ipairs(mappings) do
local mapping_key = vim.api.nvim_replace_termcodes(mapping.lhs, true, true, true)
if mapping_key == normalized_key and mapping.desc ~= 'blink.cmp' then return mapping end
end
end

--- Runs the first non blink.cmp keymap for the given mode and key
--- Gets the non blink.cmp buffer keymap for the given mode and key
--- @param mode string
--- @param key string
--- @return string | nil
function fallback.run_non_blink_keymap(mode, key)
local mapping = fallback.get_non_blink_mapping_for_key(mode, key) or {}
--- @return vim.api.keyset.keymap?
function fallback.get_non_blink_buffer_mapping_for_key(mode, key)
local ret = vim.fn.maparg(key, mode, false, true) --[[@as vim.api.keyset.keymap]]
if ret and ret.buffer == 0 then return end
if ret and ret.desc and ret.desc == 'blink.cmp' then return end
return ret.lhs ~= nil and ret or nil
end

--- Returns a function that will run the first non blink.cmp keymap for the given mode and key
--- @param mode string
--- @param key string
--- @return fun(): string?
function fallback.wrap(mode, key)
local buffer_mapping = fallback.get_non_blink_buffer_mapping_for_key(mode, key)
return function()
local mapping = buffer_mapping or fallback.get_non_blink_global_mapping_for_key(mode, key)
if mapping then return fallback.run_non_blink_keymap(mapping, key) end
end
end

--- Runs the first non blink.cmp keymap for the given mode and key
--- @param mapping vim.api.keyset.keymap
--- @param key string
--- @return string | nil
function fallback.run_non_blink_keymap(mapping, key)
-- TODO: there's likely many edge cases here. the nvim-cmp version is lacking documentation
-- and is quite complex. we should look to see if we can simplify their logic
-- https://github.com/hrsh7th/nvim-cmp/blob/ae644feb7b67bf1ce4260c231d1d4300b19c6f30/lua/cmp/utils/keymap.lua
Expand All @@ -36,7 +64,9 @@ function fallback.run_non_blink_keymap(mode, key)
end

local expr = mapping.callback()
if mapping.replace_keycodes == 1 then expr = vim.api.nvim_replace_termcodes(expr, true, true, true) end
if type(expr) == 'string' and mapping.replace_keycodes == 1 then
expr = vim.api.nvim_replace_termcodes(expr, true, true, true)
end
return expr
elseif mapping.rhs then
local rhs = vim.api.nvim_replace_termcodes(mapping.rhs, true, true, true)
Expand Down

0 comments on commit 8b553f6

Please sign in to comment.