aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/edit.c2
-rw-r--r--src/nvim/getchar.c10
-rw-r--r--src/nvim/message.c36
-rw-r--r--src/nvim/testdir/test_help.vim24
-rw-r--r--src/nvim/testdir/test_ins_complete.vim9
-rw-r--r--src/nvim/testdir/test_mapping.vim34
-rw-r--r--src/nvim/window.c60
7 files changed, 127 insertions, 48 deletions
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index f17ac52f15..f2c3f64790 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -553,7 +553,7 @@ static int insert_check(VimState *state)
Insstart_orig = Insstart;
}
- if (stop_insert_mode && !pum_visible()) {
+ if (stop_insert_mode && !compl_started) {
// ":stopinsert" used or 'insertmode' reset
s->count = 0;
return 0; // exit insert mode
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index df9297dc0b..374fa11b23 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -2902,7 +2902,8 @@ void set_maparg_lhs_rhs(const char_u *const orig_lhs, const size_t orig_lhs_len,
replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf,
REPTERM_DO_LT | REPTERM_NO_SIMPLIFY, NULL, cpo_flags);
mapargs->rhs_len = STRLEN(replaced);
- mapargs->rhs_is_noop = false;
+ // XXX: even when orig_rhs is non-empty, replace_termcodes may produce an empty string.
+ mapargs->rhs_is_noop = orig_rhs[0] != NUL && mapargs->rhs_len == 0;
mapargs->rhs = xcalloc(mapargs->rhs_len + 1, sizeof(char_u));
STRLCPY(mapargs->rhs, replaced, mapargs->rhs_len + 1);
}
@@ -3765,12 +3766,7 @@ static void showmap(mapblock_T *mp, bool local)
} else if (mp->m_str[0] == NUL) {
msg_puts_attr("<Nop>", HL_ATTR(HLF_8));
} else {
- // Remove escaping of K_SPECIAL, because "m_str" is in a format to be used
- // as typeahead.
- char_u *s = vim_strsave(mp->m_str);
- vim_unescape_ks(s);
- msg_outtrans_special(s, false, 0);
- xfree(s);
+ msg_outtrans_special(mp->m_str, false, 0);
}
if (mp->m_desc != NULL) {
diff --git a/src/nvim/message.c b/src/nvim/message.c
index f0ef4e1d4f..cdcbcc6700 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -1671,11 +1671,13 @@ const char *str2special(const char **const sp, const bool replace_spaces, const
{
static char buf[7];
- // Try to un-escape a multi-byte character. Return the un-escaped
- // string if it is a multi-byte character.
- const char *const p = mb_unescape(sp);
- if (p != NULL) {
- return p;
+ {
+ // Try to un-escape a multi-byte character. Return the un-escaped
+ // string if it is a multi-byte character.
+ const char *const p = mb_unescape(sp);
+ if (p != NULL) {
+ return p;
+ }
}
const char *str = *sp;
@@ -1698,18 +1700,24 @@ const char *str2special(const char **const sp, const bool replace_spaces, const
}
if (!IS_SPECIAL(c)) {
- const int len = utf_ptr2len((const char_u *)str);
+ *sp = str;
+ // Try to un-escape a multi-byte character after modifiers.
+ const char *p = mb_unescape(sp);
- // Check for an illegal byte.
- if (MB_BYTE2LEN((uint8_t)(*str)) > len) {
- transchar_nonprint(curbuf, (char_u *)buf, c);
- *sp = str + 1;
- return buf;
+ if (p == NULL) {
+ const int len = utf_ptr2len((const char_u *)str);
+ // Check for an illegal byte.
+ if (MB_BYTE2LEN((uint8_t)(*str)) > len) {
+ transchar_nonprint(curbuf, (char_u *)buf, c);
+ *sp = str + 1;
+ return buf;
+ }
+ *sp = str + len;
+ p = str;
}
- // Since 'special' is TRUE the multi-byte character 'c' will be
+ // Since 'special' is true the multi-byte character 'c' will be
// processed by get_special_key_name().
- c = utf_ptr2char((const char_u *)str);
- *sp = str + len;
+ c = utf_ptr2char((const char_u *)p);
} else {
*sp = str + 1;
}
diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim
index b2d943be00..9569cfa4e5 100644
--- a/src/nvim/testdir/test_help.vim
+++ b/src/nvim/testdir/test_help.vim
@@ -9,6 +9,30 @@ func Test_help_restore_snapshot()
helpclose
endfunc
+func Test_help_restore_snapshot_split()
+ " Squeeze the unnamed buffer, Xfoo and the help one side-by-side and focus
+ " the first one before calling :help.
+ let bnr = bufnr()
+ botright vsp Xfoo
+ wincmd h
+ help
+ wincmd L
+ let g:did_bufenter = v:false
+ augroup T
+ au!
+ au BufEnter Xfoo let g:did_bufenter = v:true
+ augroup END
+ helpclose
+ augroup! T
+ " We're back to the unnamed buffer.
+ call assert_equal(bnr, bufnr())
+ " No BufEnter was triggered for Xfoo.
+ call assert_equal(v:false, g:did_bufenter)
+
+ close!
+ bwipe!
+endfunc
+
func Test_help_errors()
call assert_fails('help doesnotexist', 'E149:')
call assert_fails('help!', 'E478:')
diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim
index 24eaf9e8b1..90b57323af 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -516,6 +516,15 @@ func Test_pum_stopped_by_timer()
call delete('Xpumscript')
endfunc
+func Test_complete_stopinsert_startinsert()
+ nnoremap <F2> <Cmd>startinsert<CR>
+ inoremap <F2> <Cmd>stopinsert<CR>
+ " This just checks if this causes an error
+ call feedkeys("i\<C-X>\<C-N>\<F2>\<F2>", 'x')
+ nunmap <F2>
+ iunmap <F2>
+endfunc
+
func Test_pum_with_folds_two_tabs()
CheckScreendump
diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim
index 752b1700d0..b5158295b4 100644
--- a/src/nvim/testdir/test_mapping.vim
+++ b/src/nvim/testdir/test_mapping.vim
@@ -464,6 +464,40 @@ func Test_list_mappings()
call assert_equal(['n ,n <Nop>'],
\ execute('nmap ,n')->trim()->split("\n"))
+ " verbose map
+ call assert_match("\tLast set from .*/test_mapping.vim line \\d\\+$",
+ \ execute('verbose map ,n')->trim()->split("\n")[1])
+
+ " character with K_SPECIAL byte in rhs
+ nmap foo …
+ call assert_equal(['n foo …'],
+ \ execute('nmap foo')->trim()->split("\n"))
+
+ " modified character with K_SPECIAL byte in rhs
+ nmap foo <M-…>
+ call assert_equal(['n foo <M-…>'],
+ \ execute('nmap foo')->trim()->split("\n"))
+
+ " character with K_SPECIAL byte in lhs
+ nmap … foo
+ call assert_equal(['n … foo'],
+ \ execute('nmap …')->trim()->split("\n"))
+
+ " modified character with K_SPECIAL byte in lhs
+ nmap <M-…> foo
+ call assert_equal(['n <M-…> foo'],
+ \ execute('nmap <M-…>')->trim()->split("\n"))
+
+ " map to CTRL-V
+ exe "nmap ,k \<C-V>"
+ call assert_equal(['n ,k <Nop>'],
+ \ execute('nmap ,k')->trim()->split("\n"))
+
+ " map with space at the beginning
+ exe "nmap \<C-V> w <Nop>"
+ call assert_equal(['n <Space>w <Nop>'],
+ \ execute("nmap \<C-V> w")->trim()->split("\n"))
+
nmapclear
endfunc
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 90f7145f51..6bae92c909 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -2813,10 +2813,11 @@ int win_close(win_T *win, bool free_buf, bool force)
wp = win_free_mem(win, &dir, NULL);
if (help_window) {
- // Closing the help window moves the cursor back to the original window.
- win_T *tmpwp = get_snapshot_focus(SNAP_HELP_IDX);
- if (tmpwp != NULL) {
- wp = tmpwp;
+ // Closing the help window moves the cursor back to the current window
+ // of the snapshot.
+ win_T *prev_win = get_snapshot_curwin(SNAP_HELP_IDX);
+ if (win_valid(prev_win)) {
+ wp = prev_win;
}
}
@@ -6827,6 +6828,35 @@ static void clear_snapshot_rec(frame_T *fr)
}
}
+/// Traverse a snapshot to find the previous curwin.
+static win_T *get_snapshot_curwin_rec(frame_T *ft)
+{
+ win_T *wp;
+
+ if (ft->fr_next != NULL) {
+ if ((wp = get_snapshot_curwin_rec(ft->fr_next)) != NULL) {
+ return wp;
+ }
+ }
+ if (ft->fr_child != NULL) {
+ if ((wp = get_snapshot_curwin_rec(ft->fr_child)) != NULL) {
+ return wp;
+ }
+ }
+
+ return ft->fr_win;
+}
+
+/// @return the current window stored in the snapshot or NULL.
+static win_T *get_snapshot_curwin(int idx)
+{
+ if (curtab->tp_snapshot[idx] == NULL) {
+ return NULL;
+ }
+
+ return get_snapshot_curwin_rec(curtab->tp_snapshot[idx]);
+}
+
/// Restore a previously created snapshot, if there is any.
/// This is only done if the screen size didn't change and the window layout is
/// still the same.
@@ -6899,28 +6929,6 @@ static win_T *restore_snapshot_rec(frame_T *sn, frame_T *fr)
return wp;
}
-/// Gets the focused window (the one holding the cursor) of the snapshot.
-static win_T *get_snapshot_focus(int idx)
-{
- if (curtab->tp_snapshot[idx] == NULL) {
- return NULL;
- }
-
- frame_T *sn = curtab->tp_snapshot[idx];
- // This should be equivalent to the recursive algorithm found in
- // restore_snapshot as far as traveling nodes go.
- while (sn->fr_child != NULL || sn->fr_next != NULL) {
- while (sn->fr_child != NULL) {
- sn = sn->fr_child;
- }
- if (sn->fr_next != NULL) {
- sn = sn->fr_next;
- }
- }
-
- return win_valid(sn->fr_win) ? sn->fr_win : NULL;
-}
-
/// Set "win" to be the curwin and "tp" to be the current tab page.
/// restore_win() MUST be called to undo, also when FAIL is returned.
/// No autocommands will be executed until restore_win() is called.