mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	fix(api): nvim_set_keymap() throws error even in pcall() #33228
Problem: When `nvim_set_keymap` tries to overwrite a `<unique>` mapping,
it throws an error even when called in `pcall`.
Solution: src/nvim/mapping.c:buf_do_map no longer calls `semsg`. Its
callers now decide whether to ignore the error, or use
`semsg` (not caught)/`api_set_error` (caught by `pcall`).
(cherry picked from commit ec18ebcb41)
			
			
This commit is contained in:
		 tstsrt
					tstsrt
				
			
				
					committed by
					
						![github-actions[bot]](/assets/img/avatar_default.png) github-actions[bot]
						github-actions[bot]
					
				
			
			
				
	
			
			
			![github-actions[bot]](/assets/img/avatar_default.png) github-actions[bot]
						github-actions[bot]
					
				
			
						parent
						
							2b2a90051e
						
					
				
				
					commit
					5fc6bd6454
				
			| @@ -681,12 +681,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, | |||||||
|           if ((mp->m_mode & mode) != 0 |           if ((mp->m_mode & mode) != 0 | ||||||
|               && mp->m_keylen == len |               && mp->m_keylen == len | ||||||
|               && strncmp(mp->m_keys, lhs, (size_t)len) == 0) { |               && strncmp(mp->m_keys, lhs, (size_t)len) == 0) { | ||||||
|             if (is_abbrev) { |             retval = 6; | ||||||
|               semsg(_(e_global_abbreviation_already_exists_for_str), mp->m_keys); |  | ||||||
|             } else { |  | ||||||
|               semsg(_(e_global_mapping_already_exists_for_str), mp->m_keys); |  | ||||||
|             } |  | ||||||
|             retval = 5; |  | ||||||
|             goto theend; |             goto theend; | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
| @@ -799,11 +794,6 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, | |||||||
|                 did_it = true; |                 did_it = true; | ||||||
|                 break; |                 break; | ||||||
|               } else if (args->unique) { |               } else if (args->unique) { | ||||||
|                 if (is_abbrev) { |  | ||||||
|                   semsg(_(e_abbreviation_already_exists_for_str), p); |  | ||||||
|                 } else { |  | ||||||
|                   semsg(_(e_mapping_already_exists_for_str), p); |  | ||||||
|                 } |  | ||||||
|                 retval = 5; |                 retval = 5; | ||||||
|                 goto theend; |                 goto theend; | ||||||
|               } else { |               } else { | ||||||
| @@ -962,6 +952,7 @@ theend: | |||||||
| ///         - 2 for no match | ///         - 2 for no match | ||||||
| ///         - 4 for out of mem (deprecated, WON'T HAPPEN) | ///         - 4 for out of mem (deprecated, WON'T HAPPEN) | ||||||
| ///         - 5 for entry not unique | ///         - 5 for entry not unique | ||||||
|  | ///         - 6 for buflocal unique entry conflicts with global entry | ||||||
| /// | /// | ||||||
| int do_map(int maptype, char *arg, int mode, bool is_abbrev) | int do_map(int maptype, char *arg, int mode, bool is_abbrev) | ||||||
| { | { | ||||||
| @@ -2637,16 +2628,47 @@ static void do_exmap(exarg_T *eap, int isabbrev) | |||||||
|   char *cmdp = eap->cmd; |   char *cmdp = eap->cmd; | ||||||
|   int mode = get_map_mode(&cmdp, eap->forceit || isabbrev); |   int mode = get_map_mode(&cmdp, eap->forceit || isabbrev); | ||||||
|  |  | ||||||
|   switch (do_map((*cmdp == 'n') ? MAPTYPE_NOREMAP |   int maptype; | ||||||
|                                 : (*cmdp == 'u') ? MAPTYPE_UNMAP : MAPTYPE_MAP, |   if (*cmdp == 'n') { | ||||||
|                  eap->arg, mode, isabbrev)) { |     maptype = MAPTYPE_NOREMAP; | ||||||
|  |   } else if (*cmdp == 'u') { | ||||||
|  |     maptype = MAPTYPE_UNMAP; | ||||||
|  |   } else { | ||||||
|  |     maptype = MAPTYPE_MAP; | ||||||
|  |   } | ||||||
|  |   MapArguments parsed_args; | ||||||
|  |   int result = str_to_mapargs(eap->arg, maptype == MAPTYPE_UNMAP, &parsed_args); | ||||||
|  |   switch (result) { | ||||||
|  |   case 0: | ||||||
|  |     break; | ||||||
|  |   case 1: | ||||||
|  |     emsg(_(e_invarg)); | ||||||
|  |     goto free_rhs; | ||||||
|  |     break; | ||||||
|  |   default: | ||||||
|  |     assert(false && "Unknown return code from str_to_mapargs!"); | ||||||
|  |     goto free_rhs; | ||||||
|  |   } | ||||||
|  |   switch (buf_do_map(maptype, &parsed_args, mode, isabbrev, curbuf)) { | ||||||
|   case 1: |   case 1: | ||||||
|     emsg(_(e_invarg)); |     emsg(_(e_invarg)); | ||||||
|     break; |     break; | ||||||
|   case 2: |   case 2: | ||||||
|     emsg(isabbrev ? _(e_noabbr) : _(e_nomap)); |     emsg(isabbrev ? _(e_noabbr) : _(e_nomap)); | ||||||
|     break; |     break; | ||||||
|  |   case 5: | ||||||
|  |     semsg(isabbrev ? _(e_abbreviation_already_exists_for_str) | ||||||
|  |                    : _(e_mapping_already_exists_for_str), | ||||||
|  |           parsed_args.lhs); | ||||||
|  |     break; | ||||||
|  |   case 6: | ||||||
|  |     semsg(isabbrev ? _(e_global_abbreviation_already_exists_for_str) | ||||||
|  |                    : _(e_global_mapping_already_exists_for_str), | ||||||
|  |           parsed_args.lhs); | ||||||
|   } |   } | ||||||
|  | free_rhs: | ||||||
|  |   xfree(parsed_args.rhs); | ||||||
|  |   xfree(parsed_args.orig_rhs); | ||||||
| } | } | ||||||
|  |  | ||||||
| /// ":abbreviate" and friends. | /// ":abbreviate" and friends. | ||||||
| @@ -2808,6 +2830,12 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod | |||||||
|                   is_abbrev ? e_abbreviation_already_exists_for_str |                   is_abbrev ? e_abbreviation_already_exists_for_str | ||||||
|                             : e_mapping_already_exists_for_str, lhs.data); |                             : e_mapping_already_exists_for_str, lhs.data); | ||||||
|     goto fail_and_free; |     goto fail_and_free; | ||||||
|  |     break; | ||||||
|  |   case 6: | ||||||
|  |     api_set_error(err, kErrorTypeException, | ||||||
|  |                   is_abbrev ? e_global_abbreviation_already_exists_for_str | ||||||
|  |                             : e_global_mapping_already_exists_for_str, lhs.data); | ||||||
|  |     goto fail_and_free; | ||||||
|   default: |   default: | ||||||
|     assert(false && "Unrecognized return code!"); |     assert(false && "Unrecognized return code!"); | ||||||
|     goto fail_and_free; |     goto fail_and_free; | ||||||
|   | |||||||
| @@ -1465,4 +1465,24 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function() | |||||||
|     eq(1, exec_lua [[return GlobalCount]]) |     eq(1, exec_lua [[return GlobalCount]]) | ||||||
|     eq('\nNo mapping found', n.exec_capture('nmap <C-I>')) |     eq('\nNo mapping found', n.exec_capture('nmap <C-I>')) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
|  |   it('does not overwrite in <unique> mappings', function() | ||||||
|  |     api.nvim_buf_set_keymap(0, 'i', 'lhs', 'rhs', {}) | ||||||
|  |     eq( | ||||||
|  |       'E227: Mapping already exists for lhs', | ||||||
|  |       pcall_err(api.nvim_buf_set_keymap, 0, 'i', 'lhs', 'rhs', { unique = true }) | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     api.nvim_buf_set_keymap(0, 'ia', 'lhs2', 'rhs2', {}) | ||||||
|  |     eq( | ||||||
|  |       'E226: Abbreviation already exists for lhs2', | ||||||
|  |       pcall_err(api.nvim_buf_set_keymap, 0, 'ia', 'lhs2', 'rhs2', { unique = true }) | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     api.nvim_set_keymap('n', 'lhs', 'rhs', {}) | ||||||
|  |     eq( | ||||||
|  |       'E225: Global mapping already exists for lhs', | ||||||
|  |       pcall_err(api.nvim_buf_set_keymap, 0, 'n', 'lhs', 'rhs', { unique = true }) | ||||||
|  |     ) | ||||||
|  |   end) | ||||||
| end) | end) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user