Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ use {
map_bs = true, -- map the <BS> key
map_c_h = false, -- Map the <C-h> key to delete a pair
map_c_w = false, -- map <c-w> to delete a pair if possible
map_pair = true, -- map pair keys automatically
}
```

Expand Down Expand Up @@ -370,6 +371,34 @@ require("nvim-autopairs").get_rules("'")[1].not_filetypes = { "scheme", "lisp" }
require("nvim-autopairs").get_rules("'")[1]:with_pair(cond.not_after_text("["))
```

### Manual Key Mapping

If you want to manually control key mappings instead of letting nvim-autopairs automatically map them, you can use the `map_pair` configuration option and the `get_key_handler` API:

```lua
-- Setup nvim-autopairs without automatic key mappings
require('nvim-autopairs').setup({
map_pair = false, -- disable automatic key mapping for pairs
map_cr = false, -- disable automatic CR mapping
map_bs = false, -- disable automatic BS mapping
map_c_h = false, -- disable automatic C-h mapping
map_c_w = false, -- disable automatic C-w mapping
})

local npairs = require('nvim-autopairs')

-- Manually map keys using get_key_handler
vim.keymap.set('i', '<CR>', npairs.get_key_handler('<CR>'), { expr = true, noremap = true })
vim.keymap.set('i', '(', npairs.get_key_handler('('), { expr = true, noremap = true })
vim.keymap.set('i', ')', npairs.get_key_handler(')'), { expr = true, noremap = true })
vim.keymap.set('i', '[', npairs.get_key_handler('['), { expr = true, noremap = true })
vim.keymap.set('i', '{', npairs.get_key_handler('{'), { expr = true, noremap = true })
vim.keymap.set('i', '<BS>', npairs.get_key_handler('<BS>'), { expr = true, noremap = true })
-- Add more mappings as needed
```

This gives you full control over which keys are mapped and when they are mapped.

### FastWrap

``` text
Expand Down
91 changes: 91 additions & 0 deletions examples/manual_keymap.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
-- Example demonstrating manual key mapping with nvim-autopairs
-- This file shows how to use map_pair = false and get_key_handler

local npairs = require('nvim-autopairs')

-- Setup nvim-autopairs without automatic key mapping
npairs.setup({
map_pair = false, -- disable automatic key mapping
map_cr = false, -- disable automatic CR mapping
map_bs = false, -- disable automatic BS mapping
map_c_h = false,
map_c_w = false,
})

-- Manually map keys using get_key_handler
-- For <CR> key
vim.keymap.set('i', '<CR>', npairs.get_key_handler('<CR>'), {
expr = true,
noremap = true,
desc = 'nvim-autopairs CR handler'
})

-- For bracket pairs
vim.keymap.set('i', '(', npairs.get_key_handler('('), {
expr = true,
noremap = true,
desc = 'nvim-autopairs ( handler'
})

vim.keymap.set('i', ')', npairs.get_key_handler(')'), {
expr = true,
noremap = true,
desc = 'nvim-autopairs ) handler'
})

vim.keymap.set('i', '[', npairs.get_key_handler('['), {
expr = true,
noremap = true,
desc = 'nvim-autopairs [ handler'
})

vim.keymap.set('i', ']', npairs.get_key_handler(']'), {
expr = true,
noremap = true,
desc = 'nvim-autopairs ] handler'
})

vim.keymap.set('i', '{', npairs.get_key_handler('{'), {
expr = true,
noremap = true,
desc = 'nvim-autopairs { handler'
})

vim.keymap.set('i', '}', npairs.get_key_handler('}'), {
expr = true,
noremap = true,
desc = 'nvim-autopairs } handler'
})

-- For quotes
vim.keymap.set('i', '"', npairs.get_key_handler('"'), {
expr = true,
noremap = true,
desc = 'nvim-autopairs " handler'
})

vim.keymap.set('i', "'", npairs.get_key_handler("'"), {
expr = true,
noremap = true,
desc = "nvim-autopairs ' handler"
})

vim.keymap.set('i', '`', npairs.get_key_handler('`'), {
expr = true,
noremap = true,
desc = 'nvim-autopairs ` handler'
})

-- For backspace
vim.keymap.set('i', '<BS>', npairs.get_key_handler('<BS>'), {
expr = true,
noremap = true,
desc = 'nvim-autopairs BS handler'
})

