mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	vim-patch:8.2.0916: mapping with partly modifyOtherKeys code does not work
Problem:    Mapping with partly modifyOtherKeys code does not work.
Solution:   If there is no mapping with a separate modifier include the
            modifier in the key and then try mapping again. (closes vim/vim#6200)
975a880a13
Cherry-pick applicable part of put_string_in_typebuf().
Revert related changes from 10a5825.
Use KEYLEN_PART_KEY for incomplete modifier sequence.
Omit test as it sends terminal codes. Use a Lua test instead.
			
			
This commit is contained in:
		| @@ -5640,7 +5640,7 @@ int get_literal(bool no_simplify) | |||||||
|   for (;;) { |   for (;;) { | ||||||
|     nc = plain_vgetc(); |     nc = plain_vgetc(); | ||||||
|     if (!no_simplify) { |     if (!no_simplify) { | ||||||
|       nc = merge_modifiers(nc); |       nc = merge_modifiers(nc, &mod_mask); | ||||||
|     } |     } | ||||||
|     if ((mod_mask & ~MOD_MASK_SHIFT) != 0) { |     if ((mod_mask & ~MOD_MASK_SHIFT) != 0) { | ||||||
|       // A character with non-Shift modifiers should not be a valid |       // A character with non-Shift modifiers should not be a valid | ||||||
|   | |||||||
| @@ -1442,12 +1442,12 @@ static void updatescript(int c) | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Merge "mod_mask" into "c_arg" | /// Merge "modifiers" into "c_arg". | ||||||
| int merge_modifiers(int c_arg) | int merge_modifiers(int c_arg, int *modifiers) | ||||||
| { | { | ||||||
|   int c = c_arg; |   int c = c_arg; | ||||||
|  |  | ||||||
|   if (mod_mask & MOD_MASK_CTRL) { |   if (*modifiers & MOD_MASK_CTRL) { | ||||||
|     if ((c >= '`' && c <= 0x7f) || (c >= '@' && c <= '_')) { |     if ((c >= '`' && c <= 0x7f) || (c >= '@' && c <= '_')) { | ||||||
|       c &= 0x1f; |       c &= 0x1f; | ||||||
|       if (c == 0) { |       if (c == 0) { | ||||||
| @@ -1458,7 +1458,7 @@ int merge_modifiers(int c_arg) | |||||||
|       c = 0x1e; |       c = 0x1e; | ||||||
|     } |     } | ||||||
|     if (c != c_arg) { |     if (c != c_arg) { | ||||||
|       mod_mask &= ~MOD_MASK_CTRL; |       *modifiers &= ~MOD_MASK_CTRL; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   return c; |   return c; | ||||||
| @@ -1636,7 +1636,7 @@ int vgetc(void) | |||||||
|       // cases they are put back in the typeahead buffer. |       // cases they are put back in the typeahead buffer. | ||||||
|       vgetc_mod_mask = mod_mask; |       vgetc_mod_mask = mod_mask; | ||||||
|       vgetc_char = c; |       vgetc_char = c; | ||||||
|       c = merge_modifiers(c); |       c = merge_modifiers(c, &mod_mask); | ||||||
|  |  | ||||||
|       // If mappings are enabled (i.e., not Ctrl-v) and the user directly typed |       // If mappings are enabled (i.e., not Ctrl-v) and the user directly typed | ||||||
|       // something with a meta- or alt- modifier that was not mapped, interpret |       // something with a meta- or alt- modifier that was not mapped, interpret | ||||||
| @@ -1752,6 +1752,55 @@ typedef enum { | |||||||
|   map_result_nomatch,  // no matching mapping, get char |   map_result_nomatch,  // no matching mapping, get char | ||||||
| } map_result_T; | } map_result_T; | ||||||
|  |  | ||||||
|  | /// Put "string[new_slen]" in typebuf. | ||||||
|  | /// Remove "slen" bytes. | ||||||
|  | /// @return  FAIL for error, OK otherwise. | ||||||
|  | static int put_string_in_typebuf(int offset, int slen, char_u *string, int new_slen) | ||||||
|  | { | ||||||
|  |   int extra = new_slen - slen; | ||||||
|  |   string[new_slen] = NUL; | ||||||
|  |   if (extra < 0) { | ||||||
|  |     // remove matched chars, taking care of noremap | ||||||
|  |     del_typebuf(-extra, offset); | ||||||
|  |   } else if (extra > 0) { | ||||||
|  |     // insert the extra space we need | ||||||
|  |     ins_typebuf(string + slen, REMAP_YES, offset, false, false); | ||||||
|  |   } | ||||||
|  |   // Careful: del_typebuf() and ins_typebuf() may have reallocated | ||||||
|  |   // typebuf.tb_buf[]! | ||||||
|  |   memmove(typebuf.tb_buf + typebuf.tb_off + offset, string, (size_t)new_slen); | ||||||
|  |   return OK; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Check if typebuf.tb_buf[] contains a modifer plus key that can be changed | ||||||
|  | /// into just a key, apply that. | ||||||
|  | /// Check from typebuf.tb_buf[typebuf.tb_off] to typebuf.tb_buf[typebuf.tb_off + "max_offset"]. | ||||||
|  | /// @return  the length of the replaced bytes, zero if nothing changed. | ||||||
|  | static int check_simplify_modifier(int max_offset) | ||||||
|  | { | ||||||
|  |   for (int offset = 0; offset < max_offset; offset++) { | ||||||
|  |     if (offset + 3 >= typebuf.tb_len) { | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     char_u *tp = typebuf.tb_buf + typebuf.tb_off + offset; | ||||||
|  |     if (tp[0] == K_SPECIAL && tp[1] == KS_MODIFIER) { | ||||||
|  |       int modifier = tp[2]; | ||||||
|  |       int new_c = merge_modifiers(tp[3], &modifier); | ||||||
|  |  | ||||||
|  |       if (new_c != tp[3] && modifier == 0) { | ||||||
|  |         char_u new_string[MB_MAXBYTES]; | ||||||
|  |         int len = utf_char2bytes(new_c, new_string); | ||||||
|  |  | ||||||
|  |         if (put_string_in_typebuf(offset, 4, new_string, len) == FAIL) { | ||||||
|  |           return -1; | ||||||
|  |         } | ||||||
|  |         return len; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| /// Handle mappings in the typeahead buffer. | /// Handle mappings in the typeahead buffer. | ||||||
| /// - When something was mapped, return map_result_retry for recursive mappings. | /// - When something was mapped, return map_result_retry for recursive mappings. | ||||||
| /// - When nothing mapped and typeahead has a character: return map_result_get. | /// - When nothing mapped and typeahead has a character: return map_result_get. | ||||||
| @@ -1950,16 +1999,51 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   if ((mp == NULL || max_mlen >= mp_match_len) && keylen != KEYLEN_PART_MAP) { |   if ((mp == NULL || max_mlen >= mp_match_len) && keylen != KEYLEN_PART_MAP) { | ||||||
|     // No matching mapping found or found a non-matching mapping that |     // When no matching mapping found or found a non-matching mapping that | ||||||
|     // matches at least what the matching mapping matched |     // matches at least what the matching mapping matched: | ||||||
|     keylen = 0; |     // Try to include the modifier into the key, when: | ||||||
|     (void)keylen;  // suppress clang/dead assignment |     // - mapping is allowed, | ||||||
|     // If there was no mapping, use the character from the typeahead |     // - keys have not been mapped, | ||||||
|     // buffer right here. Otherwise, use the mapping (loop around). |     // - and when not timed out, | ||||||
|     if (mp == NULL) { |     if ((no_mapping == 0 || allow_keys != 0) | ||||||
|       *keylenp = keylen; |         && (typebuf.tb_maplen == 0 | ||||||
|       return map_result_get;  // get character from typeahead |             || (p_remap && typebuf.tb_noremap[typebuf.tb_off] == RM_YES)) | ||||||
|  |         && !*timedout) { | ||||||
|  |       if (tb_c1 == K_SPECIAL | ||||||
|  |           && (typebuf.tb_len < 2 | ||||||
|  |               || (typebuf.tb_buf[typebuf.tb_off + 1] == KS_MODIFIER | ||||||
|  |                   && typebuf.tb_len < 4))) { | ||||||
|  |         // Incomplete modifier sequence: cannot decide whether to simplify yet. | ||||||
|  |         keylen = KEYLEN_PART_KEY; | ||||||
|  |         // Don't simplify if 'pastetoggle' matched partially. | ||||||
|  |       } else if (keylen != KEYLEN_PART_KEY) { | ||||||
|  |         // Try to include the modifier into the key. | ||||||
|  |         keylen = check_simplify_modifier(max_mlen + 1); | ||||||
|  |         assert(keylen >= 0); | ||||||
|  |       } | ||||||
|     } else { |     } else { | ||||||
|  |       keylen = 0; | ||||||
|  |     } | ||||||
|  |     if (keylen == 0) {  // no simplication has been done | ||||||
|  |       // If there was no mapping at all use the character from the | ||||||
|  |       // typeahead buffer right here. | ||||||
|  |       if (mp == NULL) { | ||||||
|  |         *keylenp = keylen; | ||||||
|  |         return map_result_get;  // get character from typeahead | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (keylen > 0) {  // keys have been simplified | ||||||
|  |       *keylenp = keylen; | ||||||
|  |       return map_result_retry;  // try mapping again | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (keylen < 0) { | ||||||
|  |       // Incomplete key sequence: get some more characters. | ||||||
|  |       assert(keylen == KEYLEN_PART_KEY); | ||||||
|  |     } else { | ||||||
|  |       assert(mp != NULL); | ||||||
|  |       // When a matching mapping was found use that one. | ||||||
|       keylen = mp_match_len; |       keylen = mp_match_len; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -249,6 +249,14 @@ it('typing a simplifiable key at hit-enter prompt triggers mapping vim-patch:8.2 | |||||||
|   ]]) |   ]]) | ||||||
| end) | end) | ||||||
|  |  | ||||||
|  | it('mixing simplified and unsimplified keys can trigger mapping vim-patch:8.2.0916', function() | ||||||
|  |   command('set timeoutlen=10') | ||||||
|  |   command([[imap ' <C-W>]]) | ||||||
|  |   command('imap <C-W><C-A> c-a') | ||||||
|  |   feed([[a'<C-A>]]) | ||||||
|  |   expect('c-a') | ||||||
|  | end) | ||||||
|  |  | ||||||
| describe('input non-printable chars', function() | describe('input non-printable chars', function() | ||||||
|   after_each(function() |   after_each(function() | ||||||
|     os.remove('Xtest-overwrite') |     os.remove('Xtest-overwrite') | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 zeertzjq
					zeertzjq