Commit: patch 9.2.0624: C-N/C-P cannot be mapped in complete() completion

1 view
Skip to first unread message

Christian Brabandt

unread,
10:45 AM (4 hours ago) 10:45 AM
to vim...@googlegroups.com
patch 9.2.0624: C-N/C-P cannot be mapped in complete() completion

Commit: https://github.com/vim/vim/commit/076585e6addb5688f123ef3e174fcfee993b7602
Author: Thomas M Kehrenberg <tm...@posteo.net>
Date: Sat Jun 13 14:36:58 2026 +0000

patch 9.2.0624: C-N/C-P cannot be mapped in complete() completion

Problem: Keys valid in CTRL-X mode are never mapped while insert
completion is active, so <C-N> and <C-P> cannot be remapped
for completion started by complete().
Solution: Do not disable mappings in CTRL_X_EVAL mode. In this mode a
mapping cannot interfere with selecting the completion
method, which is what the no-mapping rule exists for.

related: #6440
related: #16880
closes: #20489

Signed-off-by: Thomas M Kehrenberg <tm...@posteo.net>
Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index e8b9518e5..caad78ceb 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -1,4 +1,4 @@
-*builtin.txt* For Vim version 9.2. Last change: 2026 Jun 09
+*builtin.txt* For Vim version 9.2. Last change: 2026 Jun 13


VIM REFERENCE MANUAL by Bram Moolenaar
@@ -1986,6 +1986,14 @@ complete({startcol}, {matches}) *complete()* *E785*
The match can be selected with CTRL-N and CTRL-P as usual with
Insert mode completion. The popup menu will appear if
specified, see |ins-completion-menu|.
+ Unlike with other |ins-completion| modes, the CTRL-N and
+ CTRL-P keys can be mapped while this completion is active.
+ For example, to make CTRL-N move the selection without
+ inserting the match: >
+
+ inoremap <expr> <C-N> complete_info().mode ==# 'eval'
+ \ ? '<Down>' : '<C-N>'
+<

Example (using legacy Vim script): >

diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt
index 5946f26e7..688d65fb3 100644
--- a/runtime/doc/insert.txt
+++ b/runtime/doc/insert.txt
@@ -1,4 +1,4 @@
-*insert.txt* For Vim version 9.2. Last change: 2026 Jun 09
+*insert.txt* For Vim version 9.2. Last change: 2026 Jun 13


VIM REFERENCE MANUAL by Bram Moolenaar
@@ -690,7 +690,8 @@ When the popup menu is displayed there are a few more special keys, see
Note: The keys that are valid in CTRL-X mode are not mapped. This allows for
`:map <C-F> <C-X><C-F>` to work (assuming "<" is not in 'cpo'). The key that
ends CTRL-X mode (any key that is not a valid CTRL-X mode command) is mapped.
-Also, when doing completion with 'complete' mappings apply as usual.
+Also, when doing completion with 'complete' or when completion was started
+with |complete()| mappings apply as usual.

*E565*
Note: While completion is active Insert mode can't be used recursively and
diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index 937bf4f21..b78a3738a 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -1,4 +1,4 @@
-*version9.txt* For Vim version 9.2. Last change: 2026 Jun 09
+*version9.txt* For Vim version 9.2. Last change: 2026 Jun 13


VIM REFERENCE MANUAL by Bram Moolenaar
@@ -52677,6 +52677,8 @@ Changed ~
- On Unix, filename completion for single-file Ex commands now treats embedded
whitespace as part of the filename, like on other platforms.
- Rewrite the clientserver socketserver backend to use channels and JSON.
+- During |complete()|-triggered completion, CTRL-N and CTRL-P are now subject
+ to insert-mode mappings.


*added-9.3*
diff --git a/src/getchar.c b/src/getchar.c
index d02b9009c..25422be0d 100644
--- a/src/getchar.c
+++ b/src/getchar.c
@@ -2718,6 +2718,7 @@ typedef enum {
/*
* Check if the bytes at the start of the typeahead buffer are a character used
* in Insert mode completion. This includes the form with a CTRL modifier.
+ * During completion started by complete() keys are mapped as usual.
*/
static int
at_ins_compl_key(void)
@@ -2733,8 +2734,9 @@ at_ins_compl_key(void)
// to the CTRL-N/CTRL-P completion keys here.
&& !(p[2] & MOD_MASK_SHIFT))
c = p[3] & 0x1f;
- return (ctrl_x_mode_not_default() && vim_is_ctrl_x_key(c))
- || (compl_status_local() && (c == Ctrl_N || c == Ctrl_P));
+ return !ctrl_x_mode_eval()
+ && ((ctrl_x_mode_not_default() && vim_is_ctrl_x_key(c))
+ || (compl_status_local() && (c == Ctrl_N || c == Ctrl_P)));
}

