mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	window: Fix matchaddpos() and enhance error reporting
This commit is contained in:
		| @@ -5493,7 +5493,7 @@ matchaddpos({group}, {pos} [, {priority} [, {id} [, {dict}]]]) | |||||||
| 		sets buffer line boundaries to redraw screen. It is supposed | 		sets buffer line boundaries to redraw screen. It is supposed | ||||||
| 		to be used when fast match additions and deletions are | 		to be used when fast match additions and deletions are | ||||||
| 		required, for example to highlight matching parentheses. | 		required, for example to highlight matching parentheses. | ||||||
|  | 							*E5030* *E5031* | ||||||
| 		The list {pos} can contain one of these items: | 		The list {pos} can contain one of these items: | ||||||
| 		- A number.  This whole line will be highlighted.  The first | 		- A number.  This whole line will be highlighted.  The first | ||||||
| 		  line has number 1. | 		  line has number 1. | ||||||
| @@ -5507,6 +5507,10 @@ matchaddpos({group}, {pos} [, {priority} [, {id} [, {dict}]]]) | |||||||
| 		- A list with three numbers, e.g., [23, 11, 3]. As above, but | 		- A list with three numbers, e.g., [23, 11, 3]. As above, but | ||||||
| 		  the third number gives the length of the highlight in bytes. | 		  the third number gives the length of the highlight in bytes. | ||||||
|  |  | ||||||
|  | 		Entries with zero and negative line numbers are silently  | ||||||
|  | 		ignored, as well as entries with negative column numbers and  | ||||||
|  | 		lengths. | ||||||
|  |  | ||||||
| 		The maximum number of positions is 8. | 		The maximum number of positions is 8. | ||||||
|  |  | ||||||
| 		Example: > | 		Example: > | ||||||
|   | |||||||
| @@ -12440,56 +12440,56 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) | |||||||
|  |  | ||||||
| static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) | static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) | ||||||
| { | { | ||||||
|     rettv->vval.v_number = -1; |   rettv->vval.v_number = -1; | ||||||
|  |  | ||||||
|     char buf[NUMBUFLEN]; |   char buf[NUMBUFLEN]; | ||||||
|     const char *const group = tv_get_string_buf_chk(&argvars[0], buf); |   const char *const group = tv_get_string_buf_chk(&argvars[0], buf); | ||||||
|     if (group == NULL) { |   if (group == NULL) { | ||||||
|         return; |     return; | ||||||
|     } |   } | ||||||
|  |  | ||||||
|     if (argvars[1].v_type != VAR_LIST) { |   if (argvars[1].v_type != VAR_LIST) { | ||||||
|         EMSG2(_(e_listarg), "matchaddpos()"); |     EMSG2(_(e_listarg), "matchaddpos()"); | ||||||
|         return; |     return; | ||||||
|     } |   } | ||||||
|  |  | ||||||
|     list_T *l; |   list_T *l; | ||||||
|     l = argvars[1].vval.v_list; |   l = argvars[1].vval.v_list; | ||||||
|     if (l == NULL) { |   if (l == NULL) { | ||||||
|         return; |     return; | ||||||
|     } |   } | ||||||
|  |  | ||||||
|     bool error = false; |   bool error = false; | ||||||
|     int prio = 10; |   int prio = 10; | ||||||
|     int id = -1; |   int id = -1; | ||||||
|     const char *conceal_char = NULL; |   const char *conceal_char = NULL; | ||||||
|  |  | ||||||
|     if (argvars[2].v_type != VAR_UNKNOWN) { |   if (argvars[2].v_type != VAR_UNKNOWN) { | ||||||
|       prio = tv_get_number_chk(&argvars[2], &error); |     prio = tv_get_number_chk(&argvars[2], &error); | ||||||
|       if (argvars[3].v_type != VAR_UNKNOWN) { |     if (argvars[3].v_type != VAR_UNKNOWN) { | ||||||
|         id = tv_get_number_chk(&argvars[3], &error); |       id = tv_get_number_chk(&argvars[3], &error); | ||||||
|         if (argvars[4].v_type != VAR_UNKNOWN) { |       if (argvars[4].v_type != VAR_UNKNOWN) { | ||||||
|           if (argvars[4].v_type != VAR_DICT) { |         if (argvars[4].v_type != VAR_DICT) { | ||||||
|             EMSG(_(e_dictreq)); |           EMSG(_(e_dictreq)); | ||||||
|             return; |           return; | ||||||
|           } |         } | ||||||
|           dictitem_T *di; |         dictitem_T *di; | ||||||
|           if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) |         if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) | ||||||
|               != NULL) { |             != NULL) { | ||||||
|             conceal_char = tv_get_string(&di->di_tv); |           conceal_char = tv_get_string(&di->di_tv); | ||||||
|           } |  | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     if (error == true) { |   } | ||||||
|         return; |   if (error == true) { | ||||||
|     } |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|     // id == 3 is ok because matchaddpos() is supposed to substitute :3match  |   // id == 3 is ok because matchaddpos() is supposed to substitute :3match  | ||||||
|     if (id == 1 || id == 2) { |   if (id == 1 || id == 2) { | ||||||
|         EMSGN(_("E798: ID is reserved for \"match\": %" PRId64), id); |     EMSGN(_("E798: ID is reserved for \"match\": %" PRId64), id); | ||||||
|         return; |     return; | ||||||
|     } |   } | ||||||
|  |  | ||||||
|   rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l, |   rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l, | ||||||
|                                    conceal_char); |                                    conceal_char); | ||||||
|   | |||||||
| @@ -5619,19 +5619,17 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, | |||||||
|  |  | ||||||
|       if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) { |       if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) { | ||||||
|         const list_T *const subl = TV_LIST_ITEM_TV(li)->vval.v_list; |         const list_T *const subl = TV_LIST_ITEM_TV(li)->vval.v_list; | ||||||
|         if (subl == NULL) { |  | ||||||
|           goto fail; |  | ||||||
|         } |  | ||||||
|         const listitem_T *subli = tv_list_first(subl); |         const listitem_T *subli = tv_list_first(subl); | ||||||
|         if (subli == NULL) { |         if (subli == NULL) { | ||||||
|  |           emsgf(_("E5030: Empty list at position %d"), | ||||||
|  |                 (int)tv_list_idx_of_item(pos_list, li)); | ||||||
|           goto fail; |           goto fail; | ||||||
|         } |         } | ||||||
|         lnum = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); |         lnum = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); | ||||||
|         if (error) { |         if (error) { | ||||||
|           goto fail; |           goto fail; | ||||||
|         } |         } | ||||||
|         if (lnum == 0) { |         if (lnum <= 0) { | ||||||
|           --i; |  | ||||||
|           continue; |           continue; | ||||||
|         } |         } | ||||||
|         m->pos.pos[i].lnum = lnum; |         m->pos.pos[i].lnum = lnum; | ||||||
| @@ -5641,9 +5639,15 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, | |||||||
|           if (error) { |           if (error) { | ||||||
|             goto fail; |             goto fail; | ||||||
|           } |           } | ||||||
|  |           if (col < 0) { | ||||||
|  |             continue; | ||||||
|  |           } | ||||||
|           subli = TV_LIST_ITEM_NEXT(subl, subli); |           subli = TV_LIST_ITEM_NEXT(subl, subli); | ||||||
|           if (subli != NULL) { |           if (subli != NULL) { | ||||||
|             len = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); |             len = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); | ||||||
|  |             if (len < 0) { | ||||||
|  |               continue; | ||||||
|  |             } | ||||||
|             if (error) { |             if (error) { | ||||||
|               goto fail; |               goto fail; | ||||||
|             } |             } | ||||||
| @@ -5652,15 +5656,15 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, | |||||||
|         m->pos.pos[i].col = col; |         m->pos.pos[i].col = col; | ||||||
|         m->pos.pos[i].len = len; |         m->pos.pos[i].len = len; | ||||||
|       } else if (TV_LIST_ITEM_TV(li)->v_type == VAR_NUMBER) { |       } else if (TV_LIST_ITEM_TV(li)->v_type == VAR_NUMBER) { | ||||||
|         if (TV_LIST_ITEM_TV(li)->vval.v_number == 0) { |         if (TV_LIST_ITEM_TV(li)->vval.v_number <= 0) { | ||||||
|           i--; |  | ||||||
|           continue; |           continue; | ||||||
|         } |         } | ||||||
|         m->pos.pos[i].lnum = TV_LIST_ITEM_TV(li)->vval.v_number; |         m->pos.pos[i].lnum = TV_LIST_ITEM_TV(li)->vval.v_number; | ||||||
|         m->pos.pos[i].col = 0; |         m->pos.pos[i].col = 0; | ||||||
|         m->pos.pos[i].len = 0; |         m->pos.pos[i].len = 0; | ||||||
|       } else { |       } else { | ||||||
|         EMSG(_("List or number required")); |         emsgf(_("E5031: List or number required at position %d"), | ||||||
|  |               (int)tv_list_idx_of_item(pos_list, li)); | ||||||
|         goto fail; |         goto fail; | ||||||
|       } |       } | ||||||
|       if (toplnum == 0 || lnum < toplnum) { |       if (toplnum == 0 || lnum < toplnum) { | ||||||
|   | |||||||
| @@ -1,9 +1,12 @@ | |||||||
| local helpers = require('test.functional.helpers')(after_each) | local helpers = require('test.functional.helpers')(after_each) | ||||||
|  | local Screen = require('test.functional.ui.screen') | ||||||
|  |  | ||||||
| local eq = helpers.eq | local eq = helpers.eq | ||||||
| local clear = helpers.clear | local clear = helpers.clear | ||||||
| local funcs = helpers.funcs | local funcs = helpers.funcs | ||||||
|  | local meths = helpers.meths | ||||||
| local command = helpers.command | local command = helpers.command | ||||||
|  | local exc_exec = helpers.exc_exec | ||||||
|  |  | ||||||
| before_each(clear) | before_each(clear) | ||||||
|  |  | ||||||
| @@ -59,3 +62,95 @@ describe('matchadd()', function() | |||||||
|     }}, funcs.getmatches()) |     }}, funcs.getmatches()) | ||||||
|   end) |   end) | ||||||
| end) | end) | ||||||
|  |  | ||||||
|  | describe('matchaddpos()', function() | ||||||
|  |   it('errors out on invalid input', function() | ||||||
|  |     command('hi clear PreProc') | ||||||
|  |     eq('Vim(let):E5030: Empty list at position 0', | ||||||
|  |        exc_exec('let val = matchaddpos("PreProc", [[]])')) | ||||||
|  |     eq('Vim(let):E5030: Empty list at position 1', | ||||||
|  |        exc_exec('let val = matchaddpos("PreProc", [1, v:_null_list])')) | ||||||
|  |     eq('Vim(let):E5031: List or number required at position 1', | ||||||
|  |        exc_exec('let val = matchaddpos("PreProc", [1, v:_null_dict])')) | ||||||
|  |   end) | ||||||
|  |   it('works with 0 lnum', function() | ||||||
|  |     command('hi clear PreProc') | ||||||
|  |     eq(4, funcs.matchaddpos('PreProc', {1}, 3, 4)) | ||||||
|  |     eq({{ | ||||||
|  |       group='PreProc', | ||||||
|  |       pos1 = {1}, | ||||||
|  |       priority=3, | ||||||
|  |       id=4, | ||||||
|  |     }}, funcs.getmatches()) | ||||||
|  |     funcs.matchdelete(4) | ||||||
|  |     eq(4, funcs.matchaddpos('PreProc', {{0}, 1}, 3, 4)) | ||||||
|  |     eq({{ | ||||||
|  |       group='PreProc', | ||||||
|  |       pos1 = {1}, | ||||||
|  |       priority=3, | ||||||
|  |       id=4, | ||||||
|  |     }}, funcs.getmatches()) | ||||||
|  |     funcs.matchdelete(4) | ||||||
|  |     eq(4, funcs.matchaddpos('PreProc', {0, 1}, 3, 4)) | ||||||
|  |     eq({{ | ||||||
|  |       group='PreProc', | ||||||
|  |       pos1 = {1}, | ||||||
|  |       priority=3, | ||||||
|  |       id=4, | ||||||
|  |     }}, funcs.getmatches()) | ||||||
|  |   end) | ||||||
|  |   it('works with negative numbers', function() | ||||||
|  |     command('hi clear PreProc') | ||||||
|  |     eq(4, funcs.matchaddpos('PreProc', {-10, 1}, 3, 4)) | ||||||
|  |     eq({{ | ||||||
|  |       group='PreProc', | ||||||
|  |       pos1 = {1}, | ||||||
|  |       priority=3, | ||||||
|  |       id=4, | ||||||
|  |     }}, funcs.getmatches()) | ||||||
|  |     funcs.matchdelete(4) | ||||||
|  |     eq(4, funcs.matchaddpos('PreProc', {{-10}, 1}, 3, 4)) | ||||||
|  |     eq({{ | ||||||
|  |       group='PreProc', | ||||||
|  |       pos1 = {1}, | ||||||
|  |       priority=3, | ||||||
|  |       id=4, | ||||||
|  |     }}, funcs.getmatches()) | ||||||
|  |     funcs.matchdelete(4) | ||||||
|  |     eq(4, funcs.matchaddpos('PreProc', {{2, -1}, 1}, 3, 4)) | ||||||
|  |     eq({{ | ||||||
|  |       group='PreProc', | ||||||
|  |       pos1 = {1}, | ||||||
|  |       priority=3, | ||||||
|  |       id=4, | ||||||
|  |     }}, funcs.getmatches()) | ||||||
|  |     funcs.matchdelete(4) | ||||||
|  |     eq(4, funcs.matchaddpos('PreProc', {{2, 0, -1}, 1}, 3, 4)) | ||||||
|  |     eq({{ | ||||||
|  |       group='PreProc', | ||||||
|  |       pos1 = {1}, | ||||||
|  |       priority=3, | ||||||
|  |       id=4, | ||||||
|  |     }}, funcs.getmatches()) | ||||||
|  |   end) | ||||||
|  |   it('works with zero length', function() | ||||||
|  |     local screen = Screen.new(40, 5) | ||||||
|  |     screen:attach() | ||||||
|  |     funcs.setline(1, 'abcdef') | ||||||
|  |     command('hi PreProc guifg=Red') | ||||||
|  |     eq(4, funcs.matchaddpos('PreProc', {{1, 2, 0}}, 3, 4)) | ||||||
|  |     eq({{ | ||||||
|  |       group='PreProc', | ||||||
|  |       pos1 = {1, 2, 0}, | ||||||
|  |       priority=3, | ||||||
|  |       id=4, | ||||||
|  |     }}, funcs.getmatches()) | ||||||
|  |     screen:expect([[ | ||||||
|  |       ^a{1:b}cdef                                  | | ||||||
|  |       {2:~                                       }| | ||||||
|  |       {2:~                                       }| | ||||||
|  |       {2:~                                       }| | ||||||
|  |                                               | | ||||||
|  |     ]], {[1] = {foreground = Screen.colors.Red}, [2] = {bold = true, foreground = Screen.colors.Blue1}}) | ||||||
|  |   end) | ||||||
|  | end) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 ZyX
					ZyX