diff options
author | zeertzjq <zeertzjq@outlook.com> | 2024-04-18 06:23:11 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-18 06:23:11 +0800 |
commit | 562719033ec8bec9f6d56c166b9aef13f8a50a96 (patch) | |
tree | cef907a538f9bbcf31c5072dedd9842e3f5ff40f /src | |
parent | de6eb96fc92ed73237080c1d6a342844fda036fc (diff) | |
download | rneovim-562719033ec8bec9f6d56c166b9aef13f8a50a96.tar.gz rneovim-562719033ec8bec9f6d56c166b9aef13f8a50a96.tar.bz2 rneovim-562719033ec8bec9f6d56c166b9aef13f8a50a96.zip |
vim-patch:9.1.0343: 'showcmd' wrong for partial mapping with multibyte (#28392)
Problem: 'showcmd' is wrong for partial mapping with multibyte char,
and isn't very readable with modifyOtherKeys.
Solution: Decode multibyte char and merge modifiers into the char.
(zeertzjq)
This improves the following situations:
- Multibyte chars whose individual bytes are considered unprintable are
now shown properly in 'showcmd' area.
- Ctrl-W with modifyOtherKeys now shows ^W in 'showcmd' area.
The following situation may still need improvement:
- If the char is a special key or has modifiers that cannot be merged
into it, internal keycodes are shown in 'showcmd' area like before.
This applies to keys typed in Normal mode commands as well, and it's
hard to decide how to make it more readable due to the limited space
taken by 'showcmd', so I'll leave it for later.
closes: vim/vim#14572
https://github.com/vim/vim/commit/acdfb8a97995e0f81832207e39564ba795281108
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/getchar.c | 187 | ||||
-rw-r--r-- | src/nvim/normal.c | 15 |
2 files changed, 142 insertions, 60 deletions
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 665f60a49e..bb50ba31a9 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -84,6 +84,15 @@ static FileDescriptor scriptin[NSCRIPT] = { 0 }; #define MINIMAL_SIZE 20 // minimal size for b_str +typedef struct { + int prev_c; + uint8_t buf[MB_MAXBYTES * 3 + 4]; + size_t buflen; + unsigned pending; + bool in_special; + bool in_mbyte; +} gotchars_state_T; + static buffheader_T redobuff = { { NULL, { NUL } }, NULL, 0, 0 }; static buffheader_T old_redobuff = { { NULL, { NUL } }, NULL, 0, 0 }; static buffheader_T recordbuff = { { NULL, { NUL } }, NULL, 0, 0 }; @@ -1112,84 +1121,95 @@ void del_typebuf(int len, int offset) } } -/// Write typed characters to script file. -/// If recording is on put the character in the record buffer. -static void gotchars(const uint8_t *chars, size_t len) +/// Add a single byte to a recording or 'showcmd'. +/// Return true if a full key has been received, false otherwise. +static bool gotchars_add_byte(gotchars_state_T *state, uint8_t byte) FUNC_ATTR_NONNULL_ALL { - const uint8_t *s = chars; - int c = NUL; - static int prev_c = NUL; - static uint8_t buf[MB_MAXBYTES * 3 + 4] = { 0 }; - static size_t buflen = 0; - static unsigned pending = 0; - static bool in_special = false; - static bool in_mbyte = false; - size_t todo = len; + int c = state->buf[state->buflen++] = byte; + bool retval = false; - for (; todo--; prev_c = c) { - c = buf[buflen++] = *s++; - if (pending > 0) { - pending--; + if (state->pending > 0) { + state->pending--; + } + + // When receiving a special key sequence, store it until we have all + // the bytes and we can decide what to do with it. + if ((state->pending == 0 || state->in_mbyte) && c == K_SPECIAL) { + state->pending += 2; + if (!state->in_mbyte) { + state->in_special = true; } + } - // When receiving a special key sequence, store it until we have all - // the bytes and we can decide what to do with it. - if ((pending == 0 || in_mbyte) && c == K_SPECIAL) { - pending += 2; - if (!in_mbyte) { - in_special = true; + if (state->pending > 0) { + goto ret_false; + } + + if (!state->in_mbyte) { + if (state->in_special) { + state->in_special = false; + if (state->prev_c == KS_MODIFIER) { + // When receiving a modifier, wait for the modified key. + goto ret_false; } + c = TO_SPECIAL(state->prev_c, c); } - - if (pending > 0) { - continue; + // When receiving a multibyte character, store it until we have all + // the bytes, so that it won't be split between two buffer blocks, + // and delete_buff_tail() will work properly. + state->pending = MB_BYTE2LEN_CHECK(c) - 1; + if (state->pending > 0) { + state->in_mbyte = true; + goto ret_false; } + } else { + // Stored all bytes of a multibyte character. + state->in_mbyte = false; + } - if (!in_mbyte) { - if (in_special) { - in_special = false; - if (prev_c == KS_MODIFIER) { - // When receiving a modifier, wait for the modified key. - continue; - } - c = TO_SPECIAL(prev_c, c); - } - // When receiving a multibyte character, store it until we have all - // the bytes, so that it won't be split between two buffer blocks, - // and delete_buff_tail() will work properly. - pending = MB_BYTE2LEN_CHECK(c) - 1; - if (pending > 0) { - in_mbyte = true; - continue; - } - } else { - // Stored all bytes of a multibyte character. - in_mbyte = false; + retval = true; +ret_false: + state->prev_c = c; + return retval; +} + +/// Write typed characters to script file. +/// If recording is on put the character in the record buffer. +static void gotchars(const uint8_t *chars, size_t len) + FUNC_ATTR_NONNULL_ALL +{ + const uint8_t *s = chars; + size_t todo = len; + static gotchars_state_T state; + + while (todo-- > 0) { + if (!gotchars_add_byte(&state, *s++)) { + continue; } // Handle one byte at a time; no translation to be done. - for (size_t i = 0; i < buflen; i++) { - updatescript(buf[i]); + for (size_t i = 0; i < state.buflen; i++) { + updatescript(state.buf[i]); } - buf[buflen] = NUL; + state.buf[state.buflen] = NUL; if (reg_recording != 0) { - add_buff(&recordbuff, (char *)buf, (ptrdiff_t)buflen); + add_buff(&recordbuff, (char *)state.buf, (ptrdiff_t)state.buflen); // remember how many chars were last recorded - last_recorded_len += buflen; + last_recorded_len += state.buflen; } - if (buflen > no_on_key_len) { - vim_unescape_ks((char *)buf + no_on_key_len); - kvi_concat(on_key_buf, (char *)buf + no_on_key_len); + if (state.buflen > no_on_key_len) { + vim_unescape_ks((char *)state.buf + no_on_key_len); + kvi_concat(on_key_buf, (char *)state.buf + no_on_key_len); no_on_key_len = 0; } else { - no_on_key_len -= buflen; + no_on_key_len -= state.buflen; } - buflen = 0; + state.buflen = 0; } may_sync_undo(); @@ -1496,6 +1516,61 @@ int merge_modifiers(int c_arg, int *modifiers) return c; } +/// Add a single byte to 'showcmd' for a partially matched mapping. +/// Call add_to_showcmd() if a full key has been received. +static void add_byte_to_showcmd(uint8_t byte) +{ + static gotchars_state_T state; + + if (!p_sc || msg_silent != 0) { + return; + } + + if (!gotchars_add_byte(&state, byte)) { + return; + } + + state.buf[state.buflen] = NUL; + state.buflen = 0; + + int modifiers = 0; + int c = NUL; + + const uint8_t *ptr = state.buf; + if (ptr[0] == K_SPECIAL && ptr[1] == KS_MODIFIER && ptr[2] != NUL) { + modifiers = ptr[2]; + ptr += 3; + } + + if (*ptr != NUL) { + const char *mb_ptr = mb_unescape((const char **)&ptr); + c = mb_ptr != NULL ? utf_ptr2char(mb_ptr) : *ptr++; + if (c <= 0x7f) { + // Merge modifiers into the key to make the result more readable. + int modifiers_after = modifiers; + int mod_c = merge_modifiers(c, &modifiers_after); + if (modifiers_after == 0) { + modifiers = 0; + c = mod_c; + } + } + } + + // TODO(zeertzjq): is there a more readable and yet compact representation of + // modifiers and special keys? + if (modifiers != 0) { + add_to_showcmd(K_SPECIAL); + add_to_showcmd(KS_MODIFIER); + add_to_showcmd(modifiers); + } + if (c != NUL) { + add_to_showcmd(c); + } + while (*ptr != NUL) { + add_to_showcmd(*ptr++); + } +} + /// Get the next input character. /// Can return a special key or a multi-byte character. /// Can return NUL when called recursively, use safe_vgetc() if that's not @@ -2726,7 +2801,7 @@ static int vgetorpeek(bool advance) showcmd_idx = typebuf.tb_len - SHOWCMD_COLS; } while (showcmd_idx < typebuf.tb_len) { - add_to_showcmd(typebuf.tb_buf[typebuf.tb_off + showcmd_idx++]); + add_byte_to_showcmd(typebuf.tb_buf[typebuf.tb_off + showcmd_idx++]); } curwin->w_wcol = old_wcol; curwin->w_wrow = old_wrow; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index b40a11f6d8..22b1c8dffa 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1968,9 +1968,16 @@ bool add_to_showcmd(int c) } } - char *p = transchar(c); - if (*p == ' ') { - STRCPY(p, "<20>"); + char *p; + char mbyte_buf[MB_MAXCHAR + 1]; + if (c <= 0x7f || !vim_isprintc(c)) { + p = transchar(c); + if (*p == ' ') { + STRCPY(p, "<20>"); + } + } else { + mbyte_buf[utf_char2bytes(c, mbyte_buf)] = NUL; + p = mbyte_buf; } size_t old_len = strlen(showcmd_buf); size_t extra_len = strlen(p); @@ -2036,7 +2043,7 @@ void pop_showcmd(void) static void display_showcmd(void) { - int len = (int)strlen(showcmd_buf); + int len = vim_strsize(showcmd_buf); showcmd_is_clear = (len == 0); if (*p_sloc == 's') { |