mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +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