diff options
Diffstat (limited to 'src/getchar.c')
-rw-r--r-- | src/getchar.c | 175 |
1 files changed, 145 insertions, 30 deletions
diff --git a/src/getchar.c b/src/getchar.c index 49a24f0..1c544da 100644 --- a/src/getchar.c +++ b/src/getchar.c @@ -81,7 +81,7 @@ static int KeyNoremap = 0; // remapping flags static char_u typebuf_init[TYPELEN_INIT]; // initial typebuf.tb_buf static char_u noremapbuf_init[TYPELEN_INIT]; // initial typebuf.tb_noremap -static int last_recorded_len = 0; // number of last recorded chars +static size_t last_recorded_len = 0; // number of last recorded chars #ifdef FEAT_EVAL mapblock_T *last_used_map = NULL; @@ -163,7 +163,7 @@ get_recorded(void) * (possibly mapped) characters that stopped the recording. */ len = STRLEN(p); - if ((int)len >= last_recorded_len) + if (len >= last_recorded_len) { len -= last_recorded_len; p[len] = NUL; @@ -1282,50 +1282,104 @@ del_typebuf(int len, int offset) } /* + * State for adding bytes to a recording or 'showcmd'. + */ +typedef struct +{ + char_u buf[MB_MAXBYTES * 3 + 4]; + int prev_c; + size_t buflen; + unsigned pending_special; + unsigned pending_mbyte; +} gotchars_state_T; + +/* + * Add a single byte to a recording or 'showcmd'. + * Return TRUE if a full key has been received, FALSE otherwise. + */ + static int +gotchars_add_byte(gotchars_state_T *state, char_u byte) +{ + int c = state->buf[state->buflen++] = byte; + int retval = FALSE; + int in_special = state->pending_special > 0; + int in_mbyte = state->pending_mbyte > 0; + + if (in_special) + state->pending_special--; + else if (c == K_SPECIAL +#ifdef FEAT_GUI + || c == CSI +#endif + ) + // When receiving a special key sequence, store it until we have all + // the bytes and we can decide what to do with it. + state->pending_special = 2; + + if (state->pending_special > 0) + goto ret_false; + + if (in_mbyte) + state->pending_mbyte--; + else + { + if (in_special) + { + 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 (c == K_FOCUSGAINED || c == K_FOCUSLOST) + // Drop K_FOCUSGAINED and K_FOCUSLOST, they are not useful + // in a recording. + state->buflen = 0; + } + // 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_mbyte = MB_BYTE2LEN_CHECK(c) - 1; + } + + if (state->pending_mbyte > 0) + goto ret_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 recordbuffer. + * If recording is on put the character in the record buffer. */ static void gotchars(char_u *chars, int len) { char_u *s = chars; - int i; - static char_u buf[4]; - static int buflen = 0; + size_t i; int todo = len; + static gotchars_state_T state; - while (todo--) + while (todo-- > 0) { - buf[buflen++] = *s++; - - // When receiving a special key sequence, store it until we have all - // the bytes and we can decide what to do with it. - if (buflen == 1 && buf[0] == K_SPECIAL) - continue; - if (buflen == 2) + if (!gotchars_add_byte(&state, *s++)) continue; - if (buflen == 3 && buf[1] == KS_EXTRA - && (buf[2] == KE_FOCUSGAINED || buf[2] == KE_FOCUSLOST)) - { - // Drop K_FOCUSGAINED and K_FOCUSLOST, they are not useful in a - // recording. - buflen = 0; - continue; - } // Handle one byte at a time; no translation to be done. - for (i = 0; i < buflen; ++i) - updatescript(buf[i]); + for (i = 0; i < state.buflen; ++i) + updatescript(state.buf[i]); if (reg_recording != 0) { - buf[buflen] = NUL; - add_buff(&recordbuff, buf, (long)buflen); + state.buf[state.buflen] = NUL; + add_buff(&recordbuff, state.buf, (long)state.buflen); // remember how many chars were last recorded - last_recorded_len += buflen; + last_recorded_len += state.buflen; } - buflen = 0; + state.buflen = 0; } + may_sync_undo(); #ifdef FEAT_EVAL @@ -1713,6 +1767,67 @@ merge_modifyOtherKeys(int c_arg, int *modifiers) } /* + * 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(char_u byte) +{ + static gotchars_state_T state; + char_u *ptr; + int modifiers = 0; + int c = NUL; + + if (!p_sc || msg_silent != 0) + return; + + if (!gotchars_add_byte(&state, byte)) + return; + + state.buf[state.buflen] = NUL; + state.buflen = 0; + + ptr = state.buf; + if (ptr[0] == K_SPECIAL && ptr[1] == KS_MODIFIER && ptr[2] != NUL) + { + modifiers = ptr[2]; + ptr += 3; + } + + if (*ptr != NUL) + { + char_u *mb_ptr = mb_unescape(&ptr); + + c = mb_ptr != NULL ? (*mb_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_modifyOtherKeys(c, &modifiers_after); + + if (modifiers_after == 0) + { + modifiers = 0; + c = mod_c; + } + } + } + + // TODO: 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 @@ -1751,7 +1866,7 @@ vgetc(void) else { // number of characters recorded from the last vgetc() call - static int last_vgetc_recorded_len = 0; + static size_t last_vgetc_recorded_len = 0; mod_mask = 0; vgetc_mod_mask = 0; @@ -3544,7 +3659,7 @@ vgetorpeek(int advance) if (typebuf.tb_len > SHOWCMD_COLS) showcmd_idx = typebuf.tb_len - SHOWCMD_COLS; while (showcmd_idx < typebuf.tb_len) - (void)add_to_showcmd( + add_byte_to_showcmd( typebuf.tb_buf[typebuf.tb_off + showcmd_idx++]); curwin->w_wcol = old_wcol; curwin->w_wrow = old_wrow; |