Patch 8.2.3578
Problem:    Manipulating highlighting is complicated.
Solution:   Add the hlget() and hlset() functions. (Yegappan Lakshmanan,
            closes #9039)
Files:      runtime/doc/eval.txt, runtime/doc/syntax.txt,
            runtime/doc/usr_41.txt, runtime/doc/windows.txt, src/evalfunc.c,
            src/highlight.c, src/proto/highlight.pro,
            src/testdir/test_highlight.vim, src/testdir/test_vim9_builtin.vim


*** ../vim-8.2.3577/runtime/doc/eval.txt        2021-11-03 13:15:36.240758235 
+0000
--- runtime/doc/eval.txt        2021-11-03 21:44:06.604767942 +0000
***************
*** 2716,2721 ****
--- 2733,2740 ----
  histnr({history})             Number  highest index of a history
  hlID({name})                  Number  syntax ID of highlight group {name}
  hlexists({name})              Number  |TRUE| if highlight group {name} exists
+ hlget([{name} [, {resolve}]]) List    get highlight group attributes
+ hlset({list})                 Number  set highlight group attributes
  hostname()                    String  name of the machine Vim is running on
  iconv({expr}, {from}, {to})   String  convert encoding of {expr}
  indent({lnum})                        Number  indent of line {lnum}
***************
*** 6670,6675 ****
--- 6737,6829 ----
                Can also be used as a |method|: >
                        GetName()->hlexists()
  <
+ hlget([{name} [, {resolve}]])                         *hlget()*
+               Returns a List of all the highlight group attributes.  If the
+               optional {name} is specified, then returns a List with only
+               the attributes of the specified highlight group.  Returns an
+               empty List if the highlight group {name} is not present.
+ 
+               If the optional {resolve} argument is set to v:true and the
+               highlight group {name} is linked to another group, then the
+               link is resolved recursively and the attributes of the
+               resolved highlight group are returned.
+ 
+               Each entry in the returned List is a Dictionary with the
+               following items:
+                       cleared Boolean flag, set to v:true if the highlight
+                               group attributes are cleared or not yet
+                               specified.  See |highlight-clear|.
+                       cterm   cterm attributes. See |highlight-cterm|.
+                       ctermbg cterm background color.
+                               See |highlight-ctermbg|.
+                       ctermfg cterm foreground color.
+                               See |highlight-ctermfg|.
+                       ctermul cterm underline color.  See |highlight-ctermul|.
+                       font    highlight group font.  See |highlight-font|.
+                       gui     gui attributes. See |highlight-gui|.
+                       guibg   gui background color.  See |highlight-guibg|.
+                       guifg   gui foreground color.  See |highlight-guifg|.
+                       guisp   gui special color.  See |highlight-guisp|.
+                       id      highlight group ID.
+                       linksto linked highlight group name.
+                               See |:highlight-link|.
+                       name    highlight group name. See |group-name|.
+                       start   start terminal keycode.  See |highlight-start|.
+                       stop    stop terminal keycode.  See |highlight-stop|.
+                       term    term attributes.  See |highlight-term|.
+ 
+               The 'term', 'cterm' and 'gui' items in the above Dictionary
+               have a dictionary value with the following optional boolean
+               items: 'bold', 'standout', 'underline', 'undercurl', 'italic',
+               'reverse', 'inverse' and 'strikethrough'.
+ 
+               Example(s): >
+                       :echo hlget()
+                       :echo hlget('ModeMsg')
+                       :echo hlget('Number', v:true)
+ <
+               Can also be used as a |method|: >
+                       GetName()->hlget()
+ <
+ hlset({list})                                         *hlset()*
+               Creates or modifies the attributes of a List of highlight
+               groups.  Each item in {list} is a dictionary containing the
+               attributes of a highlight group. See |hlget()| for the list of
+               supported items in this dictionary.
+ 
+               The highlight group is identified using the 'name' item and
+               the 'id' item (if supplied) is ignored.  If a highlight group
+               with a specified name doesn't exist, then it is created.
+               Otherwise the attributes of an existing highlight group are
+               modified.
+ 
+               If an empty dictionary value is used for the 'term' or 'cterm'
+               or 'gui' entries, then the corresponding attributes are
+               cleared.  If the 'cleared' item is set to v:true, then all the
+               attributes of the highlight group are cleared.
+ 
+               The 'linksto' item can be used to link a highlight group to
+               another highlight group.  See |:highlight-link|.
+ 
+               Returns zero for success, -1 for failure.
+ 
+               Example(s): >
+                       " add bold attribute to the Visual highlight group
+                       :call hlset([#{name: 'Visual',
+                                       \ term: #{reverse: 1 , bold: 1}}])
+                       :call hlset([#{name: 'Type', guifg: 'DarkGreen'}])
+                       :let l = hlget()
+                       :call hlset(l)
+                       " clear the Search highlight group
+                       :call hlset([#{name: 'Search', cleared: v:true}])
+                       " clear the 'term' attributes for a highlight group
+                       :call hlset([#{name: 'Title', term: {}}])
+                       " create the MyHlg group linking it to DiffAdd
+                       :call hlset([#{name: 'MyHlg', linksto: 'DiffAdd'}])
+ <
+               Can also be used as a |method|: >
+                       GetAttrList()->hlset()
+ <
                                                        *hlID()*
  hlID({name})  The result is a Number, which is the ID of the highlight group
                with name {name}.  When the highlight group doesn't exist,
*** ../vim-8.2.3577/runtime/doc/syntax.txt      2021-10-24 20:34:01.430895189 
+0100
--- runtime/doc/syntax.txt      2021-11-03 21:44:06.604767942 +0000
***************
*** 4824,4829 ****
--- 4865,4871 ----
  :hi[ghlight] {group-name}
                        List one highlight group.
  
+                                               *highlight-clear*
  :hi[ghlight] clear    Reset all highlighting to the defaults.  Removes all
                        highlighting for groups added by the user!
                        Uses the current value of 'background' to decide which
*** ../vim-8.2.3577/runtime/doc/usr_41.txt      2021-09-14 16:53:39.316540671 
+0100
--- runtime/doc/usr_41.txt      2021-11-03 21:44:06.604767942 +0000
***************
*** 917,922 ****
--- 925,932 ----
        getmatches()            get all matches defined by |matchadd()| and
                                the |:match| commands
        hlexists()              check if a highlight group exists
+       hlget()                 get highlight group attributes
+       hlset()                 set highlight group attributes
        hlID()                  get ID of a highlight group
        synID()                 get syntax ID at a specific position
        synIDattr()             get a specific attribute of a syntax ID
*** ../vim-8.2.3577/runtime/doc/windows.txt     2021-02-13 17:24:19.326118995 
+0000
--- runtime/doc/windows.txt     2021-11-03 21:44:06.608767997 +0000
***************
*** 1355,1360 ****
--- 1358,1364 ----
  <             The buffer name is the name of the directory and is adjusted
                when using the |:cd| command.
  
+                                               *scratch-buffer*
  scratch               Contains text that can be discarded at any time.  It is 
kept
                when closing the window, it must be deleted explicitly.
                Settings: >
*** ../vim-8.2.3577/src/evalfunc.c      2021-11-03 13:15:36.240758235 +0000
--- src/evalfunc.c      2021-11-03 21:44:06.608767997 +0000
***************
*** 1527,1532 ****
--- 1527,1536 ----
                        ret_number,         f_hlID},
      {"hlexists",      1, 1, FEARG_1,      arg1_string,
                        ret_number_bool,    f_hlexists},
+     {"hlget",         0, 2, FEARG_1,      arg2_string_bool,
+                       ret_list_dict_any,  f_hlget},
+     {"hlset",         1, 1, FEARG_1,      arg1_list_any,
+                       ret_number_bool,    f_hlset},
      {"hostname",      0, 0, 0,            NULL,
                        ret_string,         f_hostname},
      {"iconv",         3, 3, FEARG_1,      arg3_string,
*** ../vim-8.2.3577/src/highlight.c     2021-10-25 10:30:10.052617832 +0100
--- src/highlight.c     2021-11-03 21:44:06.608767997 +0000
***************
*** 4081,4083 ****
--- 4081,4537 ----
  # endif
  }
  #endif
+ 
+ #if defined(FEAT_EVAL) || defined(PROTO)
+ /*
+  * Convert each of the highlight attribute bits (bold, standout, underline,
+  * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
+  * the attribute name as the key.
+  */
+     static dict_T *
+ highlight_get_attr_dict(int hlattr)
+ {
+     dict_T    *dict;
+     int               i;
+ 
+     dict = dict_alloc();
+     if (dict == NULL)
+       return NULL;
+ 
+     for (i = 0; hl_attr_table[i] != 0; ++i)
+     {
+       if (hlattr & hl_attr_table[i])
+       {
+           dict_add_bool(dict, hl_name_table[i], VVAL_TRUE);
+           hlattr &= ~hl_attr_table[i];        // don't want "inverse"
+       }
+     }
+ 
+     return dict;
+ }
+ 
+ /*
+  * Return the attributes of the highlight group at index 'hl_idx' as a
+  * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
+  * links recursively.
+  */
+     static dict_T *
+ highlight_get_info(int hl_idx, int resolve_link)
+ {
+     dict_T    *dict;
+     hl_group_T        *sgp;
+     dict_T    *attr_dict;
+     int               hlgid;
+ 
+     dict = dict_alloc();
+     if (dict == NULL)
+       return dict;
+ 
+     sgp = &HL_TABLE()[hl_idx];
+     // highlight group id is 1-based
+     hlgid = hl_idx + 1;
+ 
+     if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
+       goto error;
+     if (dict_add_number(dict, "id", hlgid) == FAIL)
+       goto error;
+ 
+     if (sgp->sg_link && resolve_link)
+     {
+       // resolve the highlight group link recursively
+       while (sgp->sg_link)
+       {
+           hlgid = sgp->sg_link;
+           sgp = &HL_TABLE()[sgp->sg_link - 1];
+       }
+     }
+ 
+     if (sgp->sg_term != 0)
+     {
+       attr_dict = highlight_get_attr_dict(sgp->sg_term);
+       if (attr_dict != NULL)
+           if (dict_add_dict(dict, "term", attr_dict) == FAIL)
+               goto error;
+     }
+     if (sgp->sg_start != NULL)
+       if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
+           goto error;
+     if (sgp->sg_stop != NULL)
+       if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
+           goto error;
+     if (sgp->sg_cterm != 0)
+     {
+       attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
+       if (attr_dict != NULL)
+           if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
+               goto error;
+     }
+     if (sgp->sg_cterm_fg != 0)
+       if (dict_add_string(dict, "ctermfg",
+                   highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
+           goto error;
+     if (sgp->sg_cterm_bg != 0)
+       if (dict_add_string(dict, "ctermbg",
+                       highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
+           goto error;
+     if (sgp->sg_cterm_ul != 0)
+       if (dict_add_string(dict, "ctermul",
+                       highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
+           goto error;
+     if (sgp->sg_gui != 0)
+     {
+       attr_dict = highlight_get_attr_dict(sgp->sg_gui);
+       if (attr_dict != NULL)
+           if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
+               goto error;
+     }
+     if (sgp->sg_gui_fg_name != NULL)
+       if (dict_add_string(dict, "guifg",
+                       highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
+           goto error;
+     if (sgp->sg_gui_bg_name != NULL)
+       if (dict_add_string(dict, "guibg",
+                       highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
+           goto error;
+     if (sgp->sg_gui_sp_name != NULL)
+       if (dict_add_string(dict, "guisp",
+                       highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
+           goto error;
+ # ifdef FEAT_GUI
+     if (sgp->sg_font_name != NULL)
+       if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
+           goto error;
+ # endif
+     if (sgp->sg_link)
+     {
+       char_u  *link;
+ 
+       link = HL_TABLE()[sgp->sg_link - 1].sg_name;
+       if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
+           goto error;
+     }
+     if (dict_len(dict) == 2)
+       // If only 'name' is present, then the highlight group is cleared.
+       dict_add_bool(dict, "cleared", VVAL_TRUE);
+ 
+     return dict;
+ 
+ error:
+     vim_free(dict);
+     return NULL;
+ }
+ 
+ /*
+  * "hlget([name])" function
+  * Return the attributes of a specific highlight group (if specified) or all
+  * the highlight groups.
+  */
+     void
+ f_hlget(typval_T *argvars, typval_T *rettv)
+ {
+     list_T    *list;
+     dict_T    *dict;
+     int               i;
+     char_u    *hlarg = NULL;
+     int               resolve_link = FALSE;
+ 
+     if (rettv_list_alloc(rettv) == FAIL)
+       return;
+ 
+     if (check_for_opt_string_arg(argvars, 0) == FAIL
+           || (argvars[0].v_type != VAR_UNKNOWN
+               && check_for_opt_bool_arg(argvars, 1) == FAIL))
+       return;
+ 
+     if (argvars[0].v_type != VAR_UNKNOWN)
+     {
+       // highlight group name supplied
+       hlarg = tv_get_string_chk(&argvars[0]);
+       if (hlarg == NULL)
+           return;
+ 
+       if (argvars[1].v_type != VAR_UNKNOWN)
+       {
+           int error = FALSE;
+ 
+           resolve_link = tv_get_bool_chk(&argvars[1], &error);
+           if (error)
+               return;
+       }
+     }
+ 
+     list = rettv->vval.v_list;
+     for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
+     {
+       if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
+       {
+           dict = highlight_get_info(i, resolve_link);
+           if (dict != NULL)
+               list_append_dict(list, dict);
+       }
+     }
+ }
+ 
+ /*
+  * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
+  * 'dict' or the value is not a string type. If the value is not a string type
+  * or is NULL, then 'error' is set to TRUE.
+  */
+     static char_u *
+ hldict_get_string(dict_T *dict, char_u *key, int *error)
+ {
+     dictitem_T        *di;
+ 
+     *error = FALSE;
+     di = dict_find(dict, key, -1);
+     if (di == NULL)
+       return NULL;
+ 
+     if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
+     {
+       emsg(_(e_stringreq));
+       *error = TRUE;
+       return NULL;
+     }
+ 
+     return di->di_tv.vval.v_string;
+ }
+ 
+ /*
+  * Convert the highlight attribute Dictionary at 'dict[key]' into a string
+  * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
+  * Dictionary or is NULL.
+  */
+     static int
+ hldict_attr_to_str(
+       dict_T  *dict,
+       char_u  *key,
+       char_u  *attr_str,
+       int     len)
+ {
+     dictitem_T        *di;
+     dict_T    *attrdict;
+     int               i;
+ 
+     attr_str[0] = NUL;
+     di = dict_find(dict, key, -1);
+     if (di == NULL)
+       return TRUE;
+ 
+     if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
+     {
+       emsg(_(e_dictreq));
+       return FALSE;
+     }
+ 
+     attrdict = di->di_tv.vval.v_dict;
+ 
+     // If the attribute dict is empty, then return NONE to clear the 
attributes
+     if (dict_len(attrdict) == 0)
+     {
+       vim_strcat(attr_str, (char_u *)"NONE", len);
+       return TRUE;
+     }
+ 
+     for (i = 0; i < (int)ARRAY_LENGTH(hl_name_table); i++)
+     {
+       if (dict_get_bool(attrdict, (char_u *)hl_name_table[i],
+                   VVAL_FALSE) == VVAL_TRUE)
+       {
+           if (attr_str[0] != NUL)
+               vim_strcat(attr_str, (char_u *)",", len);
+           vim_strcat(attr_str, (char_u *)hl_name_table[i], len);
+       }
+     }
+ 
+     return TRUE;
+ }
+ 
+ /*
+  * Add or update a highlight group using 'dict' items. Returns TRUE if
+  * successfully updated the highlight group.
+  */
+     static int
+ hlg_add_or_update(dict_T *dict)
+ {
+     char_u    *name;
+     int               error;
+     char_u    term_attr[80];
+     char_u    cterm_attr[80];
+     char_u    gui_attr[80];
+     char_u    *start;
+     char_u    *stop;
+     char_u    *ctermfg;
+     char_u    *ctermbg;
+     char_u    *ctermul;
+     char_u    *guifg;
+     char_u    *guibg;
+     char_u    *guisp;
+ # ifdef FEAT_GUI
+     char_u    *font;
+ # endif
+ 
+     name = hldict_get_string(dict, (char_u *)"name", &error);
+     if (name == NULL || error)
+       return FALSE;
+ 
+     if (dict_find(dict, (char_u *)"linksto", -1) != NULL)
+     {
+       char_u  *linksto;
+ 
+       // link highlight groups
+       linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
+       if (linksto == NULL || error)
+           return FALSE;
+ 
+       vim_snprintf((char *)IObuff, IOSIZE, "link %s %s", name, linksto);
+       do_highlight(IObuff, FALSE, FALSE);
+ 
+       return TRUE;
+     }
+ 
+     if (dict_find(dict, (char_u *)"cleared", -1) != NULL)
+     {
+       varnumber_T     cleared;
+ 
+       // clear a highlight group
+       cleared = dict_get_bool(dict, (char_u *)"cleared", FALSE);
+       if (cleared == TRUE)
+       {
+           vim_snprintf((char *)IObuff, IOSIZE, "clear %s", name);
+           do_highlight(IObuff, FALSE, FALSE);
+       }
+ 
+       return TRUE;
+     }
+ 
+     start = hldict_get_string(dict, (char_u *)"start", &error);
+     if (error)
+       return FALSE;
+ 
+     stop = hldict_get_string(dict, (char_u *)"stop", &error);
+     if (error)
+       return FALSE;
+ 
+     if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
+               sizeof(term_attr)))
+       return FALSE;
+ 
+     if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
+               sizeof(cterm_attr)))
+       return FALSE;
+ 
+     ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
+     if (error)
+       return FALSE;
+ 
+     ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
+     if (error)
+       return FALSE;
+ 
+     ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
+     if (error)
+       return FALSE;
+ 
+     if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr,
+               sizeof(gui_attr)))
+       return FALSE;
+ 
+     guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
+     if (error)
+       return FALSE;
+ 
+     guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
+     if (error)
+       return FALSE;
+ 
+     guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
+     if (error)
+       return FALSE;
+ 
+ # ifdef FEAT_GUI
+     font = hldict_get_string(dict, (char_u *)"font", &error);
+     if (error)
+       return FALSE;
+ # endif
+ 
+     // If none of the attributes are specified, then do nothing.
+     if (term_attr[0] == NUL && start == NULL && stop == NULL
+           && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
+           && ctermul == NULL && gui_attr[0] == NUL
+ # ifdef FEAT_GUI
+           && font == NULL
+ # endif
+           && guifg == NULL && guibg == NULL && guisp == NULL
+           )
+       return TRUE;
+ 
+     vim_snprintf((char *)IObuff, IOSIZE,
+           "%s %s%s %s%s %s%s %s%s %s%s %s%s %s%s %s%s %s%s %s%s %s%s %s%s",
+           name,
+           term_attr[0] != NUL ? "term=" : "",
+           term_attr[0] != NUL ? term_attr : (char_u *)"",
+           start != NULL ? "start=" : "",
+           start != NULL ? start : (char_u *)"",
+           stop != NULL ? "stop=" : "",
+           stop != NULL ? stop : (char_u *)"",
+           cterm_attr[0] != NUL ? "cterm=" : "",
+           cterm_attr[0] != NUL ? cterm_attr : (char_u *)"",
+           ctermfg != NULL ? "ctermfg=" : "",
+           ctermfg != NULL ? ctermfg : (char_u *)"",
+           ctermbg != NULL ? "ctermbg=" : "",
+           ctermbg != NULL ? ctermbg : (char_u *)"",
+           ctermul != NULL ? "ctermul=" : "",
+           ctermul != NULL ? ctermul : (char_u *)"",
+           gui_attr[0] != NUL ? "gui=" : "",
+           gui_attr[0] != NUL ? gui_attr : (char_u *)"",
+ # ifdef FEAT_GUI
+           font != NULL ? "font=" : "",
+           font != NULL ? font : (char_u *)"",
+ # else
+           "", "",
+ # endif
+           guifg != NULL ? "guifg=" : "",
+           guifg != NULL ? guifg : (char_u *)"",
+           guibg != NULL ? "guibg=" : "",
+           guibg != NULL ? guibg : (char_u *)"",
+           guisp != NULL ? "guisp=" : "",
+           guisp != NULL ? guisp : (char_u *)""
+               );
+ 
+     do_highlight(IObuff, FALSE, FALSE);
+ 
+     return TRUE;
+ }
+ 
+ /*
+  * "hlset([{highlight_attr}])" function
+  * Add or modify highlight groups
+  */
+     void
+ f_hlset(typval_T *argvars, typval_T *rettv)
+ {
+     listitem_T        *li;
+     dict_T    *dict;
+ 
+     rettv->vval.v_number = -1;
+ 
+     if (check_for_list_arg(argvars, 0) == FAIL)
+       return;
+ 
+     FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
+     {
+       if (li->li_tv.v_type != VAR_DICT)
+       {
+           emsg(_(e_dictreq));
+           return;
+       }
+ 
+       dict = li->li_tv.vval.v_dict;
+       if (!hlg_add_or_update(dict))
+           return;
+     }
+ 
+     rettv->vval.v_number = 0;
+ }
+ #endif
*** ../vim-8.2.3577/src/proto/highlight.pro     2021-10-24 20:34:01.430895189 
+0100
--- src/proto/highlight.pro     2021-11-03 21:46:13.294474127 +0000
***************
*** 49,52 ****
--- 49,54 ----
  char_u *get_highlight_name(expand_T *xp, int idx);
  char_u *get_highlight_name_ext(expand_T *xp, int idx, int skip_cleared);
  void free_highlight_fonts(void);
+ void f_hlget(typval_T *argvars, typval_T *rettv);
+ void f_hlset(typval_T *argvars, typval_T *rettv);
  /* vim: set ft=c : */
*** ../vim-8.2.3577/src/testdir/test_highlight.vim      2021-10-24 
20:34:01.434895238 +0100
--- src/testdir/test_highlight.vim      2021-11-03 21:44:06.608767997 +0000
***************
*** 4,9 ****
--- 4,10 ----
  source screendump.vim
  source check.vim
  source script_util.vim
+ source vim9.vim
  
  func Test_highlight()
    " basic test if ":highlight" doesn't crash
***************
*** 970,973 ****
--- 971,1134 ----
    call assert_fails("echo v:colornames['x1']")
  endfunc
  
+ " Test for the hlget() function
+ func Test_hlget()
+   let lines =<< trim END
+     call assert_notequal([], filter(hlget(), 'v:val.name == "Visual"'))
+     call assert_equal([], hlget('SomeHLGroup'))
+     highlight MyHLGroup term=standout cterm=reverse ctermfg=10 ctermbg=Black
+     call assert_equal([{'id': hlID('MyHLGroup'), 'ctermfg': '10', 'name': 
'MyHLGroup', 'term': {'standout': v:true}, 'ctermbg': '0', 'cterm': {'reverse': 
v:true}}], hlget('MyHLGroup'))
+     highlight clear MyHLGroup
+     call assert_equal(v:true, hlget('MyHLGroup')[0].cleared)
+     highlight link MyHLGroup IncSearch
+     call assert_equal('IncSearch', hlget('MyHLGroup')[0].linksto)
+     highlight clear MyHLGroup
+     call assert_equal([], hlget(test_null_string()))
+     call assert_equal([], hlget(""))
+   END
+   call CheckLegacyAndVim9Success(lines)
+ 
+   " Test for resolving highlight group links
+   let lines =<< trim END
+     highlight hlgA term=bold
+     VAR hlgAid = hlID('hlgA')
+     highlight link hlgB hlgA
+     VAR hlgBid = hlID('hlgB')
+     highlight link hlgC hlgB
+     VAR hlgCid = hlID('hlgC')
+     call assert_equal('hlgA', hlget('hlgB')[0].linksto)
+     call assert_equal('hlgB', hlget('hlgC')[0].linksto)
+     call assert_equal([{'id': hlgAid, 'name': 'hlgA',
+                       \ 'term': {'bold': v:true}}], hlget('hlgA'))
+     call assert_equal([{'id': hlgBid, 'name': 'hlgB',
+                       \ 'linksto': 'hlgA'}], hlget('hlgB'))
+     call assert_equal([{'id': hlgCid, 'name': 'hlgC',
+                       \ 'linksto': 'hlgB'}], hlget('hlgC'))
+     call assert_equal([{'id': hlgAid, 'name': 'hlgA',
+                       \ 'term': {'bold': v:true}}], hlget('hlgA', v:false))
+     call assert_equal([{'id': hlgBid, 'name': 'hlgB',
+                       \ 'linksto': 'hlgA'}], hlget('hlgB', 0))
+     call assert_equal([{'id': hlgCid, 'name': 'hlgC',
+                       \ 'linksto': 'hlgB'}], hlget('hlgC', v:false))
+     call assert_equal([{'id': hlgAid, 'name': 'hlgA',
+                       \ 'term': {'bold': v:true}}], hlget('hlgA', v:true))
+     call assert_equal([{'id': hlgBid, 'name': 'hlgB',
+                       \ 'term': {'bold': v:true}}], hlget('hlgB', 1))
+     call assert_equal([{'id': hlgCid, 'name': 'hlgC',
+                       \ 'term': {'bold': v:true}}], hlget('hlgC', v:true))
+   END
+   call CheckLegacyAndVim9Success(lines)
+ 
+   call assert_fails('call hlget([])', 'E1174:')
+   call assert_fails('call hlget("abc", "xyz")', 'E1212:')
+ endfunc
+ 
+ " Test for the hlset() function
+ func Test_hlset()
+   let lines =<< trim END
+     call assert_equal(0, hlset(test_null_list()))
+     call assert_equal(0, hlset([]))
+     call assert_fails('call hlset(["Search"])', 'E715:')
+     call hlset(hlget())
+     call hlset([{'name': 'NewHLGroup', 'cterm': {'reverse': v:true}, 
'ctermfg': '10'}])
+     call assert_equal({'reverse': v:true}, hlget('NewHLGroup')[0].cterm)
+     call hlset([{'name': 'NewHLGroup', 'cterm': {'bold': v:true}}])
+     call assert_equal({'bold': v:true}, hlget('NewHLGroup')[0].cterm)
+     call hlset([{'name': 'NewHLGroup', 'cleared': v:true}])
+     call assert_equal(v:true, hlget('NewHLGroup')[0].cleared)
+     call hlset([{'name': 'NewHLGroup', 'linksto': 'Search'}])
+     call assert_false(has_key(hlget('NewHLGroup')[0], 'cleared'))
+     call assert_equal('Search', hlget('NewHLGroup')[0].linksto)
+     call assert_fails("call hlset([{'name': [], 'ctermfg': '10'}])", 'E928:')
+     call assert_fails("call hlset([{'name': 'NewHLGroup', 'cleared': []}])",
+           \ 'E745:')
+     call assert_fails("call hlset([{'name': 'NewHLGroup', 'cterm': 'Blue'}])",
+           \ 'E715:')
+     call assert_fails("call hlset([{'name': 'NewHLGroup', 'ctermbg': []}])",
+           \ 'E928:')
+     call assert_fails("call hlset([{'name': 'NewHLGroup', 'ctermfg': []}])",
+           \ 'E928:')
+     call assert_fails("call hlset([{'name': 'NewHLGroup', 'ctermul': []}])",
+           \ 'E928:')
+     if has('gui')
+       call assert_fails("call hlset([{'name': 'NewHLGroup', 'font': []}])",
+             \ 'E928:')
+     endif
+     call assert_fails("call hlset([{'name': 'NewHLGroup', 'gui': 'Cyan'}])",
+           \ 'E715:')
+     call assert_fails("call hlset([{'name': 'NewHLGroup', 'guibg': []}])",
+           \ 'E928:')
+     call assert_fails("call hlset([{'name': 'NewHLGroup', 'guifg': []}])",
+           \ 'E928:')
+     call assert_fails("call hlset([{'name': 'NewHLGroup', 'guisp': []}])",
+           \ 'E928:')
+     call assert_fails("call hlset([{'name': 'NewHLGroup', 'linksto': []}])",
+           \ 'E928:')
+     call assert_fails("call hlset([{'name': 'NewHLGroup', 'start': []}])",
+           \ 'E928:')
+     call assert_fails("call hlset([{'name': 'NewHLGroup', 'stop': []}])",
+           \ 'E928:')
+     call assert_fails("call hlset([{'name': 'NewHLGroup', 'term': 'Cyan'}])",
+           \ 'E715:')
+     call assert_equal('Search', hlget('NewHLGroup')[0].linksto)
+     highlight clear NewHLGroup
+   END
+   call CheckLegacyAndVim9Success(lines)
+ 
+   " Test for clearing the 'term', 'cterm' and 'gui' attributes of a highlight
+   " group.
+   let lines =<< trim END
+     highlight myhlg1 term=bold cterm=italic gui=standout
+     VAR id = hlID('myhlg1')
+     call hlset([{'name': 'myhlg1', 'term': {}}])
+     call assert_equal([{'id': id, 'name': 'myhlg1',
+                 \ 'cterm': {'italic': v:true}, 'gui': {'standout': v:true}}],
+                 \ hlget('myhlg1'))
+     call hlset([{'name': 'myhlg1', 'cterm': {}}])
+     call assert_equal([{'id': id, 'name': 'myhlg1',
+                 \ 'gui': {'standout': v:true}}], hlget('myhlg1'))
+     call hlset([{'name': 'myhlg1', 'gui': {}}])
+     call assert_equal([{'id': id, 'name': 'myhlg1', 'cleared': v:true}],
+                 \ hlget('myhlg1'))
+     highlight clear myhlg1
+   END
+   call CheckLegacyAndVim9Success(lines)
+ 
+   " Test for setting all the 'term', 'cterm' and 'gui' attributes of a
+   " highlight group
+   let lines =<< trim END
+     VAR attr = {'bold': v:true, 'underline': v:true, 'undercurl': v:true,
+                 \ 'strikethrough': v:true, 'reverse': v:true, 'italic': 
v:true,
+                 \ 'standout': v:true, 'nocombine': v:true}
+     call hlset([{'name': 'myhlg2', 'term': attr, 'cterm': attr, 'gui': attr}])
+     VAR id2 = hlID('myhlg2')
+     VAR output =<< trim END
+       myhlg2         xxx 
term=bold,standout,underline,undercurl,italic,reverse,nocombine,strikethrough
+                          
cterm=bold,standout,underline,undercurl,italic,reverse,nocombine,strikethrough
+                          
gui=bold,standout,underline,undercurl,italic,reverse,nocombine,strikethrough
+     END
+     call assert_equal(output, execute('highlight myhlg2')->split("\n"))
+     call assert_equal([{'id': id2, 'name': 'myhlg2', 'gui': attr,
+                       \ 'term': attr, 'cterm': attr}], hlget('myhlg2'))
+   END
+   call CheckLegacyAndVim9Success(lines)
+ 
+   " Test for clearing some of the 'term', 'cterm' and 'gui' attributes of a
+   " highlight group
+   let lines =<< trim END
+     VAR attr = {'bold': v:false, 'underline': v:true, 'strikethrough': v:true}
+     call hlset([{'name': 'myhlg2', 'term': attr, 'cterm': attr, 'gui': attr}])
+     VAR id2 = hlID('myhlg2')
+     VAR output =<< trim END
+       myhlg2         xxx term=underline,strikethrough 
cterm=underline,strikethrough
+                          gui=underline,strikethrough
+     END
+     call assert_equal(output, execute('highlight myhlg2')->split("\n"))
+     LET attr = {'underline': v:true, 'strikethrough': v:true}
+     call assert_equal([{'id': id2, 'name': 'myhlg2', 'gui': attr,
+                       \ 'term': attr, 'cterm': attr}], hlget('myhlg2'))
+   END
+   call CheckLegacyAndVim9Success(lines)
+ endfunc
+ 
  " vim: shiftwidth=2 sts=2 expandtab
*** ../vim-8.2.3577/src/testdir/test_vim9_builtin.vim   2021-09-30 
18:59:55.938522918 +0100
--- src/testdir/test_vim9_builtin.vim   2021-11-03 21:44:06.608767997 +0000
***************
*** 1721,1726 ****
--- 1721,1736 ----
    hlexists('')->assert_equal(0)
  enddef
  
+ def Test_hlget()
+   CheckDefAndScriptFailure2(['hlget([])'], 'E1013: Argument 1: type mismatch, 
expected string but got list<unknown>', 'E1174: String required for argument 1')
+   hlget('')->assert_equal([])
+ enddef
+ 
+ def Test_hlset()
+   CheckDefAndScriptFailure2(['hlset("id")'], 'E1013: Argument 1: type 
mismatch, expected list<any> but got string', 'E1211: List required for 
argument 1')
+   hlset([])->assert_equal(0)
+ enddef
+ 
  def Test_iconv()
    CheckDefAndScriptFailure2(['iconv(1, "from", "to")'], 'E1013: Argument 1: 
type mismatch, expected string but got number', 'E1174: String required for 
argument 1')
    CheckDefAndScriptFailure2(['iconv("abc", 10, "to")'], 'E1013: Argument 2: 
type mismatch, expected string but got number', 'E1174: String required for 
argument 2')

-- 
DINGO:   You must spank her well and after you have spanked her you
         may deal with her as you like and then ... spank me.
AMAZING: And spank me!
STUNNER: And me.
LOVELY:  And me.
                 "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20211103215717.37933C80053%40moolenaar.net.

Raspunde prin e-mail lui