@@ -19,13 +19,15 @@ local default = {
1919 disable_filetype = { 'TelescopePrompt', 'spectre_panel' },
2020 disable_in_macro = false,
2121 disable_in_visualblock = false,
22− ignored_next_char = [=[[%w%%%'%[%"%.]]=],
22+ disable_in_replace_mode = true,
23+ ignored_next_char = [=[[%w%%%'%[%"%.%`%$]]=],
2324 break_undo = true,
2425 check_ts = false,
2526 enable_moveright = true,
2627 enable_afterquote = true,
2728 enable_check_bracket_line = true,
2829 enable_bracket_in_quote = true,
30+ enable_abbr = false,
2931 ts_config = {
3032 lua = { 'string', 'source' },
3133 javascript = { 'string', 'template_string' },
@@ -52,18 +54,21 @@ M.setup = function(opt)
5254 end
5355
5456 M.force_attach()
55−
56− api.nvim_exec(
57− [[
58− augroup autopairs_buf
59− autocmd!
60− autocmd BufEnter,BufWinEnter * lua require("nvim-autopairs").on_attach()
61− autocmd BufDelete * lua require("nvim-autopairs").set_buf_rule(nil,tonumber(vim.fn.expand("<abuf>")))
62− autocmd FileType * lua require("nvim-autopairs").force_attach()
63− augroup end
64− ]],
65− false
66− )
57+ local group = api.nvim_create_augroup('autopairs_buf', { clear = true })
58+ api.nvim_create_autocmd({ 'BufEnter', 'BufWinEnter' }, {
59+ group = group, pattern = '*',
60+ callback = function() M.on_attach() end
61+ })
62+ api.nvim_create_autocmd('BufDelete', {
63+ group = group, pattern = '*',
64+ callback = function(data)
65+ M.set_buf_rule(nil, tonumber(data.buf) or 0)
66+ end,
67+ })
68+ api.nvim_create_autocmd('FileType', {
69+ group = group, pattern = '*',
70+ callback = function() M.force_attach() end
71+ })
6772end
6873
6974M.add_rule = function(rule)
@@ -98,10 +103,10 @@ M.remove_rule = function(pair)
98103 if r.start_pair ~= pair then
99104 table.insert(state_tbl, r)
100105 elseif r.key_map and r.key_map ~= '' then
101− vim.api.nvim_buf_del_keymap(0, 'i', r.key_map)
106+ api.nvim_buf_del_keymap(0, 'i', r.key_map)
102107 end
103108 end
104− M.set_buf_rule(state_tbl)
109+ M.set_buf_rule(state_tbl, 0)
105110 end
106111 M.force_attach()
107112end
@@ -132,6 +137,15 @@ M.force_attach = function(bufnr)
132137 M.on_attach(bufnr)
133138end
134139
140+local del_keymaps = function()
141+ local status, autopairs_keymaps = pcall(api.nvim_buf_get_var, 0, 'autopairs_keymaps')
142+ if status and autopairs_keymaps and #autopairs_keymaps > 0 then
143+ for _, key in pairs(autopairs_keymaps) do
144+ pcall(api.nvim_buf_del_keymap, 0, 'i', key)
145+ end
146+ end
147+end
148+
135149local function is_disable()
136150 if M.state.disabled then
137151 return true
@@ -146,22 +160,23 @@ local function is_disable()
146160 return true
147161 end
148162
149− if
150− M.config.disable_in_macro
163+ if M.config.disable_in_macro
151164 and (vim.fn.reg_recording() ~= '' or vim.fn.reg_executing() ~= '')
152165 then
153166 return true
154167 end
155168
169+ if M.config.disable_in_replace_mode and vim.api.nvim_get_mode().mode == "R" then
170+ return true
171+ end
172+
156173 if M.config.disable_in_visualblock and utils.is_block_wise_mode() then
157174 return true
158175 end
159176
160177 if utils.check_filetype(M.config.disable_filetype, vim.bo.filetype) then
161− -- should have a way to remove the mapping when vim.bo.filetype = ''
162− -- now we only remove a rule
163− -- the event FileType happen after BufEnter
164− M.set_buf_rule({})
178+ del_keymaps()
179+ M.set_buf_rule({}, 0)
165180 return true
166181 end
167182 return false
@@ -169,14 +184,14 @@ end
169184
170185---@return table <number, Rule>
171186M.get_buf_rules = function(bufnr)
172− return M.state.rules[bufnr or vim.api.nvim_get_current_buf()] or {}
187+ return M.state.rules[bufnr or api.nvim_get_current_buf()] or {}
173188end
174189
175−---@param rules table list or rule
190+---@param rules nil|table list or rule
176191---@param bufnr number buffer number
177192M.set_buf_rule = function(rules, bufnr)
178193 if bufnr == 0 or bufnr == nil then
179− bufnr = vim.api.nvim_get_current_buf()
194+ bufnr = api.nvim_get_current_buf()
180195 end
181196 M.state.rules[bufnr] = rules
182197end
@@ -190,8 +205,7 @@ M.on_attach = function(bufnr)
190205
191206 local rules = {}
192207 for _, rule in pairs(M.config.rules) do
193− if
194− utils.check_filetype(rule.filetypes, vim.bo.filetype)
208+ if utils.check_filetype(rule.filetypes, vim.bo.filetype)
195209 and utils.check_not_filetype(rule.not_filetypes, vim.bo.filetype)
196210 then
197211 table.insert(rules, rule)
@@ -227,78 +241,50 @@ M.on_attach = function(bufnr)
227241 if utils.is_attached(bufnr) then
228242 return
229243 end
244+ del_keymaps()
230245 local enable_insert_auto = false
246+ local autopairs_keymaps = {}
247+ local expr_map = function(key)
248+ api.nvim_buf_set_keymap(bufnr, 'i', key, '', {
249+ expr = true,
250+ noremap = true,
251+ desc = "autopairs map key",
252+ callback = function() return M.autopairs_map(bufnr, key) end,
253+ })
254+ table.insert(autopairs_keymaps, key)
255+ end
231256 for _, rule in pairs(rules) do
232257 if rule.key_map ~= nil then
233258 if rule.is_regex == false then
234259 if rule.key_map == '' then
235260 rule.key_map = rule.start_pair:sub(#rule.start_pair)
236261 end
237− local key = string.format('"%s"', rule.key_map)
238− if rule.key_map == '"' then
239− key = [['"']]
240− end
241− local mapping = string.format('v:lua.MPairs.autopairs_map(%d,%s)', bufnr, key)
242− api.nvim_buf_set_keymap(
243− bufnr,
244− 'i',
245− rule.key_map,
246− mapping,
247− { expr = true, noremap = true }
248− )
249−
262+ expr_map(rule.key_map)
250263 local key_end = rule.key_end or rule.end_pair:sub(1, 1)
251264 if #key_end >= 1 and key_end ~= rule.key_map and rule.move_cond ~= nil then
252− mapping = string.format(
253− [[v:lua.MPairs.autopairs_map(%d, '%s')]],
254− bufnr,
255− key_end
256− )
257− vim.api.nvim_buf_set_keymap(
258− bufnr,
259− 'i',
260− key_end,
261− mapping,
262− { expr = true, noremap = true }
263− )
265+ expr_map(key_end)
264266 end
265267 else
266268 if rule.key_map ~= '' then
267− local mapping = string.format(
268− "v:lua.MPairs.autopairs_map(%d,'%s')",
269− bufnr,
270− rule.key_map
271− )
272− api.nvim_buf_set_keymap(
273− bufnr,
274− 'i',
275− rule.key_map,
276− mapping,
277− { expr = true, noremap = true }
278− )
269+ expr_map(rule.key_map)
279270 elseif rule.is_endwise == false then
280271 enable_insert_auto = true
281272 end
282273 end
283274 end
284275 end
276+ api.nvim_buf_set_var(bufnr, 'autopairs_keymaps', autopairs_keymaps)
285277
286278 if enable_insert_auto then
287279 -- capture all key use it to trigger regex pairs
288280 -- it can make an issue with paste from register
289− api.nvim_exec(
290− string.format(
291− [[
292− augroup autopairs_insert_%d
293− autocmd!
294− autocmd InsertCharPre <buffer=%d> call luaeval("require('nvim-autopairs').autopairs_insert(%d, _A)", v:char)
295− augroup end ]],
296− bufnr,
297− bufnr,
298− bufnr
299− ),
300− false
301− )
281+ api.nvim_create_autocmd('InsertCharPre', {
282+ group = api.nvim_create_augroup(string.format("autopairs_insert_%d", bufnr), { clear = true }),
283+ buffer = bufnr,
284+ callback = function()
285+ M.autopairs_insert(bufnr, vim.v.char)
286+ end
287+ })
302288 end
303289
304290 if M.config.fast_wrap and M.config.fast_wrap.map then
@@ -316,7 +302,7 @@ M.on_attach = function(bufnr)
316302 bufnr,
317303 'i',
318304 '<bs>',
319− string.format('v:lua.MPairs.autopairs_bs(%d)', bufnr),
305+ 'v:lua.MPairs.autopairs_bs()',
320306 { expr = true, noremap = true }
321307 )
322308 end
@@ -327,7 +313,7 @@ M.on_attach = function(bufnr)
327313 "i",
328314 utils.key.c_h,
329315 string.format("v:lua.MPairs.autopairs_c_h(%d)", bufnr),
330− {expr = true, noremap = true}
316+ { expr = true, noremap = true }
331317 )
332318 end
333319
@@ -347,7 +333,7 @@ local autopairs_delete = function(bufnr, key)
347333 if is_disable() then
348334 return utils.esc(key)
349335 end
350− bufnr = bufnr or 0
336+ bufnr = bufnr or api.nvim_get_current_buf()
351337 local line = utils.text_get_current_line(bufnr)
352338 local _, col = utils.get_cursor()
353339 local rules = M.get_buf_rules(bufnr)
@@ -360,22 +346,23 @@ local autopairs_delete = function(bufnr, key)
360346 #rule.end_pair,
361347 rule.is_regex
362348 )
363− if
364− utils.compare(rule.start_pair, prev_char, rule.is_regex)
349+ if utils.compare(rule.start_pair, prev_char, rule.is_regex)
365350 and utils.compare(rule.end_pair, next_char, rule.is_regex)
366351 and rule:can_del({
367352 ts_node = M.state.ts_node,
353+ rule = rule,
368354 bufnr = bufnr,
369355 prev_char = prev_char,
370356 next_char = next_char,
371357 line = line,
358+ col = col,
372359 })
373360 then
374361 local input = ''
375− for _ = 1, vim.api.nvim_strwidth(rule.start_pair), 1 do
362+ for _ = 1, api.nvim_strwidth(rule.start_pair), 1 do
376363 input = input .. utils.key.bs
377364 end
378− for _ = 1, vim.api.nvim_strwidth(rule.end_pair), 1 do
365+ for _ = 1, api.nvim_strwidth(rule.end_pair), 1 do
379366 input = input .. utils.key.del
380367 end
381368 return utils.esc('<c-g>U' .. input)
@@ -408,8 +395,8 @@ M.autopairs_map = function(bufnr, char)
408395 local rules = M.get_buf_rules(bufnr)
409396 for _, rule in pairs(rules) do
410397 if rule.start_pair then
411− if rule.key_map and rule.key_map:match('<.*>') then
412− new_text = line:sub(1, col) .. line:sub(col + 1, #line)
398+ if char:match('<.*>') then
399+ new_text = line
413400 add_char = 0
414401 else
415402 new_text = line:sub(1, col) .. char .. line:sub(col + 1, #line)
@@ -438,8 +425,7 @@ M.autopairs_map = function(bufnr, char)
438425 -- log.debug("start_pair" .. rule.start_pair)
439426 -- log.debug('prev_char' .. prev_char)
440427 -- log.debug('next_char' .. next_char)
441− if
442− utils.compare(rule.end_pair, next_char, rule.is_regex)
428+ if utils.compare(rule.end_pair, next_char, rule.is_regex)
443429 and rule:can_move(cond_opt)
444430 then
445431 local end_pair = rule:get_end_pair(cond_opt)
@@ -447,8 +433,8 @@ M.autopairs_map = function(bufnr, char)
447433 return utils.esc(utils.repeat_key(utils.key.join_right, end_pair_length))
448434 end
449435
450− if
451− utils.compare(rule.start_pair, prev_char, rule.is_regex)
436+ if rule.key_map == char
437+ and utils.compare(rule.start_pair, prev_char, rule.is_regex)
452438 and rule:can_pair(cond_opt)
453439 then
454440 local end_pair = rule:get_end_pair(cond_opt)
@@ -461,16 +447,19 @@ M.autopairs_map = function(bufnr, char)
461447 if end_pair:match('<.*>') then
462448 end_pair = utils.esc(end_pair)
463449 end
464− local result= char .. end_pair .. utils.esc(move_text)
450+ local result = char .. end_pair .. utils.esc(move_text)
465451 if rule.is_undo then
466− result = utils.esc(utils.key.undo_sequence) .. result..utils.esc(utils.key.undo_sequence)
452+ result = utils.esc(utils.key.undo_sequence) .. result .. utils.esc(utils.key.undo_sequence)
453+ end
454+ if M.config.enable_abbr then
455+ result = utils.esc(utils.key.abbr) .. result
467456 end
468457 log.debug("key_map :" .. result)
469458 return result
470459 end
471460 end
472461 end
473− return M.autopairs_afterquote(new_text, char)
462+ return M.autopairs_afterquote(new_text, utils.esc(char))
474463end
475464
476465M.autopairs_insert = function(bufnr, char)
@@ -512,8 +501,7 @@ M.autopairs_insert = function(bufnr, char)
512501 return false
513502 end
514503
515− if
516− utils.compare(rule.start_pair, prev_char, rule.is_regex)
504+ if utils.compare(rule.start_pair, prev_char, rule.is_regex)
517505 and rule:can_pair(cond_opt)
518506 then
519507 local end_pair = rule:get_end_pair(cond_opt)
@@ -557,35 +545,31 @@ M.autopairs_cr = function(bufnr)
557545 prev_char = prev_char,
558546 next_char = next_char,
559547 }
560−
561− local end_pair = rule:get_end_pair(cond_opt)
562− local end_pair_length = rule:get_end_pair_length(end_pair)
563−
564548 -- log.debug('prev_char' .. rule.start_pair)
565549 -- log.debug('prev_char' .. prev_char)
566550 -- log.debug('next_char' .. next_char)
567− if
568− rule.is_endwise
551+ if rule.is_endwise
569552 and utils.compare(rule.start_pair, prev_char, rule.is_regex)
570553 and rule:can_cr(cond_opt)
571554 then
555+ local end_pair = rule:get_end_pair(cond_opt)
556+ local end_pair_length = rule:get_end_pair_length(end_pair)
572557 return utils.esc(
573558 end_pair
574− .. utils.repeat_key(utils.key.join_left, end_pair_length)
575− -- FIXME do i need to re indent twice #118
576− .. '<cr><esc>====O'
559+ .. utils.repeat_key(utils.key.join_left, end_pair_length)
560+ -- FIXME do i need to re indent twice #118
561+ .. '<cr><esc>====O'
577562 )
578563 end
579564
580565 cond_opt.check_endwise_ts = false
581566
582− if
583− utils.compare(rule.start_pair, prev_char, rule.is_regex)
567+ if utils.compare(rule.start_pair, prev_char, rule.is_regex)
584568 and utils.compare(rule.end_pair, next_char, rule.is_regex)
585569 and rule:can_cr(cond_opt)
586570 then
587571 log.debug('do_cr')
588− return utils.esc(rule:get_map_cr({rule = rule, line = line, color = col, bufnr = bufnr}))
572+ return utils.esc(rule:get_map_cr({ rule = rule, line = line, color = col, bufnr = bufnr }))
589573 end
590574 end
591575 end
@@ -598,8 +582,7 @@ M.autopairs_afterquote = function(line, key_char)
598582 line = line or utils.text_get_current_line(0)
599583 local _, col = utils.get_cursor()
600584 local prev_char, next_char = utils.text_cusor_line(line, col + 1, 1, 1, false)
601− if
602− utils.is_bracket(prev_char)
585+ if utils.is_bracket(prev_char)
603586 and utils.is_quote(next_char)
604587 and not utils.is_in_quotes(line, col, next_char)
605588 then
@@ -617,7 +600,7 @@ M.autopairs_afterquote = function(line, key_char)
617600 is_prev_slash = char == '\\'
618601 end
619602 if count == 2 and index >= (#line - 2) then
620− local rules = M.get_buf_rules(vim.api.nvim_get_current_buf())
603+ local rules = M.get_buf_rules(api.nvim_get_current_buf())
621604 for _, rule in pairs(rules) do
622605 if rule.start_pair == prev_char and char_end ~= rule.end_pair then
623606 local new_text = line:sub(0, index)
@@ -649,13 +632,13 @@ end
649632
650633M.map_cr = function()
651634 M.completion_confirm = function()
652− if vim.fn.pumvisible() ~= 0 then
635+ if vim.fn.pumvisible() ~= 0 then
653636 return M.esc("<cr>")
654637 else
655638 return M.autopairs_cr()
656639 end
657640 end
658− vim.api.nvim_set_keymap(
641+ api.nvim_set_keymap(
659642 'i',
660643 '<CR>',
661644 'v:lua.MPairs.completion_confirm()',