/*
@@ -2872,7 +2874,8 @@ handle_mapping(
* - in insert or cmdline mode and 'paste' option set
* - waiting for "hit return to continue" and CR or SPACE typed
* - waiting for a char with --more--
- * - in Ctrl-X mode, and we get a valid char for that mode
+ * - in Ctrl-X mode (not started by complete()), and we get a valid char
+ * for that mode
* - currently receiving OSC sequence
*/
tb_c1 = typebuf.tb_buf[typebuf.tb_off];
diff --git a/src/insexpand.c b/src/insexpand.c
index bbb15b003..c63b49c7f 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -359,7 +359,7 @@ int ctrl_x_mode_omni(void)
{ return ctrl_x_mode == CTRL_X_OMNI; }
int ctrl_x_mode_spell(void)
{ return ctrl_x_mode == CTRL_X_SPELL; }
-static int ctrl_x_mode_eval(void)
+int ctrl_x_mode_eval(void)
{ return ctrl_x_mode == CTRL_X_EVAL; }
int ctrl_x_mode_line_or_eval(void)
{ return ctrl_x_mode == CTRL_X_WHOLE_LINE || ctrl_x_mode == CTRL_X_EVAL; }
diff --git a/src/proto/insexpand.pro b/src/proto/insexpand.pro
index e973eac4f..2c1882dc1 100644
--- a/src/proto/insexpand.pro
+++ b/src/proto/insexpand.pro
@@ -14,6 +14,7 @@ int ctrl_x_mode_cmdline(void);
int ctrl_x_mode_function(void);
int ctrl_x_mode_omni(void);
int ctrl_x_mode_spell(void);
+int ctrl_x_mode_eval(void);
int ctrl_x_mode_line_or_eval(void);
int ctrl_x_mode_register(void);
int ctrl_x_mode_not_default(void);
diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim
index 7eda457e1..c741fd1e7 100644
--- a/src/testdir/test_ins_complete.vim
+++ b/src/testdir/test_ins_complete.vim
@@ -6331,4 +6331,30 @@ func Test_completion_with_mapped_ctrl_r()
bwipe!
endfunc

+" Keys are mapped during completion started by complete(), but not in other
+" CTRL-X modes.
+func Test_mapped_ctrl_n_during_complete_function()
+ new
+ inoremap <buffer> <F2> <Cmd>call complete(1, ['foo', 'foobar'])<CR>
+ inoremap <buffer> <F3> <Cmd>let b:info =
+ \ [getline('.'), complete_info(['selected']).selected]<CR>
+
+ " During completion started by complete() the <C-N> mapping applies:
+ " <Down> moves the selection without inserting it.
+ inoremap <buffer> <expr> <C-N> complete_info().mode ==# 'eval' ? '<Down>' : '<C-N>'
+ call feedkeys("i\<F2>\<*C-N>\<F3>\<C-Y>\<Esc>", 'tx')
+ call assert_equal(['foo', 1], b:info)
+ call assert_equal('foobar', getline(1))
+
+ " In other CTRL-X modes the mapping is ignored: the builtin <C-N> selects
+ " and inserts the next match.
+ %delete _
+ call setline(1, ['foo', 'foobar', ''])
+ inoremap <buffer> <expr> <C-N> pumvisible() ? '<Down>' : '<C-N>'
+ call feedkeys("3GAf\<C-X>\<C-N>\<C-N>\<F3>\<C-Y>\<Esc>", 'tx')
+ call assert_equal(['foobar', 1], b:info)
+
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab nofoldenable
diff --git a/src/version.c b/src/version.c
index b798c6348..28606e3d3 100644
--- a/src/version.c
+++ b/src/version.c
@@ -754,6 +754,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 624,
/**/
623,
/**/
Reply all
Reply to author
Forward
0 new messages