-- Optionally, you can also map <C-w> and <C-h> if needed
-- vim.keymap.set('i', '<C-w>', npairs.get_key_handler('<C-w>'), {
-- expr = true,
-- noremap = true,
-- desc = 'nvim-autopairs C-w handler'
-- })
56 changes: 56 additions & 0 deletions lua/nvim-autopairs.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ local default = {
map_c_h = false,
map_c_w = false,
map_cr = true,
map_pair = true,
enabled = nil,
disable_filetype = { 'TelescopePrompt', 'spectre_panel', 'snacks_picker_input' },
disable_in_macro = true,
Expand Down Expand Up @@ -276,6 +277,9 @@ M.on_attach = function(bufnr)
local enable_insert_auto = false
local autopairs_keymaps = {}
local expr_map = function(key)
if not M.config.map_pair then
return
end
api.nvim_buf_set_keymap(bufnr, 'i', key, '', {
expr = true,
noremap = true,
Expand Down Expand Up @@ -710,5 +714,57 @@ M.map_cr = function()
)
end

--- Make letters inside angle brackets lowercase, except for
--- '<M-A>' or '<m-A>', which become '<m-A>',
--- which is because <m-a> and <M-a> are equivalent in Neovim,
--- but <m-a> and <m-A> are not.
--- @param s string
--- @return string
local function lower_bracket(s)
local res, _ = s:gsub('%b<>', function(m)
local inner = m:sub(2, -2)
if inner:match('^[mM]%-%a$') then
inner = 'm' .. inner:sub(2)
else
inner = inner:lower()
end
return '<' .. inner .. '>'
end)
return res
end

--- Get key handler function for manual keymap setup
--- @param key string The key to handle (e.g., '<CR>', '(', etc.)
--- @return function|string Handler for vim.keymap.set
M.get_key_handler = function(key)
local norm_key = lower_bracket(key)

if norm_key == '<cr>' then
return function()
return M.autopairs_cr()
end
elseif norm_key == '<bs>' then
return function()
return M.autopairs_bs()
end
elseif norm_key == '<c-h>' then
return function()
return M.autopairs_c_h()
end
elseif norm_key == '<c-w>' then
return function()
return M.autopairs_c_w()
end
elseif norm_key == '<m-e>' then
return "<esc>l<cmd>lua require('nvim-autopairs.fastwrap').show()<cr>"
else
-- For regular characters (pairs)
return function()
local bufnr = api.nvim_get_current_buf()
return M.autopairs_map(bufnr, key)
end
end
end

M.esc = utils.esc
return M
113 changes: 113 additions & 0 deletions tests/map_pair_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
local npairs = require('nvim-autopairs')
local utils = require('nvim-autopairs.utils')

describe('map_pair configuration', function()
before_each(function()
vim.cmd('new')
end)

after_each(function()
vim.cmd('bdelete!')
end)

it('should map keys by default when map_pair is true', function()
npairs.setup({ map_pair = true })
npairs.force_attach()

-- Check that keymaps are created
local status, keymaps = pcall(vim.api.nvim_buf_get_var, 0, 'autopairs_keymaps')
assert.truthy(status)
assert.truthy(keymaps)
assert.truthy(#keymaps > 0)

-- Verify that common pair characters are mapped
local has_bracket = false
for _, keymap in ipairs(keymaps) do
if keymap == '(' or keymap == '{' or keymap == '[' then
has_bracket = true
break
end
end
assert.truthy(has_bracket, "Expected at least one bracket pair to be mapped")
end)

it('should not map keys when map_pair is false', function()
npairs.setup({ map_pair = false })
npairs.force_attach()

-- Check that no keymaps are created for autopairs
local status, keymaps = pcall(vim.api.nvim_buf_get_var, 0, 'autopairs_keymaps')
if status and keymaps then
assert.are.same(0, #keymaps, "Expected no keymaps to be created when map_pair is false")
end
end)
end)

describe('get_key_handler API', function()
before_each(function()
npairs.setup({ map_pair = false })
end)

it('should return a function for CR key', function()
local handler = npairs.get_key_handler('<CR>')
assert.truthy(handler)
assert.are.same('function', type(handler))
end)

it('should return a function for BS key', function()
local handler = npairs.get_key_handler('<BS>')
assert.truthy(handler)
assert.are.same('function', type(handler))
end)

it('should return a function for C-h key', function()
local handler = npairs.get_key_handler('<C-h>')
assert.truthy(handler)
assert.are.same('function', type(handler))
end)

it('should return a function for C-w key', function()
local handler = npairs.get_key_handler('<C-w>')
assert.truthy(handler)
assert.are.same('function', type(handler))
end)

it('should return a function for regular character', function()
local handler = npairs.get_key_handler('(')
assert.truthy(handler)
assert.are.same('function', type(handler))
end)

it('should handle case-insensitive keys', function()
local handler_upper = npairs.get_key_handler('<CR>')
local handler_lower = npairs.get_key_handler('<cr>')
local handler_mixed = npairs.get_key_handler('<Cr>')
assert.truthy(handler_upper)
assert.truthy(handler_lower)
assert.truthy(handler_mixed)
assert.are.same('function', type(handler_upper))
assert.are.same('function', type(handler_lower))
assert.are.same('function', type(handler_mixed))

local handler_bs_upper = npairs.get_key_handler('<BS>')
local handler_bs_lower = npairs.get_key_handler('<bs>')
local handler_bs_mixed = npairs.get_key_handler('<Bs>')
assert.are.same('function', type(handler_bs_upper))
assert.are.same('function', type(handler_bs_lower))
assert.are.same('function', type(handler_bs_mixed))

local handler_ch_upper = npairs.get_key_handler('<C-h>')
local handler_ch_lower = npairs.get_key_handler('<c-h>')
local handler_ch_mixed = npairs.get_key_handler('<C-H>')
assert.are.same('function', type(handler_ch_upper))
assert.are.same('function', type(handler_ch_lower))
assert.are.same('function', type(handler_ch_mixed))

local handler_cw_upper = npairs.get_key_handler('<C-w>')
local handler_cw_lower = npairs.get_key_handler('<c-w>')
local handler_cw_mixed = npairs.get_key_handler('<C-W>')
assert.are.same('function', type(handler_cw_upper))
assert.are.same('function', type(handler_cw_lower))
assert.are.same('function', type(handler_cw_mixed))
end)
end)